]> git.proxmox.com Git - qemu.git/commitdiff
GDB hosted syscalls.
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 28 Jan 2007 03:10:55 +0000 (03:10 +0000)
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 28 Jan 2007 03:10:55 +0000 (03:10 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2364 c046a42c-6fe2-441c-8c8c-71466251a162

arm-semi.c
gdbstub.c
gdbstub.h

index 4254cba84aefdd31efa7de2a20990a701722072c..4ddbc73c2aebe9e486a7fd320b0f06b67f569eea 100644 (file)
 #define O_BINARY 0
 #endif
 
-int open_modeflags[12] = {
+#define GDB_O_RDONLY  0x000
+#define GDB_O_WRONLY  0x001
+#define GDB_O_RDWR    0x002
+#define GDB_O_APPEND  0x008
+#define GDB_O_CREAT   0x200
+#define GDB_O_TRUNC   0x400
+#define GDB_O_BINARY  0
+
+static int gdb_open_modeflags[12] = {
+    GDB_O_RDONLY,
+    GDB_O_RDONLY | GDB_O_BINARY,
+    GDB_O_RDWR,
+    GDB_O_RDWR | GDB_O_BINARY,
+    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
+    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
+    GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
+    GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
+    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
+    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
+    GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
+    GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
+};
+
+static int open_modeflags[12] = {
     O_RDONLY,
     O_RDONLY | O_BINARY,
     O_RDWR,
@@ -142,6 +165,35 @@ static void softmmu_unlock_user(CPUState *env, void *p, target_ulong addr,
 #define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len)
 #endif
 
+static target_ulong arm_semi_syscall_len;
+
+static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
+{
+#ifdef CONFIG_USER_ONLY
+    TaskState *ts = env->opaque;
+#endif
+    if (ret == (target_ulong)-1) {
+#ifdef CONFIG_USER_ONLY
+        ts->swi_errno = err;
+#endif
+        env->regs[0] = ret;
+    } else {
+        /* Fixup syscalls that use nonstardard return conventions.  */
+        switch (env->regs[0]) {
+        case SYS_WRITE:
+        case SYS_READ:
+            env->regs[0] = arm_semi_syscall_len - ret;
+            break;
+        case SYS_SEEK:
+            env->regs[0] = 0;
+            break;
+        default:
+            env->regs[0] = ret;
+            break;
+        }
+    }
+}
+
 #define ARG(n) tget32(args + (n) * 4)
 #define SET_ARG(n, val) tput32(args + (n) * 4,val)
 uint32_t do_arm_semihosting(CPUState *env)
@@ -170,52 +222,99 @@ uint32_t do_arm_semihosting(CPUState *env)
             else
                 return STDOUT_FILENO;
         }
-        ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0), (int)ARG(2),
+                           gdb_open_modeflags[ARG(1)]);
+            return env->regs[0];
+        } else {
+            ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
+        }
         unlock_user(s, ARG(0), 0);
         return ret;
     case SYS_CLOSE:
-        return set_swi_errno(ts, close(ARG(0)));
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
+            return env->regs[0];
+        } else {
+            return set_swi_errno(ts, close(ARG(0)));
+        }
     case SYS_WRITEC:
         {
           char c = tget8(args);
           /* Write to debug console.  stderr is near enough.  */
-          return write(STDERR_FILENO, &c, 1);
+          if (use_gdb_syscalls()) {
+                gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
+                return env->regs[0];
+          } else {
+                return write(STDERR_FILENO, &c, 1);
+          }
         }
     case SYS_WRITE0:
         s = lock_user_string(args);
-        ret = write(STDERR_FILENO, s, strlen(s));
+        len = strlen(s);
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
+            ret = env->regs[0];
+        } else {
+            ret = write(STDERR_FILENO, s, len);
+        }
         unlock_user(s, args, 0);
         return ret;
     case SYS_WRITE:
         len = ARG(2);
-        s = lock_user(ARG(1), len, 1);
-        ret = set_swi_errno(ts, write(ARG(0), s, len));
-        unlock_user(s, ARG(1), 0);
-        if (ret == (uint32_t)-1)
-            return -1;
-        return ARG(2) - ret;
+        if (use_gdb_syscalls()) {
+            arm_semi_syscall_len = len;
+            gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
+            return env->regs[0];
+        } else {
+            s = lock_user(ARG(1), len, 1);
+            ret = set_swi_errno(ts, write(ARG(0), s, len));
+            unlock_user(s, ARG(1), 0);
+            if (ret == (uint32_t)-1)
+                return -1;
+            return len - ret;
+        }
     case SYS_READ:
         len = ARG(2);
-        s = lock_user(ARG(1), len, 0);
-       do
-         ret = set_swi_errno(ts, read(ARG(0), s, len));
-       while (ret == -1 && errno == EINTR);
-        unlock_user(s, ARG(1), len);
-        if (ret == (uint32_t)-1)
-            return -1;
-        return ARG(2) - ret;
+        if (use_gdb_syscalls()) {
+            arm_semi_syscall_len = len;
+            gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
+            return env->regs[0];
+        } else {
+            s = lock_user(ARG(1), len, 0);
+            do
+              ret = set_swi_errno(ts, read(ARG(0), s, len));
+            while (ret == -1 && errno == EINTR);
+            unlock_user(s, ARG(1), len);
+            if (ret == (uint32_t)-1)
+                return -1;
+            return len - ret;
+        }
     case SYS_READC:
        /* XXX: Read from debug cosole. Not implemented.  */
         return 0;
     case SYS_ISTTY:
-        return isatty(ARG(0));
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
+            return env->regs[0];
+        } else {
+            return isatty(ARG(0));
+        }
     case SYS_SEEK:
-        ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
-       if (ret == (uint32_t)-1)
-         return -1;
-       return 0;
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "fseek,%x,%x,0", ARG(0), ARG(1));
+            return env->regs[0];
+        } else {
+            ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
+            if (ret == (uint32_t)-1)
+              return -1;
+            return 0;
+        }
     case SYS_FLEN:
-        {
+        if (use_gdb_syscalls()) {
+            /* TODO: Use stat syscall.  */
+            return -1;
+        } else {
             struct stat buf;
             ret = set_swi_errno(ts, fstat(ARG(0), &buf));
             if (ret == (uint32_t)-1)
@@ -226,12 +325,21 @@ uint32_t do_arm_semihosting(CPUState *env)
         /* XXX: Not implemented.  */
         return -1;
     case SYS_REMOVE:
-        s = lock_user_string(ARG(0));
-        ret =  set_swi_errno(ts, remove(s));
-        unlock_user(s, ARG(0), 0);
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1));
+            ret = env->regs[0];
+        } else {
+            s = lock_user_string(ARG(0));
+            ret =  set_swi_errno(ts, remove(s));
+            unlock_user(s, ARG(0), 0);
+        }
         return ret;
     case SYS_RENAME:
-        {
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
+                           ARG(0), (int)ARG(1), ARG(2), (int)ARG(3));
+            return env->regs[0];
+        } else {
             char *s2;
             s = lock_user_string(ARG(0));
             s2 = lock_user_string(ARG(2));
@@ -245,9 +353,14 @@ uint32_t do_arm_semihosting(CPUState *env)
     case SYS_TIME:
         return set_swi_errno(ts, time(NULL));
     case SYS_SYSTEM:
-        s = lock_user_string(ARG(0));
-        ret = set_swi_errno(ts, system(s));
-        unlock_user(s, ARG(0), 0);
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1));
+            return env->regs[0];
+        } else {
+            s = lock_user_string(ARG(0));
+            ret = set_swi_errno(ts, system(s));
+            unlock_user(s, ARG(0), 0);
+        }
     case SYS_ERRNO:
 #ifdef CONFIG_USER_ONLY
         return ts->swi_errno;
index dd7fe42aaa2df46c0e4cc83f9e445a30062ccf73..a26c12ca6e54e5331f0ccf37f264e5003a3c9c67 100644 (file)
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -52,6 +52,7 @@ enum RSState {
     RS_GETLINE,
     RS_CHKSUM1,
     RS_CHKSUM2,
+    RS_SYSCALL,
 };
 typedef struct GDBState {
     CPUState *env; /* current CPU */
@@ -96,6 +97,27 @@ static int get_char(GDBState *s)
 }
 #endif
 
+/* GDB stub state for use by semihosting syscalls.  */
+static GDBState *gdb_syscall_state;
+static gdb_syscall_complete_cb gdb_current_syscall_cb;
+
+enum {
+    GDB_SYS_UNKNOWN,
+    GDB_SYS_ENABLED,
+    GDB_SYS_DISABLED,
+} gdb_syscall_mode;
+
+/* If gdb is connected when the first semihosting syscall occurs then use
+   remote gdb syscalls.  Otherwise use native file IO.  */
+int use_gdb_syscalls(void)
+{
+    if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
+        gdb_syscall_mode = (gdb_syscall_state ? GDB_SYS_ENABLED
+                                              : GDB_SYS_DISABLED);
+    }
+    return gdb_syscall_mode == GDB_SYS_ENABLED;
+}
+
 static void put_buffer(GDBState *s, const uint8_t *buf, int len)
 {
 #ifdef CONFIG_USER_ONLY
@@ -755,6 +777,34 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
         vm_start();
 #endif
        return RS_IDLE;
+    case 'F':
+        {
+            target_ulong ret;
+            target_ulong err;
+
+            ret = strtoull(p, (char **)&p, 16);
+            if (*p == ',') {
+                p++;
+                err = strtoull(p, (char **)&p, 16);
+            } else {
+                err = 0;
+            }
+            if (*p == ',')
+                p++;
+            type = *p;
+            if (gdb_current_syscall_cb)
+                gdb_current_syscall_cb(s->env, ret, err);
+            if (type == 'C') {
+                put_packet(s, "T02");
+            } else {
+#ifdef CONFIG_USER_ONLY
+                s->running_state = 1;
+#else
+                vm_start();
+#endif
+            }
+        }
+        break;
     case 'g':
         reg_size = cpu_gdb_read_registers(env, mem_buf);
         memtohex(buf, mem_buf, reg_size);
@@ -855,6 +905,9 @@ static void gdb_vm_stopped(void *opaque, int reason)
     char buf[256];
     int ret;
 
+    if (s->state == RS_SYSCALL)
+        return;
+
     /* disable single step if it was enable */
     cpu_single_step(s->env, 0);
 
@@ -871,6 +924,60 @@ static void gdb_vm_stopped(void *opaque, int reason)
 }
 #endif
 
+/* Send a gdb syscall request.
+   This accepts limited printf-style format specifiers, specifically:
+    %x - target_ulong argument printed in hex.
+    %s - string pointer (target_ulong) and length (int) pair.  */
+void gdb_do_syscall(gdb_syscall_complete_cb cb, char *fmt, ...)
+{
+    va_list va;
+    char buf[256];
+    char *p;
+    target_ulong addr;
+    GDBState *s;
+
+    s = gdb_syscall_state;
+    if (!s)
+        return;
+    gdb_current_syscall_cb = cb;
+    s->state = RS_SYSCALL;
+#ifndef CONFIG_USER_ONLY
+    vm_stop(EXCP_DEBUG);
+#endif
+    s->state = RS_IDLE;
+    va_start(va, fmt);
+    p = buf;
+    *(p++) = 'F';
+    while (*fmt) {
+        if (*fmt == '%') {
+            fmt++;
+            switch (*fmt++) {
+            case 'x':
+                addr = va_arg(va, target_ulong);
+                p += sprintf(p, TARGET_FMT_lx, addr);
+                break;
+            case 's':
+                addr = va_arg(va, target_ulong);
+                p += sprintf(p, TARGET_FMT_lx "/%x", addr, va_arg(va, int));
+                break;
+            default:
+                fprintf(stderr, "gdbstub: Bad syscall format string '%s'\n",
+                        fmt - 1);
+                break;
+            }
+        } else {
+            *(p++) = *(fmt++);
+        }
+    }
+    va_end(va);
+    put_packet(s, buf);
+#ifdef CONFIG_USER_ONLY
+    gdb_handlesig(s->env, 0);
+#else
+    cpu_interrupt(s->env, CPU_INTERRUPT_EXIT);
+#endif
+}
+
 static void gdb_read_byte(GDBState *s, int ch)
 {
     CPUState *env = s->env;
@@ -942,6 +1049,8 @@ static void gdb_read_byte(GDBState *s, int ch)
                 s->state = gdb_handle_packet(s, env, s->line_buf);
             }
             break;
+        default:
+            abort();
         }
     }
 }
@@ -1034,6 +1143,8 @@ static void gdb_accept(void *opaque)
     s->env = first_cpu; /* XXX: allow to change CPU */
     s->fd = fd;
 
+    gdb_syscall_state = s;
+
     fcntl(fd, F_SETFL, O_NONBLOCK);
 }
 
@@ -1098,6 +1209,7 @@ static void gdb_chr_event(void *opaque, int event)
     switch (event) {
     case CHR_EVENT_RESET:
         vm_stop(EXCP_INTERRUPT);
+        gdb_syscall_state = opaque;
         break;
     default:
         break;
index c9d5c6827ac989674d47df9098e094a96957221a..c5b52c2e22624ed218097adc4c417ad24c94bf2b 100644 (file)
--- a/gdbstub.h
+++ b/gdbstub.h
@@ -3,6 +3,11 @@
 
 #define DEFAULT_GDBSTUB_PORT 1234
 
+typedef void (*gdb_syscall_complete_cb)(CPUState *env,
+                                        target_ulong ret, target_ulong err);
+
+void gdb_do_syscall(gdb_syscall_complete_cb cb, char *fmt, ...);
+int use_gdb_syscalls(void);
 #ifdef CONFIG_USER_ONLY
 int gdb_handlesig (CPUState *, int);
 void gdb_exit(CPUState *, int);