1/* gdbserve.c -- NLM debugging stub for Novell NetWare.
2
3   This is originally based on an m68k software stub written by Glenn
4   Engel at HP, but has changed quite a bit.  It was modified for the
5   i386 by Jim Kingdon, Cygnus Support.  It was modified to run under
6   NetWare by Ian Lance Taylor, Cygnus Support.
7
8   This code is intended to produce an NLM (a NetWare Loadable Module)
9   to run under Novell NetWare.  To create the NLM, compile this code
10   into an object file using the NLM SDK on any i386 host, and use the
11   nlmconv program (available in the GNU binutils) to transform the
12   resulting object file into an NLM.  */
13
14/****************************************************************************
15
16		THIS SOFTWARE IS NOT COPYRIGHTED
17
18   HP offers the following for use in the public domain.  HP makes no
19   warranty with regard to the software or it's performance and the
20   user accepts the software "AS IS" with all faults.
21
22   HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
23   TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
24   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25
26****************************************************************************/
27
28/****************************************************************************
29 *
30 *    The following gdb commands are supported:
31 *
32 * command          function                               Return value
33 *
34 *    g             return the value of the CPU registers  hex data or ENN
35 *    G             set the value of the CPU registers     OK or ENN
36 *
37 *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
38 *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
39 *
40 *    c             Resume at current address              SNN   ( signal NN)
41 *    cAA..AA       Continue at address AA..AA             SNN
42 *
43 *    s             Step one instruction                   SNN
44 *    sAA..AA       Step one instruction from AA..AA       SNN
45 *
46 *    k             kill
47 *
48 *    ?             What was the last sigval ?             SNN   (signal NN)
49 *
50 * All commands and responses are sent with a packet which includes a
51 * checksum.  A packet consists of
52 *
53 * $<packet info>#<checksum>.
54 *
55 * where
56 * <packet info> :: <characters representing the command or response>
57 * <checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
58 *
59 * When a packet is received, it is first acknowledged with either '+' or '-'.
60 * '+' indicates a successful transfer.  '-' indicates a failed transfer.
61 *
62 * Example:
63 *
64 * Host:                  Reply:
65 * $m0,10#2a               +$00010203040506070809101112131415#42
66 *
67 ****************************************************************************/
68
69#include <stdio.h>
70#include <string.h>
71#include <stdlib.h>
72#include <ctype.h>
73#include <errno.h>
74#include <time.h>
75
76#ifdef __i386__
77#include <dfs.h>
78#include <conio.h>
79#include <advanced.h>
80#include <debugapi.h>
81#include <process.h>
82#else
83#include <nwtypes.h>
84#include <nwdfs.h>
85#include <nwconio.h>
86#include <nwadv.h>
87#include <nwdbgapi.h>
88#include <nwthread.h>
89#endif
90
91#include <aio.h>
92#include "cpu.h"
93
94
95/****************************************************/
96/* This information is from Novell.  It is not in any of the standard
97   NetWare header files.  */
98
99struct DBG_LoadDefinitionStructure
100{
101	void *reserved1[4];
102	LONG reserved5;
103	LONG LDCodeImageOffset;
104	LONG LDCodeImageLength;
105	LONG LDDataImageOffset;
106	LONG LDDataImageLength;
107	LONG LDUninitializedDataLength;
108	LONG LDCustomDataOffset;
109	LONG LDCustomDataSize;
110	LONG reserved6[2];
111	LONG (*LDInitializationProcedure)(void);
112};
113
114#define LO_NORMAL		0x0000
115#define LO_STARTUP		0x0001
116#define LO_PROTECT		0x0002
117#define LO_DEBUG		0x0004
118#define LO_AUTO_LOAD  		0x0008
119
120/* Loader returned error codes */
121#define LOAD_COULD_NOT_FIND_FILE		1
122#define LOAD_ERROR_READING_FILE			2
123#define LOAD_NOT_NLM_FILE_FORMAT		3
124#define LOAD_WRONG_NLM_FILE_VERSION		4
125#define LOAD_REENTRANT_INITIALIZE_FAILURE	5
126#define LOAD_CAN_NOT_LOAD_MULTIPLE_COPIES	6
127#define LOAD_ALREADY_IN_PROGRESS		7
128#define LOAD_NOT_ENOUGH_MEMORY			8
129#define LOAD_INITIALIZE_FAILURE			9
130#define LOAD_INCONSISTENT_FILE_FORMAT		10
131#define LOAD_CAN_NOT_LOAD_AT_STARTUP		11
132#define LOAD_AUTO_LOAD_MODULES_NOT_LOADED	12
133#define LOAD_UNRESOLVED_EXTERNAL		13
134#define LOAD_PUBLIC_ALREADY_DEFINED		14
135/****************************************************/
136
137/* The main thread ID.  */
138static int mainthread;
139
140/* An error message for the main thread to print.  */
141static char *error_message;
142
143/* The AIO port handle.  */
144static int AIOhandle;
145
146/* BUFMAX defines the maximum number of characters in inbound/outbound
147   buffers.  At least NUMREGBYTES*2 are needed for register packets */
148#define BUFMAX (REGISTER_BYTES * 2 + 16)
149
150/* remote_debug > 0 prints ill-formed commands in valid packets and
151   checksum errors. */
152static int remote_debug = 1;
153
154static const char hexchars[] = "0123456789abcdef";
155
156unsigned char breakpoint_insn[] = BREAKPOINT;
157
158char *mem2hex (void *mem, char *buf, int count, int may_fault);
159char *hex2mem (char *buf, void *mem, int count, int may_fault);
160extern void set_step_traps (struct StackFrame *);
161extern void clear_step_traps (struct StackFrame *);
162
163static int __main() {};
164
165/* Read a character from the serial port.  This must busy wait, but
166   that's OK because we will be the only thread running anyhow.  */
167
168static int
169getDebugChar (void)
170{
171  int err;
172  LONG got;
173  unsigned char ret;
174
175  do
176    {
177      err = AIOReadData (AIOhandle, (char *) &ret, 1, &got);
178      if (err != 0)
179	{
180	  error_message = "AIOReadData failed";
181	  ResumeThread (mainthread);
182	  return -1;
183	}
184    }
185  while (got == 0);
186
187  return ret;
188}
189
190/* Write a character to the serial port.  Returns 0 on failure,
191   non-zero on success.  */
192
193static int
194putDebugChar (unsigned char c)
195{
196  int err;
197  LONG put;
198
199  put = 0;
200  while (put < 1)
201    {
202      err = AIOWriteData (AIOhandle, (char *) &c, 1, &put);
203      if (err != 0)
204	ConsolePrintf ("AIOWriteData: err = %d, put = %d\r\n", err, put);
205    }
206  return 1;
207}
208
209/* Turn a hex character into a number.  */
210
211static int
212hex (char ch)
213{
214  if ((ch >= 'a') && (ch <= 'f'))
215    return (ch-'a'+10);
216  if ((ch >= '0') && (ch <= '9'))
217    return (ch-'0');
218  if ((ch >= 'A') && (ch <= 'F'))
219    return (ch-'A'+10);
220  return (-1);
221}
222
223/* Scan for the sequence $<data>#<checksum>.  Returns 0 on failure,
224   non-zero on success.  */
225
226static int
227getpacket (char *buffer)
228{
229  unsigned char checksum;
230  unsigned char xmitcsum;
231  int i;
232  int count;
233  int ch;
234
235  do
236    {
237      /* wait around for the start character, ignore all other characters */
238      while ((ch = getDebugChar()) != '$')
239	if (ch == -1)
240	  return 0;
241      checksum = 0;
242      xmitcsum = -1;
243
244      count = 0;
245
246      /* now, read until a # or end of buffer is found */
247      while (count < BUFMAX)
248	{
249	  ch = getDebugChar();
250	  if (ch == -1)
251	    return 0;
252	  if (ch == '#')
253	    break;
254	  checksum = checksum + ch;
255	  buffer[count] = ch;
256	  count = count + 1;
257	}
258      buffer[count] = 0;
259
260      if (ch == '#')
261	{
262	  ch = getDebugChar ();
263	  if (ch == -1)
264	    return 0;
265	  xmitcsum = hex(ch) << 4;
266	  ch = getDebugChar ();
267	  if (ch == -1)
268	    return 0;
269	  xmitcsum += hex(ch);
270
271	  if (checksum != xmitcsum)
272	    {
273	      if (remote_debug)
274		ConsolePrintf ("bad checksum.  My count = 0x%x, sent=0x%x. buf=%s\n",
275			       checksum,xmitcsum,buffer);
276	      /* failed checksum */
277	      if (! putDebugChar('-'))
278		return 0;
279	      return 1;
280	    }
281	  else
282	    {
283	      /* successful transfer */
284	      if (! putDebugChar('+'))
285		return 0;
286	      /* if a sequence char is present, reply the sequence ID */
287	      if (buffer[2] == ':')
288		{
289		  if (! putDebugChar (buffer[0])
290		      || ! putDebugChar (buffer[1]))
291		    return 0;
292		  /* remove sequence chars from buffer */
293		  count = strlen(buffer);
294		  for (i=3; i <= count; i++)
295		    buffer[i-3] = buffer[i];
296		}
297	    }
298	}
299    }
300  while (checksum != xmitcsum);
301
302  if (remote_debug)
303    ConsolePrintf ("Received packet \"%s\"\r\n", buffer);
304
305  return 1;
306}
307
308/* Send the packet in buffer.  Returns 0 on failure, non-zero on
309   success.  */
310
311static int
312putpacket (char *buffer)
313{
314  unsigned char checksum;
315  int count;
316  int ch;
317
318  if (remote_debug)
319    ConsolePrintf ("Sending packet \"%s\"\r\n", buffer);
320
321  /*  $<packet info>#<checksum>. */
322  do
323    {
324      if (! putDebugChar('$'))
325	return 0;
326      checksum = 0;
327      count = 0;
328
329      while (ch=buffer[count])
330	{
331	  if (! putDebugChar(ch))
332	    return 0;
333	  checksum += ch;
334	  count += 1;
335	}
336
337      if (! putDebugChar('#')
338	  || ! putDebugChar(hexchars[checksum >> 4])
339	  || ! putDebugChar(hexchars[checksum % 16]))
340	return 0;
341
342      ch = getDebugChar ();
343      if (ch == -1)
344	return 0;
345    }
346  while (ch != '+');
347
348  return 1;
349}
350
351static char remcomInBuffer[BUFMAX];
352static char remcomOutBuffer[BUFMAX];
353static short error;
354
355static void
356debug_error (char *format, char *parm)
357{
358  if (remote_debug)
359    {
360      ConsolePrintf (format, parm);
361      ConsolePrintf ("\n");
362    }
363}
364
365/* This is set if we could get a memory access fault.  */
366static int mem_may_fault;
367
368/* Indicate to caller of mem2hex or hex2mem that there has been an
369   error.  */
370volatile int mem_err = 0;
371
372#ifndef ALTERNATE_MEM_FUNCS
373/* These are separate functions so that they are so short and sweet
374   that the compiler won't save any registers (if there is a fault
375   to mem_fault, they won't get restored, so there better not be any
376   saved).  */
377
378int
379get_char (char *addr)
380{
381  return *addr;
382}
383
384void
385set_char (char *addr, int val)
386{
387  *addr = val;
388}
389#endif /* ALTERNATE_MEM_FUNCS */
390
391/* convert the memory pointed to by mem into hex, placing result in buf */
392/* return a pointer to the last char put in buf (null) */
393/* If MAY_FAULT is non-zero, then we should set mem_err in response to
394   a fault; if zero treat a fault like any other fault in the stub.  */
395
396char *
397mem2hex (void *mem, char *buf, int count, int may_fault)
398{
399  int i;
400  unsigned char ch;
401  char *ptr = mem;
402
403  mem_may_fault = may_fault;
404  for (i = 0; i < count; i++)
405    {
406      ch = get_char (ptr++);
407      if (may_fault && mem_err)
408	return (buf);
409      *buf++ = hexchars[ch >> 4];
410      *buf++ = hexchars[ch % 16];
411    }
412  *buf = 0;
413  mem_may_fault = 0;
414  return(buf);
415}
416
417/* convert the hex array pointed to by buf into binary to be placed in mem */
418/* return a pointer to the character AFTER the last byte written */
419
420char *
421hex2mem (char *buf, void *mem, int count, int may_fault)
422{
423  int i;
424  unsigned char ch;
425  char *ptr = mem;
426
427  mem_may_fault = may_fault;
428  for (i=0;i<count;i++)
429    {
430      ch = hex(*buf++) << 4;
431      ch = ch + hex(*buf++);
432      set_char (ptr++, ch);
433      if (may_fault && mem_err)
434	return (ptr);
435    }
436  mem_may_fault = 0;
437  return(mem);
438}
439
440/* This function takes the 386 exception vector and attempts to
441   translate this number into a unix compatible signal value.  */
442
443int
444computeSignal (int exceptionVector)
445{
446  int sigval;
447  switch (exceptionVector)
448    {
449    case 0 : sigval = 8; break; /* divide by zero */
450    case 1 : sigval = 5; break; /* debug exception */
451    case 3 : sigval = 5; break; /* breakpoint */
452    case 4 : sigval = 16; break; /* into instruction (overflow) */
453    case 5 : sigval = 16; break; /* bound instruction */
454    case 6 : sigval = 4; break; /* Invalid opcode */
455    case 7 : sigval = 8; break; /* coprocessor not available */
456    case 8 : sigval = 7; break; /* double fault */
457    case 9 : sigval = 11; break; /* coprocessor segment overrun */
458    case 10 : sigval = 11; break; /* Invalid TSS */
459    case 11 : sigval = 11; break; /* Segment not present */
460    case 12 : sigval = 11; break; /* stack exception */
461    case 13 : sigval = 11; break; /* general protection */
462    case 14 : sigval = 11; break; /* page fault */
463    case 16 : sigval = 7; break; /* coprocessor error */
464    default:
465      sigval = 7;		/* "software generated"*/
466    }
467  return (sigval);
468}
469
470/**********************************************/
471/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */
472/* RETURN NUMBER OF CHARS PROCESSED           */
473/**********************************************/
474static int
475hexToInt (char **ptr, int *intValue)
476{
477  int numChars = 0;
478  int hexValue;
479
480  *intValue = 0;
481
482  while (**ptr)
483    {
484      hexValue = hex(**ptr);
485      if (hexValue >=0)
486	{
487	  *intValue = (*intValue <<4) | hexValue;
488	  numChars ++;
489	}
490      else
491	break;
492
493      (*ptr)++;
494    }
495
496  return (numChars);
497}
498
499/* This function does all command processing for interfacing to gdb.
500   It is called whenever an exception occurs in the module being
501   debugged.  */
502
503static LONG
504handle_exception (struct StackFrame *frame)
505{
506  int addr, length;
507  char *ptr;
508  static struct DBG_LoadDefinitionStructure *ldinfo = 0;
509  static unsigned char first_insn[BREAKPOINT_SIZE]; /* The first instruction in the program.  */
510
511#if 0
512  /* According to some documentation from Novell, the bell sometimes
513     may be ringing at this point.  This can be stopped on Netware 4
514     systems by calling the undocumented StopBell() function. */
515
516  StopBell ();
517#endif
518
519  if (remote_debug)
520    {
521      ConsolePrintf ("vector=%d: %s, pc=%08x, thread=%08x\r\n",
522		     frame->ExceptionNumber,
523		     frame->ExceptionDescription,
524		     frame->ExceptionPC,
525		     GetThreadID ());
526    }
527
528  switch (frame->ExceptionNumber)
529    {
530    case START_NLM_EVENT:
531      /* If the NLM just started, we record the module load information
532	 and the thread ID, and set a breakpoint at the first instruction
533	 in the program.  */
534
535      ldinfo = ((struct DBG_LoadDefinitionStructure *)
536		frame->ExceptionErrorCode);
537      memcpy (first_insn, ldinfo->LDInitializationProcedure,
538	      BREAKPOINT_SIZE);
539      memcpy (ldinfo->LDInitializationProcedure, breakpoint_insn,
540	      BREAKPOINT_SIZE);
541      flush_i_cache ();
542      return RETURN_TO_PROGRAM;
543
544    case ENTER_DEBUGGER_EVENT:
545    case KEYBOARD_BREAK_EVENT:
546      /* Pass some events on to the next debugger, in case it will handle
547	 them.  */
548      return RETURN_TO_NEXT_DEBUGGER;
549
550    case 3:			/* Breakpoint */
551      /* After we've reached the initial breakpoint, reset it.  */
552      if (frame->ExceptionPC - DECR_PC_AFTER_BREAK == (LONG) ldinfo->LDInitializationProcedure
553	  && memcmp (ldinfo->LDInitializationProcedure, breakpoint_insn,
554		     BREAKPOINT_SIZE) == 0)
555	{
556	  memcpy (ldinfo->LDInitializationProcedure, first_insn,
557		  BREAKPOINT_SIZE);
558	  frame->ExceptionPC -= DECR_PC_AFTER_BREAK;
559	  flush_i_cache ();
560	}
561      /* Normal breakpoints end up here */
562      do_status (remcomOutBuffer, frame);
563      break;
564
565    default:
566      /* At the moment, we don't care about most of the unusual NetWare
567	 exceptions.  */
568      if (frame->ExceptionNumber > 31)
569	return RETURN_TO_PROGRAM;
570
571      /* Most machine level exceptions end up here */
572      do_status (remcomOutBuffer, frame);
573      break;
574
575    case 11:			/* Segment not present */
576    case 13:			/* General protection */
577    case 14:			/* Page fault */
578      /* If we get a GP fault, and mem_may_fault is set, and the
579	 instruction pointer is near set_char or get_char, then we caused
580	 the fault ourselves accessing an illegal memory location.  */
581      if (mem_may_fault
582	  && ((frame->ExceptionPC >= (long) &set_char
583	       && frame->ExceptionPC < (long) &set_char + 50)
584	      || (frame->ExceptionPC >= (long) &get_char
585		  && frame->ExceptionPC < (long) &get_char + 50)))
586	{
587	  mem_err = 1;
588	  /* Point the instruction pointer at an assembly language stub
589	     which just returns from the function.  */
590
591	  frame->ExceptionPC += 4; /* Skip the load or store */
592
593	  /* Keep going.  This will act as though it returned from
594	     set_char or get_char.  The calling routine will check
595	     mem_err, and do the right thing.  */
596	  return RETURN_TO_PROGRAM;
597	}
598      /* Random mem fault, report it */
599      do_status (remcomOutBuffer, frame);
600      break;
601
602    case TERMINATE_NLM_EVENT:
603      /* There is no way to get the exit status.  */
604      sprintf (remcomOutBuffer, "W%02x", 0);
605      break;			/* We generate our own status */
606    }
607
608  /* FIXME: How do we know that this exception has anything to do with
609     the program we are debugging?  We can check whether the PC is in
610     the range of the module we are debugging, but that doesn't help
611     much since an error could occur in a library routine.  */
612
613  clear_step_traps (frame);
614
615  if (! putpacket(remcomOutBuffer))
616    return RETURN_TO_NEXT_DEBUGGER;
617
618  if (frame->ExceptionNumber == TERMINATE_NLM_EVENT)
619    {
620      ResumeThread (mainthread);
621      return RETURN_TO_PROGRAM;
622    }
623
624  while (1)
625    {
626      error = 0;
627      remcomOutBuffer[0] = 0;
628      if (! getpacket (remcomInBuffer))
629	return RETURN_TO_NEXT_DEBUGGER;
630      switch (remcomInBuffer[0])
631	{
632	case '?':
633	  do_status (remcomOutBuffer, frame);
634	  break;
635	case 'd':
636	  remote_debug = !(remote_debug); /* toggle debug flag */
637	  break;
638	case 'g':
639	  /* return the value of the CPU registers */
640	  frame_to_registers (frame, remcomOutBuffer);
641	  break;
642	case 'G':
643	  /* set the value of the CPU registers - return OK */
644	  registers_to_frame (&remcomInBuffer[1], frame);
645	  strcpy(remcomOutBuffer,"OK");
646	  break;
647
648	case 'm':
649	  /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
650	  /* TRY TO READ %x,%x.  IF SUCCEED, SET PTR = 0 */
651	  ptr = &remcomInBuffer[1];
652	  if (hexToInt(&ptr,&addr))
653	    if (*(ptr++) == ',')
654	      if (hexToInt(&ptr,&length))
655		{
656		  ptr = 0;
657		  mem_err = 0;
658		  mem2hex((char*) addr, remcomOutBuffer, length, 1);
659		  if (mem_err)
660		    {
661		      strcpy (remcomOutBuffer, "E03");
662		      debug_error ("memory fault");
663		    }
664		}
665
666	  if (ptr)
667	    {
668	      strcpy(remcomOutBuffer,"E01");
669	      debug_error("malformed read memory command: %s",remcomInBuffer);
670	    }
671	  break;
672
673	case 'M':
674	  /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
675	  /* TRY TO READ '%x,%x:'.  IF SUCCEED, SET PTR = 0 */
676	  ptr = &remcomInBuffer[1];
677	  if (hexToInt(&ptr,&addr))
678	    if (*(ptr++) == ',')
679	      if (hexToInt(&ptr,&length))
680		if (*(ptr++) == ':')
681		  {
682		    mem_err = 0;
683		    hex2mem(ptr, (char*) addr, length, 1);
684
685		    if (mem_err)
686		      {
687			strcpy (remcomOutBuffer, "E03");
688			debug_error ("memory fault");
689		      }
690		    else
691		      {
692			strcpy(remcomOutBuffer,"OK");
693		      }
694
695		    ptr = 0;
696		  }
697	  if (ptr)
698	    {
699	      strcpy(remcomOutBuffer,"E02");
700	      debug_error("malformed write memory command: %s",remcomInBuffer);
701	    }
702	  break;
703
704	case 'c':
705	case 's':
706	  /* cAA..AA    Continue at address AA..AA(optional) */
707	  /* sAA..AA   Step one instruction from AA..AA(optional) */
708	  /* try to read optional parameter, pc unchanged if no parm */
709	  ptr = &remcomInBuffer[1];
710	  if (hexToInt(&ptr,&addr))
711	    {
712/*	      registers[PC_REGNUM].lo = addr;*/
713	      fprintf (stderr, "Setting PC to 0x%x\n", addr);
714	      while (1);
715	    }
716
717	  if (remcomInBuffer[0] == 's')
718	    set_step_traps (frame);
719
720	  flush_i_cache ();
721	  return RETURN_TO_PROGRAM;
722
723	case 'k':
724	  /* kill the program */
725	  KillMe (ldinfo);
726	  ResumeThread (mainthread);
727	  return RETURN_TO_PROGRAM;
728
729	case 'q':		/* Query message */
730	  if (strcmp (&remcomInBuffer[1], "Offsets") == 0)
731	    {
732	      sprintf (remcomOutBuffer, "Text=%x;Data=%x;Bss=%x",
733		       ldinfo->LDCodeImageOffset,
734		       ldinfo->LDDataImageOffset,
735		       ldinfo->LDDataImageOffset + ldinfo->LDDataImageLength);
736	    }
737	  else
738	    sprintf (remcomOutBuffer, "E04, Unknown query %s", &remcomInBuffer[1]);
739	  break;
740	}
741
742      /* reply to the request */
743      if (! putpacket(remcomOutBuffer))
744	return RETURN_TO_NEXT_DEBUGGER;
745    }
746}
747
748char *progname;
749
750struct bitRate {
751  BYTE bitRate;
752  const char *bitRateString;
753};
754
755struct bitRate bitRateTable[] =
756{
757  { AIO_BAUD_50    ,      "50" },
758  { AIO_BAUD_75    ,      "75" },
759  { AIO_BAUD_110   ,     "110" },
760  { AIO_BAUD_134p5 ,   "134.5" },
761  { AIO_BAUD_150   ,     "150" },
762  { AIO_BAUD_300   ,     "300" },
763  { AIO_BAUD_600   ,     "600" },
764  { AIO_BAUD_1200  ,    "1200" },
765  { AIO_BAUD_1800  ,    "1800" },
766  { AIO_BAUD_2000  ,    "2000" },
767  { AIO_BAUD_2400  ,    "2400" },
768  { AIO_BAUD_3600  ,    "3600" },
769  { AIO_BAUD_4800  ,    "4800" },
770  { AIO_BAUD_7200  ,    "7200" },
771  { AIO_BAUD_9600  ,    "9600" },
772  { AIO_BAUD_19200 ,   "19200" },
773  { AIO_BAUD_38400 ,   "38400" },
774  { AIO_BAUD_57600 ,   "57600" },
775  { AIO_BAUD_115200,  "115200" },
776  { -1, NULL }
777};
778
779char dataBitsTable[] = "5678";
780
781char *stopBitsTable[] = { "1", "1.5", "2" };
782
783char parity[] = "NOEMS";
784
785/* Start up.  The main thread opens the named serial I/O port, loads
786   the named NLM module and then goes to sleep.  The serial I/O port
787   is named as a board number and a port number.  It would be more DOS
788   like to provide a menu of available serial ports, but I don't want
789   to have to figure out how to do that.  */
790
791int
792main (int argc, char **argv)
793{
794  int hardware, board, port;
795  BYTE bitRate;
796  BYTE dataBits;
797  BYTE stopBits;
798  BYTE parityMode;
799  LONG err;
800  struct debuggerStructure s;
801  int cmdindx;
802  char *cmdlin;
803  int i;
804
805  /* set progname */
806  progname = "gdbserve";
807
808  /* set default serial line */
809  hardware = -1;
810  board = 0;
811  port = 0;
812
813  /* set default serial line characteristics */
814  bitRate  = AIO_BAUD_9600;
815  dataBits = AIO_DATA_BITS_8;
816  stopBits = AIO_STOP_BITS_1;
817  parityMode = AIO_PARITY_NONE;
818
819  cmdindx = 0;
820  for (argc--, argv++; *argv; argc--, argv++)
821    {
822      char *bp;
823      char *ep;
824
825      if (strnicmp(*argv, "BAUD=", 5) == 0)
826	{
827	  struct bitRate *brp;
828
829	  bp = *argv + 5;
830	  for (brp = bitRateTable; brp->bitRate != (BYTE) -1; brp++)
831	    {
832	      if (strcmp(brp->bitRateString, bp) == 0)
833		{
834		  bitRate = brp->bitRate;
835		  break;
836		}
837	    }
838
839	  if (brp->bitRateString == NULL)
840	    {
841	      fprintf(stderr, "%s: %s: unknown or unsupported bit rate",
842		      progname, bp);
843	      exit (1);
844	    }
845	}
846      else if (strnicmp(*argv, "BOARD=", 6) == 0)
847        {
848	  bp = *argv + 6;
849	  board = strtol (bp, &ep, 0);
850	  if (ep == bp || *ep != '\0')
851	    {
852	      fprintf (stderr, "%s: %s: expected integer argument\n",
853		       progname, bp);
854	      exit(1);
855	    }
856	}
857#if 1				/* FIXME: this option has been depricated */
858      else if (strnicmp(*argv, "NODE=", 5) == 0)
859	{
860	  bp = *argv + 5;
861	  board = strtol (bp, &ep, 0);
862	  if (ep == bp || *ep != '\0')
863	    {
864	      fprintf (stderr, "%s: %s: expected integer argument\n",
865		       progname, bp);
866	      exit(1);
867	    }
868	}
869#endif
870      else if (strnicmp(*argv, "PORT=", 5) == 0)
871	{
872	  bp = *argv + 5;
873	  port = strtol (bp, &ep, 0);
874	  if (ep == bp || *ep != '\0')
875	    {
876	      fprintf (stderr, "%s: %s: expected integer argument\n",
877		       progname, bp);
878	      exit(1);
879	    }
880	}
881      else
882	{
883	  break;
884	}
885
886      cmdindx++;
887    }
888
889  if (argc == 0)
890    {
891      fprintf (stderr,
892	       "Usage: load %s [options] program [arguments]\n", progname);
893      exit (1);
894    }
895
896  err = AIOAcquirePort (&hardware, &board, &port, &AIOhandle);
897  if (err != AIO_SUCCESS)
898    {
899      switch (err)
900	{
901	case AIO_PORT_NOT_AVAILABLE:
902	  fprintf (stderr, "Port not available\n");
903	  break;
904
905	case AIO_BOARD_NUMBER_INVALID:
906	case AIO_PORT_NUMBER_INVALID:
907	  fprintf (stderr, "No such port\n");
908	  break;
909
910	default:
911	  fprintf (stderr, "Could not open port: %d\n", err);
912	  break;
913	}
914
915      exit (1);
916    }
917
918  err = AIOConfigurePort (AIOhandle, bitRate, dataBits, stopBits, parityMode,
919			  AIO_HARDWARE_FLOW_CONTROL_OFF);
920
921  if (err == AIO_QUALIFIED_SUCCESS)
922    {
923      AIOPORTCONFIG portConfig;
924
925      fprintf (stderr, "Port configuration changed!\n");
926
927      portConfig.returnLength = sizeof(portConfig);
928      AIOGetPortConfiguration (AIOhandle, &portConfig, NULL);
929
930      fprintf (stderr,
931	       "  Bit Rate: %s, Data Bits: %c, Stop Bits: %s, Parity: %c,\
932 Flow:%s\n",
933	       bitRateTable[portConfig.bitRate].bitRateString,
934	       dataBitsTable[portConfig.dataBits],
935	       stopBitsTable[portConfig.stopBits],
936	       parity[portConfig.parityMode],
937	       portConfig.flowCtrlMode ? "ON" : "OFF");
938    }
939  else if (err != AIO_SUCCESS)
940    {
941      fprintf (stderr, "Could not configure port: %d\n", err);
942      AIOReleasePort (AIOhandle);
943      exit (1);
944    }
945
946  if (AIOSetExternalControl(AIOhandle, AIO_EXTERNAL_CONTROL,
947			    (AIO_EXTCTRL_DTR | AIO_EXTCTRL_RTS))
948      != AIO_SUCCESS)
949    {
950      LONG extStatus, chgdExtStatus;
951
952      fprintf (stderr, "Could not set desired port controls!\n");
953      AIOGetExternalStatus (AIOhandle, &extStatus, &chgdExtStatus);
954      fprintf (stderr, "Port controls now: %d, %d\n", extStatus,
955	       chgdExtStatus);
956    }
957
958  /* Register ourselves as an alternate debugger.  */
959  memset (&s, 0, sizeof s);
960  s.DDSResourceTag = ((struct ResourceTagStructure *)
961		      AllocateResourceTag (GetNLMHandle (),
962					   (BYTE *)"gdbserver",
963					   DebuggerSignature));
964  if (s.DDSResourceTag == 0)
965    {
966      fprintf (stderr, "AllocateResourceTag failed\n");
967      AIOReleasePort (AIOhandle);
968      exit (1);
969    }
970  s.DDSdebuggerEntry = handle_exception;
971  s.DDSFlags = TSS_FRAME_BIT;
972
973  err = RegisterDebuggerRTag (&s, AT_FIRST);
974  if (err != 0)
975    {
976      fprintf (stderr, "RegisterDebuggerRTag failed\n");
977      AIOReleasePort (AIOhandle);
978      exit (1);
979    }
980
981  /* Get the command line we were invoked with, and advance it past
982     our name and the board and port arguments.  */
983  cmdlin = getcmd ((char *) NULL);
984  for (i = 0; i < cmdindx; i++)
985    {
986      while (! isspace (*cmdlin))
987	++cmdlin;
988      while (isspace (*cmdlin))
989	++cmdlin;
990    }
991
992  /* In case GDB is started before us, ack any packets (presumably
993     "$?#xx") sitting there.  */
994  if (! putDebugChar ('+'))
995    {
996      fprintf (stderr, "putDebugChar failed\n");
997      UnRegisterDebugger (&s);
998      AIOReleasePort (AIOhandle);
999      exit (1);
1000    }
1001
1002  mainthread = GetThreadID ();
1003
1004  if (remote_debug > 0)
1005    ConsolePrintf ("About to call LoadModule with \"%s\" %08x\r\n",
1006		   cmdlin, __GetScreenID (GetCurrentScreen()));
1007
1008  /* Start up the module to be debugged.  */
1009  err = LoadModule ((struct ScreenStruct *) __GetScreenID (GetCurrentScreen()),
1010		    (BYTE *)cmdlin, LO_DEBUG);
1011  if (err != 0)
1012    {
1013      fprintf (stderr, "LoadModule failed: %d\n", err);
1014      UnRegisterDebugger (&s);
1015      AIOReleasePort (AIOhandle);
1016      exit (1);
1017    }
1018
1019  /* Wait for the debugger to wake us up.  */
1020  if (remote_debug > 0)
1021    ConsolePrintf ("Suspending main thread (%08x)\r\n", mainthread);
1022  SuspendThread (mainthread);
1023  if (remote_debug > 0)
1024    ConsolePrintf ("Resuming main thread (%08x)\r\n", mainthread);
1025
1026  /* If we are woken up, print an optional error message, deregister
1027     ourselves and exit.  */
1028  if (error_message != NULL)
1029    fprintf (stderr, "%s\n", error_message);
1030  UnRegisterDebugger (&s);
1031  AIOReleasePort (AIOhandle);
1032  exit (0);
1033}
1034