Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / u-boot / common / kgdb.c
diff --git a/qemu/roms/u-boot/common/kgdb.c b/qemu/roms/u-boot/common/kgdb.c
new file mode 100644 (file)
index 0000000..8a621ad
--- /dev/null
@@ -0,0 +1,608 @@
+/* taken from arch/powerpc/kernel/ppc-stub.c */
+
+/****************************************************************************
+
+               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 its 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
+ *    qOffsets      Get section offsets.  Reply is Text=xxx;Data=yyy;Bss=zzz
+ *
+ *    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
+ *
+ * $<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 <common.h>
+
+#include <kgdb.h>
+#include <command.h>
+
+#undef KGDB_DEBUG
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound buffers
+ */
+#define BUFMAX 1024
+static char remcomInBuffer[BUFMAX];
+static char remcomOutBuffer[BUFMAX];
+static char remcomRegBuffer[BUFMAX];
+
+static int initialized = 0;
+static int kgdb_active = 0, first_entry = 1;
+static struct pt_regs entry_regs;
+static long error_jmp_buf[BUFMAX/2];
+static int longjmp_on_fault = 0;
+#ifdef KGDB_DEBUG
+static int kdebug = 1;
+#endif
+
+static const char hexchars[]="0123456789abcdef";
+
+/* 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;
+}
+
+/* Convert the memory pointed to by mem into hex, placing result in buf.
+ * Return a pointer to the last char put in buf (null).
+ */
+static unsigned char *
+mem2hex(char *mem, char *buf, int count)
+{
+       char *tmp;
+       unsigned char ch;
+
+       /*
+        * We use the upper half of buf as an intermediate buffer for the
+        * raw memory copy.  Hex conversion will work against this one.
+        */
+       tmp = buf + count;
+       longjmp_on_fault = 1;
+
+       memcpy(tmp, mem, count);
+
+       while (count-- > 0) {
+               ch = *tmp++;
+               *buf++ = hexchars[ch >> 4];
+               *buf++ = hexchars[ch & 0xf];
+       }
+       *buf = 0;
+       longjmp_on_fault = 0;
+       return (unsigned char *)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 fetched from buf.
+*/
+static char *
+hex2mem(char *buf, char *mem, int count)
+{
+       int hexValue;
+       char *tmp_raw, *tmp_hex;
+
+       /*
+        * We use the upper half of buf as an intermediate buffer for the
+        * raw memory that is converted from hex.
+        */
+       tmp_raw = buf + count * 2;
+       tmp_hex = tmp_raw - 1;
+
+       longjmp_on_fault = 1;
+       while (tmp_hex >= buf) {
+               tmp_raw--;
+               hexValue = hex(*tmp_hex--);
+               if (hexValue < 0)
+                       kgdb_error(KGDBERR_NOTHEXDIG);
+               *tmp_raw = hexValue;
+               hexValue = hex(*tmp_hex--);
+               if (hexValue < 0)
+                       kgdb_error(KGDBERR_NOTHEXDIG);
+               *tmp_raw |= hexValue << 4;
+
+       }
+
+       memcpy(mem, tmp_raw, count);
+
+       kgdb_flush_cache_range((void *)mem, (void *)(mem+count));
+       longjmp_on_fault = 0;
+
+       return buf;
+}
+
+/*
+ * 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;
+
+       longjmp_on_fault = 1;
+       while (**ptr) {
+               hexValue = hex(**ptr);
+               if (hexValue < 0)
+                       break;
+
+               *intValue = (*intValue << 4) | hexValue;
+               numChars ++;
+
+               (*ptr)++;
+       }
+       longjmp_on_fault = 0;
+
+       return (numChars);
+}
+
+/* scan for the sequence $<data>#<checksum>     */
+static void
+getpacket(char *buffer)
+{
+       unsigned char checksum;
+       unsigned char xmitcsum;
+       int i;
+       int count;
+       unsigned char ch;
+
+       do {
+               /* wait around for the start character, ignore all other
+                * characters */
+               while ((ch = (getDebugChar() & 0x7f)) != '$') {
+#ifdef KGDB_DEBUG
+                       if (kdebug)
+                               putc(ch);
+#endif
+                       ;
+               }
+
+               checksum = 0;
+               xmitcsum = -1;
+
+               count = 0;
+
+               /* now, read until a # or end of buffer is found */
+               while (count < BUFMAX) {
+                       ch = getDebugChar() & 0x7f;
+                       if (ch == '#')
+                               break;
+                       checksum = checksum + ch;
+                       buffer[count] = ch;
+                       count = count + 1;
+               }
+
+               if (count >= BUFMAX)
+                       continue;
+
+               buffer[count] = 0;
+
+               if (ch == '#') {
+                       xmitcsum = hex(getDebugChar() & 0x7f) << 4;
+                       xmitcsum |= hex(getDebugChar() & 0x7f);
+                       if (checksum != xmitcsum)
+                               putDebugChar('-');      /* failed checksum */
+                       else {
+                               putDebugChar('+'); /* successful transfer */
+                               /* if a sequence char is present, reply the ID */
+                               if (buffer[2] == ':') {
+                                       putDebugChar(buffer[0]);
+                                       putDebugChar(buffer[1]);
+                                       /* remove sequence chars from buffer */
+                                       count = strlen(buffer);
+                                       for (i=3; i <= count; i++)
+                                               buffer[i-3] = buffer[i];
+                               }
+                       }
+               }
+       } while (checksum != xmitcsum);
+}
+
+/* send the packet in buffer.  */
+static void
+putpacket(unsigned char *buffer)
+{
+       unsigned char checksum;
+       int count;
+       unsigned char ch, recv;
+
+       /*  $<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]);
+               recv = getDebugChar();
+       } while ((recv & 0x7f) != '+');
+}
+
+/*
+ * This function does all command processing for interfacing to gdb.
+ */
+static int
+handle_exception (struct pt_regs *regs)
+{
+       int addr;
+       int length;
+       char *ptr;
+       kgdb_data kd;
+       int i;
+
+       if (!initialized) {
+               printf("kgdb: exception before kgdb is initialized! huh?\n");
+               return (0);
+       }
+
+       /* probably should check which exception occured as well */
+       if (longjmp_on_fault) {
+               longjmp_on_fault = 0;
+               kgdb_longjmp(error_jmp_buf, KGDBERR_MEMFAULT);
+               panic("kgdb longjump failed!\n");
+       }
+
+       if (kgdb_active) {
+               printf("kgdb: unexpected exception from within kgdb\n");
+               return (0);
+       }
+       kgdb_active = 1;
+
+       kgdb_interruptible(0);
+
+       printf("kgdb: handle_exception; trap [0x%x]\n", kgdb_trap(regs));
+
+       if (kgdb_setjmp(error_jmp_buf) != 0)
+               panic("kgdb: error or fault in entry init!\n");
+
+       kgdb_enter(regs, &kd);
+
+       if (first_entry) {
+               /*
+                * the first time we enter kgdb, we save the processor
+                * state so that we can return to the monitor if the
+                * remote end quits gdb (or at least, tells us to quit
+                * with the 'k' packet)
+                */
+               entry_regs = *regs;
+               first_entry = 0;
+       }
+
+       ptr = remcomOutBuffer;
+
+       *ptr++ = 'T';
+
+       *ptr++ = hexchars[kd.sigval >> 4];
+       *ptr++ = hexchars[kd.sigval & 0xf];
+
+       for (i = 0; i < kd.nregs; i++) {
+               kgdb_reg *rp = &kd.regs[i];
+
+               *ptr++ = hexchars[rp->num >> 4];
+               *ptr++ = hexchars[rp->num & 0xf];
+               *ptr++ = ':';
+               ptr = (char *)mem2hex((char *)&rp->val, ptr, 4);
+               *ptr++ = ';';
+       }
+
+       *ptr = 0;
+
+#ifdef KGDB_DEBUG
+       if (kdebug)
+               printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer);
+#endif
+
+       putpacket((unsigned char *)&remcomOutBuffer);
+
+       while (1) {
+               volatile int errnum;
+
+               remcomOutBuffer[0] = 0;
+
+               getpacket(remcomInBuffer);
+               ptr = &remcomInBuffer[1];
+
+#ifdef KGDB_DEBUG
+               if (kdebug)
+                       printf("kgdb:  remcomInBuffer: %s\n", remcomInBuffer);
+#endif
+
+               errnum = kgdb_setjmp(error_jmp_buf);
+
+               if (errnum == 0) switch (remcomInBuffer[0]) {
+
+               case '?':               /* report most recent signal */
+                       remcomOutBuffer[0] = 'S';
+                       remcomOutBuffer[1] = hexchars[kd.sigval >> 4];
+                       remcomOutBuffer[2] = hexchars[kd.sigval & 0xf];
+                       remcomOutBuffer[3] = 0;
+                       break;
+
+#ifdef KGDB_DEBUG
+               case 'd':
+                       /* toggle debug flag */
+                       kdebug ^= 1;
+                       break;
+#endif
+
+               case 'g':       /* return the value of the CPU registers. */
+                       length = kgdb_getregs(regs, remcomRegBuffer, BUFMAX);
+                       mem2hex(remcomRegBuffer, remcomOutBuffer, length);
+                       break;
+
+               case 'G':   /* set the value of the CPU registers */
+                       length = strlen(ptr);
+                       if ((length & 1) != 0) kgdb_error(KGDBERR_BADPARAMS);
+                       hex2mem(ptr, remcomRegBuffer, length/2);
+                       kgdb_putregs(regs, remcomRegBuffer, length/2);
+                       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)) {
+                               mem2hex((char *)addr, remcomOutBuffer, length);
+                       } else {
+                               kgdb_error(KGDBERR_BADPARAMS);
+                       }
+                       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++ == ':') {
+                               hex2mem(ptr, (char *)addr, length);
+                               strcpy(remcomOutBuffer, "OK");
+                       } else {
+                               kgdb_error(KGDBERR_BADPARAMS);
+                       }
+                       break;
+
+
+               case 'k':    /* kill the program, actually return to monitor */
+                       kd.extype = KGDBEXIT_KILL;
+                       *regs = entry_regs;
+                       first_entry = 1;
+                       goto doexit;
+
+               case 'C':    /* CSS  continue with signal SS */
+                       *ptr = '\0';    /* ignore the signal number for now */
+                       /* fall through */
+
+               case 'c':    /* cAA..AA  Continue; address AA..AA optional */
+                       /* try to read optional parameter, pc unchanged if no parm */
+                       kd.extype = KGDBEXIT_CONTINUE;
+
+                       if (hexToInt(&ptr, &addr)) {
+                               kd.exaddr = addr;
+                               kd.extype |= KGDBEXIT_WITHADDR;
+                       }
+
+                       goto doexit;
+
+               case 'S':    /* SSS  single step with signal SS */
+                       *ptr = '\0';    /* ignore the signal number for now */
+                       /* fall through */
+
+               case 's':
+                       kd.extype = KGDBEXIT_SINGLE;
+
+                       if (hexToInt(&ptr, &addr)) {
+                               kd.exaddr = addr;
+                               kd.extype |= KGDBEXIT_WITHADDR;
+                       }
+
+               doexit:
+/* 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.
+ */
+                       kgdb_flush_cache_all();
+                       kgdb_exit(regs, &kd);
+                       kgdb_active = 0;
+                       kgdb_interruptible(1);
+                       return (1);
+
+               case 'r':               /* Reset (if user process..exit ???)*/
+                       panic("kgdb reset.");
+                       break;
+
+               case 'P':    /* Pr=v  set reg r to value v (r and v are hex) */
+                       if (hexToInt(&ptr, &addr)
+                           && *ptr++ == '='
+                           && ((length = strlen(ptr)) & 1) == 0) {
+                               hex2mem(ptr, remcomRegBuffer, length/2);
+                               kgdb_putreg(regs, addr,
+                                       remcomRegBuffer, length/2);
+                               strcpy(remcomOutBuffer,"OK");
+                       } else {
+                               kgdb_error(KGDBERR_BADPARAMS);
+                       }
+                       break;
+               }                       /* switch */
+
+               if (errnum != 0)
+                       sprintf(remcomOutBuffer, "E%02d", errnum);
+
+#ifdef KGDB_DEBUG
+               if (kdebug)
+                       printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer);
+#endif
+
+               /* reply to the request */
+               putpacket((unsigned char *)&remcomOutBuffer);
+
+       } /* while(1) */
+}
+
+/*
+ * kgdb_init must be called *after* the
+ * monitor is relocated into ram
+ */
+void
+kgdb_init(void)
+{
+       kgdb_serial_init();
+       debugger_exception_handler = handle_exception;
+       initialized = 1;
+
+       putDebugStr("kgdb ready\n");
+       puts("ready\n");
+}
+
+void
+kgdb_error(int errnum)
+{
+       longjmp_on_fault = 0;
+       kgdb_longjmp(error_jmp_buf, errnum);
+       panic("kgdb_error: longjmp failed!\n");
+}
+
+/* Output string in GDB O-packet format if GDB has connected. If nothing
+   output, returns 0 (caller must then handle output). */
+int
+kgdb_output_string (const char* s, unsigned int count)
+{
+       char buffer[512];
+
+       count = (count <= (sizeof(buffer) / 2 - 2))
+               ? count : (sizeof(buffer) / 2 - 2);
+
+       buffer[0] = 'O';
+       mem2hex ((char *)s, &buffer[1], count);
+       putpacket((unsigned char *)&buffer);
+
+       return 1;
+}
+
+void
+breakpoint(void)
+{
+       if (!initialized) {
+               printf("breakpoint() called b4 kgdb init\n");
+               return;
+       }
+
+       kgdb_breakpoint(0, 0);
+}
+
+int
+do_kgdb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+    printf("Entering KGDB mode via exception handler...\n\n");
+    kgdb_breakpoint(argc - 1, argv + 1);
+    printf("\nReturned from KGDB mode\n");
+    return 0;
+}
+
+U_BOOT_CMD(
+       kgdb, CONFIG_SYS_MAXARGS, 1,    do_kgdb,
+       "enter gdb remote debug mode",
+       "[arg0 arg1 .. argN]\n"
+       "    - executes a breakpoint so that kgdb mode is\n"
+       "      entered via the exception handler. To return\n"
+       "      to the monitor, the remote gdb debugger must\n"
+       "      execute a \"continue\" or \"quit\" command.\n"
+       "\n"
+       "      if a program is loaded by the remote gdb, any args\n"
+       "      passed to the kgdb command are given to the loaded\n"
+       "      program if it is executed (see the \"hello_world\"\n"
+       "      example program in the U-Boot examples directory)."
+);