/**************************************************************************** * * 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) * * bBB..BB Set baud rate to BB..BB OK or BNN, then sets * baud rate * * All commands and responses are sent with a packet which includes a * checksum. A packet consists of * * $#. * * where * :: * :: < two hex digits computed as modulo 256 sum of > * * 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 #include #include "dbgmon.h" #include "parser.h" #include "ctype.h" /************************************************************************ * * external low-level support routines */ extern putchar(); /* write a single character */ extern getchar(); /* read and return a single char */ /************************************************************************/ /* Stuff for stdio-like gets_debugger_check() */ #define CTRL(x) ('x'&0x1f) #define DEL 0x7f #define INTR CTRL(C) #define BELL 0x7 #define PROMPT "? " #define BUFSIZE 512 /* Big enough for register packets */ static int initialized = 0; /* !0 means we've been initialized */ static char hexchars[]="0123456789abcdef"; extern unsigned int _regs[]; /* Saved registers from client */ /* Convert ch from a hex digit to an int */ static int hex(ch) 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; } /* scan for the sequence $# */ static void getpacket(buffer) char *buffer; { unsigned char checksum; unsigned char xmitcsum; int i; int count; unsigned char ch; /* At this point, the start character ($) has been received through * the debug monitor parser. Get the remaining characters and * process them. */ checksum = 0; xmitcsum = -1; count = 0; /* read until a # or end of buffer is found */ while (count < BUFSIZE) { ch = getchar(); if (ch == '#') break; checksum = checksum + ch; buffer[count] = ch; count = count + 1; } if (count >= BUFSIZE) buffer[count] = 0; if (ch == '#') { xmitcsum = hex(getchar()) << 4; xmitcsum |= hex(getchar()); #if 0 /* Humans shouldn't have to figure out checksums to type to it. */ putchar ('+'); return; #endif if (checksum != xmitcsum) { putchar('-'); /* failed checksum */ return; /* Back to monitor loop */ } else { putchar('+'); /* successful transfer */ /* if a sequence char is present, reply the sequence ID */ if (buffer[2] == ':') { putchar(buffer[0]); putchar(buffer[1]); /* remove sequence chars from buffer */ count = strlen(buffer); for (i=3; i <= count; i++) buffer[i-3] = buffer[i]; } /* Buffer command received- go and process it. */ } } } /* send the packet in buffer. */ static void putpacket(buffer) unsigned char *buffer; { unsigned char checksum; int count; unsigned char ch; /* $#. */ do { putchar('$'); checksum = 0; count = 0; while (ch = buffer[count]) { if (! putchar(ch)) return; checksum += ch; count += 1; } putchar('#'); putchar(hexchars[checksum >> 4]); putchar(hexchars[checksum & 0xf]); } while (getchar() != '+'); } static char remcomInBuffer[BUFSIZE]; static char remcomOutBuffer[BUFSIZE]; /* 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(mem, buf, count, may_fault) unsigned char *mem; unsigned char *buf; int count; int may_fault; { unsigned char ch; while (count-- > 0) { ch = *mem++; if (mem_err) return 0; *buf++ = hexchars[ch >> 4]; *buf++ = hexchars[ch & 0xf]; } *buf = 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(buf, mem, count, may_fault) unsigned char *buf; unsigned char *mem; int count; int may_fault; { int i; unsigned char ch; for (i=0; itt && 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 lets GDB know that an exception has occured. */ static void debug_handle_exception () { int tt; /* Trap type */ int sigval; char *ptr; tt = (_regs[R_CAUSE] >> 2) & 0x0f; /* 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[R_EPC >> 4]; *ptr++ = hexchars[R_EPC & 0xf]; *ptr++ = ':'; ptr = mem2hex((char *)&_regs[R_EPC], ptr, 4, 0); *ptr++ = ';'; *ptr++ = hexchars[R_FP >> 4]; *ptr++ = hexchars[R_FP & 0xf]; *ptr++ = ':'; ptr = mem2hex((char *)&_regs[R_FP], ptr, 4, 0); *ptr++ = ';'; *ptr++ = hexchars[R_SP >> 4]; *ptr++ = hexchars[R_SP & 0xf]; *ptr++ = ':'; ptr = mem2hex((char *)&_regs[R_SP], ptr, 4, 0); *ptr++ = ';'; *ptr++ = 0; putpacket(remcomOutBuffer); return; } void process_packet() { char *ptr; int length; int addr; int sigval; int tt; /* Trap type */ remcomOutBuffer[0] = 0; getpacket(remcomInBuffer); switch (remcomInBuffer[0]) { /* Return Last SIGVAL */ case '?': tt = (_regs[R_CAUSE] >> 2) & 0x0f; sigval = computeSignal(tt); remcomOutBuffer[0] = 'S'; remcomOutBuffer[1] = hexchars[sigval >> 4]; remcomOutBuffer[2] = hexchars[sigval & 0xf]; remcomOutBuffer[3] = 0; break; /* toggle debug flag */ case 'd': break; /* Return the values of the CPU registers */ case 'g': ptr = remcomOutBuffer; ptr = mem2hex((char *)_regs, ptr, 32 * 4, 0); /* General Purpose Registers */ ptr = mem2hex((char *)&_regs[R_EPC], ptr, 9 * 4, 0); /* CP0 Registers */ break; /* set the value of the CPU registers - return OK */ case 'G': ptr = &remcomInBuffer[1]; hex2mem(ptr, (char *)_regs, 32 * 4, 0); /* General Purpose Registers */ hex2mem(ptr + 32 * 4 * 2, (char *)&_regs[R_EPC], 9 * 4, 0); /* CP0 Registers */ strcpy(remcomOutBuffer,"OK"); break; /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ case 'm': ptr = &remcomInBuffer[1]; if (hexToInt(&ptr, &addr) && *ptr++ == ',' && hexToInt(&ptr, &length)) { if (mem2hex((char *)addr, remcomOutBuffer, length, 1)) break; strcpy (remcomOutBuffer, "E03"); } else strcpy(remcomOutBuffer,"E01"); break; /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ case 'M': ptr = &remcomInBuffer[1]; 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; /* cAA..AA Continue at address AA..AA(optional) */ case 'c': /* try to read optional parameter, pc unchanged if no parm */ ptr = &remcomInBuffer[1]; if (hexToInt(&ptr, &addr)) { gdb_go ( addr ); } else { dbg_cont(); } return; /* kill the program */ case 'k': break; /* Reset */ case 'r': break; /* switch */ } /* Reply to the request */ putpacket(remcomOutBuffer); } /* * gets_debugger_check - This is the same as the stdio gets, but we also * check for a leading $ in the buffer. This so we * gracefully handle the GDB protocol packets. */ char * gets_debugger_check(buf) char *buf; { register char c; char *bufp; bufp = buf; for (;;) { c = getchar(); switch (c) { /* quote next char */ case '$': if ( buf == bufp ) process_packet(); break; case CTRL(V): c = getchar(); if (bufp < &buf[LINESIZE-3]) { rmw_byte (bufp++,c); showchar(c); } else { putchar(BELL); } break; case '\n': case '\r': putchar('\n'); rmw_byte (bufp,0); return(buf); case CTRL(H): case DEL: if (bufp > buf) { bufp--; putchar(CTRL(H)); putchar(' '); putchar(CTRL(H)); } break; case CTRL(U): if (bufp > buf) { printf("^U\n%s", PROMPT); bufp = buf; } break; case '\t': c = ' '; default: /* * Make sure there's room for this character * plus a trailing \n and 0 byte */ if (isprint(c) && bufp < &buf[LINESIZE-3]) { rmw_byte ( bufp++, c ); putchar(c); } else { putchar(BELL); } break; } } }