aboutsummaryrefslogtreecommitdiff
path: root/gdb/nlm/gdbserve.c
diff options
context:
space:
mode:
authorStu Grossman <grossman@cygnus>1994-03-25 01:38:12 +0000
committerStu Grossman <grossman@cygnus>1994-03-25 01:38:12 +0000
commit27847c6f99c457a02816c2082fa91e0a8c408d00 (patch)
tree202f093abaad0f3caf5ac879f63675ce10aa8b11 /gdb/nlm/gdbserve.c
parent193c5f93a17206ea7e83b7ae476e1a997e1ff108 (diff)
downloadgdb-27847c6f99c457a02816c2082fa91e0a8c408d00.zip
gdb-27847c6f99c457a02816c2082fa91e0a8c408d00.tar.gz
gdb-27847c6f99c457a02816c2082fa91e0a8c408d00.tar.bz2
Netware debugger stub NLM.
Diffstat (limited to 'gdb/nlm/gdbserve.c')
-rw-r--r--gdb/nlm/gdbserve.c1101
1 files changed, 1101 insertions, 0 deletions
diff --git a/gdb/nlm/gdbserve.c b/gdb/nlm/gdbserve.c
new file mode 100644
index 0000000..b97847f
--- /dev/null
+++ b/gdb/nlm/gdbserve.c
@@ -0,0 +1,1101 @@
+/* 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 <nwdfs.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+/*#include <ctype.h>*/
+#include <time.h>
+/*#include <aio.h>*/
+#include <nwconio.h>
+#include <nwadv.h>
+#include <nwdbgapi.h>
+/*#include <process.h>*/
+#include <errno.h>
+#include <nwthread.h>
+#include "alpha-patch.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 RA_REGNUM 26 /* Contains return address value */
+#define SP_REGNUM 30 /* Contains address of top of stack */
+#define PC_REGNUM 64 /* Contains program counter */
+#define FP_REGNUM 65 /* Virtual frame pointer */
+#define V0_REGNUM 0 /* Function integer return value */
+#define NUM_REGS 66 /* Number of machine registers */
+#define REGISTER_BYTES (NUM_REGS * 8) /* Total size of registers array */
+
+/*#define flush_i_cache() asm("call_pal 0x86")*/
+
+static char *mem2hex (void *mem, char *buf, int count, int may_fault);
+static char *hex2mem (char *buf, void *mem, int count, int may_fault);
+
+#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;
+
+ err = AIOWriteData (AIOhandle, (char *) &c, 1, &put);
+ if (err != 0 || put != 1)
+ {
+ error_message = "AIOWriteData failed";
+ ResumeThread (mainthread);
+ return 0;
+ }
+ return 1;
+}
+
+/* Get the registers out of the frame information. */
+
+static void
+frame_to_registers (frame, regs)
+ struct StackFrame *frame;
+ char *regs;
+{
+ mem2hex (&frame->ExceptionRegs[SF_REG_PC], &regs[PC_REGNUM * 8 * 2], 8, 0);
+
+ mem2hex (&frame->ExceptionRegs[SF_IREG_OFFSET], &regs[V0_REGNUM * 8 * 2], 8 * 64, 0);
+}
+
+/* Put the registers back into the frame information. */
+
+static void
+registers_to_frame (regs, frame)
+ char *regs;
+ struct StackFrame *frame;
+{
+ hex2mem (&regs[PC_REGNUM * 8 * 2], &frame->ExceptionRegs[SF_REG_PC], 8, 0);
+
+ hex2mem (&regs[V0_REGNUM * 8 * 2], &frame->ExceptionRegs[SF_IREG_OFFSET], 8 * 64, 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 ();
+#if 0
+asm (".globl just_return");
+asm (".globl _just_return");
+asm ("just_return:");
+asm ("_just_return:");
+asm ("leave");
+asm ("ret");
+#endif
+
+/* 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_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 (mem);
+ }
+ 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);
+}
+
+union inst
+{
+ LONG l;
+
+ struct
+ {
+ union
+ {
+ struct
+ {
+ unsigned hint : 16;
+ unsigned rb : 5;
+ unsigned ra : 5;
+ unsigned opcode : 6;
+ } jump;
+ struct
+ {
+ signed disp : 21;
+ unsigned ra : 5;
+ unsigned opcode : 6;
+ } branch;
+ } variant;
+ } inst;
+};
+
+static LONG saved_inst;
+static LONG *saved_inst_pc = 0;
+static LONG saved_target_inst;
+static LONG *saved_target_inst_pc = 0;
+
+static void
+set_step_breakpoint (pc, frame)
+ LONG *pc;
+ struct StackFrame *frame;
+{
+ union inst inst;
+ LONG *target;
+ int opcode;
+ int ra, rb;
+
+ inst.l = *pc++;
+
+ opcode = inst.inst.variant.branch.opcode;
+
+ if ((opcode & 0x30) == 0x30) /* A branch of some sort */
+ target = inst.inst.variant.branch.disp + pc;
+ else if (opcode == 0x1a) /* jmp, ret, etc... */
+ target = (LONG *)(frame->ExceptionRegs[SF_IREG_OFFSET
+ + inst.inst.variant.jump.rb].lo
+ & ~3);
+ else
+ target = pc;
+
+ saved_inst = *pc;
+ *pc = 0x80; /* call_pal bpt */
+ saved_inst_pc = pc;
+
+ if (target != pc)
+ {
+ saved_target_inst = *target;
+ *target = 0x80; /* call_pal bpt */
+ saved_target_inst_pc = target;
+ }
+}
+
+/* Remove step breakpoints. Returns non-zero if pc was at a step breakpoint,
+ zero otherwise. This routine works even if there were no step breakpoints
+ set. */
+
+static int
+clear_step_breakpoint (pc)
+ LONG *pc;
+{
+ int retcode;
+
+ if (saved_inst_pc == pc || saved_target_inst_pc == pc)
+ retcode = 1;
+ else
+ retcode = 0;
+
+ if (saved_inst_pc)
+ {
+ *saved_inst_pc = saved_inst;
+ saved_inst_pc = 0;
+ }
+
+ if (saved_target_inst_pc)
+ {
+ *saved_target_inst_pc = saved_target_inst;
+ saved_target_inst_pc = 0;
+ }
+
+ return retcode;
+}
+
+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->ExceptionRegs[SF_REG_PC], ptr + 3, 8, 0);
+ *ptr++ = ';';
+
+ sprintf (ptr, "%02x:", SP_REGNUM);
+ ptr = mem2hex (&frame->ExceptionRegs[SF_IREG_OFFSET + SP_REGNUM], ptr + 3, 8, 0);
+ *ptr++ = ';';
+
+ sprintf (ptr, "%02x:", RA_REGNUM);
+ ptr = mem2hex (&frame->ExceptionRegs[SF_IREG_OFFSET + RA_REGNUM], ptr + 3, 8, 0);
+ *ptr++ = ';';
+
+ sprintf (ptr, "%02x:", FP_REGNUM);
+ ptr = mem2hex (&frame->ExceptionRegs[SF_IREG_OFFSET + FP_REGNUM], ptr + 3, 8, 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 (struct StackFrame *frame)
+{
+ int addr, length;
+ char *ptr;
+ static struct DBG_LoadDefinitionStructure *ldinfo = 0;
+ static LONG first_insn; /* 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, pc=%08x, thread=%08x\r\n",
+ frame->ExceptionNumber,
+ frame->ExceptionDescription,
+ frame->ExceptionRegs[SF_REG_PC].lo,
+ 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);
+ first_insn = *(LONG *)ldinfo->LDInitializationProcedure;
+ *(LONG *)ldinfo->LDInitializationProcedure = 0x80; /* call_pal bpt */
+ flush_i_cache ();
+ 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->ExceptionRegs[SF_REG_PC].lo == (LONG) ldinfo->LDInitializationProcedure
+ && *(LONG *) ldinfo->LDInitializationProcedure == 0x80)
+ {
+ *(LONG *) ldinfo->LDInitializationProcedure = first_insn;
+ 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->ExceptionRegs[SF_REG_PC].lo >= (long) &set_char
+ && frame->ExceptionRegs[SF_REG_PC].lo < (long) &set_char + 50)
+ || (frame->ExceptionRegs[SF_REG_PC].lo >= (long) &get_char
+ && frame->ExceptionRegs[SF_REG_PC].lo < (long) &get_char + 50)))
+ {
+ mem_err = 1;
+ /* Point the instruction pointer at an assembly language stub
+ which just returns from the function. */
+
+ frame->ExceptionRegs[SF_REG_PC].lo += 4; /* Skip the load or store */
+
+ /* 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;
+
+ case TERMINATE_NLM_EVENT:
+ /* There is no way to get the exit status. */
+ sprintf (remcomOutBuffer, "W%02x", 0);
+ break; /* We generate our own status */
+ }
+
+ /* 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_breakpoint (frame->ExceptionRegs[SF_REG_PC]);
+
+ if (! putpacket(remcomOutBuffer))
+ return RETURN_TO_NEXT_DEBUGGER;
+
+ if (frame->ExceptionNumber == TERMINATE_NLM_EVENT)
+ {
+ ResumeThread (mainthread);
+ return RETURN_TO_PROGRAM;
+ }
+
+ 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;*/
+ fprintf (stderr, "Setting PC to 0x%x\n", addr);
+ while (1);
+ }
+
+ if (remcomInBuffer[0] == 's')
+ set_step_breakpoint (frame->ExceptionRegs[SF_REG_PC].lo);
+
+ flush_i_cache ();
+ return RETURN_TO_PROGRAM;
+
+ case 'k':
+ /* kill the program */
+ KillMe (ldinfo);
+ ResumeThread (mainthread);
+ 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 *baudRates[] = { "50", "75", "110", "134.5", "150", "300", "600", "1200",
+ "1800", "2000", "2400", "3600", "4800", "7200", "9600",
+ "19200", "38400", "57600", "115200" };
+
+char dataBits[] = "5678";
+
+char *stopBits[] = { "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;
+ LONG err;
+ struct debuggerStructure s;
+ char *cmdlin;
+ int i;
+
+/* Use the -B option to invoke the NID if you want to debug the stub. */
+
+ if (argc > 1 && strcmp(argv[1], "-B") == 0)
+ {
+ Breakpoint(argc);
+ ++argv, --argc;
+ }
+
+ if (argc < 4)
+ {
+ fprintf (stderr,
+ "Usage: load gdbserve board port program [arguments]\n");
+ exit (1);
+ }
+
+ hardware = -1;
+ board = strtol (argv[1], (char **) NULL, 0);
+ port = strtol (argv[2], (char **) NULL, 0);
+
+ 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, AIO_BAUD_9600, AIO_DATA_BITS_8,
+ AIO_STOP_BITS_1, AIO_PARITY_NONE,
+ AIO_HARDWARE_FLOW_CONTROL_OFF);
+
+ if (err == AIO_QUALIFIED_SUCCESS)
+ {
+ AIOPORTCONFIG portConfig;
+ AIODVRCONFIG dvrConfig;
+
+ fprintf (stderr, "Port configuration changed!\n");
+ AIOGetPortConfiguration (AIOhandle, &portConfig, &dvrConfig);
+ fprintf (stderr,
+ " Bit Rate: %s, Data Bits: %c, Stop Bits: %s, Parity: %c,\
+ Flow:%s\n",
+ baudRates[portConfig.bitRate],
+ dataBits[portConfig.dataBits],
+ stopBits[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 the board and port arguments. */
+ cmdlin = getcmd ((char *) NULL);
+ for (i = 0; i < 2; 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)
+ fprintf (stderr, "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)
+ fprintf (stderr, "Suspending main thread (%08x)\r\n", mainthread);
+ SuspendThread (mainthread);
+ if (remote_debug > 0)
+ fprintf (stderr, "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);
+}