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 SPARC by Stu Grossman, Cygnus Support.
33 * Based on sparc-stub.c, it's modified for SPARClite Debug Unit hardware
34 * breakpoint support to create sparclite-stub.c, by Kung Hsu, Cygnus Support.
36 * This code has been extensively tested on the Fujitsu SPARClite demo board.
38 * To enable debugger support, two things need to happen. One, a
39 * call to set_debug_traps() is necessary in order to allow any breakpoints
40 * or error conditions to be properly intercepted and reported to gdb.
41 * Two, a breakpoint needs to be generated to begin communication. This
42 * is most easily accomplished by a call to breakpoint(). Breakpoint()
43 * simulates a breakpoint by executing a trap #1.
47 * The following gdb commands are supported:
49 * command function Return value
51 * g return the value of the CPU registers hex data or ENN
52 * G set the value of the CPU registers OK or ENN
54 * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
55 * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
57 * c Resume at current address SNN ( signal NN)
58 * cAA..AA Continue at address AA..AA SNN
60 * s Step one instruction SNN
61 * sAA..AA Step one instruction from AA..AA SNN
65 * ? What was the last sigval ? SNN (signal NN)
67 * bBB..BB Set baud rate to BB..BB OK or BNN, then sets
70 * All commands and responses are sent with a packet which includes a
71 * checksum. A packet consists of
73 * $<packet info>#<checksum>.
76 * <packet info> :: <characters representing the command or response>
77 * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
79 * When a packet is received, it is first acknowledged with either '+' or '-'.
80 * '+' indicates a successful transfer. '-' indicates a failed transfer.
85 * $m0,10#2a +$00010203040506070809101112131415#42
87 ****************************************************************************/
92 /************************************************************************
94 * external low-level support routines
97 extern putDebugChar(); /* write a single character */
98 extern getDebugChar(); /* read and return a single char */
100 /************************************************************************/
101 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
102 /* at least NUMREGBYTES*2 are needed for register packets */
105 static int initialized = 0; /* !0 means we've been initialized */
107 extern void breakinst();
108 static void hw_breakpoint();
109 static void set_mem_fault_trap();
110 static void get_in_break_mode();
112 static const char hexchars[]="0123456789abcdef";
116 /* Number of bytes of registers. */
117 #define NUMREGBYTES (NUMREGS * 4)
118 enum regnames {G0, G1, G2, G3, G4, G5, G6, G7,
119 O0, O1, O2, O3, O4, O5, SP, O7,
120 L0, L1, L2, L3, L4, L5, L6, L7,
121 I0, I1, I2, I3, I4, I5, FP, I7,
123 F0, F1, F2, F3, F4, F5, F6, F7,
124 F8, F9, F10, F11, F12, F13, F14, F15,
125 F16, F17, F18, F19, F20, F21, F22, F23,
126 F24, F25, F26, F27, F28, F29, F30, F31,
127 Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR,
128 DIA1, DIA2, DDA1, DDA2, DDV1, DDV2, DCR, DSR };
130 /*************************** ASSEMBLY CODE MACROS *************************/
133 extern void trap_low();
136 .reserve trapstack, 1000 * 4, \"bss\", 8
147 ! This function is called when any SPARC trap (except window overflow or
148 ! underflow) occurs. It makes sure that the invalid register window is still
149 ! available before jumping into C code. It will also restore the world if you
150 ! return from handle_exception.
157 srl %l3, %l0, %l4 ! wim >> cwp
159 bne window_fine ! Branch if not in the invalid window
162 ! Handle window overflow
164 mov %g1, %l4 ! Save g1, we use it to hold the wim
165 srl %l3, 1, %g1 ! Rotate wim right
167 bg good_wim ! Branch if new wim is non-zero
170 ! At this point, we need to bring a 1 into the high order bit of the wim.
171 ! Since we don't want to make any assumptions about the number of register
172 ! windows, we figure it out dynamically so as to setup the wim correctly.
174 not %g1 ! Fill g1 with ones
175 mov %g1, %wim ! Fill the wim with ones
179 mov %wim, %g1 ! Read back the wim
180 inc %g1 ! Now g1 has 1 just to left of wim
181 srl %g1, 1, %g1 ! Now put 1 at top of wim
182 mov %g0, %wim ! Clear wim so that subsequent save
188 save %g0, %g0, %g0 ! Slip into next window
189 mov %g1, %wim ! Install the new wim
191 std %l0, [%sp + 0 * 4] ! save L & I registers
192 std %l2, [%sp + 2 * 4]
193 std %l4, [%sp + 4 * 4]
194 std %l6, [%sp + 6 * 4]
196 std %i0, [%sp + 8 * 4]
197 std %i2, [%sp + 10 * 4]
198 std %i4, [%sp + 12 * 4]
199 std %i6, [%sp + 14 * 4]
201 restore ! Go back to trap window.
202 mov %l4, %g1 ! Restore %g1
205 sethi %hi(in_trap_handler), %l4
206 ld [%lo(in_trap_handler) + %l4], %l5
211 set trapstack+1000*4, %sp ! Switch to trap stack
214 st %l5, [%lo(in_trap_handler) + %l4]
215 sub %sp,(16+1+6+1+80)*4,%sp ! Make room for input & locals
216 ! + hidden arg + arg spill
217 ! + doubleword alignment
218 ! + registers[72] local var
220 std %g0, [%sp + (24 + 0) * 4] ! registers[Gx]
221 std %g2, [%sp + (24 + 2) * 4]
222 std %g4, [%sp + (24 + 4) * 4]
223 std %g6, [%sp + (24 + 6) * 4]
225 std %i0, [%sp + (24 + 8) * 4] ! registers[Ox]
226 std %i2, [%sp + (24 + 10) * 4]
227 std %i4, [%sp + (24 + 12) * 4]
228 std %i6, [%sp + (24 + 14) * 4]
229 ! F0->F31 not implemented
232 st %l4, [%sp + (24 + 64) * 4] ! Y
233 st %l0, [%sp + (24 + 65) * 4] ! PSR
234 st %l3, [%sp + (24 + 66) * 4] ! WIM
235 st %l5, [%sp + (24 + 67) * 4] ! TBR
236 st %l1, [%sp + (24 + 68) * 4] ! PC
237 st %l2, [%sp + (24 + 69) * 4] ! NPC
238 ! CPSR and FPSR not impl
240 mov %l4, %psr ! Turn on traps, disable interrupts
244 call _get_in_break_mode
249 sethi %hi(0xff00), %l5
250 or %l5, %lo(0xff00), %l5
253 st %l4, [%sp + (24 + 72) * 4] ! DIA1, debug instr addr 1
256 st %l4, [%sp + (24 + 73) * 4] ! DIA2, debug instr addr 2
259 st %l4, [%sp + (24 + 74) * 4] ! DDA1, debug data addr 1
262 st %l4, [%sp + (24 + 75) * 4] ! DDA2, debug data addr 2
265 st %l4, [%sp + (24 + 76) * 4] ! DDV1, debug data val 1
268 st %l4, [%sp + (24 + 77) * 4] ! DDV2, debug data val 2
271 st %l4, [%sp + (24 + 78) * 4] ! DCR, debug control reg
274 st %l4, [%sp + (24 + 79) * 4] ! DSR, debug status reg
278 mov %l4, %psr ! Turn on traps, disable interrupts
282 call _handle_exception
283 add %sp, 24 * 4, %o0 ! Pass address of registers
285 ! Reload all of the registers that aren't on the stack
287 ld [%sp + (24 + 1) * 4], %g1 ! registers[Gx]
288 ldd [%sp + (24 + 2) * 4], %g2
289 ldd [%sp + (24 + 4) * 4], %g4
290 ldd [%sp + (24 + 6) * 4], %g6
292 ldd [%sp + (24 + 8) * 4], %i0 ! registers[Ox]
293 ldd [%sp + (24 + 10) * 4], %i2
294 ldd [%sp + (24 + 12) * 4], %i4
295 ldd [%sp + (24 + 14) * 4], %i6
297 sethi %hi(0xff00), %l2
298 or %l2, %lo(0xff00), %l2
299 ldd [%sp + (24 + 72) * 4], %l4 ! DIA1, debug instr addr 1
305 ldd [%sp + (24 + 74) * 4], %l4 ! DDA1, debug data addr 1
312 ldd [%sp + (24 + 76) * 4], %l4 ! DDV1, debug data value 1
319 ld [%sp + (24 + 78) * 4], %l4 ! DCR, debug control reg
320 ld [%sp + (24 + 79) * 4], %l5 ! DSR, debug control reg
331 ldd [%sp + (24 + 64) * 4], %l0 ! Y & PSR
332 ldd [%sp + (24 + 68) * 4], %l2 ! PC & NPC
334 restore ! Ensure that previous window is valid
335 save %g0, %g0, %g0 ! by causing a window_underflow trap
338 mov %l1, %psr ! Make sure that traps are disabled
340 sethi %hi(in_trap_handler), %l4
341 ld [%lo(in_trap_handler) + %l4], %l5
343 st %l5, [%lo(in_trap_handler) + %l4]
345 jmpl %l2, %g0 ! Restore old PC
346 rett %l3 ! Restore old nPC
349 /* Convert ch from a hex digit to an int */
355 if (ch >= 'a' && ch <= 'f')
357 if (ch >= '0' && ch <= '9')
359 if (ch >= 'A' && ch <= 'F')
364 /* scan for the sequence $<data>#<checksum> */
370 unsigned char checksum;
371 unsigned char xmitcsum;
378 /* wait around for the start character, ignore all other characters */
379 while ((ch = (getDebugChar() & 0x7f)) != '$') ;
386 /* now, read until a # or end of buffer is found */
387 while (count < BUFMAX)
389 ch = getDebugChar() & 0x7f;
392 checksum = checksum + ch;
404 xmitcsum = hex(getDebugChar() & 0x7f) << 4;
405 xmitcsum |= hex(getDebugChar() & 0x7f);
407 /* Humans shouldn't have to figure out checksums to type to it. */
411 if (checksum != xmitcsum)
412 putDebugChar('-'); /* failed checksum */
415 putDebugChar('+'); /* successful transfer */
416 /* if a sequence char is present, reply the sequence ID */
417 if (buffer[2] == ':')
419 putDebugChar(buffer[0]);
420 putDebugChar(buffer[1]);
421 /* remove sequence chars from buffer */
422 count = strlen(buffer);
423 for (i=3; i <= count; i++)
424 buffer[i-3] = buffer[i];
429 while (checksum != xmitcsum);
432 /* send the packet in buffer. */
436 unsigned char *buffer;
438 unsigned char checksum;
442 /* $<packet info>#<checksum>. */
449 while (ch = buffer[count])
451 if (! putDebugChar(ch))
458 putDebugChar(hexchars[checksum >> 4]);
459 putDebugChar(hexchars[checksum & 0xf]);
462 while ((getDebugChar() & 0x7f) != '+');
465 static char remcomInBuffer[BUFMAX];
466 static char remcomOutBuffer[BUFMAX];
468 /* Indicate to caller of mem2hex or hex2mem that there has been an
470 static volatile int mem_err = 0;
472 /* Convert the memory pointed to by mem into hex, placing result in buf.
473 * Return a pointer to the last char put in buf (null), in case of mem fault,
475 * If MAY_FAULT is non-zero, then we will handle memory faults by returning
476 * a 0, else treat a fault like any other fault in the stub.
479 static unsigned char *
480 mem2hex(mem, buf, count, may_fault)
488 set_mem_fault_trap(may_fault);
495 *buf++ = hexchars[ch >> 4];
496 *buf++ = hexchars[ch & 0xf];
501 set_mem_fault_trap(0);
506 /* convert the hex array pointed to by buf into binary to be placed in mem
507 * return a pointer to the character AFTER the last byte written */
510 hex2mem(buf, mem, count, may_fault)
519 set_mem_fault_trap(may_fault);
521 for (i=0; i<count; i++)
523 ch = hex(*buf++) << 4;
530 set_mem_fault_trap(0);
535 /* This table contains the mapping between SPARC hardware trap types, and
536 signals, which are primarily what GDB understands. It also indicates
537 which hardware traps we need to commandeer when initializing the stub. */
539 static struct hard_trap_info
541 unsigned char tt; /* Trap type code for SPARClite */
542 unsigned char signo; /* Signal that we map this trap into */
543 } hard_trap_info[] = {
544 {1, SIGSEGV}, /* instruction access error */
545 {2, SIGILL}, /* privileged instruction */
546 {3, SIGILL}, /* illegal instruction */
547 {4, SIGEMT}, /* fp disabled */
548 {36, SIGEMT}, /* cp disabled */
549 {7, SIGBUS}, /* mem address not aligned */
550 {9, SIGSEGV}, /* data access exception */
551 {10, SIGEMT}, /* tag overflow */
552 {128+1, SIGTRAP}, /* ta 1 - normal breakpoint instruction */
553 {255, SIGTRAP}, /* hardware breakpoint */
554 {0, 0} /* Must be last */
557 /* Set up exception handlers for tracing and breakpoints */
562 struct hard_trap_info *ht;
564 for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
565 exceptionHandler(ht->tt, trap_low);
567 /* In case GDB is started before us, ack any packets (presumably
568 "$?#xx") sitting there. */
576 ! Trap handler for memory errors. This just sets mem_err to be non-zero. It
577 ! assumes that %l1 is non-zero. This should be safe, as it is doubtful that
578 ! 0 would ever contain code that could mem fault. This routine will skip
579 ! past the faulting instruction after setting mem_err.
585 sethi %hi(_mem_err), %l0
586 st %l1, [%l0 + %lo(_mem_err)]
592 set_mem_fault_trap(enable)
595 extern void fltr_set_mem_err();
599 exceptionHandler(9, fltr_set_mem_err);
601 exceptionHandler(9, trap_low);
608 _dummy_hw_breakpoint:
616 set_hw_breakpoint_trap(enable)
619 extern void dummy_hw_breakpoint();
622 exceptionHandler(255, dummy_hw_breakpoint);
624 exceptionHandler(255, trap_low);
630 set_hw_breakpoint_trap(1);
633 sethi %hi(0xff10), %l4
634 or %l4, %lo(0xff10), %l4
641 set_hw_breakpoint_trap(0);
644 /* Convert the SPARC hardware trap type code to a unix signal number. */
650 struct hard_trap_info *ht;
652 for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
656 return SIGHUP; /* default for things we don't know about */
660 * While we find nice hex chars, build an int.
661 * Return number of chars processed.
665 hexToInt(char **ptr, int *intValue)
674 hexValue = hex(**ptr);
678 *intValue = (*intValue << 4) | hexValue;
688 * This function does all command procesing for interfacing to gdb. It
689 * returns 1 if you should skip the instruction at the trap address, 0
695 handle_exception (registers)
696 unsigned long *registers;
698 int tt; /* Trap type */
706 /* First, we must force all of the windows to be spilled out */
708 asm(" save %sp, -64, %sp
726 if (registers[PC] == (unsigned long)breakinst)
728 registers[PC] = registers[NPC];
731 sp = (unsigned long *)registers[SP];
733 dsr = (unsigned long)registers[DSR];
740 tt = (registers[TBR] >> 4) & 0xff;
743 /* reply to host that an exception has occurred */
744 sigval = computeSignal(tt);
745 ptr = remcomOutBuffer;
748 *ptr++ = hexchars[sigval >> 4];
749 *ptr++ = hexchars[sigval & 0xf];
751 *ptr++ = hexchars[PC >> 4];
752 *ptr++ = hexchars[PC & 0xf];
754 ptr = mem2hex((char *)®isters[PC], ptr, 4, 0);
757 *ptr++ = hexchars[FP >> 4];
758 *ptr++ = hexchars[FP & 0xf];
760 ptr = mem2hex(sp + 8 + 6, ptr, 4, 0); /* FP */
763 *ptr++ = hexchars[SP >> 4];
764 *ptr++ = hexchars[SP & 0xf];
766 ptr = mem2hex((char *)&sp, ptr, 4, 0);
769 *ptr++ = hexchars[NPC >> 4];
770 *ptr++ = hexchars[NPC & 0xf];
772 ptr = mem2hex((char *)®isters[NPC], ptr, 4, 0);
775 *ptr++ = hexchars[O7 >> 4];
776 *ptr++ = hexchars[O7 & 0xf];
778 ptr = mem2hex((char *)®isters[O7], ptr, 4, 0);
783 putpacket(remcomOutBuffer);
787 remcomOutBuffer[0] = 0;
789 getpacket(remcomInBuffer);
790 switch (remcomInBuffer[0])
793 remcomOutBuffer[0] = 'S';
794 remcomOutBuffer[1] = hexchars[sigval >> 4];
795 remcomOutBuffer[2] = hexchars[sigval & 0xf];
796 remcomOutBuffer[3] = 0;
800 /* toggle debug flag */
803 case 'g': /* return the value of the CPU registers */
805 ptr = remcomOutBuffer;
806 ptr = mem2hex((char *)registers, ptr, 16 * 4, 0); /* G & O regs */
807 ptr = mem2hex(sp + 0, ptr, 16 * 4, 0); /* L & I regs */
808 memset(ptr, '0', 32 * 8); /* Floating point */
809 ptr = mem2hex((char *)®isters[Y],
812 0); /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
813 mem2hex((char *)®isters[DIA1], ptr,
814 8 * 4, 0); /* DIA1, DIA2, DDA1, DDA2, DDV1, DDV2, DCR, DSR */
818 case 'G': /* set the value of the CPU registers - return OK */
820 unsigned long *newsp, psr;
822 psr = registers[PSR];
824 ptr = &remcomInBuffer[1];
825 hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */
826 hex2mem(ptr + 16 * 4 * 2, sp + 0, 16 * 4, 0); /* L & I regs */
827 hex2mem(ptr + 64 * 4 * 2, (char *)®isters[Y],
828 8 * 4, 0); /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
829 hex2mem(ptr + 72 * 4 * 2, (char *)®isters[DIA1],
830 8 * 4, 0); /* DIA1, DIA2, DDA1, DDA2, DDV1, DDV2, DCR, DSR */
832 /* See if the stack pointer has moved. If so, then copy the saved
833 locals and ins to the new location. This keeps the window
834 overflow and underflow routines happy. */
836 newsp = (unsigned long *)registers[SP];
838 sp = memcpy(newsp, sp, 16 * 4);
840 /* Don't allow CWP to be modified. */
842 if (psr != registers[PSR])
843 registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f);
845 strcpy(remcomOutBuffer,"OK");
849 case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
850 /* Try to read %x,%x. */
852 ptr = &remcomInBuffer[1];
854 if (hexToInt(&ptr, &addr)
856 && hexToInt(&ptr, &length))
858 if (mem2hex((char *)addr, remcomOutBuffer, length, 1))
861 strcpy (remcomOutBuffer, "E03");
864 strcpy(remcomOutBuffer,"E01");
867 case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
868 /* Try to read '%x,%x:'. */
870 ptr = &remcomInBuffer[1];
872 if (hexToInt(&ptr, &addr)
874 && hexToInt(&ptr, &length)
877 if (hex2mem(ptr, (char *)addr, length, 1))
878 strcpy(remcomOutBuffer, "OK");
880 strcpy(remcomOutBuffer, "E03");
883 strcpy(remcomOutBuffer, "E02");
886 case 'c': /* cAA..AA Continue at address AA..AA(optional) */
887 /* try to read optional parameter, pc unchanged if no parm */
889 ptr = &remcomInBuffer[1];
890 if (hexToInt(&ptr, &addr))
892 registers[PC] = addr;
893 registers[NPC] = addr + 4;
896 /* Need to flush the instruction cache here, as we may have deposited a
897 breakpoint, and the icache probably has no way of knowing that a data ref to
898 some location may have changed something that is in the instruction cache.
904 /* kill the program */
905 case 'k' : /* do nothing */
908 case 't': /* Test feature */
909 asm (" std %f30,[%sp]");
912 case 'r': /* Reset */
918 Disabled until we can unscrew this properly
920 case 'b': /* bBB... Set baud rate to BB... */
923 extern void set_timer_3();
925 ptr = &remcomInBuffer[1];
926 if (!hexToInt(&ptr, &baudrate))
928 strcpy(remcomOutBuffer,"B01");
932 /* Convert baud rate to uart clock divider */
945 strcpy(remcomOutBuffer,"B02");
949 putpacket("OK"); /* Ack before changing speed */
950 set_timer_3(baudrate); /* Set it */
956 /* reply to the request */
957 putpacket(remcomOutBuffer);
961 /* This function will generate a breakpoint exception. It is used at the
962 beginning of a program to sync up with a debugger and can be used
963 otherwise as a quick means to stop program execution and "break" into
972 asm(" .globl _breakinst