]> git.proxmox.com Git - mirror_qemu.git/blobdiff - gdbstub.c
sparc64 marge (Blue Swirl)
[mirror_qemu.git] / gdbstub.c
index 9a491db6decd1cda83e76bf0c81af9559d75a25a..5586df29fbd636c023b0e3f014de445606f9b0c0 100644 (file)
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1,7 +1,7 @@
 /*
  * gdb server stub
  * 
- * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2003-2005 Fabrice Bellard
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
+#ifdef CONFIG_USER_ONLY
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "qemu.h"
+#else
 #include "vl.h"
+#endif
 
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -32,17 +43,25 @@ enum RSState {
     RS_CHKSUM1,
     RS_CHKSUM2,
 };
-
-static int gdbserver_fd;
+/* XXX: This is not thread safe.  Do we care?  */
+static int gdbserver_fd = -1;
 
 typedef struct GDBState {
-    enum RSState state;
+    enum RSState state; /* parsing state */
     int fd;
     char line_buf[4096];
     int line_buf_index;
     int line_csum;
+#ifdef CONFIG_USER_ONLY
+    int running_state;
+#endif
 } GDBState;
 
+#ifdef CONFIG_USER_ONLY
+/* XXX: remove this hack.  */
+static GDBState gdbserver_state;
+#endif
+
 static int get_char(GDBState *s)
 {
     uint8_t ch;
@@ -274,7 +293,7 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
 #elif defined (TARGET_SPARC)
 static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
 {
-    uint32_t *registers = (uint32_t *)mem_buf, tmp;
+    target_ulong *registers = (target_ulong *)mem_buf;
     int i;
 
     /* fill in g0..g7 */
@@ -289,10 +308,15 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
     for (i = 0; i < 32; i++) {
         registers[i + 32] = tswapl(*((uint32_t *)&env->fpr[i]));
     }
+#ifndef TARGET_SPARC64
     /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
     registers[64] = tswapl(env->y);
-    tmp = GET_PSR(env);
-    registers[65] = tswapl(tmp);
+    {
+       target_ulong tmp;
+
+       tmp = GET_PSR(env);
+       registers[65] = tswapl(tmp);
+    }
     registers[66] = tswapl(env->wim);
     registers[67] = tswapl(env->tbr);
     registers[68] = tswapl(env->pc);
@@ -300,13 +324,24 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
     registers[70] = tswapl(env->fsr);
     registers[71] = 0; /* csr */
     registers[72] = 0;
-
-    return 73 * 4;
+    return 73 * sizeof(target_ulong);
+#else
+    for (i = 0; i < 32; i += 2) {
+        registers[i/2 + 64] = tswapl(*((uint64_t *)&env->fpr[i]));
+    }
+    registers[81] = tswapl(env->pc);
+    registers[82] = tswapl(env->npc);
+    registers[83] = tswapl(env->tstate[env->tl]);
+    registers[84] = tswapl(env->fsr);
+    registers[85] = tswapl(env->fprs);
+    registers[86] = tswapl(env->y);
+    return 87 * sizeof(target_ulong);
+#endif
 }
 
 static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
 {
-    uint32_t *registers = (uint32_t *)mem_buf;
+    target_ulong *registers = (target_ulong *)mem_buf;
     int i;
 
     /* fill in g0..g7 */
@@ -315,12 +350,13 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
     }
     /* fill in register window */
     for(i = 0; i < 24; i++) {
-        env->regwptr[i] = tswapl(registers[i]);
+        env->regwptr[i] = tswapl(registers[i + 8]);
     }
     /* fill in fprs */
     for (i = 0; i < 32; i++) {
         *((uint32_t *)&env->fpr[i]) = tswapl(registers[i + 32]);
     }
+#ifndef TARGET_SPARC64
     /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
     env->y = tswapl(registers[64]);
     PUT_PSR(env, tswapl(registers[65]));
@@ -329,9 +365,62 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
     env->pc = tswapl(registers[68]);
     env->npc = tswapl(registers[69]);
     env->fsr = tswapl(registers[70]);
-}
 #else
+    for (i = 0; i < 32; i += 2) {
+       uint64_t tmp;
+       tmp = tswapl(registers[i/2 + 64]) << 32;
+       tmp |= tswapl(registers[i/2 + 64 + 1]);
+        *((uint64_t *)&env->fpr[i]) = tmp;
+    }
+    env->pc = tswapl(registers[81]);
+    env->npc = tswapl(registers[82]);
+    env->tstate[env->tl] = tswapl(registers[83]);
+    env->fsr = tswapl(registers[84]);
+    env->fprs = tswapl(registers[85]);
+    env->y = tswapl(registers[86]);
+#endif
+}
+#elif defined (TARGET_ARM)
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+    int i;
+    uint8_t *ptr;
+
+    ptr = mem_buf;
+    /* 16 core integer registers (4 bytes each).  */
+    for (i = 0; i < 16; i++)
+      {
+        *(uint32_t *)ptr = tswapl(env->regs[i]);
+        ptr += 4;
+      }
+    /* 8 FPA registers (12 bytes each), FPS (4 bytes).
+       Not yet implemented.  */
+    memset (ptr, 0, 8 * 12 + 4);
+    ptr += 8 * 12 + 4;
+    /* CPSR (4 bytes).  */
+    *(uint32_t *)ptr = tswapl (env->cpsr);
+    ptr += 4;
+
+    return ptr - mem_buf;
+}
 
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+    int i;
+    uint8_t *ptr;
+
+    ptr = mem_buf;
+    /* Core integer registers.  */
+    for (i = 0; i < 16; i++)
+      {
+        env->regs[i] = tswapl(*(uint32_t *)ptr);
+        ptr += 4;
+      }
+    /* Ignore FPA regs and scr.  */
+    ptr += 8 * 12 + 4;
+    env->cpsr = tswapl(*(uint32_t *)ptr);
+}
+#else
 static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
 {
     return 0;
@@ -343,10 +432,8 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
 
 #endif
 
-/* port = 0 means default port */
-static int gdb_handle_packet(GDBState *s, const char *line_buf)
+static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
 {
-    CPUState *env = cpu_single_env;
     const char *p;
     int ch, reg_size, type;
     char buf[4096];
@@ -361,6 +448,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
     ch = *p++;
     switch(ch) {
     case '?':
+        /* TODO: Make this return the correct value for user-mode.  */
         snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
         put_packet(s, buf);
         break;
@@ -376,8 +464,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
             env->npc = addr + 4;
 #endif
         }
+#ifdef CONFIG_USER_ONLY
+        s->running_state = 1;
+#else
         vm_start();
-        break;
+#endif
+       return RS_IDLE;
     case 's':
         if (*p != '\0') {
             addr = strtoul(p, (char **)&p, 16);
@@ -391,8 +483,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
 #endif
         }
         cpu_single_step(env, 1);
+#ifdef CONFIG_USER_ONLY
+        s->running_state = 1;
+#else
         vm_start();
-        break;
+#endif
+       return RS_IDLE;
     case 'g':
         reg_size = cpu_gdb_read_registers(env, mem_buf);
         memtohex(buf, mem_buf, reg_size);
@@ -420,11 +516,11 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
         if (*p == ',')
             p++;
         len = strtoul(p, (char **)&p, 16);
-        if (*p == ',')
+        if (*p == ':')
             p++;
         hextomem(mem_buf, p, len);
         if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
-            put_packet(s, "ENN");
+            put_packet(s, "E14");
         else
             put_packet(s, "OK");
         break;
@@ -442,7 +538,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
             put_packet(s, "OK");
         } else {
         breakpoint_error:
-            put_packet(s, "ENN");
+            put_packet(s, "E22");
         }
         break;
     case 'z':
@@ -472,6 +568,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
 
 extern void tb_flush(CPUState *env);
 
+#ifndef CONFIG_USER_ONLY
 static void gdb_vm_stopped(void *opaque, int reason)
 {
     GDBState *s = opaque;
@@ -490,17 +587,21 @@ static void gdb_vm_stopped(void *opaque, int reason)
     snprintf(buf, sizeof(buf), "S%02x", ret);
     put_packet(s, buf);
 }
+#endif
 
-static void gdb_read_byte(GDBState *s, int ch)
+static void gdb_read_byte(GDBState *s, CPUState *env, int ch)
 {
     int i, csum;
     char reply[1];
 
+#ifndef CONFIG_USER_ONLY
     if (vm_running) {
         /* when the CPU is running, we cannot do anything except stop
            it when receiving a char */
         vm_stop(EXCP_INTERRUPT);
-    } else {
+    } else 
+#endif
+    {
         switch(s->state) {
         case RS_IDLE:
             if (ch == '$') {
@@ -535,13 +636,74 @@ static void gdb_read_byte(GDBState *s, int ch)
             } else {
                 reply[0] = '+';
                 put_buffer(s, reply, 1);
-                s->state = gdb_handle_packet(s, s->line_buf);
+                s->state = gdb_handle_packet(s, env, s->line_buf);
             }
             break;
         }
     }
 }
 
+#ifdef CONFIG_USER_ONLY
+int
+gdb_handlesig (CPUState *env, int sig)
+{
+  GDBState *s;
+  char buf[256];
+  int n;
+
+  if (gdbserver_fd < 0)
+    return sig;
+
+  s = &gdbserver_state;
+
+  /* disable single step if it was enabled */
+  cpu_single_step(env, 0);
+  tb_flush(env);
+
+  if (sig != 0)
+    {
+      snprintf(buf, sizeof(buf), "S%02x", sig);
+      put_packet(s, buf);
+    }
+
+  sig = 0;
+  s->state = RS_IDLE;
+  s->running_state = 0;
+  while (s->running_state == 0) {
+      n = read (s->fd, buf, 256);
+      if (n > 0)
+        {
+          int i;
+
+          for (i = 0; i < n; i++)
+            gdb_read_byte (s, env, buf[i]);
+        }
+      else if (n == 0 || errno != EAGAIN)
+        {
+          /* XXX: Connection closed.  Should probably wait for annother
+             connection before continuing.  */
+          return sig;
+        }
+  }
+  return sig;
+}
+
+/* Tell the remote gdb that the process has exited.  */
+void gdb_exit(CPUState *env, int code)
+{
+  GDBState *s;
+  char buf[4];
+
+  if (gdbserver_fd < 0)
+    return;
+
+  s = &gdbserver_state;
+
+  snprintf(buf, sizeof(buf), "W%02x", code);
+  put_packet(s, buf);
+}
+
+#else
 static int gdb_can_read(void *opaque)
 {
     return 256;
@@ -559,10 +721,12 @@ static void gdb_read(void *opaque, const uint8_t *buf, int size)
         vm_start();
     } else {
         for(i = 0; i < size; i++)
-            gdb_read_byte(s, buf[i]);
+            gdb_read_byte(s, cpu_single_env, buf[i]);
     }
 }
 
+#endif
+
 static void gdb_accept(void *opaque, const uint8_t *buf, int size)
 {
     GDBState *s;
@@ -585,15 +749,21 @@ static void gdb_accept(void *opaque, const uint8_t *buf, int size)
     val = 1;
     setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
     
+#ifdef CONFIG_USER_ONLY
+    s = &gdbserver_state;
+    memset (s, 0, sizeof (GDBState));
+#else
     s = qemu_mallocz(sizeof(GDBState));
     if (!s) {
         close(fd);
         return;
     }
+#endif
     s->fd = fd;
 
     fcntl(fd, F_SETFL, O_NONBLOCK);
 
+#ifndef CONFIG_USER_ONLY
     /* stop the VM */
     vm_stop(EXCP_INTERRUPT);
 
@@ -601,6 +771,7 @@ static void gdb_accept(void *opaque, const uint8_t *buf, int size)
     qemu_add_fd_read_handler(s->fd, gdb_can_read, gdb_read, s);
     /* when the VM is stopped, the following callback is called */
     qemu_add_vm_stop_handler(gdb_vm_stopped, s);
+#endif
 }
 
 static int gdbserver_open(int port)
@@ -631,7 +802,9 @@ static int gdbserver_open(int port)
         perror("listen");
         return -1;
     }
+#ifndef CONFIG_USER_ONLY
     fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
     return fd;
 }
 
@@ -641,6 +814,10 @@ int gdbserver_start(int port)
     if (gdbserver_fd < 0)
         return -1;
     /* accept connections */
+#ifdef CONFIG_USER_ONLY
+    gdb_accept (NULL, NULL, 0);
+#else
     qemu_add_fd_read_handler(gdbserver_fd, NULL, gdb_accept, NULL);
+#endif
     return 0;
 }