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
99 struct 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. */
138 static int mainthread;
139
140 /* An error message for the main thread to print. */
141 static char *error_message;
142
143 /* The AIO port handle. */
144 static 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. */
152 static int remote_debug = 1;
153
154 static const char hexchars[] = "0123456789abcdef";
155
156 unsigned char breakpoint_insn[] = BREAKPOINT;
157
158 char *mem2hex (void *mem, char *buf, int count, int may_fault);
159 char *hex2mem (char *buf, void *mem, int count, int may_fault);
160 extern void set_step_traps (struct StackFrame *);
161 extern void clear_step_traps (struct StackFrame *);
162
__main()163 static 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
168 static int
getDebugChar(void)169 getDebugChar (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
193 static int
putDebugChar(unsigned char c)194 putDebugChar (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
211 static int
hex(char ch)212 hex (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
226 static int
getpacket(char * buffer)227 getpacket (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
311 static int
putpacket(char * buffer)312 putpacket (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
351 static char remcomInBuffer[BUFMAX];
352 static char remcomOutBuffer[BUFMAX];
353 static short error;
354
355 static void
debug_error(char * format,char * parm)356 debug_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. */
366 static int mem_may_fault;
367
368 /* Indicate to caller of mem2hex or hex2mem that there has been an
369 error. */
370 volatile 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
378 int
get_char(char * addr)379 get_char (char *addr)
380 {
381 return *addr;
382 }
383
384 void
set_char(char * addr,int val)385 set_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
396 char *
mem2hex(void * mem,char * buf,int count,int may_fault)397 mem2hex (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
420 char *
hex2mem(char * buf,void * mem,int count,int may_fault)421 hex2mem (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
443 int
computeSignal(int exceptionVector)444 computeSignal (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 /**********************************************/
474 static int
hexToInt(char ** ptr,int * intValue)475 hexToInt (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
503 static LONG
handle_exception(struct StackFrame * frame)504 handle_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
748 char *progname;
749
750 struct bitRate {
751 BYTE bitRate;
752 const char *bitRateString;
753 };
754
755 struct 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
779 char dataBitsTable[] = "5678";
780
781 char *stopBitsTable[] = { "1", "1.5", "2" };
782
783 char 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
791 int
main(int argc,char ** argv)792 main (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