123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778 |
- /****************************************************************************
- THIS SOFTWARE IS NOT COPYRIGHTED
- HP offers the following for use in the public domain. HP makes no
- warranty with regard to the software or it's performance and the
- user accepts the software "AS IS" with all faults.
- HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
- TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- ****************************************************************************/
- /****************************************************************************
- * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
- *
- * Module name: remcom.c $
- * Revision: 1.34 $
- * Date: 91/03/09 12:29:49 $
- * Contributor: Lake Stevens Instrument Division$
- *
- * Description: low level support for gdb debugger. $
- *
- * Considerations: only works on target hardware $
- *
- * Written by: Glenn Engel $
- * ModuleState: Experimental $
- *
- * NOTES: See Below $
- *
- * Modified for SPARC by Stu Grossman, Cygnus Support.
- *
- * This code has been extensively tested on the Fujitsu SPARClite demo board.
- *
- * To enable debugger support, two things need to happen. One, a
- * call to set_debug_traps() is necessary in order to allow any breakpoints
- * or error conditions to be properly intercepted and reported to gdb.
- * Two, a breakpoint needs to be generated to begin communication. This
- * is most easily accomplished by a call to breakpoint(). Breakpoint()
- * simulates a breakpoint by executing a trap #1.
- *
- *************
- *
- * The following gdb commands are supported:
- *
- * command function Return value
- *
- * g return the value of the CPU registers hex data or ENN
- * G set the value of the CPU registers OK or ENN
- *
- * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
- * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
- *
- * c Resume at current address SNN ( signal NN)
- * cAA..AA Continue at address AA..AA SNN
- *
- * s Step one instruction SNN
- * sAA..AA Step one instruction from AA..AA SNN
- *
- * k kill
- *
- * ? What was the last sigval ? SNN (signal NN)
- *
- * All commands and responses are sent with a packet which includes a
- * checksum. A packet consists of
- *
- * $<packet info>#<checksum>.
- *
- * where
- * <packet info> :: <characters representing the command or response>
- * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
- *
- * When a packet is received, it is first acknowledged with either '+' or '-'.
- * '+' indicates a successful transfer. '-' indicates a failed transfer.
- *
- * Example:
- *
- * Host: Reply:
- * $m0,10#2a +$00010203040506070809101112131415#42
- *
- ****************************************************************************/
- #include <string.h>
- #include <signal.h>
- /************************************************************************
- *
- * external low-level support routines
- */
- extern void putDebugChar(); /* write a single character */
- extern int getDebugChar(); /* read and return a single char */
- /************************************************************************/
- /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
- /* at least NUMREGBYTES*2 are needed for register packets */
- #define BUFMAX 2048
- static int initialized = 0; /* !0 means we've been initialized */
- static void set_mem_fault_trap();
- static const char hexchars[]="0123456789abcdef";
- #define NUMREGS 72
- /* Number of bytes of registers. */
- #define NUMREGBYTES (NUMREGS * 4)
- enum regnames {G0, G1, G2, G3, G4, G5, G6, G7,
- O0, O1, O2, O3, O4, O5, SP, O7,
- L0, L1, L2, L3, L4, L5, L6, L7,
- I0, I1, I2, I3, I4, I5, FP, I7,
- F0, F1, F2, F3, F4, F5, F6, F7,
- F8, F9, F10, F11, F12, F13, F14, F15,
- F16, F17, F18, F19, F20, F21, F22, F23,
- F24, F25, F26, F27, F28, F29, F30, F31,
- Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR };
- /*************************** ASSEMBLY CODE MACROS *************************/
- /* */
- extern void trap_low();
- asm("
- .reserve trapstack, 1000 * 4, \"bss\", 8
- .data
- .align 4
- in_trap_handler:
- .word 0
- .text
- .align 4
- ! This function is called when any SPARC trap (except window overflow or
- ! underflow) occurs. It makes sure that the invalid register window is still
- ! available before jumping into C code. It will also restore the world if you
- ! return from handle_exception.
- .globl _trap_low
- _trap_low:
- mov %psr, %l0
- mov %wim, %l3
- srl %l3, %l0, %l4 ! wim >> cwp
- cmp %l4, 1
- bne window_fine ! Branch if not in the invalid window
- nop
- ! Handle window overflow
- mov %g1, %l4 ! Save g1, we use it to hold the wim
- srl %l3, 1, %g1 ! Rotate wim right
- tst %g1
- bg good_wim ! Branch if new wim is non-zero
- nop
- ! At this point, we need to bring a 1 into the high order bit of the wim.
- ! Since we don't want to make any assumptions about the number of register
- ! windows, we figure it out dynamically so as to setup the wim correctly.
- not %g1 ! Fill g1 with ones
- mov %g1, %wim ! Fill the wim with ones
- nop
- nop
- nop
- mov %wim, %g1 ! Read back the wim
- inc %g1 ! Now g1 has 1 just to left of wim
- srl %g1, 1, %g1 ! Now put 1 at top of wim
- mov %g0, %wim ! Clear wim so that subsequent save
- nop ! won't trap
- nop
- nop
- good_wim:
- save %g0, %g0, %g0 ! Slip into next window
- mov %g1, %wim ! Install the new wim
- std %l0, [%sp + 0 * 4] ! save L & I registers
- std %l2, [%sp + 2 * 4]
- std %l4, [%sp + 4 * 4]
- std %l6, [%sp + 6 * 4]
- std %i0, [%sp + 8 * 4]
- std %i2, [%sp + 10 * 4]
- std %i4, [%sp + 12 * 4]
- std %i6, [%sp + 14 * 4]
- restore ! Go back to trap window.
- mov %l4, %g1 ! Restore %g1
- window_fine:
- sethi %hi(in_trap_handler), %l4
- ld [%lo(in_trap_handler) + %l4], %l5
- tst %l5
- bg recursive_trap
- inc %l5
- set trapstack+1000*4, %sp ! Switch to trap stack
- recursive_trap:
- st %l5, [%lo(in_trap_handler) + %l4]
- sub %sp,(16+1+6+1+72)*4,%sp ! Make room for input & locals
- ! + hidden arg + arg spill
- ! + doubleword alignment
- ! + registers[72] local var
- std %g0, [%sp + (24 + 0) * 4] ! registers[Gx]
- std %g2, [%sp + (24 + 2) * 4]
- std %g4, [%sp + (24 + 4) * 4]
- std %g6, [%sp + (24 + 6) * 4]
- std %i0, [%sp + (24 + 8) * 4] ! registers[Ox]
- std %i2, [%sp + (24 + 10) * 4]
- std %i4, [%sp + (24 + 12) * 4]
- std %i6, [%sp + (24 + 14) * 4]
- ! F0->F31 not implemented
- mov %y, %l4
- mov %tbr, %l5
- st %l4, [%sp + (24 + 64) * 4] ! Y
- st %l0, [%sp + (24 + 65) * 4] ! PSR
- st %l3, [%sp + (24 + 66) * 4] ! WIM
- st %l5, [%sp + (24 + 67) * 4] ! TBR
- st %l1, [%sp + (24 + 68) * 4] ! PC
- st %l2, [%sp + (24 + 69) * 4] ! NPC
- ! CPSR and FPSR not impl
- or %l0, 0xf20, %l4
- mov %l4, %psr ! Turn on traps, disable interrupts
- call _handle_exception
- add %sp, 24 * 4, %o0 ! Pass address of registers
- ! Reload all of the registers that aren't on the stack
- ld [%sp + (24 + 1) * 4], %g1 ! registers[Gx]
- ldd [%sp + (24 + 2) * 4], %g2
- ldd [%sp + (24 + 4) * 4], %g4
- ldd [%sp + (24 + 6) * 4], %g6
- ldd [%sp + (24 + 8) * 4], %i0 ! registers[Ox]
- ldd [%sp + (24 + 10) * 4], %i2
- ldd [%sp + (24 + 12) * 4], %i4
- ldd [%sp + (24 + 14) * 4], %i6
- ldd [%sp + (24 + 64) * 4], %l0 ! Y & PSR
- ldd [%sp + (24 + 68) * 4], %l2 ! PC & NPC
- restore ! Ensure that previous window is valid
- save %g0, %g0, %g0 ! by causing a window_underflow trap
- mov %l0, %y
- mov %l1, %psr ! Make sure that traps are disabled
- ! for rett
- sethi %hi(in_trap_handler), %l4
- ld [%lo(in_trap_handler) + %l4], %l5
- dec %l5
- st %l5, [%lo(in_trap_handler) + %l4]
- jmpl %l2, %g0 ! Restore old PC
- rett %l3 ! Restore old nPC
- ");
- /* Convert ch from a hex digit to an int */
- static int
- hex (unsigned char ch)
- {
- if (ch >= 'a' && ch <= 'f')
- return ch-'a'+10;
- if (ch >= '0' && ch <= '9')
- return ch-'0';
- if (ch >= 'A' && ch <= 'F')
- return ch-'A'+10;
- return -1;
- }
- static char remcomInBuffer[BUFMAX];
- static char remcomOutBuffer[BUFMAX];
- /* scan for the sequence $<data>#<checksum> */
- unsigned char *
- getpacket (void)
- {
- unsigned char *buffer = &remcomInBuffer[0];
- unsigned char checksum;
- unsigned char xmitcsum;
- int count;
- char ch;
- while (1)
- {
- /* wait around for the start character, ignore all other characters */
- while ((ch = getDebugChar ()) != '$')
- ;
- retry:
- checksum = 0;
- xmitcsum = -1;
- count = 0;
- /* now, read until a # or end of buffer is found */
- while (count < BUFMAX - 1)
- {
- ch = getDebugChar ();
- if (ch == '$')
- goto retry;
- if (ch == '#')
- break;
- checksum = checksum + ch;
- buffer[count] = ch;
- count = count + 1;
- }
- buffer[count] = 0;
- if (ch == '#')
- {
- ch = getDebugChar ();
- xmitcsum = hex (ch) << 4;
- ch = getDebugChar ();
- xmitcsum += hex (ch);
- if (checksum != xmitcsum)
- {
- putDebugChar ('-'); /* failed checksum */
- }
- else
- {
- putDebugChar ('+'); /* successful transfer */
- /* if a sequence char is present, reply the sequence ID */
- if (buffer[2] == ':')
- {
- putDebugChar (buffer[0]);
- putDebugChar (buffer[1]);
- return &buffer[3];
- }
- return &buffer[0];
- }
- }
- }
- }
- /* send the packet in buffer. */
- static void
- putpacket (unsigned char *buffer)
- {
- unsigned char checksum;
- int count;
- unsigned char ch;
- /* $<packet info>#<checksum>. */
- do
- {
- putDebugChar('$');
- checksum = 0;
- count = 0;
- while (ch = buffer[count])
- {
- putDebugChar(ch);
- checksum += ch;
- count += 1;
- }
- putDebugChar('#');
- putDebugChar(hexchars[checksum >> 4]);
- putDebugChar(hexchars[checksum & 0xf]);
- }
- while (getDebugChar() != '+');
- }
- /* Indicate to caller of mem2hex or hex2mem that there has been an
- error. */
- static volatile int mem_err = 0;
- /* Convert the memory pointed to by mem into hex, placing result in buf.
- * Return a pointer to the last char put in buf (null), in case of mem fault,
- * return 0.
- * If MAY_FAULT is non-zero, then we will handle memory faults by returning
- * a 0, else treat a fault like any other fault in the stub.
- */
- static unsigned char *
- mem2hex (unsigned char *mem, unsigned char *buf, int count, int may_fault)
- {
- unsigned char ch;
- set_mem_fault_trap(may_fault);
- while (count-- > 0)
- {
- ch = *mem++;
- if (mem_err)
- return 0;
- *buf++ = hexchars[ch >> 4];
- *buf++ = hexchars[ch & 0xf];
- }
- *buf = 0;
- set_mem_fault_trap(0);
- return buf;
- }
- /* convert the hex array pointed to by buf into binary to be placed in mem
- * return a pointer to the character AFTER the last byte written */
- static char *
- hex2mem (unsigned char *buf, unsigned char *mem, int count, int may_fault)
- {
- int i;
- unsigned char ch;
- set_mem_fault_trap(may_fault);
- for (i=0; i<count; i++)
- {
- ch = hex(*buf++) << 4;
- ch |= hex(*buf++);
- *mem++ = ch;
- if (mem_err)
- return 0;
- }
- set_mem_fault_trap(0);
- return mem;
- }
- /* This table contains the mapping between SPARC hardware trap types, and
- signals, which are primarily what GDB understands. It also indicates
- which hardware traps we need to commandeer when initializing the stub. */
- static struct hard_trap_info
- {
- unsigned char tt; /* Trap type code for SPARClite */
- unsigned char signo; /* Signal that we map this trap into */
- } hard_trap_info[] = {
- {1, SIGSEGV}, /* instruction access error */
- {2, SIGILL}, /* privileged instruction */
- {3, SIGILL}, /* illegal instruction */
- {4, SIGEMT}, /* fp disabled */
- {36, SIGEMT}, /* cp disabled */
- {7, SIGBUS}, /* mem address not aligned */
- {9, SIGSEGV}, /* data access exception */
- {10, SIGEMT}, /* tag overflow */
- {128+1, SIGTRAP}, /* ta 1 - normal breakpoint instruction */
- {0, 0} /* Must be last */
- };
- /* Set up exception handlers for tracing and breakpoints */
- void
- set_debug_traps (void)
- {
- struct hard_trap_info *ht;
- for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
- exceptionHandler(ht->tt, trap_low);
- initialized = 1;
- }
- asm ("
- ! Trap handler for memory errors. This just sets mem_err to be non-zero. It
- ! assumes that %l1 is non-zero. This should be safe, as it is doubtful that
- ! 0 would ever contain code that could mem fault. This routine will skip
- ! past the faulting instruction after setting mem_err.
- .text
- .align 4
- _fltr_set_mem_err:
- sethi %hi(_mem_err), %l0
- st %l1, [%l0 + %lo(_mem_err)]
- jmpl %l2, %g0
- rett %l2+4
- ");
- static void
- set_mem_fault_trap (int enable)
- {
- extern void fltr_set_mem_err();
- mem_err = 0;
- if (enable)
- exceptionHandler(9, fltr_set_mem_err);
- else
- exceptionHandler(9, trap_low);
- }
- /* Convert the SPARC hardware trap type code to a unix signal number. */
- static int
- computeSignal (int tt)
- {
- struct hard_trap_info *ht;
- for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
- if (ht->tt == tt)
- return ht->signo;
- return SIGHUP; /* default for things we don't know about */
- }
- /*
- * While we find nice hex chars, build an int.
- * Return number of chars processed.
- */
- static int
- hexToInt(char **ptr, int *intValue)
- {
- int numChars = 0;
- int hexValue;
- *intValue = 0;
- while (**ptr)
- {
- hexValue = hex(**ptr);
- if (hexValue < 0)
- break;
- *intValue = (*intValue << 4) | hexValue;
- numChars ++;
- (*ptr)++;
- }
- return (numChars);
- }
- /*
- * This function does all command procesing for interfacing to gdb. It
- * returns 1 if you should skip the instruction at the trap address, 0
- * otherwise.
- */
- extern void breakinst();
- static void
- handle_exception (unsigned long *registers)
- {
- int tt; /* Trap type */
- int sigval;
- int addr;
- int length;
- char *ptr;
- unsigned long *sp;
- /* First, we must force all of the windows to be spilled out */
- asm(" save %sp, -64, %sp
- save %sp, -64, %sp
- save %sp, -64, %sp
- save %sp, -64, %sp
- save %sp, -64, %sp
- save %sp, -64, %sp
- save %sp, -64, %sp
- save %sp, -64, %sp
- restore
- restore
- restore
- restore
- restore
- restore
- restore
- restore
- ");
- if (registers[PC] == (unsigned long)breakinst)
- {
- registers[PC] = registers[NPC];
- registers[NPC] += 4;
- }
- sp = (unsigned long *)registers[SP];
- tt = (registers[TBR] >> 4) & 0xff;
- /* reply to host that an exception has occurred */
- sigval = computeSignal(tt);
- ptr = remcomOutBuffer;
- *ptr++ = 'T';
- *ptr++ = hexchars[sigval >> 4];
- *ptr++ = hexchars[sigval & 0xf];
- *ptr++ = hexchars[PC >> 4];
- *ptr++ = hexchars[PC & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)®isters[PC], ptr, 4, 0);
- *ptr++ = ';';
- *ptr++ = hexchars[FP >> 4];
- *ptr++ = hexchars[FP & 0xf];
- *ptr++ = ':';
- ptr = mem2hex(sp + 8 + 6, ptr, 4, 0); /* FP */
- *ptr++ = ';';
- *ptr++ = hexchars[SP >> 4];
- *ptr++ = hexchars[SP & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)&sp, ptr, 4, 0);
- *ptr++ = ';';
- *ptr++ = hexchars[NPC >> 4];
- *ptr++ = hexchars[NPC & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)®isters[NPC], ptr, 4, 0);
- *ptr++ = ';';
- *ptr++ = hexchars[O7 >> 4];
- *ptr++ = hexchars[O7 & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)®isters[O7], ptr, 4, 0);
- *ptr++ = ';';
- *ptr++ = 0;
- putpacket(remcomOutBuffer);
- while (1)
- {
- remcomOutBuffer[0] = 0;
- ptr = getpacket();
- switch (*ptr++)
- {
- case '?':
- remcomOutBuffer[0] = 'S';
- remcomOutBuffer[1] = hexchars[sigval >> 4];
- remcomOutBuffer[2] = hexchars[sigval & 0xf];
- remcomOutBuffer[3] = 0;
- break;
- case 'd': /* toggle debug flag */
- break;
- case 'g': /* return the value of the CPU registers */
- {
- ptr = remcomOutBuffer;
- ptr = mem2hex((char *)registers, ptr, 16 * 4, 0); /* G & O regs */
- ptr = mem2hex(sp + 0, ptr, 16 * 4, 0); /* L & I regs */
- memset(ptr, '0', 32 * 8); /* Floating point */
- mem2hex((char *)®isters[Y],
- ptr + 32 * 4 * 2,
- 8 * 4,
- 0); /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
- }
- break;
- case 'G': /* set the value of the CPU registers - return OK */
- {
- unsigned long *newsp, psr;
- psr = registers[PSR];
- hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */
- hex2mem(ptr + 16 * 4 * 2, sp + 0, 16 * 4, 0); /* L & I regs */
- hex2mem(ptr + 64 * 4 * 2, (char *)®isters[Y],
- 8 * 4, 0); /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
- /* See if the stack pointer has moved. If so, then copy the saved
- locals and ins to the new location. This keeps the window
- overflow and underflow routines happy. */
- newsp = (unsigned long *)registers[SP];
- if (sp != newsp)
- sp = memcpy(newsp, sp, 16 * 4);
- /* Don't allow CWP to be modified. */
- if (psr != registers[PSR])
- registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f);
- strcpy(remcomOutBuffer,"OK");
- }
- break;
- case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
- /* Try to read %x,%x. */
- if (hexToInt(&ptr, &addr)
- && *ptr++ == ','
- && hexToInt(&ptr, &length))
- {
- if (mem2hex((char *)addr, remcomOutBuffer, length, 1))
- break;
- strcpy (remcomOutBuffer, "E03");
- }
- else
- strcpy(remcomOutBuffer,"E01");
- break;
- case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
- /* Try to read '%x,%x:'. */
- if (hexToInt(&ptr, &addr)
- && *ptr++ == ','
- && hexToInt(&ptr, &length)
- && *ptr++ == ':')
- {
- if (hex2mem(ptr, (char *)addr, length, 1))
- strcpy(remcomOutBuffer, "OK");
- else
- strcpy(remcomOutBuffer, "E03");
- }
- else
- strcpy(remcomOutBuffer, "E02");
- break;
- case 'c': /* cAA..AA Continue at address AA..AA(optional) */
- /* try to read optional parameter, pc unchanged if no parm */
- if (hexToInt(&ptr, &addr))
- {
- registers[PC] = addr;
- registers[NPC] = addr + 4;
- }
- /* Need to flush the instruction cache here, as we may have deposited a
- breakpoint, and the icache probably has no way of knowing that a data ref to
- some location may have changed something that is in the instruction cache.
- */
- flush_i_cache();
- return;
- /* kill the program */
- case 'k' : /* do nothing */
- break;
- #if 0
- case 't': /* Test feature */
- asm (" std %f30,[%sp]");
- break;
- #endif
- case 'r': /* Reset */
- asm ("call 0
- nop ");
- break;
- } /* switch */
- /* reply to the request */
- putpacket(remcomOutBuffer);
- }
- }
- /* This function will generate a breakpoint exception. It is used at the
- beginning of a program to sync up with a debugger and can be used
- otherwise as a quick means to stop program execution and "break" into
- the debugger. */
- void
- breakpoint (void)
- {
- if (!initialized)
- return;
- asm(" .globl _breakinst
- _breakinst: ta 1
- ");
- }
|