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 (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 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 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 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 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 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 379 get_char (char *addr) 380 { 381 return *addr; 382 } 383 384 void 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 * 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 * 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 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 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 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 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