Merge branch 'master' of /home/nchip/public_html/qemu into garage-push
[qemu] / gdbstub.c
index 8967276..9bd4375 100644 (file)
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -15,7 +15,7 @@
  *
  * You should have received a copy 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
  */
 #include "config.h"
 #include "qemu-common.h"
@@ -30,6 +30,7 @@
 
 #include "qemu.h"
 #else
+#include "monitor.h"
 #include "qemu-char.h"
 #include "sysemu.h"
 #include "gdbstub.h"
 #define MAX_PACKET_LENGTH 4096
 
 #include "qemu_socket.h"
-#ifdef _WIN32
-/* XXX: these constants may be independent of the host ones even for Unix */
-#ifndef SIGTRAP
-#define SIGTRAP 5
+#include "kvm.h"
+
+
+enum {
+    GDB_SIGNAL_0 = 0,
+    GDB_SIGNAL_INT = 2,
+    GDB_SIGNAL_TRAP = 5,
+    GDB_SIGNAL_UNKNOWN = 143
+};
+
+#ifdef CONFIG_USER_ONLY
+
+/* Map target signal numbers to GDB protocol signal numbers and vice
+ * versa.  For user emulation's currently supported systems, we can
+ * assume most signals are defined.
+ */
+
+static int gdb_signal_table[] = {
+    0,
+    TARGET_SIGHUP,
+    TARGET_SIGINT,
+    TARGET_SIGQUIT,
+    TARGET_SIGILL,
+    TARGET_SIGTRAP,
+    TARGET_SIGABRT,
+    -1, /* SIGEMT */
+    TARGET_SIGFPE,
+    TARGET_SIGKILL,
+    TARGET_SIGBUS,
+    TARGET_SIGSEGV,
+    TARGET_SIGSYS,
+    TARGET_SIGPIPE,
+    TARGET_SIGALRM,
+    TARGET_SIGTERM,
+    TARGET_SIGURG,
+    TARGET_SIGSTOP,
+    TARGET_SIGTSTP,
+    TARGET_SIGCONT,
+    TARGET_SIGCHLD,
+    TARGET_SIGTTIN,
+    TARGET_SIGTTOU,
+    TARGET_SIGIO,
+    TARGET_SIGXCPU,
+    TARGET_SIGXFSZ,
+    TARGET_SIGVTALRM,
+    TARGET_SIGPROF,
+    TARGET_SIGWINCH,
+    -1, /* SIGLOST */
+    TARGET_SIGUSR1,
+    TARGET_SIGUSR2,
+#ifdef TARGET_SIGPWR
+    TARGET_SIGPWR,
+#else
+    -1,
 #endif
-#ifndef SIGINT
-#define SIGINT 2
+    -1, /* SIGPOLL */
+    -1,
+    -1,
+    -1,
+    -1,
+    -1,
+    -1,
+    -1,
+    -1,
+    -1,
+    -1,
+    -1,
+#ifdef __SIGRTMIN
+    __SIGRTMIN + 1,
+    __SIGRTMIN + 2,
+    __SIGRTMIN + 3,
+    __SIGRTMIN + 4,
+    __SIGRTMIN + 5,
+    __SIGRTMIN + 6,
+    __SIGRTMIN + 7,
+    __SIGRTMIN + 8,
+    __SIGRTMIN + 9,
+    __SIGRTMIN + 10,
+    __SIGRTMIN + 11,
+    __SIGRTMIN + 12,
+    __SIGRTMIN + 13,
+    __SIGRTMIN + 14,
+    __SIGRTMIN + 15,
+    __SIGRTMIN + 16,
+    __SIGRTMIN + 17,
+    __SIGRTMIN + 18,
+    __SIGRTMIN + 19,
+    __SIGRTMIN + 20,
+    __SIGRTMIN + 21,
+    __SIGRTMIN + 22,
+    __SIGRTMIN + 23,
+    __SIGRTMIN + 24,
+    __SIGRTMIN + 25,
+    __SIGRTMIN + 26,
+    __SIGRTMIN + 27,
+    __SIGRTMIN + 28,
+    __SIGRTMIN + 29,
+    __SIGRTMIN + 30,
+    __SIGRTMIN + 31,
+    -1, /* SIGCANCEL */
+    __SIGRTMIN,
+    __SIGRTMIN + 32,
+    __SIGRTMIN + 33,
+    __SIGRTMIN + 34,
+    __SIGRTMIN + 35,
+    __SIGRTMIN + 36,
+    __SIGRTMIN + 37,
+    __SIGRTMIN + 38,
+    __SIGRTMIN + 39,
+    __SIGRTMIN + 40,
+    __SIGRTMIN + 41,
+    __SIGRTMIN + 42,
+    __SIGRTMIN + 43,
+    __SIGRTMIN + 44,
+    __SIGRTMIN + 45,
+    __SIGRTMIN + 46,
+    __SIGRTMIN + 47,
+    __SIGRTMIN + 48,
+    __SIGRTMIN + 49,
+    __SIGRTMIN + 50,
+    __SIGRTMIN + 51,
+    __SIGRTMIN + 52,
+    __SIGRTMIN + 53,
+    __SIGRTMIN + 54,
+    __SIGRTMIN + 55,
+    __SIGRTMIN + 56,
+    __SIGRTMIN + 57,
+    __SIGRTMIN + 58,
+    __SIGRTMIN + 59,
+    __SIGRTMIN + 60,
+    __SIGRTMIN + 61,
+    __SIGRTMIN + 62,
+    __SIGRTMIN + 63,
+    __SIGRTMIN + 64,
+    __SIGRTMIN + 65,
+    __SIGRTMIN + 66,
+    __SIGRTMIN + 67,
+    __SIGRTMIN + 68,
+    __SIGRTMIN + 69,
+    __SIGRTMIN + 70,
+    __SIGRTMIN + 71,
+    __SIGRTMIN + 72,
+    __SIGRTMIN + 73,
+    __SIGRTMIN + 74,
+    __SIGRTMIN + 75,
+    __SIGRTMIN + 76,
+    __SIGRTMIN + 77,
+    __SIGRTMIN + 78,
+    __SIGRTMIN + 79,
+    __SIGRTMIN + 80,
+    __SIGRTMIN + 81,
+    __SIGRTMIN + 82,
+    __SIGRTMIN + 83,
+    __SIGRTMIN + 84,
+    __SIGRTMIN + 85,
+    __SIGRTMIN + 86,
+    __SIGRTMIN + 87,
+    __SIGRTMIN + 88,
+    __SIGRTMIN + 89,
+    __SIGRTMIN + 90,
+    __SIGRTMIN + 91,
+    __SIGRTMIN + 92,
+    __SIGRTMIN + 93,
+    __SIGRTMIN + 94,
+    __SIGRTMIN + 95,
+    -1, /* SIGINFO */
+    -1, /* UNKNOWN */
+    -1, /* DEFAULT */
+    -1,
+    -1,
+    -1,
+    -1,
+    -1,
+    -1
 #endif
+};
 #else
-#include <signal.h>
+/* In system mode we only need SIGINT and SIGTRAP; other signals
+   are not yet supported.  */
+
+enum {
+    TARGET_SIGINT = 2,
+    TARGET_SIGTRAP = 5
+};
+
+static int gdb_signal_table[] = {
+    -1,
+    -1,
+    TARGET_SIGINT,
+    -1,
+    -1,
+    TARGET_SIGTRAP
+};
+#endif
+
+#ifdef CONFIG_USER_ONLY
+static int target_signal_to_gdb (int sig)
+{
+    int i;
+    for (i = 0; i < ARRAY_SIZE (gdb_signal_table); i++)
+        if (gdb_signal_table[i] == sig)
+            return i;
+    return GDB_SIGNAL_UNKNOWN;
+}
 #endif
 
+static int gdb_signal_to_target (int sig)
+{
+    if (sig < ARRAY_SIZE (gdb_signal_table))
+        return gdb_signal_table[sig];
+    else
+        return -1;
+}
+
 //#define DEBUG_GDB
 
 typedef struct GDBRegisterState {
@@ -62,6 +265,7 @@ typedef struct GDBRegisterState {
 } GDBRegisterState;
 
 enum RSState {
+    RS_INACTIVE,
     RS_IDLE,
     RS_GETLINE,
     RS_CHKSUM1,
@@ -84,6 +288,7 @@ typedef struct GDBState {
     int running_state;
 #else
     CharDriverState *chr;
+    CharDriverState *mon_chr;
 #endif
 } GDBState;
 
@@ -129,7 +334,7 @@ static int get_char(GDBState *s)
 
 static gdb_syscall_complete_cb gdb_current_syscall_cb;
 
-enum {
+static enum {
     GDB_SYS_UNKNOWN,
     GDB_SYS_ENABLED,
     GDB_SYS_DISABLED,
@@ -419,7 +624,17 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int i)
 
 #elif defined (TARGET_PPC)
 
+/* Old gdb always expects FP registers.  Newer (xml-aware) gdb only
+   expects whatever the target description contains.  Due to a
+   historical mishap the FP registers appear in between core integer
+   regs and PC, MSR, CR, and so forth.  We hack round this by giving the
+   FP regs zero size when talking to a newer gdb.  */
 #define NUM_CORE_REGS 71
+#if defined (TARGET_PPC64)
+#define GDB_CORE_XML "power64-core.xml"
+#else
+#define GDB_CORE_XML "power-core.xml"
+#endif
 
 static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
 {
@@ -428,6 +643,8 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
         GET_REGL(env->gpr[n]);
     } else if (n < 64) {
         /* fprs */
+        if (gdb_has_xml)
+            return 0;
         stfq_p(mem_buf, env->fpr[n-32]);
         return 8;
     } else {
@@ -445,7 +662,12 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
         case 67: GET_REGL(env->lr);
         case 68: GET_REGL(env->ctr);
         case 69: GET_REGL(env->xer);
-        case 70: GET_REG32(0); /* fpscr */
+        case 70:
+            {
+                if (gdb_has_xml)
+                    return 0;
+                GET_REG32(0); /* fpscr */
+            }
         }
     }
     return 0;
@@ -459,6 +681,8 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
         return sizeof(target_ulong);
     } else if (n < 64) {
         /* fprs */
+        if (gdb_has_xml)
+            return 0;
         env->fpr[n-32] = ldfq_p(mem_buf);
         return 8;
     } else {
@@ -488,6 +712,8 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
             return sizeof(target_ulong);
         case 70:
             /* fpscr */
+            if (gdb_has_xml)
+                return 0;
             return 4;
         }
     }
@@ -499,7 +725,7 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
 #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
 #define NUM_CORE_REGS 86
 #else
-#define NUM_CORE_REGS 73
+#define NUM_CORE_REGS 72
 #endif
 
 #ifdef TARGET_ABI32
@@ -533,7 +759,7 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
     case 69: GET_REGA(env->npc);
     case 70: GET_REGA(env->fsr);
     case 71: GET_REGA(0); /* csr */
-    case 72: GET_REGA(0);
+    default: GET_REGA(0);
     }
 #else
     if (n < 64) {
@@ -930,6 +1156,36 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
 
     return 4;
 }
+#elif defined (TARGET_MICROBLAZE)
+
+#define NUM_CORE_REGS (32 + 5)
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+    if (n < 32) {
+       GET_REG32(env->regs[n]);
+    } else {
+       GET_REG32(env->sregs[n - 32]);
+    }
+    return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+    uint32_t tmp;
+
+    if (n > NUM_CORE_REGS)
+       return 0;
+
+    tmp = ldl_p(mem_buf);
+
+    if (n < 32) {
+       env->regs[n] = tmp;
+    } else {
+       env->sregs[n - 32] = tmp;
+    }
+    return 4;
+}
 #elif defined (TARGET_CRIS)
 
 #define NUM_CORE_REGS 49
@@ -990,6 +1246,56 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
 
     return 4;
 }
+#elif defined (TARGET_ALPHA)
+
+#define NUM_CORE_REGS 65
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+    if (n < 31) {
+       GET_REGL(env->ir[n]);
+    }
+    else if (n == 31) {
+       GET_REGL(0);
+    }
+    else if (n<63) {
+       uint64_t val;
+
+       val=*((uint64_t *)&env->fir[n-32]);
+       GET_REGL(val);
+    }
+    else if (n==63) {
+       GET_REGL(env->fpcr);
+    }
+    else if (n==64) {
+       GET_REGL(env->pc);
+    }
+    else {
+       GET_REGL(0);
+    }
+
+    return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+    target_ulong tmp;
+    tmp = ldtul_p(mem_buf);
+
+    if (n < 31) {
+        env->ir[n] = tmp;
+    }
+
+    if (n > 31 && n < 63) {
+        env->fir[n - 32] = ldfl_p(mem_buf);
+    }
+
+    if (n == 64 ) {
+       env->pc=tmp;
+    }
+
+    return 8;
+}
 #else
 
 #define NUM_CORE_REGS 0
@@ -1030,7 +1336,7 @@ static int memtox(char *buf, const char *mem, int len)
     return p - buf;
 }
 
-const char *get_feature_xml(const char *p, const char **newp)
+static const char *get_feature_xml(const char *p, const char **newp)
 {
     extern const char *const xml_builtin[][2];
     size_t len;
@@ -1057,11 +1363,11 @@ const char *get_feature_xml(const char *p, const char **newp)
                      GDB_CORE_XML);
 
             for (r = first_cpu->gdb_regs; r; r = r->next) {
-                strcat(target_xml, "<xi:include href=\"");
-                strcat(target_xml, r->xml);
-                strcat(target_xml, "\"/>");
+                pstrcat(target_xml, sizeof(target_xml), "<xi:include href=\"");
+                pstrcat(target_xml, sizeof(target_xml), r->xml);
+                pstrcat(target_xml, sizeof(target_xml), "\"/>");
             }
-            strcat(target_xml, "</target>");
+            pstrcat(target_xml, sizeof(target_xml), "</target>");
         }
         return target_xml;
     }
@@ -1144,13 +1450,6 @@ void gdb_register_coprocessor(CPUState * env,
     }
 }
 
-/* GDB breakpoint/watchpoint types */
-#define GDB_BREAKPOINT_SW        0
-#define GDB_BREAKPOINT_HW        1
-#define GDB_WATCHPOINT_WRITE     2
-#define GDB_WATCHPOINT_READ      3
-#define GDB_WATCHPOINT_ACCESS    4
-
 #ifndef CONFIG_USER_ONLY
 static const int xlat_gdb_type[] = {
     [GDB_WATCHPOINT_WRITE]  = BP_GDB | BP_MEM_WRITE,
@@ -1164,6 +1463,9 @@ static int gdb_breakpoint_insert(target_ulong addr, target_ulong len, int type)
     CPUState *env;
     int err = 0;
 
+    if (kvm_enabled())
+        return kvm_insert_breakpoint(gdbserver_state->c_cpu, addr, len, type);
+
     switch (type) {
     case GDB_BREAKPOINT_SW:
     case GDB_BREAKPOINT_HW:
@@ -1195,6 +1497,9 @@ static int gdb_breakpoint_remove(target_ulong addr, target_ulong len, int type)
     CPUState *env;
     int err = 0;
 
+    if (kvm_enabled())
+        return kvm_remove_breakpoint(gdbserver_state->c_cpu, addr, len, type);
+
     switch (type) {
     case GDB_BREAKPOINT_SW:
     case GDB_BREAKPOINT_HW:
@@ -1224,6 +1529,11 @@ static void gdb_breakpoint_remove_all(void)
 {
     CPUState *env;
 
+    if (kvm_enabled()) {
+        kvm_remove_all_breakpoints(gdbserver_state->c_cpu);
+        return;
+    }
+
     for (env = first_cpu; env != NULL; env = env->next_cpu) {
         cpu_breakpoint_remove_all(env, BP_GDB);
 #ifndef CONFIG_USER_ONLY
@@ -1232,11 +1542,59 @@ static void gdb_breakpoint_remove_all(void)
     }
 }
 
+static void gdb_set_cpu_pc(GDBState *s, target_ulong pc)
+{
+#if defined(TARGET_I386)
+    s->c_cpu->eip = pc;
+    cpu_synchronize_state(s->c_cpu, 1);
+#elif defined (TARGET_PPC)
+    s->c_cpu->nip = pc;
+#elif defined (TARGET_SPARC)
+    s->c_cpu->pc = pc;
+    s->c_cpu->npc = pc + 4;
+#elif defined (TARGET_ARM)
+    s->c_cpu->regs[15] = pc;
+#elif defined (TARGET_SH4)
+    s->c_cpu->pc = pc;
+#elif defined (TARGET_MIPS)
+    s->c_cpu->active_tc.PC = pc;
+#elif defined (TARGET_MICROBLAZE)
+    s->c_cpu->sregs[SR_PC] = pc;
+#elif defined (TARGET_CRIS)
+    s->c_cpu->pc = pc;
+#elif defined (TARGET_ALPHA)
+    s->c_cpu->pc = pc;
+#endif
+}
+
+static inline int gdb_id(CPUState *env)
+{
+#if defined(CONFIG_USER_ONLY) && defined(USE_NPTL)
+    return env->host_tid;
+#else
+    return env->cpu_index + 1;
+#endif
+}
+
+static CPUState *find_cpu(uint32_t thread_id)
+{
+    CPUState *env;
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        if (gdb_id(env) == thread_id) {
+            return env;
+        }
+    }
+
+    return NULL;
+}
+
 static int gdb_handle_packet(GDBState *s, const char *line_buf)
 {
     CPUState *env;
     const char *p;
-    int ch, reg_size, type, res, thread;
+    uint32_t thread;
+    int ch, reg_size, type, res;
     char buf[MAX_PACKET_LENGTH];
     uint8_t mem_buf[MAX_PACKET_LENGTH];
     uint8_t *registers;
@@ -1250,8 +1608,8 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
     switch(ch) {
     case '?':
         /* TODO: Make this return the correct value for user-mode.  */
-        snprintf(buf, sizeof(buf), "T%02xthread:%02x;", SIGTRAP,
-                 s->c_cpu->cpu_index+1);
+        snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP,
+                 gdb_id(s->c_cpu));
         put_packet(s, buf);
         /* Remove all the breakpoints when this query is issued,
          * because gdb is doing and initial connect and the state
@@ -1262,27 +1620,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
     case 'c':
         if (*p != '\0') {
             addr = strtoull(p, (char **)&p, 16);
-#if defined(TARGET_I386)
-            s->c_cpu->eip = addr;
-#elif defined (TARGET_PPC)
-            s->c_cpu->nip = addr;
-#elif defined (TARGET_SPARC)
-            s->c_cpu->pc = addr;
-            s->c_cpu->npc = addr + 4;
-#elif defined (TARGET_ARM)
-            s->c_cpu->regs[15] = addr;
-#elif defined (TARGET_SH4)
-            s->c_cpu->pc = addr;
-#elif defined (TARGET_MIPS)
-            s->c_cpu->active_tc.PC = addr;
-#elif defined (TARGET_CRIS)
-            s->c_cpu->pc = addr;
-#endif
+            gdb_set_cpu_pc(s, addr);
         }
+        s->signal = 0;
         gdb_continue(s);
        return RS_IDLE;
     case 'C':
-        s->signal = strtoul(p, (char **)&p, 16);
+        s->signal = gdb_signal_to_target (strtoul(p, (char **)&p, 16));
+        if (s->signal == -1)
+            s->signal = 0;
         gdb_continue(s);
         return RS_IDLE;
     case 'k':
@@ -1298,22 +1644,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
     case 's':
         if (*p != '\0') {
             addr = strtoull(p, (char **)&p, 16);
-#if defined(TARGET_I386)
-            s->c_cpu->eip = addr;
-#elif defined (TARGET_PPC)
-            s->c_cpu->nip = addr;
-#elif defined (TARGET_SPARC)
-            s->c_cpu->pc = addr;
-            s->c_cpu->npc = addr + 4;
-#elif defined (TARGET_ARM)
-            s->c_cpu->regs[15] = addr;
-#elif defined (TARGET_SH4)
-            s->c_cpu->pc = addr;
-#elif defined (TARGET_MIPS)
-            s->c_cpu->active_tc.PC = addr;
-#elif defined (TARGET_CRIS)
-            s->c_cpu->pc = addr;
-#endif
+            gdb_set_cpu_pc(s, addr);
         }
         cpu_single_step(s->c_cpu, sstep_flags);
         gdb_continue(s);
@@ -1343,6 +1674,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
         }
         break;
     case 'g':
+        cpu_synchronize_state(s->g_cpu, 0);
         len = 0;
         for (addr = 0; addr < num_g_regs; addr++) {
             reg_size = gdb_read_register(s->g_cpu, mem_buf + len, addr);
@@ -1360,6 +1692,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
             len -= reg_size;
             registers += reg_size;
         }
+        cpu_synchronize_state(s->g_cpu, 1);
         put_packet(s, "OK");
         break;
     case 'm':
@@ -1440,9 +1773,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
             put_packet(s, "OK");
             break;
         }
-        for (env = first_cpu; env != NULL; env = env->next_cpu)
-            if (env->cpu_index + 1 == thread)
-                break;
+        env = find_cpu(thread);
         if (env == NULL) {
             put_packet(s, "E22");
             break;
@@ -1463,14 +1794,13 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
         break;
     case 'T':
         thread = strtoull(p, (char **)&p, 16);
-#ifndef CONFIG_USER_ONLY
-        if (thread > 0 && thread < smp_cpus + 1)
-#else
-        if (thread == 1)
-#endif
-             put_packet(s, "OK");
-        else
+        env = find_cpu(thread);
+
+        if (env != NULL) {
+            put_packet(s, "OK");
+        } else {
             put_packet(s, "E22");
+        }
         break;
     case 'q':
     case 'Q':
@@ -1508,7 +1838,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
         } else if (strcmp(p,"sThreadInfo") == 0) {
         report_cpuinfo:
             if (s->query_cpu) {
-                snprintf(buf, sizeof(buf), "m%x", s->query_cpu->cpu_index+1);
+                snprintf(buf, sizeof(buf), "m%x", gdb_id(s->query_cpu));
                 put_packet(s, buf);
                 s->query_cpu = s->query_cpu->next_cpu;
             } else
@@ -1516,18 +1846,18 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
             break;
         } else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) {
             thread = strtoull(p+16, (char **)&p, 16);
-            for (env = first_cpu; env != NULL; env = env->next_cpu)
-                if (env->cpu_index + 1 == thread) {
-                    len = snprintf((char *)mem_buf, sizeof(mem_buf),
-                                   "CPU#%d [%s]", env->cpu_index,
-                                   env->halted ? "halted " : "running");
-                    memtohex(buf, mem_buf, len);
-                    put_packet(s, buf);
-                    break;
-                }
+            env = find_cpu(thread);
+            if (env != NULL) {
+                cpu_synchronize_state(env, 0);
+                len = snprintf((char *)mem_buf, sizeof(mem_buf),
+                               "CPU#%d [%s]", env->cpu_index,
+                               env->halted ? "halted " : "running");
+                memtohex(buf, mem_buf, len);
+                put_packet(s, buf);
+            }
             break;
         }
-#ifdef CONFIG_LINUX_USER
+#ifdef CONFIG_USER_ONLY
         else if (strncmp(p, "Offsets", 7) == 0) {
             TaskState *ts = s->c_cpu->opaque;
 
@@ -1540,11 +1870,26 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
             put_packet(s, buf);
             break;
         }
-#endif
+#else /* !CONFIG_USER_ONLY */
+        else if (strncmp(p, "Rcmd,", 5) == 0) {
+            int len = strlen(p + 5);
+
+            if ((len % 2) != 0) {
+                put_packet(s, "E01");
+                break;
+            }
+            hextomem(mem_buf, p + 5, len);
+            len = len / 2;
+            mem_buf[len++] = 0;
+            qemu_chr_read(s->mon_chr, mem_buf, len);
+            put_packet(s, "OK");
+            break;
+        }
+#endif /* !CONFIG_USER_ONLY */
         if (strncmp(p, "Supported", 9) == 0) {
             snprintf(buf, sizeof(buf), "PacketSize=%x", MAX_PACKET_LENGTH);
 #ifdef GDB_CORE_XML
-            strcat(buf, ";qXfer:features:read+");
+            pstrcat(buf, sizeof(buf), ";qXfer:features:read+");
 #endif
             put_packet(s, buf);
             break;
@@ -1609,7 +1954,7 @@ void gdb_set_stop_cpu(CPUState *env)
 }
 
 #ifndef CONFIG_USER_ONLY
-static void gdb_vm_stopped(void *opaque, int reason)
+static void gdb_vm_state_change(void *opaque, int running, int reason)
 {
     GDBState *s = gdbserver_state;
     CPUState *env = s->c_cpu;
@@ -1617,7 +1962,8 @@ static void gdb_vm_stopped(void *opaque, int reason)
     const char *type;
     int ret;
 
-    if (s->state == RS_SYSCALL)
+    if (running || (reason != EXCP_DEBUG && reason != EXCP_INTERRUPT) ||
+        s->state == RS_INACTIVE || s->state == RS_SYSCALL)
         return;
 
     /* disable single step if it was enable */
@@ -1638,20 +1984,18 @@ static void gdb_vm_stopped(void *opaque, int reason)
             }
             snprintf(buf, sizeof(buf),
                      "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
-                     SIGTRAP, env->cpu_index+1, type,
+                     GDB_SIGNAL_TRAP, gdb_id(env), type,
                      env->watchpoint_hit->vaddr);
             put_packet(s, buf);
             env->watchpoint_hit = NULL;
             return;
         }
        tb_flush(env);
-        ret = SIGTRAP;
-    } else if (reason == EXCP_INTERRUPT) {
-        ret = SIGINT;
+        ret = GDB_SIGNAL_TRAP;
     } else {
-        ret = 0;
+        ret = GDB_SIGNAL_INT;
     }
-    snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, env->cpu_index+1);
+    snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, gdb_id(env));
     put_packet(s, buf);
 }
 #endif
@@ -1717,7 +2061,7 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
 #ifdef CONFIG_USER_ONLY
     gdb_handlesig(s->c_cpu, 0);
 #else
-    cpu_interrupt(s->c_cpu, CPU_INTERRUPT_EXIT);
+    cpu_exit(s->c_cpu);
 #endif
 }
 
@@ -1799,6 +2143,19 @@ static void gdb_read_byte(GDBState *s, int ch)
 
 #ifdef CONFIG_USER_ONLY
 int
+gdb_queuesig (void)
+{
+    GDBState *s;
+
+    s = gdbserver_state;
+
+    if (gdbserver_fd < 0 || s->fd < 0)
+        return 0;
+    else
+        return 1;
+}
+
+int
 gdb_handlesig (CPUState *env, int sig)
 {
   GDBState *s;
@@ -1815,7 +2172,7 @@ gdb_handlesig (CPUState *env, int sig)
 
   if (sig != 0)
     {
-      snprintf(buf, sizeof(buf), "S%02x", sig);
+      snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb (sig));
       put_packet(s, buf);
     }
   /* put_packet() might have detected that the peer terminated the 
@@ -1861,6 +2218,19 @@ void gdb_exit(CPUState *env, int code)
   put_packet(s, buf);
 }
 
+/* Tell the remote gdb that the process has exited due to SIG.  */
+void gdb_signalled(CPUState *env, int sig)
+{
+  GDBState *s;
+  char buf[4];
+
+  s = gdbserver_state;
+  if (gdbserver_fd < 0 || s->fd < 0)
+    return;
+
+  snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb (sig));
+  put_packet(s, buf);
+}
 
 static void gdb_accept(void)
 {
@@ -1885,13 +2255,6 @@ static void gdb_accept(void)
     setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
 
     s = qemu_mallocz(sizeof(GDBState));
-    if (!s) {
-        errno = ENOMEM;
-        perror("accept");
-        return;
-    }
-
-    memset (s, 0, sizeof (GDBState));
     s->c_cpu = first_cpu;
     s->g_cpu = first_cpu;
     s->fd = fd;
@@ -1942,6 +2305,18 @@ int gdbserver_start(int port)
     gdb_accept();
     return 0;
 }
+
+/* Disable gdb stub for child processes.  */
+void gdbserver_fork(CPUState *env)
+{
+    GDBState *s = gdbserver_state;
+    if (gdbserver_fd < 0 || s->fd < 0)
+      return;
+    close(s->fd);
+    s->fd = -1;
+    cpu_breakpoint_remove_all(env, BP_GDB);
+    cpu_watchpoint_remove_all(env, BP_GDB);
+}
 #else
 static int gdb_chr_can_receive(void *opaque)
 {
@@ -1971,40 +2346,99 @@ static void gdb_chr_event(void *opaque, int event)
     }
 }
 
-int gdbserver_start(const char *port)
+static void gdb_monitor_output(GDBState *s, const char *msg, int len)
 {
-    GDBState *s;
-    char gdbstub_port_name[128];
-    int port_num;
-    char *p;
-    CharDriverState *chr;
+    char buf[MAX_PACKET_LENGTH];
 
-    if (!port || !*port)
-      return -1;
+    buf[0] = 'O';
+    if (len > (MAX_PACKET_LENGTH/2) - 1)
+        len = (MAX_PACKET_LENGTH/2) - 1;
+    memtohex(buf + 1, (uint8_t *)msg, len);
+    put_packet(s, buf);
+}
 
-    port_num = strtol(port, &p, 10);
-    if (*p == 0) {
-        /* A numeric value is interpreted as a port number.  */
-        snprintf(gdbstub_port_name, sizeof(gdbstub_port_name),
-                 "tcp::%d,nowait,nodelay,server", port_num);
-        port = gdbstub_port_name;
+static int gdb_monitor_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    const char *p = (const char *)buf;
+    int max_sz;
+
+    max_sz = (sizeof(gdbserver_state->last_packet) - 2) / 2;
+    for (;;) {
+        if (len <= max_sz) {
+            gdb_monitor_output(gdbserver_state, p, len);
+            break;
+        }
+        gdb_monitor_output(gdbserver_state, p, max_sz);
+        p += max_sz;
+        len -= max_sz;
     }
+    return len;
+}
+
+#ifndef _WIN32
+static void gdb_sigterm_handler(int signal)
+{
+    if (vm_running)
+        vm_stop(EXCP_INTERRUPT);
+}
+#endif
 
-    chr = qemu_chr_open("gdb", port);
-    if (!chr)
+int gdbserver_start(const char *device)
+{
+    GDBState *s;
+    char gdbstub_device_name[128];
+    CharDriverState *chr = NULL;
+    CharDriverState *mon_chr;
+
+    if (!device)
         return -1;
+    if (strcmp(device, "none") != 0) {
+        if (strstart(device, "tcp:", NULL)) {
+            /* enforce required TCP attributes */
+            snprintf(gdbstub_device_name, sizeof(gdbstub_device_name),
+                     "%s,nowait,nodelay,server", device);
+            device = gdbstub_device_name;
+        }
+#ifndef _WIN32
+        else if (strcmp(device, "stdio") == 0) {
+            struct sigaction act;
 
-    s = qemu_mallocz(sizeof(GDBState));
+            memset(&act, 0, sizeof(act));
+            act.sa_handler = gdb_sigterm_handler;
+            sigaction(SIGINT, &act, NULL);
+        }
+#endif
+        chr = qemu_chr_open("gdb", device, NULL);
+        if (!chr)
+            return -1;
+
+        qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive,
+                              gdb_chr_event, NULL);
+    }
+
+    s = gdbserver_state;
     if (!s) {
-        return -1;
+        s = qemu_mallocz(sizeof(GDBState));
+        gdbserver_state = s;
+
+        qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
+
+        /* Initialize a monitor terminal for gdb */
+        mon_chr = qemu_mallocz(sizeof(*mon_chr));
+        mon_chr->chr_write = gdb_monitor_write;
+        monitor_init(mon_chr, 0);
+    } else {
+        if (s->chr)
+            qemu_chr_close(s->chr);
+        mon_chr = s->mon_chr;
+        memset(s, 0, sizeof(GDBState));
     }
     s->c_cpu = first_cpu;
     s->g_cpu = first_cpu;
     s->chr = chr;
-    gdbserver_state = s;
-    qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive,
-                          gdb_chr_event, NULL);
-    qemu_add_vm_stop_handler(gdb_vm_stopped, NULL);
+    s->state = chr ? RS_IDLE : RS_INACTIVE;
+    s->mon_chr = mon_chr;
+
     return 0;
 }
 #endif