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 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 169 getDebugChar () 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 194 putDebugChar (c) 195 unsigned char c; 196 { 197 int err; 198 LONG put; 199 200 put = 0; 201 while (put < 1) 202 { 203 err = AIOWriteData (AIOhandle, (char *) &c, 1, &put); 204 if (err != 0) 205 ConsolePrintf ("AIOWriteData: err = %d, put = %d\r\n", err, put); 206 } 207 return 1; 208 } 209 210 /* Turn a hex character into a number. */ 211 212 static int 213 hex (ch) 214 char ch; 215 { 216 if ((ch >= 'a') && (ch <= 'f')) 217 return (ch-'a'+10); 218 if ((ch >= '0') && (ch <= '9')) 219 return (ch-'0'); 220 if ((ch >= 'A') && (ch <= 'F')) 221 return (ch-'A'+10); 222 return (-1); 223 } 224 225 /* Scan for the sequence $<data>#<checksum>. Returns 0 on failure, 226 non-zero on success. */ 227 228 static int 229 getpacket (buffer) 230 char * buffer; 231 { 232 unsigned char checksum; 233 unsigned char xmitcsum; 234 int i; 235 int count; 236 int ch; 237 238 do 239 { 240 /* wait around for the start character, ignore all other characters */ 241 while ((ch = getDebugChar()) != '$') 242 if (ch == -1) 243 return 0; 244 checksum = 0; 245 xmitcsum = -1; 246 247 count = 0; 248 249 /* now, read until a # or end of buffer is found */ 250 while (count < BUFMAX) 251 { 252 ch = getDebugChar(); 253 if (ch == -1) 254 return 0; 255 if (ch == '#') 256 break; 257 checksum = checksum + ch; 258 buffer[count] = ch; 259 count = count + 1; 260 } 261 buffer[count] = 0; 262 263 if (ch == '#') 264 { 265 ch = getDebugChar (); 266 if (ch == -1) 267 return 0; 268 xmitcsum = hex(ch) << 4; 269 ch = getDebugChar (); 270 if (ch == -1) 271 return 0; 272 xmitcsum += hex(ch); 273 274 if (checksum != xmitcsum) 275 { 276 if (remote_debug) 277 ConsolePrintf ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n", 278 checksum,xmitcsum,buffer); 279 /* failed checksum */ 280 if (! putDebugChar('-')) 281 return 0; 282 return 1; 283 } 284 else 285 { 286 /* successful transfer */ 287 if (! putDebugChar('+')) 288 return 0; 289 /* if a sequence char is present, reply the sequence ID */ 290 if (buffer[2] == ':') 291 { 292 if (! putDebugChar (buffer[0]) 293 || ! putDebugChar (buffer[1])) 294 return 0; 295 /* remove sequence chars from buffer */ 296 count = strlen(buffer); 297 for (i=3; i <= count; i++) 298 buffer[i-3] = buffer[i]; 299 } 300 } 301 } 302 } 303 while (checksum != xmitcsum); 304 305 if (remote_debug) 306 ConsolePrintf ("Received packet \"%s\"\r\n", buffer); 307 308 return 1; 309 } 310 311 /* Send the packet in buffer. Returns 0 on failure, non-zero on 312 success. */ 313 314 static int 315 putpacket (buffer) 316 char * buffer; 317 { 318 unsigned char checksum; 319 int count; 320 int ch; 321 322 if (remote_debug) 323 ConsolePrintf ("Sending packet \"%s\"\r\n", buffer); 324 325 /* $<packet info>#<checksum>. */ 326 do 327 { 328 if (! putDebugChar('$')) 329 return 0; 330 checksum = 0; 331 count = 0; 332 333 while (ch=buffer[count]) 334 { 335 if (! putDebugChar(ch)) 336 return 0; 337 checksum += ch; 338 count += 1; 339 } 340 341 if (! putDebugChar('#') 342 || ! putDebugChar(hexchars[checksum >> 4]) 343 || ! putDebugChar(hexchars[checksum % 16])) 344 return 0; 345 346 ch = getDebugChar (); 347 if (ch == -1) 348 return 0; 349 } 350 while (ch != '+'); 351 352 return 1; 353 } 354 355 static char remcomInBuffer[BUFMAX]; 356 static char remcomOutBuffer[BUFMAX]; 357 static short error; 358 359 static void 360 debug_error (format, parm) 361 char *format; 362 char *parm; 363 { 364 if (remote_debug) 365 { 366 ConsolePrintf (format, parm); 367 ConsolePrintf ("\n"); 368 } 369 } 370 371 /* This is set if we could get a memory access fault. */ 372 static int mem_may_fault; 373 374 /* Indicate to caller of mem2hex or hex2mem that there has been an 375 error. */ 376 volatile int mem_err = 0; 377 378 #ifndef ALTERNATE_MEM_FUNCS 379 /* These are separate functions so that they are so short and sweet 380 that the compiler won't save any registers (if there is a fault 381 to mem_fault, they won't get restored, so there better not be any 382 saved). */ 383 384 int 385 get_char (addr) 386 char *addr; 387 { 388 return *addr; 389 } 390 391 void 392 set_char (addr, val) 393 char *addr; 394 int val; 395 { 396 *addr = val; 397 } 398 #endif /* ALTERNATE_MEM_FUNCS */ 399 400 /* convert the memory pointed to by mem into hex, placing result in buf */ 401 /* return a pointer to the last char put in buf (null) */ 402 /* If MAY_FAULT is non-zero, then we should set mem_err in response to 403 a fault; if zero treat a fault like any other fault in the stub. */ 404 405 char * 406 mem2hex (mem, buf, count, may_fault) 407 void *mem; 408 char *buf; 409 int count; 410 int may_fault; 411 { 412 int i; 413 unsigned char ch; 414 char *ptr = mem; 415 416 mem_may_fault = may_fault; 417 for (i = 0; i < count; i++) 418 { 419 ch = get_char (ptr++); 420 if (may_fault && mem_err) 421 return (buf); 422 *buf++ = hexchars[ch >> 4]; 423 *buf++ = hexchars[ch % 16]; 424 } 425 *buf = 0; 426 mem_may_fault = 0; 427 return(buf); 428 } 429 430 /* convert the hex array pointed to by buf into binary to be placed in mem */ 431 /* return a pointer to the character AFTER the last byte written */ 432 433 char * 434 hex2mem (buf, mem, count, may_fault) 435 char *buf; 436 void *mem; 437 int count; 438 int may_fault; 439 { 440 int i; 441 unsigned char ch; 442 char *ptr = mem; 443 444 mem_may_fault = may_fault; 445 for (i=0;i<count;i++) 446 { 447 ch = hex(*buf++) << 4; 448 ch = ch + hex(*buf++); 449 set_char (ptr++, ch); 450 if (may_fault && mem_err) 451 return (ptr); 452 } 453 mem_may_fault = 0; 454 return(mem); 455 } 456 457 /* This function takes the 386 exception vector and attempts to 458 translate this number into a unix compatible signal value. */ 459 460 int 461 computeSignal (exceptionVector) 462 int exceptionVector; 463 { 464 int sigval; 465 switch (exceptionVector) 466 { 467 case 0 : sigval = 8; break; /* divide by zero */ 468 case 1 : sigval = 5; break; /* debug exception */ 469 case 3 : sigval = 5; break; /* breakpoint */ 470 case 4 : sigval = 16; break; /* into instruction (overflow) */ 471 case 5 : sigval = 16; break; /* bound instruction */ 472 case 6 : sigval = 4; break; /* Invalid opcode */ 473 case 7 : sigval = 8; break; /* coprocessor not available */ 474 case 8 : sigval = 7; break; /* double fault */ 475 case 9 : sigval = 11; break; /* coprocessor segment overrun */ 476 case 10 : sigval = 11; break; /* Invalid TSS */ 477 case 11 : sigval = 11; break; /* Segment not present */ 478 case 12 : sigval = 11; break; /* stack exception */ 479 case 13 : sigval = 11; break; /* general protection */ 480 case 14 : sigval = 11; break; /* page fault */ 481 case 16 : sigval = 7; break; /* coprocessor error */ 482 default: 483 sigval = 7; /* "software generated"*/ 484 } 485 return (sigval); 486 } 487 488 /**********************************************/ 489 /* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ 490 /* RETURN NUMBER OF CHARS PROCESSED */ 491 /**********************************************/ 492 static int 493 hexToInt(ptr, intValue) 494 char **ptr; 495 int *intValue; 496 { 497 int numChars = 0; 498 int hexValue; 499 500 *intValue = 0; 501 502 while (**ptr) 503 { 504 hexValue = hex(**ptr); 505 if (hexValue >=0) 506 { 507 *intValue = (*intValue <<4) | hexValue; 508 numChars ++; 509 } 510 else 511 break; 512 513 (*ptr)++; 514 } 515 516 return (numChars); 517 } 518 519 /* This function does all command processing for interfacing to gdb. 520 It is called whenever an exception occurs in the module being 521 debugged. */ 522 523 static LONG 524 handle_exception (frame) 525 struct StackFrame *frame; 526 { 527 int addr, length; 528 char *ptr; 529 static struct DBG_LoadDefinitionStructure *ldinfo = 0; 530 static unsigned char first_insn[BREAKPOINT_SIZE]; /* The first instruction in the program. */ 531 532 #if 0 533 /* According to some documentation from Novell, the bell sometimes 534 may be ringing at this point. This can be stopped on Netware 4 535 systems by calling the undocumented StopBell() function. */ 536 537 StopBell (); 538 #endif 539 540 if (remote_debug) 541 { 542 ConsolePrintf ("vector=%d: %s, pc=%08x, thread=%08x\r\n", 543 frame->ExceptionNumber, 544 frame->ExceptionDescription, 545 frame->ExceptionPC, 546 GetThreadID ()); 547 } 548 549 switch (frame->ExceptionNumber) 550 { 551 case START_NLM_EVENT: 552 /* If the NLM just started, we record the module load information 553 and the thread ID, and set a breakpoint at the first instruction 554 in the program. */ 555 556 ldinfo = ((struct DBG_LoadDefinitionStructure *) 557 frame->ExceptionErrorCode); 558 memcpy (first_insn, ldinfo->LDInitializationProcedure, 559 BREAKPOINT_SIZE); 560 memcpy (ldinfo->LDInitializationProcedure, breakpoint_insn, 561 BREAKPOINT_SIZE); 562 flush_i_cache (); 563 return RETURN_TO_PROGRAM; 564 565 case ENTER_DEBUGGER_EVENT: 566 case KEYBOARD_BREAK_EVENT: 567 /* Pass some events on to the next debugger, in case it will handle 568 them. */ 569 return RETURN_TO_NEXT_DEBUGGER; 570 571 case 3: /* Breakpoint */ 572 /* After we've reached the initial breakpoint, reset it. */ 573 if (frame->ExceptionPC - DECR_PC_AFTER_BREAK == (LONG) ldinfo->LDInitializationProcedure 574 && memcmp (ldinfo->LDInitializationProcedure, breakpoint_insn, 575 BREAKPOINT_SIZE) == 0) 576 { 577 memcpy (ldinfo->LDInitializationProcedure, first_insn, 578 BREAKPOINT_SIZE); 579 frame->ExceptionPC -= DECR_PC_AFTER_BREAK; 580 flush_i_cache (); 581 } 582 /* Normal breakpoints end up here */ 583 do_status (remcomOutBuffer, frame); 584 break; 585 586 default: 587 /* At the moment, we don't care about most of the unusual NetWare 588 exceptions. */ 589 if (frame->ExceptionNumber > 31) 590 return RETURN_TO_PROGRAM; 591 592 /* Most machine level exceptions end up here */ 593 do_status (remcomOutBuffer, frame); 594 break; 595 596 case 11: /* Segment not present */ 597 case 13: /* General protection */ 598 case 14: /* Page fault */ 599 /* If we get a GP fault, and mem_may_fault is set, and the 600 instruction pointer is near set_char or get_char, then we caused 601 the fault ourselves accessing an illegal memory location. */ 602 if (mem_may_fault 603 && ((frame->ExceptionPC >= (long) &set_char 604 && frame->ExceptionPC < (long) &set_char + 50) 605 || (frame->ExceptionPC >= (long) &get_char 606 && frame->ExceptionPC < (long) &get_char + 50))) 607 { 608 mem_err = 1; 609 /* Point the instruction pointer at an assembly language stub 610 which just returns from the function. */ 611 612 frame->ExceptionPC += 4; /* Skip the load or store */ 613 614 /* Keep going. This will act as though it returned from 615 set_char or get_char. The calling routine will check 616 mem_err, and do the right thing. */ 617 return RETURN_TO_PROGRAM; 618 } 619 /* Random mem fault, report it */ 620 do_status (remcomOutBuffer, frame); 621 break; 622 623 case TERMINATE_NLM_EVENT: 624 /* There is no way to get the exit status. */ 625 sprintf (remcomOutBuffer, "W%02x", 0); 626 break; /* We generate our own status */ 627 } 628 629 /* FIXME: How do we know that this exception has anything to do with 630 the program we are debugging? We can check whether the PC is in 631 the range of the module we are debugging, but that doesn't help 632 much since an error could occur in a library routine. */ 633 634 clear_step_traps (frame); 635 636 if (! putpacket(remcomOutBuffer)) 637 return RETURN_TO_NEXT_DEBUGGER; 638 639 if (frame->ExceptionNumber == TERMINATE_NLM_EVENT) 640 { 641 ResumeThread (mainthread); 642 return RETURN_TO_PROGRAM; 643 } 644 645 while (1) 646 { 647 error = 0; 648 remcomOutBuffer[0] = 0; 649 if (! getpacket (remcomInBuffer)) 650 return RETURN_TO_NEXT_DEBUGGER; 651 switch (remcomInBuffer[0]) 652 { 653 case '?': 654 do_status (remcomOutBuffer, frame); 655 break; 656 case 'd': 657 remote_debug = !(remote_debug); /* toggle debug flag */ 658 break; 659 case 'g': 660 /* return the value of the CPU registers */ 661 frame_to_registers (frame, remcomOutBuffer); 662 break; 663 case 'G': 664 /* set the value of the CPU registers - return OK */ 665 registers_to_frame (&remcomInBuffer[1], frame); 666 strcpy(remcomOutBuffer,"OK"); 667 break; 668 669 case 'm': 670 /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ 671 /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ 672 ptr = &remcomInBuffer[1]; 673 if (hexToInt(&ptr,&addr)) 674 if (*(ptr++) == ',') 675 if (hexToInt(&ptr,&length)) 676 { 677 ptr = 0; 678 mem_err = 0; 679 mem2hex((char*) addr, remcomOutBuffer, length, 1); 680 if (mem_err) 681 { 682 strcpy (remcomOutBuffer, "E03"); 683 debug_error ("memory fault"); 684 } 685 } 686 687 if (ptr) 688 { 689 strcpy(remcomOutBuffer,"E01"); 690 debug_error("malformed read memory command: %s",remcomInBuffer); 691 } 692 break; 693 694 case 'M': 695 /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ 696 /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ 697 ptr = &remcomInBuffer[1]; 698 if (hexToInt(&ptr,&addr)) 699 if (*(ptr++) == ',') 700 if (hexToInt(&ptr,&length)) 701 if (*(ptr++) == ':') 702 { 703 mem_err = 0; 704 hex2mem(ptr, (char*) addr, length, 1); 705 706 if (mem_err) 707 { 708 strcpy (remcomOutBuffer, "E03"); 709 debug_error ("memory fault"); 710 } 711 else 712 { 713 strcpy(remcomOutBuffer,"OK"); 714 } 715 716 ptr = 0; 717 } 718 if (ptr) 719 { 720 strcpy(remcomOutBuffer,"E02"); 721 debug_error("malformed write memory command: %s",remcomInBuffer); 722 } 723 break; 724 725 case 'c': 726 case 's': 727 /* cAA..AA Continue at address AA..AA(optional) */ 728 /* sAA..AA Step one instruction from AA..AA(optional) */ 729 /* try to read optional parameter, pc unchanged if no parm */ 730 ptr = &remcomInBuffer[1]; 731 if (hexToInt(&ptr,&addr)) 732 { 733 /* registers[PC_REGNUM].lo = addr;*/ 734 fprintf (stderr, "Setting PC to 0x%x\n", addr); 735 while (1); 736 } 737 738 if (remcomInBuffer[0] == 's') 739 set_step_traps (frame); 740 741 flush_i_cache (); 742 return RETURN_TO_PROGRAM; 743 744 case 'k': 745 /* kill the program */ 746 KillMe (ldinfo); 747 ResumeThread (mainthread); 748 return RETURN_TO_PROGRAM; 749 750 case 'q': /* Query message */ 751 if (strcmp (&remcomInBuffer[1], "Offsets") == 0) 752 { 753 sprintf (remcomOutBuffer, "Text=%x;Data=%x;Bss=%x", 754 ldinfo->LDCodeImageOffset, 755 ldinfo->LDDataImageOffset, 756 ldinfo->LDDataImageOffset + ldinfo->LDDataImageLength); 757 } 758 else 759 sprintf (remcomOutBuffer, "E04, Unknown query %s", &remcomInBuffer[1]); 760 break; 761 } 762 763 /* reply to the request */ 764 if (! putpacket(remcomOutBuffer)) 765 return RETURN_TO_NEXT_DEBUGGER; 766 } 767 } 768 769 char *progname; 770 771 struct bitRate { 772 BYTE bitRate; 773 const char *bitRateString; 774 }; 775 776 struct bitRate bitRateTable[] = 777 { 778 { AIO_BAUD_50 , "50" }, 779 { AIO_BAUD_75 , "75" }, 780 { AIO_BAUD_110 , "110" }, 781 { AIO_BAUD_134p5 , "134.5" }, 782 { AIO_BAUD_150 , "150" }, 783 { AIO_BAUD_300 , "300" }, 784 { AIO_BAUD_600 , "600" }, 785 { AIO_BAUD_1200 , "1200" }, 786 { AIO_BAUD_1800 , "1800" }, 787 { AIO_BAUD_2000 , "2000" }, 788 { AIO_BAUD_2400 , "2400" }, 789 { AIO_BAUD_3600 , "3600" }, 790 { AIO_BAUD_4800 , "4800" }, 791 { AIO_BAUD_7200 , "7200" }, 792 { AIO_BAUD_9600 , "9600" }, 793 { AIO_BAUD_19200 , "19200" }, 794 { AIO_BAUD_38400 , "38400" }, 795 { AIO_BAUD_57600 , "57600" }, 796 { AIO_BAUD_115200, "115200" }, 797 { -1, NULL } 798 }; 799 800 char dataBitsTable[] = "5678"; 801 802 char *stopBitsTable[] = { "1", "1.5", "2" }; 803 804 char parity[] = "NOEMS"; 805 806 /* Start up. The main thread opens the named serial I/O port, loads 807 the named NLM module and then goes to sleep. The serial I/O port 808 is named as a board number and a port number. It would be more DOS 809 like to provide a menu of available serial ports, but I don't want 810 to have to figure out how to do that. */ 811 812 int 813 main (argc, argv) 814 int argc; 815 char **argv; 816 { 817 int hardware, board, port; 818 BYTE bitRate; 819 BYTE dataBits; 820 BYTE stopBits; 821 BYTE parityMode; 822 LONG err; 823 struct debuggerStructure s; 824 int cmdindx; 825 char *cmdlin; 826 int i; 827 828 /* set progname */ 829 progname = "gdbserve"; 830 831 /* set default serial line */ 832 hardware = -1; 833 board = 0; 834 port = 0; 835 836 /* set default serial line characteristics */ 837 bitRate = AIO_BAUD_9600; 838 dataBits = AIO_DATA_BITS_8; 839 stopBits = AIO_STOP_BITS_1; 840 parityMode = AIO_PARITY_NONE; 841 842 cmdindx = 0; 843 for (argc--, argv++; *argv; argc--, argv++) 844 { 845 char *bp; 846 char *ep; 847 848 if (strnicmp(*argv, "BAUD=", 5) == 0) 849 { 850 struct bitRate *brp; 851 852 bp = *argv + 5; 853 for (brp = bitRateTable; brp->bitRate != (BYTE) -1; brp++) 854 { 855 if (strcmp(brp->bitRateString, bp) == 0) 856 { 857 bitRate = brp->bitRate; 858 break; 859 } 860 } 861 862 if (brp->bitRateString == NULL) 863 { 864 fprintf(stderr, "%s: %s: unknown or unsupported bit rate", 865 progname, bp); 866 exit (1); 867 } 868 } 869 else if (strnicmp(*argv, "BOARD=", 6) == 0) 870 { 871 bp = *argv + 6; 872 board = strtol (bp, &ep, 0); 873 if (ep == bp || *ep != '\0') 874 { 875 fprintf (stderr, "%s: %s: expected integer argument\n", 876 progname, bp); 877 exit(1); 878 } 879 } 880 #if 1 /* FIXME: this option has been depricated */ 881 else if (strnicmp(*argv, "NODE=", 5) == 0) 882 { 883 bp = *argv + 5; 884 board = strtol (bp, &ep, 0); 885 if (ep == bp || *ep != '\0') 886 { 887 fprintf (stderr, "%s: %s: expected integer argument\n", 888 progname, bp); 889 exit(1); 890 } 891 } 892 #endif 893 else if (strnicmp(*argv, "PORT=", 5) == 0) 894 { 895 bp = *argv + 5; 896 port = strtol (bp, &ep, 0); 897 if (ep == bp || *ep != '\0') 898 { 899 fprintf (stderr, "%s: %s: expected integer argument\n", 900 progname, bp); 901 exit(1); 902 } 903 } 904 else 905 { 906 break; 907 } 908 909 cmdindx++; 910 } 911 912 if (argc == 0) 913 { 914 fprintf (stderr, 915 "Usage: load %s [options] program [arguments]\n", progname); 916 exit (1); 917 } 918 919 err = AIOAcquirePort (&hardware, &board, &port, &AIOhandle); 920 if (err != AIO_SUCCESS) 921 { 922 switch (err) 923 { 924 case AIO_PORT_NOT_AVAILABLE: 925 fprintf (stderr, "Port not available\n"); 926 break; 927 928 case AIO_BOARD_NUMBER_INVALID: 929 case AIO_PORT_NUMBER_INVALID: 930 fprintf (stderr, "No such port\n"); 931 break; 932 933 default: 934 fprintf (stderr, "Could not open port: %d\n", err); 935 break; 936 } 937 938 exit (1); 939 } 940 941 err = AIOConfigurePort (AIOhandle, bitRate, dataBits, stopBits, parityMode, 942 AIO_HARDWARE_FLOW_CONTROL_OFF); 943 944 if (err == AIO_QUALIFIED_SUCCESS) 945 { 946 AIOPORTCONFIG portConfig; 947 948 fprintf (stderr, "Port configuration changed!\n"); 949 950 portConfig.returnLength = sizeof(portConfig); 951 AIOGetPortConfiguration (AIOhandle, &portConfig, NULL); 952 953 fprintf (stderr, 954 " Bit Rate: %s, Data Bits: %c, Stop Bits: %s, Parity: %c,\ 955 Flow:%s\n", 956 bitRateTable[portConfig.bitRate].bitRateString, 957 dataBitsTable[portConfig.dataBits], 958 stopBitsTable[portConfig.stopBits], 959 parity[portConfig.parityMode], 960 portConfig.flowCtrlMode ? "ON" : "OFF"); 961 } 962 else if (err != AIO_SUCCESS) 963 { 964 fprintf (stderr, "Could not configure port: %d\n", err); 965 AIOReleasePort (AIOhandle); 966 exit (1); 967 } 968 969 if (AIOSetExternalControl(AIOhandle, AIO_EXTERNAL_CONTROL, 970 (AIO_EXTCTRL_DTR | AIO_EXTCTRL_RTS)) 971 != AIO_SUCCESS) 972 { 973 LONG extStatus, chgdExtStatus; 974 975 fprintf (stderr, "Could not set desired port controls!\n"); 976 AIOGetExternalStatus (AIOhandle, &extStatus, &chgdExtStatus); 977 fprintf (stderr, "Port controls now: %d, %d\n", extStatus, 978 chgdExtStatus); 979 } 980 981 /* Register ourselves as an alternate debugger. */ 982 memset (&s, 0, sizeof s); 983 s.DDSResourceTag = ((struct ResourceTagStructure *) 984 AllocateResourceTag (GetNLMHandle (), 985 (BYTE *)"gdbserver", 986 DebuggerSignature)); 987 if (s.DDSResourceTag == 0) 988 { 989 fprintf (stderr, "AllocateResourceTag failed\n"); 990 AIOReleasePort (AIOhandle); 991 exit (1); 992 } 993 s.DDSdebuggerEntry = handle_exception; 994 s.DDSFlags = TSS_FRAME_BIT; 995 996 err = RegisterDebuggerRTag (&s, AT_FIRST); 997 if (err != 0) 998 { 999 fprintf (stderr, "RegisterDebuggerRTag failed\n"); 1000 AIOReleasePort (AIOhandle); 1001 exit (1); 1002 } 1003 1004 /* Get the command line we were invoked with, and advance it past 1005 our name and the board and port arguments. */ 1006 cmdlin = getcmd ((char *) NULL); 1007 for (i = 0; i < cmdindx; i++) 1008 { 1009 while (! isspace (*cmdlin)) 1010 ++cmdlin; 1011 while (isspace (*cmdlin)) 1012 ++cmdlin; 1013 } 1014 1015 /* In case GDB is started before us, ack any packets (presumably 1016 "$?#xx") sitting there. */ 1017 if (! putDebugChar ('+')) 1018 { 1019 fprintf (stderr, "putDebugChar failed\n"); 1020 UnRegisterDebugger (&s); 1021 AIOReleasePort (AIOhandle); 1022 exit (1); 1023 } 1024 1025 mainthread = GetThreadID (); 1026 1027 if (remote_debug > 0) 1028 ConsolePrintf ("About to call LoadModule with \"%s\" %08x\r\n", 1029 cmdlin, __GetScreenID (GetCurrentScreen())); 1030 1031 /* Start up the module to be debugged. */ 1032 err = LoadModule ((struct ScreenStruct *) __GetScreenID (GetCurrentScreen()), 1033 (BYTE *)cmdlin, LO_DEBUG); 1034 if (err != 0) 1035 { 1036 fprintf (stderr, "LoadModule failed: %d\n", err); 1037 UnRegisterDebugger (&s); 1038 AIOReleasePort (AIOhandle); 1039 exit (1); 1040 } 1041 1042 /* Wait for the debugger to wake us up. */ 1043 if (remote_debug > 0) 1044 ConsolePrintf ("Suspending main thread (%08x)\r\n", mainthread); 1045 SuspendThread (mainthread); 1046 if (remote_debug > 0) 1047 ConsolePrintf ("Resuming main thread (%08x)\r\n", mainthread); 1048 1049 /* If we are woken up, print an optional error message, deregister 1050 ourselves and exit. */ 1051 if (error_message != NULL) 1052 fprintf (stderr, "%s\n", error_message); 1053 UnRegisterDebugger (&s); 1054 AIOReleasePort (AIOhandle); 1055 exit (0); 1056 } 1057