-/* i386-nlmstub.c -- NLM debugging stub for the i386.
-
- This is originally based on an m68k software stub written by Glenn
- Engel at HP, but has changed quite a bit. It was modified for the
- i386 by Jim Kingdon, Cygnus Support. It was modified to run under
- NetWare by Ian Lance Taylor, Cygnus Support.
-
- This code is intended to produce an NLM (a NetWare Loadable Module)
- to run under NetWare on an i386 platform. To create the NLM,
- compile this code into an object file using the NLM SDK on any i386
- host, and use the nlmconv program (available in the GNU binutils)
- to transform the resulting object file into an NLM. */
-
-/****************************************************************************
-
- THIS SOFTWARE IS NOT COPYRIGHTED
-
- HP offers the following for use in the public domain. HP makes no
- warranty with regard to the software or it's performance and the
- user accepts the software "AS IS" with all faults.
-
- HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
- TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-****************************************************************************/
-
-/****************************************************************************
- *
- * The following gdb commands are supported:
- *
- * command function Return value
- *
- * g return the value of the CPU registers hex data or ENN
- * G set the value of the CPU registers OK or ENN
- *
- * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
- * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
- *
- * c Resume at current address SNN ( signal NN)
- * cAA..AA Continue at address AA..AA SNN
- *
- * s Step one instruction SNN
- * sAA..AA Step one instruction from AA..AA SNN
- *
- * k kill
- *
- * ? What was the last sigval ? SNN (signal NN)
- *
- * All commands and responses are sent with a packet which includes a
- * checksum. A packet consists of
- *
- * $<packet info>#<checksum>.
- *
- * where
- * <packet info> :: <characters representing the command or response>
- * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
- *
- * When a packet is received, it is first acknowledged with either '+' or '-'.
- * '+' indicates a successful transfer. '-' indicates a failed transfer.
- *
- * Example:
- *
- * Host: Reply:
- * $m0,10#2a +$00010203040506070809101112131415#42
- *
- ****************************************************************************/
-
-#include <dfs.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <time.h>
-#include <aio.h>
-#include <conio.h>
-#include <advanced.h>
-#include <debugapi.h>
-#include <process.h>
-#include <errno.h>
-
-/****************************************************/
-/* This information is from Novell. It is not in any of the standard
- NetWare header files. */
-
-struct DBG_LoadDefinitionStructure
-{
- void *reserved1[4];
- LONG reserved5;
- LONG LDCodeImageOffset;
- LONG LDCodeImageLength;
- LONG LDDataImageOffset;
- LONG LDDataImageLength;
- LONG LDUninitializedDataLength;
- LONG LDCustomDataOffset;
- LONG LDCustomDataSize;
- LONG reserved6[2];
- LONG (*LDInitializationProcedure)(void);
-};
-
-#define LO_NORMAL 0x0000
-#define LO_STARTUP 0x0001
-#define LO_PROTECT 0x0002
-#define LO_DEBUG 0x0004
-#define LO_AUTO_LOAD 0x0008
-
-/* Loader returned error codes */
-#define LOAD_COULD_NOT_FIND_FILE 1
-#define LOAD_ERROR_READING_FILE 2
-#define LOAD_NOT_NLM_FILE_FORMAT 3
-#define LOAD_WRONG_NLM_FILE_VERSION 4
-#define LOAD_REENTRANT_INITIALIZE_FAILURE 5
-#define LOAD_CAN_NOT_LOAD_MULTIPLE_COPIES 6
-#define LOAD_ALREADY_IN_PROGRESS 7
-#define LOAD_NOT_ENOUGH_MEMORY 8
-#define LOAD_INITIALIZE_FAILURE 9
-#define LOAD_INCONSISTENT_FILE_FORMAT 10
-#define LOAD_CAN_NOT_LOAD_AT_STARTUP 11
-#define LOAD_AUTO_LOAD_MODULES_NOT_LOADED 12
-#define LOAD_UNRESOLVED_EXTERNAL 13
-#define LOAD_PUBLIC_ALREADY_DEFINED 14
-/****************************************************/
-
-/* The main thread ID. */
-static int mainthread;
-
-/* An error message for the main thread to print. */
-static char *error_message;
-
-/* The AIO port handle. */
-static int AIOhandle;
-
-/* BUFMAX defines the maximum number of characters in inbound/outbound
- buffers. At least NUMREGBYTES*2 are needed for register packets */
-#define BUFMAX (REGISTER_BYTES * 2 + 16)
-
-/* remote_debug > 0 prints ill-formed commands in valid packets and
- checksum errors. */
-static int remote_debug = 1;
-
-static const char hexchars[] = "0123456789abcdef";
-
-/* Register values. All of these values *MUST* agree with tm.h */
-#define SP_REGNUM 4 /* Contains address of top of stack */
-#define PC_REGNUM 8 /* Contains program counter */
-#define FP_REGNUM 5 /* Virtual frame pointer */
-#define NUM_REGS 16 /* Number of machine registers */
-#define REGISTER_BYTES (NUM_REGS * 4) /* Total size of registers array */
-
-#define ExceptionPC ExceptionEIP
-#define DECR_PC_AFTER_BREAK 1 /* int 3 leaves PC pointing after insn */
-#define BREAKPOINT {0xcc}
-#define StackFrame T_TSS_StackFrame
-
-unsigned char breakpoint_insn[] = BREAKPOINT;
-#define BREAKPOINT_SIZE (sizeof breakpoint_insn)
-
-static void flush_i_cache() {}
-
-static char *mem2hex (void *mem, char *buf, int count, int may_fault);
-static char *hex2mem (char *buf, void *mem, int count, int may_fault);
-static void set_step_traps (struct StackFrame *);
-static void clear_step_traps (struct StackFrame *);
-
-#if 0
-__main() {};
-#endif
-
-/* Read a character from the serial port. This must busy wait, but
- that's OK because we will be the only thread running anyhow. */
-
-static int
-getDebugChar ()
-{
- int err;
- LONG got;
- unsigned char ret;
-
- do
- {
- err = AIOReadData (AIOhandle, (char *) &ret, 1, &got);
- if (err != 0)
- {
- error_message = "AIOReadData failed";
- ResumeThread (mainthread);
- return -1;
- }
- }
- while (got == 0);
-
- return ret;
-}
-
-/* Write a character to the serial port. Returns 0 on failure,
- non-zero on success. */
-
-static int
-putDebugChar (c)
- unsigned char c;
-{
- int err;
- LONG put;
-
- put = 0;
- while (put < 1)
- {
- err = AIOWriteData (AIOhandle, (char *) &c, 1, &put);
- if (err != 0)
- ConsolePrintf ("AIOWriteData: err = %d, put = %d\r\n", err, put);
- }
- return 1;
-}
-
-/* Get the registers out of the frame information. */
-
-static void
-frame_to_registers (frame, regs)
- struct StackFrame *frame;
- char *regs;
-{
- /* Copy EAX -> EDI */
- mem2hex (&frame->ExceptionEAX, ®s[0 * 4 * 2], 4 * 8, 0);
-
- /* Copy EIP & PS */
- mem2hex (&frame->ExceptionPC, ®s[8 * 4 * 2], 4 * 2, 0);
-
- /* Copy CS, SS, DS */
- mem2hex (&frame->ExceptionCS, ®s[10 * 4 * 2], 4 * 3, 0);
-
- /* Copy ES */
- mem2hex (&frame->ExceptionES, ®s[13 * 4 * 2], 4 * 1, 0);
-
- /* Copy FS & GS */
- mem2hex (&frame->ExceptionFS, ®s[14 * 4 * 2], 4 * 2, 0);
-}
-
-/* Put the registers back into the frame information. */
-
-static void
-registers_to_frame (regs, frame)
- char *regs;
- struct StackFrame *frame;
-{
- /* Copy EAX -> EDI */
- hex2mem (®s[0 * 4 * 2], &frame->ExceptionEAX, 4 * 8, 0);
-
- /* Copy EIP & PS */
- hex2mem (®s[8 * 4 * 2], &frame->ExceptionPC, 4 * 2, 0);
-
- /* Copy CS, SS, DS */
- hex2mem (®s[10 * 4 * 2], &frame->ExceptionCS, 4 * 3, 0);
-
- /* Copy ES */
- hex2mem (®s[13 * 4 * 2], &frame->ExceptionES, 4 * 1, 0);
-
- /* Copy FS & GS */
- hex2mem (®s[14 * 4 * 2], &frame->ExceptionFS, 4 * 2, 0);
-}
-
-/* Turn a hex character into a number. */
-
-static int
-hex (ch)
- char ch;
-{
- if ((ch >= 'a') && (ch <= 'f'))
- return (ch-'a'+10);
- if ((ch >= '0') && (ch <= '9'))
- return (ch-'0');
- if ((ch >= 'A') && (ch <= 'F'))
- return (ch-'A'+10);
- return (-1);
-}
-
-/* Scan for the sequence $<data>#<checksum>. Returns 0 on failure,
- non-zero on success. */
-
-static int
-getpacket (buffer)
- char * buffer;
-{
- unsigned char checksum;
- unsigned char xmitcsum;
- int i;
- int count;
- int ch;
-
- do
- {
- /* wait around for the start character, ignore all other characters */
- while ((ch = getDebugChar()) != '$')
- if (ch == -1)
- return 0;
- checksum = 0;
- xmitcsum = -1;
-
- count = 0;
-
- /* now, read until a # or end of buffer is found */
- while (count < BUFMAX)
- {
- ch = getDebugChar();
- if (ch == -1)
- return 0;
- if (ch == '#')
- break;
- checksum = checksum + ch;
- buffer[count] = ch;
- count = count + 1;
- }
- buffer[count] = 0;
-
- if (ch == '#')
- {
- ch = getDebugChar ();
- if (ch == -1)
- return 0;
- xmitcsum = hex(ch) << 4;
- ch = getDebugChar ();
- if (ch == -1)
- return 0;
- xmitcsum += hex(ch);
-
- if (checksum != xmitcsum)
- {
- if (remote_debug)
- ConsolePrintf ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n",
- checksum,xmitcsum,buffer);
- /* failed checksum */
- if (! putDebugChar('-'))
- return 0;
- return 1;
- }
- else
- {
- /* successful transfer */
- if (! putDebugChar('+'))
- return 0;
- /* if a sequence char is present, reply the sequence ID */
- if (buffer[2] == ':')
- {
- if (! putDebugChar (buffer[0])
- || ! putDebugChar (buffer[1]))
- return 0;
- /* remove sequence chars from buffer */
- count = strlen(buffer);
- for (i=3; i <= count; i++)
- buffer[i-3] = buffer[i];
- }
- }
- }
- }
- while (checksum != xmitcsum);
-
- if (remote_debug)
- ConsolePrintf ("Received packet \"%s\"\r\n", buffer);
-
- return 1;
-}
-
-/* Send the packet in buffer. Returns 0 on failure, non-zero on
- success. */
-
-static int
-putpacket (buffer)
- char * buffer;
-{
- unsigned char checksum;
- int count;
- int ch;
-
- if (remote_debug)
- ConsolePrintf ("Sending packet \"%s\"\r\n", buffer);
-
- /* $<packet info>#<checksum>. */
- do
- {
- if (! putDebugChar('$'))
- return 0;
- checksum = 0;
- count = 0;
-
- while (ch=buffer[count])
- {
- if (! putDebugChar(ch))
- return 0;
- checksum += ch;
- count += 1;
- }
-
- if (! putDebugChar('#')
- || ! putDebugChar(hexchars[checksum >> 4])
- || ! putDebugChar(hexchars[checksum % 16]))
- return 0;
-
- ch = getDebugChar ();
- if (ch == -1)
- return 0;
- }
- while (ch != '+');
-
- return 1;
-}
-
-static char remcomInBuffer[BUFMAX];
-static char remcomOutBuffer[BUFMAX];
-static short error;
-
-static void
-debug_error (format, parm)
- char *format;
- char *parm;
-{
- if (remote_debug)
- {
- ConsolePrintf (format, parm);
- ConsolePrintf ("\n");
- }
-}
-
-/* This is set if we could get a memory access fault. */
-static int mem_may_fault;
-
-/* Indicate to caller of mem2hex or hex2mem that there has been an
- error. */
-static volatile int mem_err = 0;
-
-/* These are separate functions so that they are so short and sweet
- that the compiler won't save any registers (if there is a fault
- to mem_fault, they won't get restored, so there better not be any
- saved). */
-
-static int
-get_char (addr)
- char *addr;
-{
- return *addr;
-}
-
-static void
-set_char (addr, val)
- char *addr;
- int val;
-{
- *addr = val;
-}
-
-/* This bit of assembly language just returns from a function. If a
- memory error occurs within get_char or set_char, the debugger
- handler points EIP at these instructions to get out. */
-
-extern void just_return ();
-asm (".globl just_return");
-asm (".globl _just_return");
-asm ("just_return:");
-asm ("_just_return:");
-asm ("leave");
-asm ("ret");
-
-/* convert the memory pointed to by mem into hex, placing result in buf */
-/* return a pointer to the last char put in buf (null) */
-/* If MAY_FAULT is non-zero, then we should set mem_err in response to
- a fault; if zero treat a fault like any other fault in the stub. */
-
-static char *
-mem2hex (mem, buf, count, may_fault)
- void *mem;
- char *buf;
- int count;
- int may_fault;
-{
- int i;
- unsigned char ch;
- char *ptr = mem;
-
- mem_may_fault = may_fault;
- for (i = 0; i < count; i++)
- {
- ch = get_char (ptr++);
- if (may_fault && mem_err)
- return (buf);
- *buf++ = hexchars[ch >> 4];
- *buf++ = hexchars[ch % 16];
- }
- *buf = 0;
- mem_may_fault = 0;
- return(buf);
-}
-
-/* convert the hex array pointed to by buf into binary to be placed in mem */
-/* return a pointer to the character AFTER the last byte written */
-
-static char *
-hex2mem (buf, mem, count, may_fault)
- char *buf;
- void *mem;
- int count;
- int may_fault;
-{
- int i;
- unsigned char ch;
- char *ptr = mem;
-
- mem_may_fault = may_fault;
- for (i=0;i<count;i++)
- {
- ch = hex(*buf++) << 4;
- ch = ch + hex(*buf++);
- set_char (ptr++, ch);
- if (may_fault && mem_err)
- return (ptr);
- }
- mem_may_fault = 0;
- return(mem);
-}
-
-/* This function takes the 386 exception vector and attempts to
- translate this number into a unix compatible signal value. */
-
-static int
-computeSignal (exceptionVector)
- int exceptionVector;
-{
- int sigval;
- switch (exceptionVector)
- {
- case 0 : sigval = 8; break; /* divide by zero */
- case 1 : sigval = 5; break; /* debug exception */
- case 3 : sigval = 5; break; /* breakpoint */
- case 4 : sigval = 16; break; /* into instruction (overflow) */
- case 5 : sigval = 16; break; /* bound instruction */
- case 6 : sigval = 4; break; /* Invalid opcode */
- case 7 : sigval = 8; break; /* coprocessor not available */
- case 8 : sigval = 7; break; /* double fault */
- case 9 : sigval = 11; break; /* coprocessor segment overrun */
- case 10 : sigval = 11; break; /* Invalid TSS */
- case 11 : sigval = 11; break; /* Segment not present */
- case 12 : sigval = 11; break; /* stack exception */
- case 13 : sigval = 11; break; /* general protection */
- case 14 : sigval = 11; break; /* page fault */
- case 16 : sigval = 7; break; /* coprocessor error */
- default:
- sigval = 7; /* "software generated"*/
- }
- return (sigval);
-}
-
-/**********************************************/
-/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */
-/* RETURN NUMBER OF CHARS PROCESSED */
-/**********************************************/
-static int
-hexToInt(ptr, intValue)
- char **ptr;
- int *intValue;
-{
- int numChars = 0;
- int hexValue;
-
- *intValue = 0;
-
- while (**ptr)
- {
- hexValue = hex(**ptr);
- if (hexValue >=0)
- {
- *intValue = (*intValue <<4) | hexValue;
- numChars ++;
- }
- else
- break;
-
- (*ptr)++;
- }
-
- return (numChars);
-}
-
-static void
-set_step_traps (frame)
- struct StackFrame *frame;
-{
- frame->ExceptionSystemFlags |= 0x100;
-}
-
-static void
-clear_step_traps (frame)
- struct StackFrame *frame;
-{
- frame->ExceptionSystemFlags &= ~0x100;
-}
-
-static void
-do_status (ptr, frame)
- char *ptr;
- struct StackFrame *frame;
-{
- int sigval;
-
- sigval = computeSignal (frame->ExceptionNumber);
-
- sprintf (ptr, "T%02x", sigval);
- ptr += 3;
-
- sprintf (ptr, "%02x:", PC_REGNUM);
- ptr = mem2hex (&frame->ExceptionPC, ptr + 3, 4, 0);
- *ptr++ = ';';
-
- sprintf (ptr, "%02x:", SP_REGNUM);
- ptr = mem2hex (&frame->ExceptionESP, ptr + 3, 4, 0);
- *ptr++ = ';';
-
- sprintf (ptr, "%02x:", FP_REGNUM);
- ptr = mem2hex (&frame->ExceptionEBP, ptr + 3, 4, 0);
- *ptr++ = ';';
-
- *ptr = '\000';
-}
-
-/* This function does all command processing for interfacing to gdb.
- It is called whenever an exception occurs in the module being
- debugged. */
-
-static LONG
-handle_exception (frame)
- struct StackFrame *frame;
-{
- int addr, length;
- char *ptr;
- static int thread_killed = 0;
- static int thread_started = 0;
- static struct DBG_LoadDefinitionStructure *ldinfo = 0;
- static unsigned char first_insn[BREAKPOINT_SIZE]; /* The first instruction in the program. */
-
- /* Apparently the bell can sometimes be ringing at this point, and
- should be stopped. */
- StopBell ();
-
- if (remote_debug)
- {
- ConsolePrintf ("vector=%d: %s, sr=%08x, pc=%08x, thread=%08x\r\n",
- frame->ExceptionNumber,
- frame->ExceptionDescription,
- frame->ExceptionSystemFlags,
- frame->ExceptionPC,
- GetThreadID ());
- }
-
- switch (frame->ExceptionNumber)
- {
- case START_NLM_EVENT:
- /* If the NLM just started, we record the module load information
- and the thread ID, and set a breakpoint at the first instruction
- in the program. */
-
- ldinfo = ((struct DBG_LoadDefinitionStructure *)
- frame->ExceptionErrorCode);
- memcpy (first_insn, ldinfo->LDInitializationProcedure,
- BREAKPOINT_SIZE);
- memcpy (ldinfo->LDInitializationProcedure, breakpoint_insn,
- BREAKPOINT_SIZE);
- flush_i_cache ();
- return RETURN_TO_PROGRAM;
-
- case START_THREAD_EVENT:
- thread_started = 1;
- return RETURN_TO_PROGRAM;
-
- case TERMINATE_NLM_EVENT:
- if (!thread_killed)
- {
- /* NetWare processes don't have an exit status so we
- generate our own */
- sprintf (remcomOutBuffer, "W%02x", 0);
- putpacket(remcomOutBuffer);
- }
- ResumeThread (mainthread);
- return RETURN_TO_PROGRAM;
-
- case ENTER_DEBUGGER_EVENT:
- case KEYBOARD_BREAK_EVENT:
- /* Pass some events on to the next debugger, in case it will handle
- them. */
- return RETURN_TO_NEXT_DEBUGGER;
-
- case 3: /* Breakpoint */
- /* After we've reached the initial breakpoint, reset it. */
- if (frame->ExceptionPC - DECR_PC_AFTER_BREAK == (LONG) ldinfo->LDInitializationProcedure
- && memcmp (ldinfo->LDInitializationProcedure, breakpoint_insn,
- BREAKPOINT_SIZE) == 0)
- {
- memcpy (ldinfo->LDInitializationProcedure, first_insn,
- BREAKPOINT_SIZE);
- frame->ExceptionPC -= DECR_PC_AFTER_BREAK;
- flush_i_cache ();
- }
- /* Normal breakpoints end up here */
- do_status (remcomOutBuffer, frame);
- break;
-
- default:
- /* At the moment, we don't care about most of the unusual NetWare
- exceptions. */
- if (frame->ExceptionNumber > 31)
- return RETURN_TO_PROGRAM;
-
- /* Most machine level exceptions end up here */
- do_status (remcomOutBuffer, frame);
- break;
-
- case 11: /* Segment not present */
- case 13: /* General protection */
- case 14: /* Page fault */
- /* If we get a GP fault, and mem_may_fault is set, and the
- instruction pointer is near set_char or get_char, then we caused
- the fault ourselves accessing an illegal memory location. */
- if (mem_may_fault
- && ((frame->ExceptionPC >= (long) &set_char
- && frame->ExceptionPC < (long) &set_char + 50)
- || (frame->ExceptionPC >= (long) &get_char
- && frame->ExceptionPC < (long) &get_char + 50)))
- {
- mem_err = 1;
- /* Point the instruction pointer at an assembly language stub
- which just returns from the function. */
-
- frame->ExceptionPC = (long) &just_return;
-
- /* Keep going. This will act as though it returned from
- set_char or get_char. The calling routine will check
- mem_err, and do the right thing. */
- return RETURN_TO_PROGRAM;
- }
- /* Random mem fault, report it */
- do_status (remcomOutBuffer, frame);
- break;
- }
-
- /* We point the PC at _exit() and continue to kill the NLM, but that
- won't work until it's thread has been started. */
- if (thread_started && thread_killed)
- {
- frame->ExceptionPC = &_exit;
- return RETURN_TO_PROGRAM;
- }
-
- /* FIXME: How do we know that this exception has anything to do with
- the program we are debugging? We can check whether the PC is in
- the range of the module we are debugging, but that doesn't help
- much since an error could occur in a library routine. */
-
- clear_step_traps (frame);
-
- if (! putpacket(remcomOutBuffer))
- return RETURN_TO_NEXT_DEBUGGER;
-
- while (1)
- {
- error = 0;
- remcomOutBuffer[0] = 0;
- if (! getpacket (remcomInBuffer))
- return RETURN_TO_NEXT_DEBUGGER;
- switch (remcomInBuffer[0])
- {
- case '?':
- do_status (remcomOutBuffer, frame);
- break;
- case 'd':
- remote_debug = !(remote_debug); /* toggle debug flag */
- break;
- case 'g':
- /* return the value of the CPU registers */
- frame_to_registers (frame, remcomOutBuffer);
- break;
- case 'G':
- /* set the value of the CPU registers - return OK */
- registers_to_frame (&remcomInBuffer[1], frame);
- strcpy(remcomOutBuffer,"OK");
- break;
-
- case 'm':
- /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
- /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
- ptr = &remcomInBuffer[1];
- if (hexToInt(&ptr,&addr))
- if (*(ptr++) == ',')
- if (hexToInt(&ptr,&length))
- {
- ptr = 0;
- mem_err = 0;
- mem2hex((char*) addr, remcomOutBuffer, length, 1);
- if (mem_err)
- {
- strcpy (remcomOutBuffer, "E03");
- debug_error ("memory fault");
- }
- }
-
- if (ptr)
- {
- strcpy(remcomOutBuffer,"E01");
- debug_error("malformed read memory command: %s",remcomInBuffer);
- }
- break;
-
- case 'M':
- /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
- /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
- ptr = &remcomInBuffer[1];
- if (hexToInt(&ptr,&addr))
- if (*(ptr++) == ',')
- if (hexToInt(&ptr,&length))
- if (*(ptr++) == ':')
- {
- mem_err = 0;
- hex2mem(ptr, (char*) addr, length, 1);
-
- if (mem_err)
- {
- strcpy (remcomOutBuffer, "E03");
- debug_error ("memory fault");
- }
- else
- {
- strcpy(remcomOutBuffer,"OK");
- }
-
- ptr = 0;
- }
- if (ptr)
- {
- strcpy(remcomOutBuffer,"E02");
- debug_error("malformed write memory command: %s",remcomInBuffer);
- }
- break;
-
- case 'c':
- case 's':
- /* cAA..AA Continue at address AA..AA(optional) */
- /* sAA..AA Step one instruction from AA..AA(optional) */
- /* try to read optional parameter, pc unchanged if no parm */
- ptr = &remcomInBuffer[1];
- if (hexToInt(&ptr,&addr))
- {
-/* registers[PC_REGNUM].lo = addr;*/
- ConsolePrintf("Setting PC to 0x%x\n", addr);
- while (1);
- }
-
- if (remcomInBuffer[0] == 's')
- set_step_traps (frame);
-
- flush_i_cache ();
- return RETURN_TO_PROGRAM;
-
- case 'k':
- /* The undocumented netware call KillMe() is supposed to
- schedule the NLM to be killed when it next blocks. What
- really happens is that the server hangs as it tries to
- unload the NLM.
-
- So, since netware won't cooperate, we just point the PC
- at the start of _exit() and continue, while noting that
- we've killed the process. */
-
- thread_killed = 1;
- if (thread_started)
- frame->ExceptionPC = &_exit;
- return RETURN_TO_PROGRAM;
-
- case 'q': /* Query message */
- if (strcmp (&remcomInBuffer[1], "Offsets") == 0)
- {
- sprintf (remcomOutBuffer, "Text=%x;Data=%x;Bss=%x",
- ldinfo->LDCodeImageOffset,
- ldinfo->LDDataImageOffset,
- ldinfo->LDDataImageOffset + ldinfo->LDDataImageLength);
- }
- else
- sprintf (remcomOutBuffer, "E04, Unknown query %s", &remcomInBuffer[1]);
- break;
- }
-
- /* reply to the request */
- if (! putpacket(remcomOutBuffer))
- return RETURN_TO_NEXT_DEBUGGER;
- }
-}
-
-
-char *progname;
-
-struct bitRate {
- BYTE bitRate;
- const char *bitRateString;
-};
-
-struct bitRate bitRateTable[] =
-{
- { AIO_BAUD_50 , "50" },
- { AIO_BAUD_75 , "75" },
- { AIO_BAUD_110 , "110" },
- { AIO_BAUD_134p5 , "134.5" },
- { AIO_BAUD_150 , "150" },
- { AIO_BAUD_300 , "300" },
- { AIO_BAUD_600 , "600" },
- { AIO_BAUD_1200 , "1200" },
- { AIO_BAUD_1800 , "1800" },
- { AIO_BAUD_2000 , "2000" },
- { AIO_BAUD_2400 , "2400" },
- { AIO_BAUD_3600 , "3600" },
- { AIO_BAUD_4800 , "4800" },
- { AIO_BAUD_7200 , "7200" },
- { AIO_BAUD_9600 , "9600" },
- { AIO_BAUD_19200 , "19200" },
- { AIO_BAUD_38400 , "38400" },
- { AIO_BAUD_57600 , "57600" },
- { AIO_BAUD_115200, "115200" },
- { -1, NULL }
-};
-
-char dataBitsTable[] = "5678";
-
-char *stopBitsTable[] = { "1", "1.5", "2" };
-
-char parity[] = "NOEMS";
-
-/* Start up. The main thread opens the named serial I/O port, loads
- the named NLM module and then goes to sleep. The serial I/O port
- is named as a board number and a port number. It would be more DOS
- like to provide a menu of available serial ports, but I don't want
- to have to figure out how to do that. */
-
-int
-main (argc, argv)
- int argc;
- char **argv;
-{
- int hardware, board, port;
- BYTE bitRate;
- BYTE dataBits;
- BYTE stopBits;
- BYTE parityMode;
- LONG err;
- struct debuggerStructure s;
- int cmdindx;
- char *cmdlin;
- int i;
-
- /* set progname */
- progname = "nlmstub";
-
- hardware = -1;
- board = 0;
- port = 0;
-
- /* set default serial line characteristics */
- bitRate = AIO_BAUD_9600;
- dataBits = AIO_DATA_BITS_8;
- stopBits = AIO_STOP_BITS_1;
- parityMode = AIO_PARITY_NONE;
-
- cmdindx = 0;
- for (argc--, argv++; *argv; argc--, argv++)
- {
- char *bp;
- char *ep;
-
- if (strnicmp(*argv, "BAUD=", 5) == 0)
- {
- struct bitRate *brp;
-
- bp = *argv + 5;
- for (brp = bitRateTable; brp->bitRate != (BYTE) -1; brp++)
- {
- if (strcmp(brp->bitRateString, bp) == 0)
- {
- bitRate = brp->bitRate;
- break;
- }
- }
-
- if (brp->bitRateString == NULL)
- {
- fprintf(stderr, "%s: %s: unknown or unsupported bit rate",
- progname, bp);
- exit (1);
- }
- }
- else if (strnicmp(*argv, "NODE=", 5) == 0)
- {
- bp = *argv + 5;
- board = strtol (bp, &ep, 0);
- if (ep == bp || *ep != '\0')
- {
- fprintf (stderr, "%s: %s: expected integer argument\n",
- progname, bp);
- exit(1);
- }
- }
- else if (strnicmp(*argv, "PORT=", 5) == 0)
- {
- bp = *argv + 5;
- port = strtol (bp, &ep, 0);
- if (ep == bp || *ep != '\0')
- {
- fprintf (stderr, "%s: %s: expected integer argument\n",
- progname, bp);
- exit(1);
- }
- }
- else
- {
- break;
- }
-
- cmdindx++;
- }
-
- if (argc == 0)
- {
- fprintf (stderr,
- "Usage: load %s [options] program [arguments]\n", progname);
- exit (1);
- }
-
- err = AIOAcquirePort (&hardware, &board, &port, &AIOhandle);
- if (err != AIO_SUCCESS)
- {
- switch (err)
- {
- case AIO_PORT_NOT_AVAILABLE:
- fprintf (stderr, "Port not available\n");
- break;
-
- case AIO_BOARD_NUMBER_INVALID:
- case AIO_PORT_NUMBER_INVALID:
- fprintf (stderr, "No such port\n");
- break;
-
- default:
- fprintf (stderr, "Could not open port: %d\n", err);
- break;
- }
-
- exit (1);
- }
-
- err = AIOConfigurePort (AIOhandle, bitRate, dataBits, stopBits, parityMode,
- AIO_HARDWARE_FLOW_CONTROL_OFF);
-
- if (err == AIO_QUALIFIED_SUCCESS)
- {
- AIOPORTCONFIG portConfig;
-
- fprintf (stderr, "Port configuration changed!\n");
-
- portConfig.returnLength = sizeof(portConfig);
- AIOGetPortConfiguration (AIOhandle, &portConfig, NULL);
-
- fprintf (stderr,
- " Bit Rate: %s, Data Bits: %c, Stop Bits: %s, Parity: %c,\
- Flow:%s\n",
- bitRateTable[portConfig.bitRate].bitRateString,
- dataBitsTable[portConfig.dataBits],
- stopBitsTable[portConfig.stopBits],
- parity[portConfig.parityMode],
- portConfig.flowCtrlMode ? "ON" : "OFF");
- }
- else if (err != AIO_SUCCESS)
- {
- fprintf (stderr, "Could not configure port: %d\n", err);
- AIOReleasePort (AIOhandle);
- exit (1);
- }
-
- if (AIOSetExternalControl(AIOhandle, AIO_EXTERNAL_CONTROL,
- (AIO_EXTCTRL_DTR | AIO_EXTCTRL_RTS))
- != AIO_SUCCESS)
- {
- LONG extStatus, chgdExtStatus;
-
- fprintf (stderr, "Could not set desired port controls!\n");
- AIOGetExternalStatus (AIOhandle, &extStatus, &chgdExtStatus);
- fprintf (stderr, "Port controls now: %d, %d\n", extStatus,
- chgdExtStatus);
- }
-
- /* Register ourselves as an alternate debugger. */
- memset (&s, 0, sizeof s);
- s.DDSResourceTag = ((struct ResourceTagStructure *)
- AllocateResourceTag (GetNLMHandle (),
- (BYTE *)"gdbserver",
- DebuggerSignature));
- if (s.DDSResourceTag == 0)
- {
- fprintf (stderr, "AllocateResourceTag failed\n");
- AIOReleasePort (AIOhandle);
- exit (1);
- }
- s.DDSdebuggerEntry = handle_exception;
- s.DDSFlags = TSS_FRAME_BIT;
-
- err = RegisterDebuggerRTag (&s, AT_FIRST);
- if (err != 0)
- {
- fprintf (stderr, "RegisterDebuggerRTag failed\n");
- AIOReleasePort (AIOhandle);
- exit (1);
- }
-
- /* Get the command line we were invoked with, and advance it past
- our name and command line arguments. */
- cmdlin = getcmd ((char *) NULL);
- for (i = 0; i < cmdindx; i++)
- {
- while (! isspace (*cmdlin))
- cmdlin++;
- while (isspace (*cmdlin))
- cmdlin++;
- }
-
- /* In case GDB is started before us, ack any packets (presumably
- "$?#xx") sitting there. */
- if (! putDebugChar ('+'))
- {
- fprintf (stderr, "putDebugChar failed\n");
- UnRegisterDebugger (&s);
- AIOReleasePort (AIOhandle);
- exit (1);
- }
-
- mainthread = GetThreadID ();
-
- if (remote_debug > 0)
- ConsolePrintf ("About to call LoadModule with \"%s\" %08x\r\n",
- cmdlin, __GetScreenID (GetCurrentScreen()));
-
- /* Start up the module to be debugged. */
- err = LoadModule ((struct ScreenStruct *) __GetScreenID (GetCurrentScreen()),
- (BYTE *)cmdlin, LO_DEBUG);
- if (err != 0)
- {
- fprintf (stderr, "LoadModule failed: %d\n", err);
- UnRegisterDebugger (&s);
- AIOReleasePort (AIOhandle);
- exit (1);
- }
-
- /* Wait for the debugger to wake us up. */
- if (remote_debug > 0)
- ConsolePrintf ("Suspending main thread (%08x)\r\n", mainthread);
- SuspendThread (mainthread);
- if (remote_debug > 0)
- ConsolePrintf ("Resuming main thread (%08x)\r\n", mainthread);
-
- /* If we are woken up, print an optional error message, deregister
- ourselves and exit. */
- if (error_message != NULL)
- fprintf (stderr, "%s\n", error_message);
- UnRegisterDebugger (&s);
- AIOReleasePort (AIOhandle);
- exit (0);
-}