1 /****************************************************************************
3 THIS SOFTWARE IS NOT COPYRIGHTED
5 HP offers the following for use in the public domain. HP makes no
6 warranty with regard to the software or it's performance and the
7 user accepts the software "AS IS" with all faults.
9 HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
10 TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
11 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13 ****************************************************************************/
15 /****************************************************************************
16 * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
18 * Module name: remcom.c $
20 * Date: 91/03/09 12:29:49 $
21 * Contributor: Lake Stevens Instrument Division$
23 * Description: low level support for gdb debugger. $
25 * Considerations: only works on target hardware $
27 * Written by: Glenn Engel $
28 * ModuleState: Experimental $
32 * Modified for 386 by Jim Kingdon, Cygnus Support.
34 * To enable debugger support, two things need to happen. One, a
35 * call to set_debug_traps() is necessary in order to allow any breakpoints
36 * or error conditions to be properly intercepted and reported to gdb.
37 * Two, a breakpoint needs to be generated to begin communication. This
38 * is most easily accomplished by a call to breakpoint(). Breakpoint()
39 * simulates a breakpoint by executing a trap #1.
41 * The external function exceptionHandler() is
42 * used to attach a specific handler to a specific 386 vector number.
43 * It should use the same privilege level it runs at. It should
44 * install it as an interrupt gate so that interrupts are masked
45 * while the handler runs.
46 * Also, need to assign exceptionHook and oldExceptionHook.
48 * Because gdb will sometimes write to the stack area to execute function
49 * calls, this program cannot rely on using the supervisor stack so it
50 * uses it's own stack area reserved in the int array remcomStack.
54 * The following gdb commands are supported:
56 * command function Return value
58 * g return the value of the CPU registers hex data or ENN
59 * G set the value of the CPU registers OK or ENN
61 * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
62 * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
64 * c Resume at current address SNN ( signal NN)
65 * cAA..AA Continue at address AA..AA SNN
67 * s Step one instruction SNN
68 * sAA..AA Step one instruction from AA..AA SNN
72 * ? What was the last sigval ? SNN (signal NN)
74 * All commands and responses are sent with a packet which includes a
75 * checksum. A packet consists of
77 * $<packet info>#<checksum>.
80 * <packet info> :: <characters representing the command or response>
81 * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
83 * When a packet is received, it is first acknowledged with either '+' or '-'.
84 * '+' indicates a successful transfer. '-' indicates a failed transfer.
89 * $m0,10#2a +$00010203040506070809101112131415#42
91 ****************************************************************************/
97 /************************************************************************
99 * external low-level support routines
101 typedef void (*ExceptionHook)(int); /* pointer to function with int parm */
102 typedef void (*Function)(); /* pointer to a function */
104 extern putDebugChar(); /* write a single character */
105 extern getDebugChar(); /* read and return a single char */
107 extern Function exceptionHandler(); /* assign an exception handler */
108 extern ExceptionHook exceptionHook; /* hook variable for errors/exceptions */
110 /************************************************************************/
111 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
112 /* at least NUMREGBYTES*2 are needed for register packets */
115 static char initialized; /* boolean flag. != 0 means we've been initialized */
118 /* debug > 0 prints ill-formed commands in valid packets & checksum errors */
122 static const char hexchars[]="0123456789abcdef";
124 /* Number of bytes of registers. */
125 #define NUMREGBYTES 64
126 enum regnames {EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
127 PC /* also known as eip */,
128 PS /* also known as eflags */,
129 CS, SS, DS, ES, FS, GS};
132 * these should not be static cuz they can be used outside this module
134 int registers[NUMREGBYTES/4];
136 #define STACKSIZE 10000
137 int remcomStack[STACKSIZE/sizeof(int)];
138 static int* stackPtr = &remcomStack[STACKSIZE/sizeof(int) - 1];
141 * In many cases, the system will want to continue exception processing
142 * when a continue command is given.
143 * oldExceptionHook is a function to invoke in this case.
146 static ExceptionHook oldExceptionHook;
148 /*************************** ASSEMBLY CODE MACROS *************************/
152 return_to_prog PARAMS ((void));
154 /* Restore the program's registers (including the stack pointer, which
155 means we get the right stack and don't have to worry about popping our
156 return address and any stack frames and so on) and return. */
158 asm(".globl _return_to_prog");
159 asm("_return_to_prog:");
160 asm(" movw _registers+44, %ss");
161 asm(" movl _registers+16, %esp");
162 asm(" movl _registers+4, %ecx");
163 asm(" movl _registers+8, %edx");
164 asm(" movl _registers+12, %ebx");
165 asm(" movl _registers+20, %ebp");
166 asm(" movl _registers+24, %esi");
167 asm(" movl _registers+28, %edi");
168 asm(" movw _registers+48, %ds");
169 asm(" movw _registers+52, %es");
170 asm(" movw _registers+56, %fs");
171 asm(" movw _registers+60, %gs");
172 asm(" movl _registers+36, %eax");
173 asm(" pushl %eax"); /* saved eflags */
174 asm(" movl _registers+40, %eax");
175 asm(" pushl %eax"); /* saved cs */
176 asm(" movl _registers+32, %eax");
177 asm(" pushl %eax"); /* saved eip */
178 asm(" movl _registers, %eax");
179 /* use iret to restore pc and flags together so
180 that trace flag works right. */
183 #define BREAKPOINT() asm(" int $3");
185 /* Put the error code here just in case the user cares. */
187 /* Likewise, the vector number here (since GDB only gets the signal
188 number through the usual means, and that's not very specific). */
189 int gdb_i386vector = -1;
191 /* GDB stores segment registers in 32-bit words (that's just the way
192 m-i386v.h is written). So zero the appropriate areas in registers. */
193 #define SAVE_REGISTERS1() \
194 asm ("movl %eax, _registers"); \
195 asm ("movl %ecx, _registers+4"); \
196 asm ("movl %edx, _registers+8"); \
197 asm ("movl %ebx, _registers+12"); \
198 asm ("movl %ebp, _registers+20"); \
199 asm ("movl %esi, _registers+24"); \
200 asm ("movl %edi, _registers+28"); \
201 asm ("movw $0, %ax"); \
202 asm ("movw %ds, _registers+48"); \
203 asm ("movw %ax, _registers+50"); \
204 asm ("movw %es, _registers+52"); \
205 asm ("movw %ax, _registers+54"); \
206 asm ("movw %fs, _registers+56"); \
207 asm ("movw %ax, _registers+58"); \
208 asm ("movw %gs, _registers+60"); \
209 asm ("movw %ax, _registers+62");
210 #define SAVE_ERRCODE() \
212 asm ("movl %ebx, _gdb_i386errcode");
213 #define SAVE_REGISTERS2() \
214 asm ("popl %ebx"); /* old eip */ \
215 asm ("movl %ebx, _registers+32"); \
216 asm ("popl %ebx"); /* old cs */ \
217 asm ("movl %ebx, _registers+40"); \
218 asm ("movw %ax, _registers+42"); \
219 asm ("popl %ebx"); /* old eflags */ \
220 asm ("movl %ebx, _registers+36"); \
221 /* Now that we've done the pops, we can save the stack pointer."); */ \
222 asm ("movw %ss, _registers+44"); \
223 asm ("movw %ax, _registers+46"); \
224 asm ("movl %esp, _registers+16");
226 /* See if mem_fault_routine is set, if so just IRET to that address. */
227 #define CHECK_FAULT() \
228 asm ("cmpl $0, _mem_fault_routine"); \
229 asm ("jne mem_fault");
232 /* OK to clobber temp registers; we're just going to end up in set_mem_err. */
233 /* Pop error code from the stack and save it. */
235 asm (" movl %eax, _gdb_i386errcode");
237 asm (" popl %eax"); /* eip */
238 /* We don't want to return there, we want to return to the function
239 pointed to by mem_fault_routine instead. */
240 asm (" movl _mem_fault_routine, %eax");
241 asm (" popl %ecx"); /* cs (low 16 bits; junk in hi 16 bits). */
242 asm (" popl %edx"); /* eflags */
244 /* Remove this stack frame; when we do the iret, we will be going to
245 the start of a function, so we want the stack to look just like it
246 would after a "call" instruction. */
249 /* Push the stuff that iret wants. */
250 asm (" pushl %edx"); /* eflags */
251 asm (" pushl %ecx"); /* cs */
252 asm (" pushl %eax"); /* eip */
254 /* Zero mem_fault_routine. */
255 asm (" movl $0, %eax");
256 asm (" movl %eax, _mem_fault_routine");
260 #define CALL_HOOK() asm("call _remcomHandler");
262 /* This function is called when a i386 exception occurs. It saves
263 * all the cpu regs in the _registers array, munges the stack a bit,
264 * and invokes an exception handler (remcom_handler).
266 * stack on entry: stack on exit:
267 * old eflags vector number
268 * old cs (zero-filled to 32 bits)
272 extern void _catchException3();
274 asm(".globl __catchException3");
275 asm("__catchException3:");
281 /* Same thing for exception 1. */
282 extern void _catchException1();
284 asm(".globl __catchException1");
285 asm("__catchException1:");
291 /* Same thing for exception 0. */
292 extern void _catchException0();
294 asm(".globl __catchException0");
295 asm("__catchException0:");
301 /* Same thing for exception 4. */
302 extern void _catchException4();
304 asm(".globl __catchException4");
305 asm("__catchException4:");
311 /* Same thing for exception 5. */
312 extern void _catchException5();
314 asm(".globl __catchException5");
315 asm("__catchException5:");
321 /* Same thing for exception 6. */
322 extern void _catchException6();
324 asm(".globl __catchException6");
325 asm("__catchException6:");
331 /* Same thing for exception 7. */
332 extern void _catchException7();
334 asm(".globl __catchException7");
335 asm("__catchException7:");
341 /* Same thing for exception 8. */
342 extern void _catchException8();
344 asm(".globl __catchException8");
345 asm("__catchException8:");
352 /* Same thing for exception 9. */
353 extern void _catchException9();
355 asm(".globl __catchException9");
356 asm("__catchException9:");
362 /* Same thing for exception 10. */
363 extern void _catchException10();
365 asm(".globl __catchException10");
366 asm("__catchException10:");
373 /* Same thing for exception 12. */
374 extern void _catchException12();
376 asm(".globl __catchException12");
377 asm("__catchException12:");
384 /* Same thing for exception 16. */
385 extern void _catchException16();
387 asm(".globl __catchException16");
388 asm("__catchException16:");
394 /* For 13, 11, and 14 we have to deal with the CHECK_FAULT stuff. */
396 /* Same thing for exception 13. */
397 extern void _catchException13 ();
399 asm (".globl __catchException13");
400 asm ("__catchException13:");
408 /* Same thing for exception 11. */
409 extern void _catchException11 ();
411 asm (".globl __catchException11");
412 asm ("__catchException11:");
420 /* Same thing for exception 14. */
421 extern void _catchException14 ();
423 asm (".globl __catchException14");
424 asm ("__catchException14:");
433 * remcomHandler is a front end for handle_exception. It moves the
434 * stack pointer into an area reserved for debugger use.
436 asm("_remcomHandler:");
437 asm(" popl %eax"); /* pop off return address */
438 asm(" popl %eax"); /* get the exception number */
439 asm(" movl _stackPtr, %esp"); /* move to remcom stack area */
440 asm(" pushl %eax"); /* push exception onto stack */
441 asm(" call _handle_exception"); /* this never returns */
443 void _returnFromException()
451 if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
452 if ((ch >= '0') && (ch <= '9')) return (ch-'0');
453 if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
458 /* scan for the sequence $<data>#<checksum> */
459 void getpacket(buffer)
462 unsigned char checksum;
463 unsigned char xmitcsum;
469 /* wait around for the start character, ignore all other characters */
470 while ((ch = getDebugChar()) != '$');
476 /* now, read until a # or end of buffer is found */
477 while (count < BUFMAX) {
479 if (ch == '#') break;
480 checksum = checksum + ch;
487 xmitcsum = hex(getDebugChar()) << 4;
488 xmitcsum += hex(getDebugChar());
489 if ((remote_debug ) && (checksum != xmitcsum)) {
490 fprintf(stderr,"bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n",
491 checksum,xmitcsum,buffer);
494 if (checksum != xmitcsum) putDebugChar('-'); /* failed checksum */
496 putDebugChar('+'); /* successful transfer */
497 /* if a sequence char is present, reply the sequence ID */
498 if (buffer[2] == ':') {
499 putDebugChar( buffer[0] );
500 putDebugChar( buffer[1] );
501 /* remove sequence chars from buffer */
502 count = strlen(buffer);
503 for (i=3; i <= count; i++) buffer[i-3] = buffer[i];
507 } while (checksum != xmitcsum);
511 /* send the packet in buffer. */
514 void putpacket(buffer)
517 unsigned char checksum;
521 /* $<packet info>#<checksum>. */
527 while (ch=buffer[count]) {
528 if (! putDebugChar(ch)) return;
534 putDebugChar(hexchars[checksum >> 4]);
535 putDebugChar(hexchars[checksum % 16]);
537 } while (getDebugChar() != '+');
541 char remcomInBuffer[BUFMAX];
542 char remcomOutBuffer[BUFMAX];
546 void debug_error(format, parm)
550 if (remote_debug) fprintf(stderr,format,parm);
553 /* Address of a routine to RTE to if we get a memory fault. */
554 static NORETURN void (*mem_fault_routine)() = NULL;
556 /* Indicate to caller of mem2hex or hex2mem that there has been an
558 static volatile int mem_err = 0;
566 /* These are separate functions so that they are so short and sweet
567 that the compiler won't save any registers (if there is a fault
568 to mem_fault, they won't get restored, so there better not be any
585 /* convert the memory pointed to by mem into hex, placing result in buf */
586 /* return a pointer to the last char put in buf (null) */
587 /* If MAY_FAULT is non-zero, then we should set mem_err in response to
588 a fault; if zero treat a fault like any other fault in the stub. */
589 char* mem2hex(mem, buf, count, may_fault)
599 mem_fault_routine = set_mem_err;
600 for (i=0;i<count;i++) {
601 ch = get_char (mem++);
602 if (may_fault && mem_err)
604 *buf++ = hexchars[ch >> 4];
605 *buf++ = hexchars[ch % 16];
609 mem_fault_routine = NULL;
613 /* convert the hex array pointed to by buf into binary to be placed in mem */
614 /* return a pointer to the character AFTER the last byte written */
615 char* hex2mem(buf, mem, count, may_fault)
625 mem_fault_routine = set_mem_err;
626 for (i=0;i<count;i++) {
627 ch = hex(*buf++) << 4;
628 ch = ch + hex(*buf++);
629 set_char (mem++, ch);
630 if (may_fault && mem_err)
634 mem_fault_routine = NULL;
638 /* this function takes the 386 exception vector and attempts to
639 translate this number into a unix compatible signal value */
640 int computeSignal( exceptionVector )
644 switch (exceptionVector) {
645 case 0 : sigval = 8; break; /* divide by zero */
646 case 1 : sigval = 5; break; /* debug exception */
647 case 3 : sigval = 5; break; /* breakpoint */
648 case 4 : sigval = 16; break; /* into instruction (overflow) */
649 case 5 : sigval = 16; break; /* bound instruction */
650 case 6 : sigval = 4; break; /* Invalid opcode */
651 case 7 : sigval = 8; break; /* coprocessor not available */
652 case 8 : sigval = 7; break; /* double fault */
653 case 9 : sigval = 11; break; /* coprocessor segment overrun */
654 case 10 : sigval = 11; break; /* Invalid TSS */
655 case 11 : sigval = 11; break; /* Segment not present */
656 case 12 : sigval = 11; break; /* stack exception */
657 case 13 : sigval = 11; break; /* general protection */
658 case 14 : sigval = 11; break; /* page fault */
659 case 16 : sigval = 7; break; /* coprocessor error */
661 sigval = 7; /* "software generated"*/
666 /**********************************************/
667 /* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */
668 /* RETURN NUMBER OF CHARS PROCESSED */
669 /**********************************************/
670 int hexToInt(char **ptr, int *intValue)
679 hexValue = hex(**ptr);
682 *intValue = (*intValue <<4) | hexValue;
695 * This function does all command procesing for interfacing to gdb.
697 void handle_exception(int exceptionVector)
704 gdb_i386vector = exceptionVector;
706 if (remote_debug) printf("vector=%d, sr=0x%x, pc=0x%x\n",
711 /* reply to host that an exception has occurred */
712 sigval = computeSignal( exceptionVector );
713 remcomOutBuffer[0] = 'S';
714 remcomOutBuffer[1] = hexchars[sigval >> 4];
715 remcomOutBuffer[2] = hexchars[sigval % 16];
716 remcomOutBuffer[3] = 0;
718 putpacket(remcomOutBuffer);
722 remcomOutBuffer[0] = 0;
723 getpacket(remcomInBuffer);
724 switch (remcomInBuffer[0]) {
725 case '?' : remcomOutBuffer[0] = 'S';
726 remcomOutBuffer[1] = hexchars[sigval >> 4];
727 remcomOutBuffer[2] = hexchars[sigval % 16];
728 remcomOutBuffer[3] = 0;
730 case 'd' : remote_debug = !(remote_debug); /* toggle debug flag */
732 case 'g' : /* return the value of the CPU registers */
733 mem2hex((char*) registers, remcomOutBuffer, NUMREGBYTES, 0);
735 case 'G' : /* set the value of the CPU registers - return OK */
736 hex2mem(&remcomInBuffer[1], (char*) registers, NUMREGBYTES, 0);
737 strcpy(remcomOutBuffer,"OK");
740 /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
742 /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
743 ptr = &remcomInBuffer[1];
744 if (hexToInt(&ptr,&addr))
746 if (hexToInt(&ptr,&length))
750 mem2hex((char*) addr, remcomOutBuffer, length, 1);
752 strcpy (remcomOutBuffer, "E03");
753 debug_error ("memory fault");
759 strcpy(remcomOutBuffer,"E01");
760 debug_error("malformed read memory command: %s",remcomInBuffer);
764 /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
766 /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
767 ptr = &remcomInBuffer[1];
768 if (hexToInt(&ptr,&addr))
770 if (hexToInt(&ptr,&length))
774 hex2mem(ptr, (char*) addr, length, 1);
777 strcpy (remcomOutBuffer, "E03");
778 debug_error ("memory fault");
780 strcpy(remcomOutBuffer,"OK");
787 strcpy(remcomOutBuffer,"E02");
788 debug_error("malformed write memory command: %s",remcomInBuffer);
792 /* cAA..AA Continue at address AA..AA(optional) */
793 /* sAA..AA Step one instruction from AA..AA(optional) */
796 /* try to read optional parameter, pc unchanged if no parm */
797 ptr = &remcomInBuffer[1];
798 if (hexToInt(&ptr,&addr))
799 registers[ PC ] = addr;
801 newPC = registers[ PC];
803 /* clear the trace bit */
804 registers[ PS ] &= 0xfffffeff;
806 /* set the trace bit if we're stepping */
807 if (remcomInBuffer[0] == 's') registers[ PS ] |= 0x100;
810 * If we found a match for the PC AND we are not returning
811 * as a result of a breakpoint (33),
812 * trace exception (9), nmi (31), jmp to
813 * the old exception handler as if this code never ran.
816 /* Don't really think we need this, except maybe for protection
819 * invoke the previous handler.
821 if (oldExceptionHook)
822 (*oldExceptionHook) (frame->exceptionVector);
823 newPC = registers[ PC ]; /* pc may have changed */
826 _returnFromException(); /* this is a jump */
830 /* kill the program */
831 case 'k' : /* do nothing */
836 /* reply to the request */
837 putpacket(remcomOutBuffer);
841 /* this function is used to set up exception handlers for tracing and
843 void set_debug_traps()
845 extern void remcomHandler();
848 stackPtr = &remcomStack[STACKSIZE/sizeof(int) - 1];
850 exceptionHandler (0, _catchException0);
851 exceptionHandler (1, _catchException1);
852 exceptionHandler (3, _catchException3);
853 exceptionHandler (4, _catchException4);
854 exceptionHandler (5, _catchException5);
855 exceptionHandler (6, _catchException6);
856 exceptionHandler (7, _catchException7);
857 exceptionHandler (8, _catchException8);
858 exceptionHandler (9, _catchException9);
859 exceptionHandler (10, _catchException10);
860 exceptionHandler (11, _catchException11);
861 exceptionHandler (12, _catchException12);
862 exceptionHandler (13, _catchException13);
863 exceptionHandler (14, _catchException14);
864 exceptionHandler (16, _catchException16);
866 if (exceptionHook != remcomHandler)
868 oldExceptionHook = exceptionHook;
869 exceptionHook = remcomHandler;
872 /* In case GDB is started before us, ack any packets (presumably
873 "$?#xx") sitting there. */
880 /* This function will generate a breakpoint exception. It is used at the
881 beginning of a program to sync up with a debugger and can be used
882 otherwise as a quick means to stop program execution and "break" into
896 int waitlimit = 1000000;
910 for (i = 0; i < waitlimit; i++) ;