summaryrefslogtreecommitdiff
path: root/gdb/nlm/gdbserve.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/nlm/gdbserve.c')
-rw-r--r--gdb/nlm/gdbserve.c1056
1 files changed, 1056 insertions, 0 deletions
diff --git a/gdb/nlm/gdbserve.c b/gdb/nlm/gdbserve.c
new file mode 100644
index 00000000000..62adf3a8d1b
--- /dev/null
+++ b/gdb/nlm/gdbserve.c
@@ -0,0 +1,1056 @@
+/* gdbserve.c -- NLM debugging stub for Novell NetWare.
+
+ 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 Novell NetWare. 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+
+#ifdef __i386__
+#include <dfs.h>
+#include <conio.h>
+#include <advanced.h>
+#include <debugapi.h>
+#include <process.h>
+#else
+#include <nwtypes.h>
+#include <nwdfs.h>
+#include <nwconio.h>
+#include <nwadv.h>
+#include <nwdbgapi.h>
+#include <nwthread.h>
+#endif
+
+#include <aio.h>
+#include "cpu.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";
+
+unsigned char breakpoint_insn[] = BREAKPOINT;
+
+char *mem2hex (void *mem, char *buf, int count, int may_fault);
+char *hex2mem (char *buf, void *mem, int count, int may_fault);
+extern void set_step_traps (struct StackFrame *);
+extern void clear_step_traps (struct StackFrame *);
+
+static int __main() {};
+
+/* 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;
+}
+
+/* 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. */
+volatile int mem_err = 0;
+
+#ifndef ALTERNATE_MEM_FUNCS
+/* 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). */
+
+int
+get_char (addr)
+ char *addr;
+{
+ return *addr;
+}
+
+void
+set_char (addr, val)
+ char *addr;
+ int val;
+{
+ *addr = val;
+}
+#endif /* ALTERNATE_MEM_FUNCS */
+
+/* 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. */
+
+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 */
+
+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. */
+
+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);
+}
+
+/* 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 struct DBG_LoadDefinitionStructure *ldinfo = 0;
+ static unsigned char first_insn[BREAKPOINT_SIZE]; /* The first instruction in the program. */
+
+#if 0
+ /* According to some documentation from Novell, the bell sometimes
+ may be ringing at this point. This can be stopped on Netware 4
+ systems by calling the undocumented StopBell() function. */
+
+ StopBell ();
+#endif
+
+ if (remote_debug)
+ {
+ ConsolePrintf ("vector=%d: %s, pc=%08x, thread=%08x\r\n",
+ frame->ExceptionNumber,
+ frame->ExceptionDescription,
+ 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 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 += 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_traps (frame);
+
+ 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_traps (frame);
+
+ 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 *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 = "gdbserve";
+
+ /* set default serial line */
+ 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, "BOARD=", 6) == 0)
+ {
+ bp = *argv + 6;
+ board = strtol (bp, &ep, 0);
+ if (ep == bp || *ep != '\0')
+ {
+ fprintf (stderr, "%s: %s: expected integer argument\n",
+ progname, bp);
+ exit(1);
+ }
+ }
+#if 1 /* FIXME: this option has been depricated */
+ 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);
+ }
+ }
+#endif
+ 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 the board and port 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);
+}