gdb support for user mode (Paul Brook)
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 17 Apr 2005 19:16:13 +0000 (19:16 +0000)
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 17 Apr 2005 19:16:13 +0000 (19:16 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1367 c046a42c-6fe2-441c-8c8c-71466251a162

14 files changed:
Makefile.target
exec.c
gdbstub.c
gdbstub.h [new file with mode: 0644]
linux-user/main.c
linux-user/qemu.h
linux-user/signal.c
target-arm/cpu.h
target-arm/op.c
target-arm/translate.c
target-i386/cpu.h
target-ppc/cpu.h
target-sparc/cpu.h
vl.h

index 16f6496..789848e 100644 (file)
@@ -298,6 +298,9 @@ endif
 ifeq ($(ARCH),ia64)
 OBJS += ia64-syscall.o
 endif
+ifdef CONFIG_GDBSTUB
+OBJS+=gdbstub.o
+endif
 
 all: $(PROGS)
 
diff --git a/exec.c b/exec.c
index 264775e..f923043 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -1076,7 +1076,7 @@ static void tb_reset_jump_recursive(TranslationBlock *tb)
     tb_reset_jump_recursive2(tb, 1);
 }
 
-#if defined(TARGET_I386) || defined(TARGET_PPC) || defined(TARGET_SPARC)
+#if defined(TARGET_HAS_ICE)
 static void breakpoint_invalidate(CPUState *env, target_ulong pc)
 {
     target_ulong phys_addr;
@@ -1090,7 +1090,7 @@ static void breakpoint_invalidate(CPUState *env, target_ulong pc)
    breakpoint is reached */
 int cpu_breakpoint_insert(CPUState *env, target_ulong pc)
 {
-#if defined(TARGET_I386) || defined(TARGET_PPC) || defined(TARGET_SPARC)
+#if defined(TARGET_HAS_ICE)
     int i;
     
     for(i = 0; i < env->nb_breakpoints; i++) {
@@ -1112,7 +1112,7 @@ int cpu_breakpoint_insert(CPUState *env, target_ulong pc)
 /* remove a breakpoint */
 int cpu_breakpoint_remove(CPUState *env, target_ulong pc)
 {
-#if defined(TARGET_I386) || defined(TARGET_PPC) || defined(TARGET_SPARC)
+#if defined(TARGET_HAS_ICE)
     int i;
     for(i = 0; i < env->nb_breakpoints; i++) {
         if (env->breakpoints[i] == pc)
@@ -1120,9 +1120,9 @@ int cpu_breakpoint_remove(CPUState *env, target_ulong pc)
     }
     return -1;
  found:
-    memmove(&env->breakpoints[i], &env->breakpoints[i + 1],
-            (env->nb_breakpoints - (i + 1)) * sizeof(env->breakpoints[0]));
     env->nb_breakpoints--;
+    if (i < env->nb_breakpoints)
+      env->breakpoints[i] = env->breakpoints[env->nb_breakpoints];
 
     breakpoint_invalidate(env, pc);
     return 0;
@@ -1135,7 +1135,7 @@ int cpu_breakpoint_remove(CPUState *env, target_ulong pc)
    CPU loop after each instruction */
 void cpu_single_step(CPUState *env, int enabled)
 {
-#if defined(TARGET_I386) || defined(TARGET_PPC) || defined(TARGET_SPARC)
+#if defined(TARGET_HAS_ICE)
     if (env->singlestep_enabled != enabled) {
         env->singlestep_enabled = enabled;
         /* must flush all the translated code to avoid inconsistancies */
index 1e95c7c..5dc93c4 100644 (file)
--- a/gdbstub.c
+++ b/gdbstub.c
  * 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>
@@ -31,9 +42,10 @@ enum RSState {
     RS_GETLINE,
     RS_CHKSUM1,
     RS_CHKSUM2,
+    RS_CONTINUE
 };
-
-static int gdbserver_fd;
+/* XXX: This is not thread safe.  Do we care?  */
+static int gdbserver_fd = -1;
 
 typedef struct GDBState {
     enum RSState state;
@@ -43,6 +55,11 @@ typedef struct GDBState {
     int line_csum;
 } GDBState;
 
+#ifdef CONFIG_USER_ONLY
+/* XXX: remove this hack.  */
+static GDBState gdbserver_state;
+#endif
+
 static int get_char(GDBState *s)
 {
     uint8_t ch;
@@ -330,8 +347,47 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
     env->npc = tswapl(registers[69]);
     env->fsr = tswapl(registers[70]);
 }
-#else
+#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 +399,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 +415,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 +431,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
             env->npc = addr + 4;
 #endif
         }
-        vm_start();
-        break;
+        return RS_CONTINUE;
     case 's':
         if (*p != '\0') {
             addr = strtoul(p, (char **)&p, 16);
@@ -391,8 +445,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
 #endif
         }
         cpu_single_step(env, 1);
-        vm_start();
-        break;
+        return RS_CONTINUE;
     case 'g':
         reg_size = cpu_gdb_read_registers(env, mem_buf);
         memtohex(buf, mem_buf, reg_size);
@@ -472,6 +525,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 +544,20 @@ 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 {
+#endif
         switch(s->state) {
         case RS_IDLE:
             if (ch == '$') {
@@ -535,13 +592,67 @@ 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;
+        case RS_CONTINUE:
+#ifndef CONFIG_USER_ONLY
+            vm_start();
+            s->state = RS_IDLE;
+#endif
+            break;
         }
+#ifndef CONFIG_USER_ONLY
     }
+#endif
 }
 
+#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);
+    }
+
+  /* TODO: How do we terminate this loop?  */
+  sig = 0;
+  s->state = RS_IDLE;
+  while (s->state != RS_CONTINUE)
+    {
+      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;
+}
+#else
 static int gdb_can_read(void *opaque)
 {
     return 256;
@@ -559,10 +670,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 +698,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 +720,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 +751,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 +763,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;
 }
diff --git a/gdbstub.h b/gdbstub.h
new file mode 100644 (file)
index 0000000..63b88da
--- /dev/null
+++ b/gdbstub.h
@@ -0,0 +1,11 @@
+#ifndef GDBSTUB_H
+#define GDBSTUB_H
+
+#define DEFAULT_GDBSTUB_PORT 1234
+
+#ifdef CONFIG_USER_ONLY
+int gdb_handlesig (CPUState *, int);
+#endif
+int gdbserver_start(int);
+
+#endif
index 126a919..0dcffb1 100644 (file)
@@ -278,6 +278,20 @@ void cpu_loop(CPUX86State *env)
         case EXCP_INTERRUPT:
             /* just indicate that signals should be handled asap */
             break;
+        case EXCP_DEBUG:
+            {
+                int sig;
+
+                sig = gdb_handlesig (env, TARGET_SIGTRAP);
+                if (sig)
+                  {
+                    info.si_signo = sig;
+                    info.si_errno = 0;
+                    info.si_code = TARGET_TRAP_BRKPT;
+                    queue_signal(info.si_signo, &info);
+                  }
+            }
+            break;
         default:
             pc = env->segs[R_CS].base + env->eip;
             fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", 
@@ -379,6 +393,20 @@ void cpu_loop(CPUARMState *env)
                 queue_signal(info.si_signo, &info);
             }
             break;
+        case EXCP_DEBUG:
+            {
+                int sig;
+
+                sig = gdb_handlesig (env, TARGET_SIGTRAP);
+                if (sig)
+                  {
+                    info.si_signo = sig;
+                    info.si_errno = 0;
+                    info.si_code = TARGET_TRAP_BRKPT;
+                    queue_signal(info.si_signo, &info);
+                  }
+            }
+            break;
         default:
         error:
             fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", 
@@ -529,6 +557,20 @@ void cpu_loop (CPUSPARCState *env)
             break;
        case 0x100: // XXX, why do we get these?
            break;
+        case EXCP_DEBUG:
+            {
+                int sig;
+
+                sig = gdb_handlesig (env, TARGET_SIGTRAP);
+                if (sig)
+                  {
+                    info.si_signo = sig;
+                    info.si_errno = 0;
+                    info.si_code = TARGET_TRAP_BRKPT;
+                    queue_signal(info.si_signo, &info);
+                  }
+            }
+            break;
         default:
             printf ("Unhandled trap: 0x%x\n", trapnr);
             cpu_dump_state(env, stderr, fprintf, 0);
@@ -911,8 +953,20 @@ void cpu_loop(CPUPPCState *env)
         case EXCP_INTERRUPT:
             /* Don't know why this should ever happen... */
             break;
-       case EXCP_DEBUG:
-           break;
+        case EXCP_DEBUG:
+            {
+                int sig;
+
+                sig = gdb_handlesig (env, TARGET_SIGTRAP);
+                if (sig)
+                  {
+                    info.si_signo = sig;
+                    info.si_errno = 0;
+                    info.si_code = TARGET_TRAP_BRKPT;
+                    queue_signal(info.si_signo, &info);
+                  }
+            }
+            break;
         default:
             fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", 
                     trapnr);
@@ -930,10 +984,11 @@ void cpu_loop(CPUPPCState *env)
 void usage(void)
 {
     printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2004 Fabrice Bellard\n"
-           "usage: qemu-" TARGET_ARCH " [-h] [-d opts] [-L path] [-s size] program [arguments...]\n"
+           "usage: qemu-" TARGET_ARCH " [-h] [-g] [-d opts] [-L path] [-s size] program [arguments...]\n"
            "Linux CPU emulator (compiled for %s emulation)\n"
            "\n"
            "-h           print this help\n"
+           "-g           wait gdb connection to port %d\n"
            "-L path      set the elf interpreter prefix (default=%s)\n"
            "-s size      set the stack size in bytes (default=%ld)\n"
            "\n"
@@ -944,6 +999,7 @@ void usage(void)
            "-d options   activate log (logfile=%s)\n"
            "-p pagesize  set the host page size to 'pagesize'\n",
            TARGET_ARCH,
+           DEFAULT_GDBSTUB_PORT,
            interp_prefix, 
            x86_stack_size,
            DEBUG_LOGFILE);
@@ -967,6 +1023,7 @@ int main(int argc, char **argv)
     CPUState *env;
     int optind;
     const char *r;
+    int use_gdbstub = 0;
     
     if (argc <= 1)
         usage();
@@ -1020,6 +1077,8 @@ int main(int argc, char **argv)
                 fprintf(stderr, "page size must be a power of two\n");
                 exit(1);
             }
+        } else if (!strcmp(r, "g")) {
+            use_gdbstub = 1;
         } else 
 #ifdef USE_CODE_COPY
         if (!strcmp(r, "no-code-copy")) {
@@ -1176,6 +1235,10 @@ int main(int argc, char **argv)
 #error unsupported target CPU
 #endif
 
+    if (use_gdbstub) {
+        gdbserver_start (DEFAULT_GDBSTUB_PORT);
+        gdb_handlesig(env, 0);
+    }
     cpu_loop(env);
     /* never exits */
     return 0;
index c176a58..2a815eb 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "cpu.h"
 #include "syscall.h"
+#include "gdbstub.h"
 
 /* This struct is used to hold certain information about the image.
  * Basically, it replicates in user space what would be certain
index 7a904ad..a7c06c9 100644 (file)
@@ -1675,6 +1675,12 @@ void process_pending_signals(void *cpu_env)
     k->first = q->next;
     if (!k->first)
         k->pending = 0;
+      
+    sig = gdb_handlesig (cpu_env, sig);
+    if (!sig) {
+        fprintf (stderr, "Lost signal\n");
+        abort();
+    }
 
     handler = k->sa._sa_handler;
     if (handler == TARGET_SIG_DFL) {
index 1346175..ef7469d 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "softfloat.h"
 
+#define TARGET_HAS_ICE 1
+
 #define EXCP_UDEF            1   /* undefined instruction */
 #define EXCP_SWI             2   /* software interrupt */
 #define EXCP_PREFETCH_ABORT  3
@@ -62,6 +64,11 @@ typedef struct CPUARMState {
     int user_mode_only;
     uint32_t address;
 
+    /* ICE debug support.  */
+    target_ulong breakpoints[MAX_BREAKPOINTS];
+    int nb_breakpoints;
+    int singlestep_enabled;
+
     /* in order to avoid passing too many arguments to the memory
        write helpers, we store some rarely used information in the CPU
        context) */
index 42ee4f9..f4cbb6e 100644 (file)
@@ -858,6 +858,12 @@ void OPPROTO op_undef_insn(void)
     cpu_loop_exit();
 }
 
+void OPPROTO op_debug(void)
+{
+    env->exception_index = EXCP_DEBUG;
+    cpu_loop_exit();
+}
+
 /* VFP support.  We follow the convention used for VFP instrunctions:
    Single precition routines have a "s" suffix, double precision a
    "d" suffix.  */
index 39c28e7..280b68c 100644 (file)
@@ -2026,6 +2026,17 @@ static inline int gen_intermediate_code_internal(CPUState *env,
     dc->pc = pc_start;
     lj = -1;
     do {
+        if (env->nb_breakpoints > 0) {
+            for(j = 0; j < env->nb_breakpoints; j++) {
+                if (env->breakpoints[j] == dc->pc) {
+                    gen_op_movl_T0_im((long)dc->pc);
+                    gen_op_movl_reg_TN[0][15]();
+                    gen_op_debug();
+                    dc->is_jmp = DISAS_JUMP;
+                    break;
+                }
+            }
+        }
         if (search_pc) {
             j = gen_opc_ptr - gen_opc_buf;
             if (lj < j) {
@@ -2040,7 +2051,8 @@ static inline int gen_intermediate_code_internal(CPUState *env,
           disas_thumb_insn(dc);
         else
           disas_arm_insn(env, dc);
-    } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end && 
+    } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
+             !env->singlestep_enabled &&
              (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32));
     switch(dc->is_jmp) {
     case DISAS_JUMP_NEXT:
index ce65c34..d0157ce 100644 (file)
@@ -34,6 +34,8 @@
    close to the modifying instruction */
 #define TARGET_HAS_PRECISE_SMC
 
+#define TARGET_HAS_ICE 1
+
 #include "cpu-defs.h"
 
 #include "softfloat.h"
index 70ee835..12f7c17 100644 (file)
@@ -29,6 +29,8 @@
 
 #include "softfloat.h"
 
+#define TARGET_HAS_ICE 1
+
 /* Instruction types */
 enum {
     PPC_NONE     = 0x0000,
index 67a6e3e..b556e23 100644 (file)
@@ -17,6 +17,8 @@
 
 #include "softfloat.h"
 
+#define TARGET_HAS_ICE 1
+
 /*#define EXCP_INTERRUPT 0x100*/
 
 /* trap definitions */
diff --git a/vl.h b/vl.h
index 5091ca1..0fe94ac 100644 (file)
--- a/vl.h
+++ b/vl.h
@@ -71,6 +71,7 @@ static inline char *realpath(const char *path, char *resolved_path)
 #else
 
 #include "cpu.h"
+#include "gdbstub.h"
 
 #endif /* !defined(QEMU_TOOL) */
 
@@ -829,10 +830,4 @@ const char *readline_get_history(unsigned int index);
 void readline_start(const char *prompt, int is_password,
                     ReadLineFunc *readline_func, void *opaque);
 
-/* gdbstub.c */
-
-#define DEFAULT_GDBSTUB_PORT 1234
-
-int gdbserver_start(int port);
-
 #endif /* VL_H */