aboutsummaryrefslogtreecommitdiffstats
path: root/gdbstub.c
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@google.com>2009-09-14 14:32:27 -0700
committerDavid 'Digit' Turner <digit@google.com>2009-09-14 14:32:27 -0700
commit5d8f37ad78fc66901af50c762029a501561f3b23 (patch)
tree206790f8f21000850a98c4f9590a79e779106278 /gdbstub.c
parentcd059b15f2c7df69f4a087bd66900eb172e41d1c (diff)
downloadexternal_qemu-5d8f37ad78fc66901af50c762029a501561f3b23.zip
external_qemu-5d8f37ad78fc66901af50c762029a501561f3b23.tar.gz
external_qemu-5d8f37ad78fc66901af50c762029a501561f3b23.tar.bz2
Merge upstream QEMU 10.0.50 into the Android source tree.
This change integrates many changes from the upstream QEMU sources. Its main purpose is to enable correct ARMv6 and ARMv7 support to the Android emulator. Due to the nature of the upstream code base, this unfortunately also required changes to many other parts of the source. Note that to ensure easier integrations in the future, some source files and directories that have heavy Android-specific customization have been renamed with an -android suffix. The original files are still there for easier integration tracking, but *never* compiled. For example: net.c net-android.c qemu-char.c qemu-char-android.c slirp/ slirp-android/ etc... Tested on linux-x86, darwin-x86 and windows host machines.
Diffstat (limited to 'gdbstub.c')
-rw-r--r--gdbstub.c2383
1 files changed, 1617 insertions, 766 deletions
diff --git a/gdbstub.c b/gdbstub.c
index 7923dba..9bd4375 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -15,9 +15,10 @@
*
* 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"
#ifdef CONFIG_USER_ONLY
#include <stdlib.h>
#include <stdio.h>
@@ -29,29 +30,242 @@
#include "qemu.h"
#else
-#include "qemu-common.h"
+#include "monitor.h"
#include "qemu-char.h"
#include "sysemu.h"
-#include "cpu.h"
#include "gdbstub.h"
#endif
+#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 {
+ int base_reg;
+ int num_regs;
+ gdb_reg_cb get_reg;
+ gdb_reg_cb set_reg;
+ const char *xml;
+ struct GDBRegisterState *next;
+} GDBRegisterState;
+
enum RSState {
+ RS_INACTIVE,
RS_IDLE,
RS_GETLINE,
RS_CHKSUM1,
@@ -59,12 +273,14 @@ enum RSState {
RS_SYSCALL,
};
typedef struct GDBState {
- CPUState *env; /* current CPU */
+ CPUState *c_cpu; /* current CPU for step/continue ops */
+ CPUState *g_cpu; /* current CPU for other ops */
+ CPUState *query_cpu; /* for q{f|s}ThreadInfo */
enum RSState state; /* parsing state */
- char line_buf[4096];
+ char line_buf[MAX_PACKET_LENGTH];
int line_buf_index;
int line_csum;
- uint8_t last_packet[4100];
+ uint8_t last_packet[MAX_PACKET_LENGTH + 4];
int last_packet_len;
int signal;
#ifdef CONFIG_USER_ONLY
@@ -72,6 +288,7 @@ typedef struct GDBState {
int running_state;
#else
CharDriverState *chr;
+ CharDriverState *mon_chr;
#endif
} GDBState;
@@ -80,27 +297,31 @@ typedef struct GDBState {
*/
static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER;
+static GDBState *gdbserver_state;
+
+/* This is an ugly hack to cope with both new and old gdb.
+ If gdb sends qXfer:features:read then assume we're talking to a newish
+ gdb that understands target descriptions. */
+static int gdb_has_xml;
+
#ifdef CONFIG_USER_ONLY
/* XXX: This is not thread safe. Do we care? */
static int gdbserver_fd = -1;
-/* XXX: remove this hack. */
-static GDBState gdbserver_state;
-
static int get_char(GDBState *s)
{
uint8_t ch;
int ret;
for(;;) {
- ret = socket_recv(s->fd, &ch, 1);
+ ret = recv(s->fd, &ch, 1, 0);
if (ret < 0) {
if (errno == ECONNRESET)
s->fd = -1;
if (errno != EINTR && errno != EAGAIN)
return -1;
} else if (ret == 0) {
- socket_close(s->fd);
+ close(s->fd);
s->fd = -1;
return -1;
} else {
@@ -111,11 +332,9 @@ 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 {
+static enum {
GDB_SYS_UNKNOWN,
GDB_SYS_ENABLED,
GDB_SYS_DISABLED,
@@ -126,8 +345,8 @@ enum {
int use_gdb_syscalls(void)
{
if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
- gdb_syscall_mode = (gdb_syscall_state ? GDB_SYS_ENABLED
- : GDB_SYS_DISABLED);
+ gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED
+ : GDB_SYS_DISABLED);
}
return gdb_syscall_mode == GDB_SYS_ENABLED;
}
@@ -148,7 +367,7 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int len)
int ret;
while (len > 0) {
- ret = socket_send(s->fd, buf, len);
+ ret = send(s->fd, buf, len, 0);
if (ret < 0) {
if (errno != EINTR && errno != EAGAIN)
return;
@@ -206,19 +425,14 @@ static void hextomem(uint8_t *mem, const char *buf, int len)
}
/* return -1 if error, 0 if OK */
-static int put_packet(GDBState *s, const char *buf)
+static int put_packet_binary(GDBState *s, const char *buf, int len)
{
- int len, csum, i;
+ int csum, i;
uint8_t *p;
-#ifdef DEBUG_GDB
- printf("reply='%s'\n", buf);
-#endif
-
for(;;) {
p = s->last_packet;
*(p++) = '$';
- len = strlen(buf);
memcpy(p, buf, len);
p += len;
csum = 0;
@@ -245,522 +459,558 @@ static int put_packet(GDBState *s, const char *buf)
return 0;
}
+/* return -1 if error, 0 if OK */
+static int put_packet(GDBState *s, const char *buf)
+{
+#ifdef DEBUG_GDB
+ printf("reply='%s'\n", buf);
+#endif
+
+ return put_packet_binary(s, buf, strlen(buf));
+}
+
+/* The GDB remote protocol transfers values in target byte order. This means
+ we can use the raw memory access routines to access the value buffer.
+ Conveniently, these also handle the case where the buffer is mis-aligned.
+ */
+#define GET_REG8(val) do { \
+ stb_p(mem_buf, val); \
+ return 1; \
+ } while(0)
+#define GET_REG16(val) do { \
+ stw_p(mem_buf, val); \
+ return 2; \
+ } while(0)
+#define GET_REG32(val) do { \
+ stl_p(mem_buf, val); \
+ return 4; \
+ } while(0)
+#define GET_REG64(val) do { \
+ stq_p(mem_buf, val); \
+ return 8; \
+ } while(0)
+
+#if TARGET_LONG_BITS == 64
+#define GET_REGL(val) GET_REG64(val)
+#define ldtul_p(addr) ldq_p(addr)
+#else
+#define GET_REGL(val) GET_REG32(val)
+#define ldtul_p(addr) ldl_p(addr)
+#endif
+
#if defined(TARGET_I386)
#ifdef TARGET_X86_64
-static const uint8_t gdb_x86_64_regs[16] = {
+static const int gpr_map[16] = {
R_EAX, R_EBX, R_ECX, R_EDX, R_ESI, R_EDI, R_EBP, R_ESP,
- 8, 9, 10, 11, 12, 13, 14, 15,
+ 8, 9, 10, 11, 12, 13, 14, 15
};
+#else
+static const int gpr_map[8] = {0, 1, 2, 3, 4, 5, 6, 7};
#endif
-static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
-{
- int i, fpus, nb_regs;
- uint8_t *p;
+#define NUM_CORE_REGS (CPU_NB_REGS * 2 + 25)
- p = mem_buf;
-#ifdef TARGET_X86_64
- if (env->hflags & HF_CS64_MASK) {
- nb_regs = 16;
- for(i = 0; i < 16; i++) {
- *(uint64_t *)p = tswap64(env->regs[gdb_x86_64_regs[i]]);
- p += 8;
- }
- *(uint64_t *)p = tswap64(env->eip);
- p += 8;
- } else
-#endif
- {
- nb_regs = 8;
- for(i = 0; i < 8; i++) {
- *(uint32_t *)p = tswap32(env->regs[i]);
- p += 4;
- }
- *(uint32_t *)p = tswap32(env->eip);
- p += 4;
- }
-
- *(uint32_t *)p = tswap32(env->eflags);
- p += 4;
- *(uint32_t *)p = tswap32(env->segs[R_CS].selector);
- p += 4;
- *(uint32_t *)p = tswap32(env->segs[R_SS].selector);
- p += 4;
- *(uint32_t *)p = tswap32(env->segs[R_DS].selector);
- p += 4;
- *(uint32_t *)p = tswap32(env->segs[R_ES].selector);
- p += 4;
- *(uint32_t *)p = tswap32(env->segs[R_FS].selector);
- p += 4;
- *(uint32_t *)p = tswap32(env->segs[R_GS].selector);
- p += 4;
- for(i = 0; i < 8; i++) {
- /* XXX: convert floats */
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < CPU_NB_REGS) {
+ GET_REGL(env->regs[gpr_map[n]]);
+ } else if (n >= CPU_NB_REGS + 8 && n < CPU_NB_REGS + 16) {
+ /* FIXME: byteswap float values. */
#ifdef USE_X86LDOUBLE
- memcpy(p, &env->fpregs[i], 10);
+ memcpy(mem_buf, &env->fpregs[n - (CPU_NB_REGS + 8)], 10);
#else
- memset(p, 0, 10);
-#endif
- p += 10;
- }
- *(uint32_t *)p = tswap32(env->fpuc); /* fctrl */
- p += 4;
- fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
- *(uint32_t *)p = tswap32(fpus); /* fstat */
- p += 4;
- *(uint32_t *)p = 0; /* ftag */
- p += 4;
- *(uint32_t *)p = 0; /* fiseg */
- p += 4;
- *(uint32_t *)p = 0; /* fioff */
- p += 4;
- *(uint32_t *)p = 0; /* foseg */
- p += 4;
- *(uint32_t *)p = 0; /* fooff */
- p += 4;
- *(uint32_t *)p = 0; /* fop */
- p += 4;
- for(i = 0; i < nb_regs; i++) {
- *(uint64_t *)p = tswap64(env->xmm_regs[i].XMM_Q(0));
- p += 8;
- *(uint64_t *)p = tswap64(env->xmm_regs[i].XMM_Q(1));
- p += 8;
- }
- *(uint32_t *)p = tswap32(env->mxcsr);
- p += 4;
- return p - mem_buf;
-}
-
-static inline void cpu_gdb_load_seg(CPUState *env, const uint8_t **pp,
- int sreg)
-{
- const uint8_t *p;
- uint32_t sel;
- p = *pp;
- sel = tswap32(*(uint32_t *)p);
- p += 4;
- if (sel != env->segs[sreg].selector) {
-#if defined(CONFIG_USER_ONLY)
- cpu_x86_load_seg(env, sreg, sel);
-#else
- /* XXX: do it with a debug function which does not raise an
- exception */
+ memset(mem_buf, 0, 10);
#endif
+ return 10;
+ } else if (n >= CPU_NB_REGS + 24) {
+ n -= CPU_NB_REGS + 24;
+ if (n < CPU_NB_REGS) {
+ stq_p(mem_buf, env->xmm_regs[n].XMM_Q(0));
+ stq_p(mem_buf + 8, env->xmm_regs[n].XMM_Q(1));
+ return 16;
+ } else if (n == CPU_NB_REGS) {
+ GET_REG32(env->mxcsr);
+ }
+ } else {
+ n -= CPU_NB_REGS;
+ switch (n) {
+ case 0: GET_REGL(env->eip);
+ case 1: GET_REG32(env->eflags);
+ case 2: GET_REG32(env->segs[R_CS].selector);
+ case 3: GET_REG32(env->segs[R_SS].selector);
+ case 4: GET_REG32(env->segs[R_DS].selector);
+ case 5: GET_REG32(env->segs[R_ES].selector);
+ case 6: GET_REG32(env->segs[R_FS].selector);
+ case 7: GET_REG32(env->segs[R_GS].selector);
+ /* 8...15 x87 regs. */
+ case 16: GET_REG32(env->fpuc);
+ case 17: GET_REG32((env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11);
+ case 18: GET_REG32(0); /* ftag */
+ case 19: GET_REG32(0); /* fiseg */
+ case 20: GET_REG32(0); /* fioff */
+ case 21: GET_REG32(0); /* foseg */
+ case 22: GET_REG32(0); /* fooff */
+ case 23: GET_REG32(0); /* fop */
+ /* 24+ xmm regs. */
+ }
}
- *pp = p;
+ return 0;
}
-static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int i)
{
- const uint8_t *p = mem_buf;
- int i, nb_regs;
- uint16_t fpus;
+ uint32_t tmp;
-#ifdef TARGET_X86_64
- if (env->hflags & HF_CS64_MASK) {
- nb_regs = 16;
- for(i = 0; i < 16; i++) {
- env->regs[gdb_x86_64_regs[i]] = tswap64(*(uint64_t *)p);
- p += 8;
- }
- env->eip = tswap64(*(uint64_t *)p);
- p += 8;
- } else
+ if (i < CPU_NB_REGS) {
+ env->regs[gpr_map[i]] = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ } else if (i >= CPU_NB_REGS + 8 && i < CPU_NB_REGS + 16) {
+ i -= CPU_NB_REGS + 8;
+#ifdef USE_X86LDOUBLE
+ memcpy(&env->fpregs[i], mem_buf, 10);
#endif
- {
- nb_regs = 8;
- for(i = 0; i < 8; i++) {
- env->regs[i] = tswap32(*(uint32_t *)p);
- p += 4;
+ return 10;
+ } else if (i >= CPU_NB_REGS + 24) {
+ i -= CPU_NB_REGS + 24;
+ if (i < CPU_NB_REGS) {
+ env->xmm_regs[i].XMM_Q(0) = ldq_p(mem_buf);
+ env->xmm_regs[i].XMM_Q(1) = ldq_p(mem_buf + 8);
+ return 16;
+ } else if (i == CPU_NB_REGS) {
+ env->mxcsr = ldl_p(mem_buf);
+ return 4;
}
- env->eip = tswap32(*(uint32_t *)p);
- p += 4;
- }
- env->eflags = tswap32(*(uint32_t *)p);
- p += 4;
- cpu_gdb_load_seg(env, &p, R_CS);
- cpu_gdb_load_seg(env, &p, R_SS);
- cpu_gdb_load_seg(env, &p, R_DS);
- cpu_gdb_load_seg(env, &p, R_ES);
- cpu_gdb_load_seg(env, &p, R_FS);
- cpu_gdb_load_seg(env, &p, R_GS);
-
- /* FPU state */
- for(i = 0; i < 8; i++) {
- /* XXX: convert floats */
-#ifdef USE_X86LDOUBLE
- memcpy(&env->fpregs[i], p, 10);
+ } else {
+ i -= CPU_NB_REGS;
+ switch (i) {
+ case 0: env->eip = ldtul_p(mem_buf); return sizeof(target_ulong);
+ case 1: env->eflags = ldl_p(mem_buf); return 4;
+#if defined(CONFIG_USER_ONLY)
+#define LOAD_SEG(index, sreg)\
+ tmp = ldl_p(mem_buf);\
+ if (tmp != env->segs[sreg].selector)\
+ cpu_x86_load_seg(env, sreg, tmp);
+#else
+/* FIXME: Honor segment registers. Needs to avoid raising an exception
+ when the selector is invalid. */
+#define LOAD_SEG(index, sreg) do {} while(0)
#endif
- p += 10;
- }
- env->fpuc = tswap32(*(uint32_t *)p); /* fctrl */
- p += 4;
- fpus = tswap32(*(uint32_t *)p);
- p += 4;
- env->fpstt = (fpus >> 11) & 7;
- env->fpus = fpus & ~0x3800;
- p += 4 * 6;
-
- if (size >= ((p - mem_buf) + 16 * nb_regs + 4)) {
- /* SSE state */
- for(i = 0; i < nb_regs; i++) {
- env->xmm_regs[i].XMM_Q(0) = tswap64(*(uint64_t *)p);
- p += 8;
- env->xmm_regs[i].XMM_Q(1) = tswap64(*(uint64_t *)p);
- p += 8;
+ case 2: LOAD_SEG(10, R_CS); return 4;
+ case 3: LOAD_SEG(11, R_SS); return 4;
+ case 4: LOAD_SEG(12, R_DS); return 4;
+ case 5: LOAD_SEG(13, R_ES); return 4;
+ case 6: LOAD_SEG(14, R_FS); return 4;
+ case 7: LOAD_SEG(15, R_GS); return 4;
+ /* 8...15 x87 regs. */
+ case 16: env->fpuc = ldl_p(mem_buf); return 4;
+ case 17:
+ tmp = ldl_p(mem_buf);
+ env->fpstt = (tmp >> 11) & 7;
+ env->fpus = tmp & ~0x3800;
+ return 4;
+ case 18: /* ftag */ return 4;
+ case 19: /* fiseg */ return 4;
+ case 20: /* fioff */ return 4;
+ case 21: /* foseg */ return 4;
+ case 22: /* fooff */ return 4;
+ case 23: /* fop */ return 4;
+ /* 24+ xmm regs. */
}
- env->mxcsr = tswap32(*(uint32_t *)p);
- p += 4;
}
+ /* Unrecognised register. */
+ return 0;
}
#elif defined (TARGET_PPC)
-static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
-{
- uint32_t *registers = (uint32_t *)mem_buf, tmp;
- int i;
- /* fill in gprs */
- for(i = 0; i < 32; i++) {
- registers[i] = tswapl(env->gpr[i]);
- }
- /* fill in fprs */
- for (i = 0; i < 32; i++) {
- registers[(i * 2) + 32] = tswapl(*((uint32_t *)&env->fpr[i]));
- registers[(i * 2) + 33] = tswapl(*((uint32_t *)&env->fpr[i] + 1));
- }
- /* nip, msr, ccr, lnk, ctr, xer, mq */
- registers[96] = tswapl(env->nip);
- registers[97] = tswapl(env->msr);
- tmp = 0;
- for (i = 0; i < 8; i++)
- tmp |= env->crf[i] << (32 - ((i + 1) * 4));
- registers[98] = tswapl(tmp);
- registers[99] = tswapl(env->lr);
- registers[100] = tswapl(env->ctr);
- registers[101] = tswapl(ppc_load_xer(env));
- registers[102] = 0;
+/* 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
- return 103 * 4;
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ /* gprs */
+ 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 {
+ switch (n) {
+ case 64: GET_REGL(env->nip);
+ case 65: GET_REGL(env->msr);
+ case 66:
+ {
+ uint32_t cr = 0;
+ int i;
+ for (i = 0; i < 8; i++)
+ cr |= env->crf[i] << (32 - ((i + 1) * 4));
+ GET_REG32(cr);
+ }
+ case 67: GET_REGL(env->lr);
+ case 68: GET_REGL(env->ctr);
+ case 69: GET_REGL(env->xer);
+ case 70:
+ {
+ if (gdb_has_xml)
+ return 0;
+ GET_REG32(0); /* fpscr */
+ }
+ }
+ }
+ return 0;
}
-static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
{
- uint32_t *registers = (uint32_t *)mem_buf;
- int i;
-
- /* fill in gprs */
- for (i = 0; i < 32; i++) {
- env->gpr[i] = tswapl(registers[i]);
- }
- /* fill in fprs */
- for (i = 0; i < 32; i++) {
- *((uint32_t *)&env->fpr[i]) = tswapl(registers[(i * 2) + 32]);
- *((uint32_t *)&env->fpr[i] + 1) = tswapl(registers[(i * 2) + 33]);
- }
- /* nip, msr, ccr, lnk, ctr, xer, mq */
- env->nip = tswapl(registers[96]);
- ppc_store_msr(env, tswapl(registers[97]));
- registers[98] = tswapl(registers[98]);
- for (i = 0; i < 8; i++)
- env->crf[i] = (registers[98] >> (32 - ((i + 1) * 4))) & 0xF;
- env->lr = tswapl(registers[99]);
- env->ctr = tswapl(registers[100]);
- ppc_store_xer(env, tswapl(registers[101]));
+ if (n < 32) {
+ /* gprs */
+ env->gpr[n] = ldtul_p(mem_buf);
+ 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 {
+ switch (n) {
+ case 64:
+ env->nip = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ case 65:
+ ppc_store_msr(env, ldtul_p(mem_buf));
+ return sizeof(target_ulong);
+ case 66:
+ {
+ uint32_t cr = ldl_p(mem_buf);
+ int i;
+ for (i = 0; i < 8; i++)
+ env->crf[i] = (cr >> (32 - ((i + 1) * 4))) & 0xF;
+ return 4;
+ }
+ case 67:
+ env->lr = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ case 68:
+ env->ctr = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ case 69:
+ env->xer = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ case 70:
+ /* fpscr */
+ if (gdb_has_xml)
+ return 0;
+ return 4;
+ }
+ }
+ return 0;
}
+
#elif defined (TARGET_SPARC)
-#ifdef TARGET_ABI32
-#define tswap_abi(val) tswap32(val &0xffffffff)
+
+#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
+#define NUM_CORE_REGS 86
#else
-#define tswap_abi(val) tswapl(val)
+#define NUM_CORE_REGS 72
#endif
-static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
-{
+
#ifdef TARGET_ABI32
- abi_ulong *registers = (abi_ulong *)mem_buf;
+#define GET_REGA(val) GET_REG32(val)
#else
- target_ulong *registers = (target_ulong *)mem_buf;
+#define GET_REGA(val) GET_REGL(val)
#endif
- int i;
- /* fill in g0..g7 */
- for(i = 0; i < 8; i++) {
- registers[i] = tswap_abi(env->gregs[i]);
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 8) {
+ /* g0..g7 */
+ GET_REGA(env->gregs[n]);
}
- /* fill in register window */
- for(i = 0; i < 24; i++) {
- registers[i + 8] = tswap_abi(env->regwptr[i]);
+ if (n < 32) {
+ /* register window */
+ GET_REGA(env->regwptr[n - 8]);
}
-#if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
- /* fill in fprs */
- for (i = 0; i < 32; i++) {
- registers[i + 32] = tswap_abi(*((uint32_t *)&env->fpr[i]));
+#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64)
+ if (n < 64) {
+ /* fprs */
+ GET_REG32(*((uint32_t *)&env->fpr[n - 32]));
}
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
- registers[64] = tswap_abi(env->y);
- {
- uint32_t tmp;
-
- tmp = GET_PSR(env);
- registers[65] = tswap32(tmp);
- }
- registers[66] = tswap_abi(env->wim);
- registers[67] = tswap_abi(env->tbr);
- registers[68] = tswap_abi(env->pc);
- registers[69] = tswap_abi(env->npc);
- registers[70] = tswap_abi(env->fsr);
- registers[71] = 0; /* csr */
- registers[72] = 0;
- return 73 * sizeof(uint32_t);
+ switch (n) {
+ case 64: GET_REGA(env->y);
+ case 65: GET_REGA(GET_PSR(env));
+ case 66: GET_REGA(env->wim);
+ case 67: GET_REGA(env->tbr);
+ case 68: GET_REGA(env->pc);
+ case 69: GET_REGA(env->npc);
+ case 70: GET_REGA(env->fsr);
+ case 71: GET_REGA(0); /* csr */
+ default: GET_REGA(0);
+ }
#else
- /* fill in fprs */
- for (i = 0; i < 64; i += 2) {
- uint64_t tmp;
+ if (n < 64) {
+ /* f0-f31 */
+ GET_REG32(*((uint32_t *)&env->fpr[n - 32]));
+ }
+ if (n < 80) {
+ /* f32-f62 (double width, even numbers only) */
+ uint64_t val;
- tmp = ((uint64_t)*(uint32_t *)&env->fpr[i]) << 32;
- tmp |= *(uint32_t *)&env->fpr[i + 1];
- registers[i / 2 + 32] = tswap64(tmp);
+ val = (uint64_t)*((uint32_t *)&env->fpr[(n - 64) * 2 + 32]) << 32;
+ val |= *((uint32_t *)&env->fpr[(n - 64) * 2 + 33]);
+ GET_REG64(val);
}
- registers[64] = tswapl(env->pc);
- registers[65] = tswapl(env->npc);
- registers[66] = tswapl(((uint64_t)GET_CCR(env) << 32) |
+ switch (n) {
+ case 80: GET_REGL(env->pc);
+ case 81: GET_REGL(env->npc);
+ case 82: GET_REGL(((uint64_t)GET_CCR(env) << 32) |
((env->asi & 0xff) << 24) |
((env->pstate & 0xfff) << 8) |
GET_CWP64(env));
- registers[67] = tswapl(env->fsr);
- registers[68] = tswapl(env->fprs);
- registers[69] = tswapl(env->y);
- return 70 * sizeof(target_ulong);
+ case 83: GET_REGL(env->fsr);
+ case 84: GET_REGL(env->fprs);
+ case 85: GET_REGL(env->y);
+ }
#endif
+ return 0;
}
-static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
{
-#ifdef TARGET_ABI32
- abi_ulong *registers = (abi_ulong *)mem_buf;
+#if defined(TARGET_ABI32)
+ abi_ulong tmp;
+
+ tmp = ldl_p(mem_buf);
#else
- target_ulong *registers = (target_ulong *)mem_buf;
+ target_ulong tmp;
+
+ tmp = ldtul_p(mem_buf);
#endif
- int i;
- /* fill in g0..g7 */
- for(i = 0; i < 7; i++) {
- env->gregs[i] = tswap_abi(registers[i]);
- }
- /* fill in register window */
- for(i = 0; i < 24; i++) {
- env->regwptr[i] = tswap_abi(registers[i + 8]);
+ if (n < 8) {
+ /* g0..g7 */
+ env->gregs[n] = tmp;
+ } else if (n < 32) {
+ /* register window */
+ env->regwptr[n - 8] = tmp;
}
-#if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
- /* fill in fprs */
- for (i = 0; i < 32; i++) {
- *((uint32_t *)&env->fpr[i]) = tswap_abi(registers[i + 32]);
+#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64)
+ else if (n < 64) {
+ /* fprs */
+ *((uint32_t *)&env->fpr[n - 32]) = tmp;
+ } else {
+ /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
+ switch (n) {
+ case 64: env->y = tmp; break;
+ case 65: PUT_PSR(env, tmp); break;
+ case 66: env->wim = tmp; break;
+ case 67: env->tbr = tmp; break;
+ case 68: env->pc = tmp; break;
+ case 69: env->npc = tmp; break;
+ case 70: env->fsr = tmp; break;
+ default: return 0;
+ }
}
- /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
- env->y = tswap_abi(registers[64]);
- PUT_PSR(env, tswap_abi(registers[65]));
- env->wim = tswap_abi(registers[66]);
- env->tbr = tswap_abi(registers[67]);
- env->pc = tswap_abi(registers[68]);
- env->npc = tswap_abi(registers[69]);
- env->fsr = tswap_abi(registers[70]);
+ return 4;
#else
- for (i = 0; i < 64; i += 2) {
- uint64_t tmp;
-
- tmp = tswap64(registers[i / 2 + 32]);
- *((uint32_t *)&env->fpr[i]) = tmp >> 32;
- *((uint32_t *)&env->fpr[i + 1]) = tmp & 0xffffffff;
- }
- env->pc = tswapl(registers[64]);
- env->npc = tswapl(registers[65]);
- {
- uint64_t tmp = tswapl(registers[66]);
-
- PUT_CCR(env, tmp >> 32);
- env->asi = (tmp >> 24) & 0xff;
- env->pstate = (tmp >> 8) & 0xfff;
- PUT_CWP64(env, tmp & 0xff);
+ else if (n < 64) {
+ /* f0-f31 */
+ env->fpr[n] = ldfl_p(mem_buf);
+ return 4;
+ } else if (n < 80) {
+ /* f32-f62 (double width, even numbers only) */
+ *((uint32_t *)&env->fpr[(n - 64) * 2 + 32]) = tmp >> 32;
+ *((uint32_t *)&env->fpr[(n - 64) * 2 + 33]) = tmp;
+ } else {
+ switch (n) {
+ case 80: env->pc = tmp; break;
+ case 81: env->npc = tmp; break;
+ case 82:
+ PUT_CCR(env, tmp >> 32);
+ env->asi = (tmp >> 24) & 0xff;
+ env->pstate = (tmp >> 8) & 0xfff;
+ PUT_CWP64(env, tmp & 0xff);
+ break;
+ case 83: env->fsr = tmp; break;
+ case 84: env->fprs = tmp; break;
+ case 85: env->y = tmp; break;
+ default: return 0;
+ }
}
- env->fsr = tswapl(registers[67]);
- env->fprs = tswapl(registers[68]);
- env->y = tswapl(registers[69]);
+ return 8;
#endif
}
-#undef tswap_abi
#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 (cpsr_read(env));
- ptr += 4;
+/* Old gdb always expect FPA registers. Newer (xml-aware) gdb only expect
+ whatever the target description contains. Due to a historical mishap
+ the FPA registers appear in between core integer regs and the CPSR.
+ We hack round this by giving the FPA regs zero size when talking to a
+ newer gdb. */
+#define NUM_CORE_REGS 26
+#define GDB_CORE_XML "arm-core.xml"
- 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;
- cpsr_write (env, tswapl(*(uint32_t *)ptr), 0xffffffff);
-}
-#elif defined (TARGET_M68K)
-static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
{
- int i;
- uint8_t *ptr;
- CPU_DoubleU u;
-
- ptr = mem_buf;
- /* D0-D7 */
- for (i = 0; i < 8; i++) {
- *(uint32_t *)ptr = tswapl(env->dregs[i]);
- ptr += 4;
+ if (n < 16) {
+ /* Core integer register. */
+ GET_REG32(env->regs[n]);
}
- /* A0-A7 */
- for (i = 0; i < 8; i++) {
- *(uint32_t *)ptr = tswapl(env->aregs[i]);
- ptr += 4;
+ if (n < 24) {
+ /* FPA registers. */
+ if (gdb_has_xml)
+ return 0;
+ memset(mem_buf, 0, 12);
+ return 12;
}
- *(uint32_t *)ptr = tswapl(env->sr);
- ptr += 4;
- *(uint32_t *)ptr = tswapl(env->pc);
- ptr += 4;
- /* F0-F7. The 68881/68040 have 12-bit extended precision registers.
- ColdFire has 8-bit double precision registers. */
- for (i = 0; i < 8; i++) {
- u.d = env->fregs[i];
- *(uint32_t *)ptr = tswap32(u.l.upper);
- *(uint32_t *)ptr = tswap32(u.l.lower);
+ switch (n) {
+ case 24:
+ /* FPA status register. */
+ if (gdb_has_xml)
+ return 0;
+ GET_REG32(0);
+ case 25:
+ /* CPSR */
+ GET_REG32(cpsr_read(env));
}
- /* FP control regs (not implemented). */
- memset (ptr, 0, 3 * 4);
- ptr += 3 * 4;
-
- return ptr - mem_buf;
+ /* Unknown register. */
+ return 0;
}
-static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
-{
- int i;
- uint8_t *ptr;
- CPU_DoubleU u;
-
- ptr = mem_buf;
- /* D0-D7 */
- for (i = 0; i < 8; i++) {
- env->dregs[i] = tswapl(*(uint32_t *)ptr);
- ptr += 4;
- }
- /* A0-A7 */
- for (i = 0; i < 8; i++) {
- env->aregs[i] = tswapl(*(uint32_t *)ptr);
- ptr += 4;
- }
- env->sr = tswapl(*(uint32_t *)ptr);
- ptr += 4;
- env->pc = tswapl(*(uint32_t *)ptr);
- ptr += 4;
- /* F0-F7. The 68881/68040 have 12-bit extended precision registers.
- ColdFire has 8-bit double precision registers. */
- for (i = 0; i < 8; i++) {
- u.l.upper = tswap32(*(uint32_t *)ptr);
- u.l.lower = tswap32(*(uint32_t *)ptr);
- env->fregs[i] = u.d;
- }
- /* FP control regs (not implemented). */
- ptr += 3 * 4;
-}
-#elif defined (TARGET_MIPS)
-static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
{
- int i;
- uint8_t *ptr;
-
- ptr = mem_buf;
- for (i = 0; i < 32; i++)
- {
- *(target_ulong *)ptr = tswapl(env->active_tc.gpr[i]);
- ptr += sizeof(target_ulong);
- }
+ uint32_t tmp;
- *(target_ulong *)ptr = (int32_t)tswap32(env->CP0_Status);
- ptr += sizeof(target_ulong);
+ tmp = ldl_p(mem_buf);
- *(target_ulong *)ptr = tswapl(env->active_tc.LO[0]);
- ptr += sizeof(target_ulong);
+ /* Mask out low bit of PC to workaround gdb bugs. This will probably
+ cause problems if we ever implement the Jazelle DBX extensions. */
+ if (n == 15)
+ tmp &= ~1;
- *(target_ulong *)ptr = tswapl(env->active_tc.HI[0]);
- ptr += sizeof(target_ulong);
+ if (n < 16) {
+ /* Core integer register. */
+ env->regs[n] = tmp;
+ return 4;
+ }
+ if (n < 24) { /* 16-23 */
+ /* FPA registers (ignored). */
+ if (gdb_has_xml)
+ return 0;
+ return 12;
+ }
+ switch (n) {
+ case 24:
+ /* FPA status register (ignored). */
+ if (gdb_has_xml)
+ return 0;
+ return 4;
+ case 25:
+ /* CPSR */
+ cpsr_write (env, tmp, 0xffffffff);
+ return 4;
+ }
+ /* Unknown register. */
+ return 0;
+}
- *(target_ulong *)ptr = tswapl(env->CP0_BadVAddr);
- ptr += sizeof(target_ulong);
+#elif defined (TARGET_M68K)
- *(target_ulong *)ptr = (int32_t)tswap32(env->CP0_Cause);
- ptr += sizeof(target_ulong);
+#define NUM_CORE_REGS 18
- *(target_ulong *)ptr = tswapl(env->active_tc.PC);
- ptr += sizeof(target_ulong);
+#define GDB_CORE_XML "cf-core.xml"
- if (env->CP0_Config1 & (1 << CP0C1_FP))
- {
- for (i = 0; i < 32; i++)
- {
- if (env->CP0_Status & (1 << CP0St_FR))
- *(target_ulong *)ptr = tswapl(env->fpu->fpr[i].d);
- else
- *(target_ulong *)ptr = tswap32(env->fpu->fpr[i].w[FP_ENDIAN_IDX]);
- ptr += sizeof(target_ulong);
- }
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 8) {
+ /* D0-D7 */
+ GET_REG32(env->dregs[n]);
+ } else if (n < 16) {
+ /* A0-A7 */
+ GET_REG32(env->aregs[n - 8]);
+ } else {
+ switch (n) {
+ case 16: GET_REG32(env->sr);
+ case 17: GET_REG32(env->pc);
+ }
+ }
+ /* FP registers not included here because they vary between
+ ColdFire and m68k. Use XML bits for these. */
+ return 0;
+}
- *(target_ulong *)ptr = (int32_t)tswap32(env->fpu->fcr31);
- ptr += sizeof(target_ulong);
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint32_t tmp;
- *(target_ulong *)ptr = (int32_t)tswap32(env->fpu->fcr0);
- ptr += sizeof(target_ulong);
- }
+ tmp = ldl_p(mem_buf);
- /* "fp", pseudo frame pointer. Not yet implemented in gdb. */
- *(target_ulong *)ptr = 0;
- ptr += sizeof(target_ulong);
+ if (n < 8) {
+ /* D0-D7 */
+ env->dregs[n] = tmp;
+ } else if (n < 8) {
+ /* A0-A7 */
+ env->aregs[n - 8] = tmp;
+ } else {
+ switch (n) {
+ case 16: env->sr = tmp; break;
+ case 17: env->pc = tmp; break;
+ default: return 0;
+ }
+ }
+ return 4;
+}
+#elif defined (TARGET_MIPS)
- /* Registers for embedded use, we just pad them. */
- for (i = 0; i < 16; i++)
- {
- *(target_ulong *)ptr = 0;
- ptr += sizeof(target_ulong);
- }
+#define NUM_CORE_REGS 73
- /* Processor ID. */
- *(target_ulong *)ptr = (int32_t)tswap32(env->CP0_PRid);
- ptr += sizeof(target_ulong);
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ GET_REGL(env->active_tc.gpr[n]);
+ }
+ if (env->CP0_Config1 & (1 << CP0C1_FP)) {
+ if (n >= 38 && n < 70) {
+ if (env->CP0_Status & (1 << CP0St_FR))
+ GET_REGL(env->active_fpu.fpr[n - 38].d);
+ else
+ GET_REGL(env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX]);
+ }
+ switch (n) {
+ case 70: GET_REGL((int32_t)env->active_fpu.fcr31);
+ case 71: GET_REGL((int32_t)env->active_fpu.fcr0);
+ }
+ }
+ switch (n) {
+ case 32: GET_REGL((int32_t)env->CP0_Status);
+ case 33: GET_REGL(env->active_tc.LO[0]);
+ case 34: GET_REGL(env->active_tc.HI[0]);
+ case 35: GET_REGL(env->CP0_BadVAddr);
+ case 36: GET_REGL((int32_t)env->CP0_Cause);
+ case 37: GET_REGL(env->active_tc.PC);
+ case 72: GET_REGL(0); /* fp */
+ case 89: GET_REGL((int32_t)env->CP0_PRid);
+ }
+ if (n >= 73 && n <= 88) {
+ /* 16 embedded regs. */
+ GET_REGL(0);
+ }
- return ptr - mem_buf;
+ return 0;
}
/* convert MIPS rounding mode in FCR31 to IEEE library */
@@ -772,204 +1022,582 @@ static unsigned int ieee_rm[] =
float_round_down
};
#define RESTORE_ROUNDING_MODE \
- set_float_rounding_mode(ieee_rm[env->fpu->fcr31 & 3], &env->fpu->fp_status)
+ set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], &env->active_fpu.fp_status)
-static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
{
- int i;
- uint8_t *ptr;
-
- ptr = mem_buf;
- for (i = 0; i < 32; i++)
- {
- env->active_tc.gpr[i] = tswapl(*(target_ulong *)ptr);
- ptr += sizeof(target_ulong);
- }
+ target_ulong tmp;
- env->CP0_Status = tswapl(*(target_ulong *)ptr);
- ptr += sizeof(target_ulong);
+ tmp = ldtul_p(mem_buf);
- env->active_tc.LO[0] = tswapl(*(target_ulong *)ptr);
- ptr += sizeof(target_ulong);
+ if (n < 32) {
+ env->active_tc.gpr[n] = tmp;
+ return sizeof(target_ulong);
+ }
+ if (env->CP0_Config1 & (1 << CP0C1_FP)
+ && n >= 38 && n < 73) {
+ if (n < 70) {
+ if (env->CP0_Status & (1 << CP0St_FR))
+ env->active_fpu.fpr[n - 38].d = tmp;
+ else
+ env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX] = tmp;
+ }
+ switch (n) {
+ case 70:
+ env->active_fpu.fcr31 = tmp & 0xFF83FFFF;
+ /* set rounding mode */
+ RESTORE_ROUNDING_MODE;
+#ifndef CONFIG_SOFTFLOAT
+ /* no floating point exception for native float */
+ SET_FP_ENABLE(env->active_fpu.fcr31, 0);
+#endif
+ break;
+ case 71: env->active_fpu.fcr0 = tmp; break;
+ }
+ return sizeof(target_ulong);
+ }
+ switch (n) {
+ case 32: env->CP0_Status = tmp; break;
+ case 33: env->active_tc.LO[0] = tmp; break;
+ case 34: env->active_tc.HI[0] = tmp; break;
+ case 35: env->CP0_BadVAddr = tmp; break;
+ case 36: env->CP0_Cause = tmp; break;
+ case 37: env->active_tc.PC = tmp; break;
+ case 72: /* fp, ignored */ break;
+ default:
+ if (n > 89)
+ return 0;
+ /* Other registers are readonly. Ignore writes. */
+ break;
+ }
- env->active_tc.HI[0] = tswapl(*(target_ulong *)ptr);
- ptr += sizeof(target_ulong);
+ return sizeof(target_ulong);
+}
+#elif defined (TARGET_SH4)
- env->CP0_BadVAddr = tswapl(*(target_ulong *)ptr);
- ptr += sizeof(target_ulong);
+/* Hint: Use "set architecture sh4" in GDB to see fpu registers */
+/* FIXME: We should use XML for this. */
- env->CP0_Cause = tswapl(*(target_ulong *)ptr);
- ptr += sizeof(target_ulong);
+#define NUM_CORE_REGS 59
- env->active_tc.PC = tswapl(*(target_ulong *)ptr);
- ptr += sizeof(target_ulong);
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 8) {
+ if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
+ GET_REGL(env->gregs[n + 16]);
+ } else {
+ GET_REGL(env->gregs[n]);
+ }
+ } else if (n < 16) {
+ GET_REGL(env->gregs[n - 8]);
+ } else if (n >= 25 && n < 41) {
+ GET_REGL(env->fregs[(n - 25) + ((env->fpscr & FPSCR_FR) ? 16 : 0)]);
+ } else if (n >= 43 && n < 51) {
+ GET_REGL(env->gregs[n - 43]);
+ } else if (n >= 51 && n < 59) {
+ GET_REGL(env->gregs[n - (51 - 16)]);
+ }
+ switch (n) {
+ case 16: GET_REGL(env->pc);
+ case 17: GET_REGL(env->pr);
+ case 18: GET_REGL(env->gbr);
+ case 19: GET_REGL(env->vbr);
+ case 20: GET_REGL(env->mach);
+ case 21: GET_REGL(env->macl);
+ case 22: GET_REGL(env->sr);
+ case 23: GET_REGL(env->fpul);
+ case 24: GET_REGL(env->fpscr);
+ case 41: GET_REGL(env->ssr);
+ case 42: GET_REGL(env->spc);
+ }
- if (env->CP0_Config1 & (1 << CP0C1_FP))
- {
- for (i = 0; i < 32; i++)
- {
- if (env->CP0_Status & (1 << CP0St_FR))
- env->fpu->fpr[i].d = tswapl(*(target_ulong *)ptr);
- else
- env->fpu->fpr[i].w[FP_ENDIAN_IDX] = tswapl(*(target_ulong *)ptr);
- ptr += sizeof(target_ulong);
- }
+ return 0;
+}
- env->fpu->fcr31 = tswapl(*(target_ulong *)ptr) & 0xFF83FFFF;
- ptr += sizeof(target_ulong);
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint32_t tmp;
- /* The remaining registers are assumed to be read-only. */
+ tmp = ldl_p(mem_buf);
- /* set rounding mode */
- RESTORE_ROUNDING_MODE;
+ if (n < 8) {
+ if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
+ env->gregs[n + 16] = tmp;
+ } else {
+ env->gregs[n] = tmp;
+ }
+ return 4;
+ } else if (n < 16) {
+ env->gregs[n - 8] = tmp;
+ return 4;
+ } else if (n >= 25 && n < 41) {
+ env->fregs[(n - 25) + ((env->fpscr & FPSCR_FR) ? 16 : 0)] = tmp;
+ } else if (n >= 43 && n < 51) {
+ env->gregs[n - 43] = tmp;
+ return 4;
+ } else if (n >= 51 && n < 59) {
+ env->gregs[n - (51 - 16)] = tmp;
+ return 4;
+ }
+ switch (n) {
+ case 16: env->pc = tmp;
+ case 17: env->pr = tmp;
+ case 18: env->gbr = tmp;
+ case 19: env->vbr = tmp;
+ case 20: env->mach = tmp;
+ case 21: env->macl = tmp;
+ case 22: env->sr = tmp;
+ case 23: env->fpul = tmp;
+ case 24: env->fpscr = tmp;
+ case 41: env->ssr = tmp;
+ case 42: env->spc = tmp;
+ default: return 0;
+ }
-#ifndef CONFIG_SOFTFLOAT
- /* no floating point exception for native float */
- SET_FP_ENABLE(env->fcr31, 0);
-#endif
- }
+ return 4;
}
-#elif defined (TARGET_SH4)
+#elif defined (TARGET_MICROBLAZE)
-/* Hint: Use "set architecture sh4" in GDB to see fpu registers */
+#define NUM_CORE_REGS (32 + 5)
-static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
{
- uint32_t *ptr = (uint32_t *)mem_buf;
- int i;
+ if (n < 32) {
+ GET_REG32(env->regs[n]);
+ } else {
+ GET_REG32(env->sregs[n - 32]);
+ }
+ return 0;
+}
-#define SAVE(x) *ptr++=tswapl(x)
- if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
- for (i = 0; i < 8; i++) SAVE(env->gregs[i + 16]);
- } else {
- for (i = 0; i < 8; i++) SAVE(env->gregs[i]);
- }
- for (i = 8; i < 16; i++) SAVE(env->gregs[i]);
- SAVE (env->pc);
- SAVE (env->pr);
- SAVE (env->gbr);
- SAVE (env->vbr);
- SAVE (env->mach);
- SAVE (env->macl);
- SAVE (env->sr);
- SAVE (env->fpul);
- SAVE (env->fpscr);
- for (i = 0; i < 16; i++)
- SAVE(env->fregs[i + ((env->fpscr & FPSCR_FR) ? 16 : 0)]);
- SAVE (env->ssr);
- SAVE (env->spc);
- for (i = 0; i < 8; i++) SAVE(env->gregs[i]);
- for (i = 0; i < 8; i++) SAVE(env->gregs[i + 16]);
- return ((uint8_t *)ptr - mem_buf);
-}
-
-static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
-{
- uint32_t *ptr = (uint32_t *)mem_buf;
- int i;
-
-#define LOAD(x) (x)=*ptr++;
- if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
- for (i = 0; i < 8; i++) LOAD(env->gregs[i + 16]);
- } else {
- for (i = 0; i < 8; i++) LOAD(env->gregs[i]);
- }
- for (i = 8; i < 16; i++) LOAD(env->gregs[i]);
- LOAD (env->pc);
- LOAD (env->pr);
- LOAD (env->gbr);
- LOAD (env->vbr);
- LOAD (env->mach);
- LOAD (env->macl);
- LOAD (env->sr);
- LOAD (env->fpul);
- LOAD (env->fpscr);
- for (i = 0; i < 16; i++)
- LOAD(env->fregs[i + ((env->fpscr & FPSCR_FR) ? 16 : 0)]);
- LOAD (env->ssr);
- LOAD (env->spc);
- for (i = 0; i < 8; i++) LOAD(env->gregs[i]);
- for (i = 0; i < 8; i++) LOAD(env->gregs[i + 16]);
+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)
-static int cris_save_32 (unsigned char *d, uint32_t value)
+#define NUM_CORE_REGS 49
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
{
- *d++ = (value);
- *d++ = (value >>= 8);
- *d++ = (value >>= 8);
- *d++ = (value >>= 8);
- return 4;
+ uint8_t srs;
+
+ srs = env->pregs[PR_SRS];
+ if (n < 16) {
+ GET_REG32(env->regs[n]);
+ }
+
+ if (n >= 21 && n < 32) {
+ GET_REG32(env->pregs[n - 16]);
+ }
+ if (n >= 33 && n < 49) {
+ GET_REG32(env->sregs[srs][n - 33]);
+ }
+ switch (n) {
+ case 16: GET_REG8(env->pregs[0]);
+ case 17: GET_REG8(env->pregs[1]);
+ case 18: GET_REG32(env->pregs[2]);
+ case 19: GET_REG8(srs);
+ case 20: GET_REG16(env->pregs[4]);
+ case 32: GET_REG32(env->pc);
+ }
+
+ return 0;
}
-static int cris_save_16 (unsigned char *d, uint32_t value)
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
{
- *d++ = (value);
- *d++ = (value >>= 8);
- return 2;
+ uint32_t tmp;
+
+ if (n > 49)
+ return 0;
+
+ tmp = ldl_p(mem_buf);
+
+ if (n < 16) {
+ env->regs[n] = tmp;
+ }
+
+ if (n >= 21 && n < 32) {
+ env->pregs[n - 16] = tmp;
+ }
+
+ /* FIXME: Should support function regs be writable? */
+ switch (n) {
+ case 16: return 1;
+ case 17: return 1;
+ case 18: env->pregs[PR_PID] = tmp; break;
+ case 19: return 1;
+ case 20: return 2;
+ case 32: env->pc = tmp; break;
+ }
+
+ return 4;
}
-static int cris_save_8 (unsigned char *d, uint32_t value)
+#elif defined (TARGET_ALPHA)
+
+#define NUM_CORE_REGS 65
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
{
- *d++ = (value);
- return 1;
+ 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;
}
-/* FIXME: this will bug on archs not supporting unaligned word accesses. */
-static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
{
- uint8_t *ptr = mem_buf;
- uint8_t srs;
- int i;
+ 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
- for (i = 0; i < 16; i++)
- ptr += cris_save_32 (ptr, env->regs[i]);
+#define NUM_CORE_REGS 0
- srs = env->pregs[PR_SRS];
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ return 0;
+}
- ptr += cris_save_8 (ptr, env->pregs[0]);
- ptr += cris_save_8 (ptr, env->pregs[1]);
- ptr += cris_save_32 (ptr, env->pregs[2]);
- ptr += cris_save_8 (ptr, srs);
- ptr += cris_save_16 (ptr, env->pregs[4]);
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ return 0;
+}
- for (i = 5; i < 16; i++)
- ptr += cris_save_32 (ptr, env->pregs[i]);
+#endif
- ptr += cris_save_32 (ptr, env->pc);
+static int num_g_regs = NUM_CORE_REGS;
- for (i = 0; i < 16; i++)
- ptr += cris_save_32 (ptr, env->sregs[srs][i]);
+#ifdef GDB_CORE_XML
+/* Encode data using the encoding for 'x' packets. */
+static int memtox(char *buf, const char *mem, int len)
+{
+ char *p = buf;
+ char c;
+
+ while (len--) {
+ c = *(mem++);
+ switch (c) {
+ case '#': case '$': case '*': case '}':
+ *(p++) = '}';
+ *(p++) = c ^ 0x20;
+ break;
+ default:
+ *(p++) = c;
+ break;
+ }
+ }
+ return p - buf;
+}
- return ((uint8_t *)ptr - mem_buf);
+static const char *get_feature_xml(const char *p, const char **newp)
+{
+ extern const char *const xml_builtin[][2];
+ size_t len;
+ int i;
+ const char *name;
+ static char target_xml[1024];
+
+ len = 0;
+ while (p[len] && p[len] != ':')
+ len++;
+ *newp = p + len;
+
+ name = NULL;
+ if (strncmp(p, "target.xml", len) == 0) {
+ /* Generate the XML description for this CPU. */
+ if (!target_xml[0]) {
+ GDBRegisterState *r;
+
+ snprintf(target_xml, sizeof(target_xml),
+ "<?xml version=\"1.0\"?>"
+ "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
+ "<target>"
+ "<xi:include href=\"%s\"/>",
+ GDB_CORE_XML);
+
+ for (r = first_cpu->gdb_regs; r; r = r->next) {
+ pstrcat(target_xml, sizeof(target_xml), "<xi:include href=\"");
+ pstrcat(target_xml, sizeof(target_xml), r->xml);
+ pstrcat(target_xml, sizeof(target_xml), "\"/>");
+ }
+ pstrcat(target_xml, sizeof(target_xml), "</target>");
+ }
+ return target_xml;
+ }
+ for (i = 0; ; i++) {
+ name = xml_builtin[i][0];
+ if (!name || (strncmp(name, p, len) == 0 && strlen(name) == len))
+ break;
+ }
+ return name ? xml_builtin[i][1] : NULL;
}
+#endif
-static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+static int gdb_read_register(CPUState *env, uint8_t *mem_buf, int reg)
{
- uint32_t *ptr = (uint32_t *)mem_buf;
- int i;
+ GDBRegisterState *r;
-#define LOAD(x) (x)=*ptr++;
- for (i = 0; i < 16; i++) LOAD(env->regs[i]);
- LOAD (env->pc);
+ if (reg < NUM_CORE_REGS)
+ return cpu_gdb_read_register(env, mem_buf, reg);
+
+ for (r = env->gdb_regs; r; r = r->next) {
+ if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) {
+ return r->get_reg(env, mem_buf, reg - r->base_reg);
+ }
+ }
+ return 0;
}
-#else
-static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+
+static int gdb_write_register(CPUState *env, uint8_t *mem_buf, int reg)
{
+ GDBRegisterState *r;
+
+ if (reg < NUM_CORE_REGS)
+ return cpu_gdb_write_register(env, mem_buf, reg);
+
+ for (r = env->gdb_regs; r; r = r->next) {
+ if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) {
+ return r->set_reg(env, mem_buf, reg - r->base_reg);
+ }
+ }
return 0;
}
-static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+/* Register a supplemental set of CPU registers. If g_pos is nonzero it
+ specifies the first register number and these registers are included in
+ a standard "g" packet. Direction is relative to gdb, i.e. get_reg is
+ gdb reading a CPU register, and set_reg is gdb modifying a CPU register.
+ */
+
+void gdb_register_coprocessor(CPUState * env,
+ gdb_reg_cb get_reg, gdb_reg_cb set_reg,
+ int num_regs, const char *xml, int g_pos)
{
+ GDBRegisterState *s;
+ GDBRegisterState **p;
+ static int last_reg = NUM_CORE_REGS;
+
+ s = (GDBRegisterState *)qemu_mallocz(sizeof(GDBRegisterState));
+ s->base_reg = last_reg;
+ s->num_regs = num_regs;
+ s->get_reg = get_reg;
+ s->set_reg = set_reg;
+ s->xml = xml;
+ p = &env->gdb_regs;
+ while (*p) {
+ /* Check for duplicates. */
+ if (strcmp((*p)->xml, xml) == 0)
+ return;
+ p = &(*p)->next;
+ }
+ /* Add to end of list. */
+ last_reg += num_regs;
+ *p = s;
+ if (g_pos) {
+ if (g_pos != s->base_reg) {
+ fprintf(stderr, "Error: Bad gdb register numbering for '%s'\n"
+ "Expected %d got %d\n", xml, g_pos, s->base_reg);
+ } else {
+ num_g_regs = last_reg;
+ }
+ }
}
+#ifndef CONFIG_USER_ONLY
+static const int xlat_gdb_type[] = {
+ [GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE,
+ [GDB_WATCHPOINT_READ] = BP_GDB | BP_MEM_READ,
+ [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS,
+};
+#endif
+
+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:
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_breakpoint_insert(env, addr, BP_GDB, NULL);
+ if (err)
+ break;
+ }
+ return err;
+#ifndef CONFIG_USER_ONLY
+ case GDB_WATCHPOINT_WRITE:
+ case GDB_WATCHPOINT_READ:
+ case GDB_WATCHPOINT_ACCESS:
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_watchpoint_insert(env, addr, len, xlat_gdb_type[type],
+ NULL);
+ if (err)
+ break;
+ }
+ return err;
#endif
+ default:
+ return -ENOSYS;
+ }
+}
-static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
+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:
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_breakpoint_remove(env, addr, BP_GDB);
+ if (err)
+ break;
+ }
+ return err;
+#ifndef CONFIG_USER_ONLY
+ case GDB_WATCHPOINT_WRITE:
+ case GDB_WATCHPOINT_READ:
+ case GDB_WATCHPOINT_ACCESS:
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_watchpoint_remove(env, addr, len, xlat_gdb_type[type]);
+ if (err)
+ break;
+ }
+ return err;
+#endif
+ default:
+ return -ENOSYS;
+ }
+}
+
+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
+ cpu_watchpoint_remove_all(env, BP_GDB);
+#endif
+ }
+}
+
+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;
- char buf[4096];
- uint8_t mem_buf[4096];
- uint32_t *registers;
+ uint32_t thread;
+ int ch, reg_size, type, res;
+ char buf[MAX_PACKET_LENGTH];
+ uint8_t mem_buf[MAX_PACKET_LENGTH];
+ uint8_t *registers;
target_ulong addr, len;
#ifdef DEBUG_GDB
@@ -980,39 +1608,27 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
switch(ch) {
case '?':
/* TODO: Make this return the correct value for user-mode. */
- snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
+ 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
* should be cleaned up.
*/
- cpu_breakpoint_remove_all(env);
- cpu_watchpoint_remove_all(env);
+ gdb_breakpoint_remove_all();
break;
case 'c':
if (*p != '\0') {
addr = strtoull(p, (char **)&p, 16);
-#if defined(TARGET_I386)
- env->eip = addr;
-#elif defined (TARGET_PPC)
- env->nip = addr;
-#elif defined (TARGET_SPARC)
- env->pc = addr;
- env->npc = addr + 4;
-#elif defined (TARGET_ARM)
- env->regs[15] = addr;
-#elif defined (TARGET_SH4)
- env->pc = addr;
-#elif defined (TARGET_MIPS)
- env->active_tc.PC = addr;
-#elif defined (TARGET_CRIS)
- env->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':
@@ -1021,32 +1637,16 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
exit(0);
case 'D':
/* Detach packet */
- cpu_breakpoint_remove_all(env);
- cpu_watchpoint_remove_all(env);
+ gdb_breakpoint_remove_all();
gdb_continue(s);
put_packet(s, "OK");
break;
case 's':
if (*p != '\0') {
addr = strtoull(p, (char **)&p, 16);
-#if defined(TARGET_I386)
- env->eip = addr;
-#elif defined (TARGET_PPC)
- env->nip = addr;
-#elif defined (TARGET_SPARC)
- env->pc = addr;
- env->npc = addr + 4;
-#elif defined (TARGET_ARM)
- env->regs[15] = addr;
-#elif defined (TARGET_SH4)
- env->pc = addr;
-#elif defined (TARGET_MIPS)
- env->active_tc.PC = addr;
-#elif defined (TARGET_CRIS)
- env->pc = addr;
-#endif
+ gdb_set_cpu_pc(s, addr);
}
- cpu_single_step(env, sstep_flags);
+ cpu_single_step(s->c_cpu, sstep_flags);
gdb_continue(s);
return RS_IDLE;
case 'F':
@@ -1065,7 +1665,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
p++;
type = *p;
if (gdb_current_syscall_cb)
- gdb_current_syscall_cb(s->env, ret, err);
+ gdb_current_syscall_cb(s->c_cpu, ret, err);
if (type == 'C') {
put_packet(s, "T02");
} else {
@@ -1074,15 +1674,25 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
}
break;
case 'g':
- reg_size = cpu_gdb_read_registers(env, mem_buf);
- memtohex(buf, mem_buf, reg_size);
+ 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);
+ len += reg_size;
+ }
+ memtohex(buf, mem_buf, len);
put_packet(s, buf);
break;
case 'G':
- registers = (void *)mem_buf;
+ registers = mem_buf;
len = strlen(p) / 2;
hextomem((uint8_t *)registers, p, len);
- cpu_gdb_write_registers(env, mem_buf, len);
+ for (addr = 0; addr < num_g_regs && len > 0; addr++) {
+ reg_size = gdb_write_register(s->g_cpu, registers, addr);
+ len -= reg_size;
+ registers += reg_size;
+ }
+ cpu_synchronize_state(s->g_cpu, 1);
put_packet(s, "OK");
break;
case 'm':
@@ -1090,7 +1700,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
if (*p == ',')
p++;
len = strtoull(p, NULL, 16);
- if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0) {
+ if (cpu_memory_rw_debug(s->g_cpu, addr, mem_buf, len, 0) != 0) {
put_packet (s, "E14");
} else {
memtohex(buf, mem_buf, len);
@@ -1105,12 +1715,39 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
if (*p == ':')
p++;
hextomem(mem_buf, p, len);
- if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
+ if (cpu_memory_rw_debug(s->g_cpu, addr, mem_buf, len, 1) != 0)
put_packet(s, "E14");
else
put_packet(s, "OK");
break;
+ case 'p':
+ /* Older gdb are really dumb, and don't use 'g' if 'p' is avaialable.
+ This works, but can be very slow. Anything new enough to
+ understand XML also knows how to use this properly. */
+ if (!gdb_has_xml)
+ goto unknown_command;
+ addr = strtoull(p, (char **)&p, 16);
+ reg_size = gdb_read_register(s->g_cpu, mem_buf, addr);
+ if (reg_size) {
+ memtohex(buf, mem_buf, reg_size);
+ put_packet(s, buf);
+ } else {
+ put_packet(s, "E14");
+ }
+ break;
+ case 'P':
+ if (!gdb_has_xml)
+ goto unknown_command;
+ addr = strtoull(p, (char **)&p, 16);
+ if (*p == '=')
+ p++;
+ reg_size = strlen(p) / 2;
+ hextomem(mem_buf, p, reg_size);
+ gdb_write_register(s->g_cpu, mem_buf, addr);
+ put_packet(s, "OK");
+ break;
case 'Z':
+ case 'z':
type = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
@@ -1118,55 +1755,51 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
if (*p == ',')
p++;
len = strtoull(p, (char **)&p, 16);
+ if (ch == 'Z')
+ res = gdb_breakpoint_insert(addr, len, type);
+ else
+ res = gdb_breakpoint_remove(addr, len, type);
+ if (res >= 0)
+ put_packet(s, "OK");
+ else if (res == -ENOSYS)
+ put_packet(s, "");
+ else
+ put_packet(s, "E22");
+ break;
+ case 'H':
+ type = *p++;
+ thread = strtoull(p, (char **)&p, 16);
+ if (thread == -1 || thread == 0) {
+ put_packet(s, "OK");
+ break;
+ }
+ env = find_cpu(thread);
+ if (env == NULL) {
+ put_packet(s, "E22");
+ break;
+ }
switch (type) {
- case 0:
- case 1:
- if (cpu_breakpoint_insert(env, addr) < 0)
- goto breakpoint_error;
+ case 'c':
+ s->c_cpu = env;
put_packet(s, "OK");
break;
-#ifndef CONFIG_USER_ONLY
- case 2:
- type = PAGE_WRITE;
- goto insert_watchpoint;
- case 3:
- type = PAGE_READ;
- goto insert_watchpoint;
- case 4:
- type = PAGE_READ | PAGE_WRITE;
- insert_watchpoint:
- if (cpu_watchpoint_insert(env, addr, type) < 0)
- goto breakpoint_error;
+ case 'g':
+ s->g_cpu = env;
put_packet(s, "OK");
break;
-#endif
default:
- put_packet(s, "");
- break;
+ put_packet(s, "E22");
+ break;
}
break;
- breakpoint_error:
- put_packet(s, "E22");
- break;
+ case 'T':
+ thread = strtoull(p, (char **)&p, 16);
+ env = find_cpu(thread);
- case 'z':
- type = strtoul(p, (char **)&p, 16);
- if (*p == ',')
- p++;
- addr = strtoull(p, (char **)&p, 16);
- if (*p == ',')
- p++;
- len = strtoull(p, (char **)&p, 16);
- if (type == 0 || type == 1) {
- cpu_breakpoint_remove(env, addr);
+ if (env != NULL) {
put_packet(s, "OK");
-#ifndef CONFIG_USER_ONLY
- } else if (type >= 2 || type <= 4) {
- cpu_watchpoint_remove(env, addr);
- put_packet(s, "OK");
-#endif
} else {
- put_packet(s, "");
+ put_packet(s, "E22");
}
break;
case 'q':
@@ -1194,10 +1827,39 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
sstep_flags = type;
put_packet(s, "OK");
break;
+ } else if (strcmp(p,"C") == 0) {
+ /* "Current thread" remains vague in the spec, so always return
+ * the first CPU (gdb returns the first thread). */
+ put_packet(s, "QC1");
+ break;
+ } else if (strcmp(p,"fThreadInfo") == 0) {
+ s->query_cpu = first_cpu;
+ goto report_cpuinfo;
+ } else if (strcmp(p,"sThreadInfo") == 0) {
+ report_cpuinfo:
+ if (s->query_cpu) {
+ snprintf(buf, sizeof(buf), "m%x", gdb_id(s->query_cpu));
+ put_packet(s, buf);
+ s->query_cpu = s->query_cpu->next_cpu;
+ } else
+ put_packet(s, "l");
+ break;
+ } else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) {
+ thread = strtoull(p+16, (char **)&p, 16);
+ 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 = env->opaque;
+ TaskState *ts = s->c_cpu->opaque;
snprintf(buf, sizeof(buf),
"Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx
@@ -1208,9 +1870,75 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
put_packet(s, buf);
break;
}
+#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
+ pstrcat(buf, sizeof(buf), ";qXfer:features:read+");
+#endif
+ put_packet(s, buf);
+ break;
+ }
+#ifdef GDB_CORE_XML
+ if (strncmp(p, "Xfer:features:read:", 19) == 0) {
+ const char *xml;
+ target_ulong total_len;
+
+ gdb_has_xml = 1;
+ p += 19;
+ xml = get_feature_xml(p, &p);
+ if (!xml) {
+ snprintf(buf, sizeof(buf), "E00");
+ put_packet(s, buf);
+ break;
+ }
+
+ if (*p == ':')
+ p++;
+ addr = strtoul(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ len = strtoul(p, (char **)&p, 16);
+
+ total_len = strlen(xml);
+ if (addr > total_len) {
+ snprintf(buf, sizeof(buf), "E00");
+ put_packet(s, buf);
+ break;
+ }
+ if (len > (MAX_PACKET_LENGTH - 5) / 2)
+ len = (MAX_PACKET_LENGTH - 5) / 2;
+ if (len < total_len - addr) {
+ buf[0] = 'm';
+ len = memtox(buf + 1, xml + addr, len);
+ } else {
+ buf[0] = 'l';
+ len = memtox(buf + 1, xml + addr, total_len - addr);
+ }
+ put_packet_binary(s, buf, len + 1);
+ break;
+ }
#endif
- /* Fall through. */
+ /* Unrecognised 'q' command. */
+ goto unknown_command;
+
default:
+ unknown_command:
/* put empty packet */
buf[0] = '\0';
put_packet(s, buf);
@@ -1219,38 +1947,55 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
return RS_IDLE;
}
-extern void tb_flush(CPUState *env);
+void gdb_set_stop_cpu(CPUState *env)
+{
+ gdbserver_state->c_cpu = env;
+ gdbserver_state->g_cpu = 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 = opaque;
+ GDBState *s = gdbserver_state;
+ CPUState *env = s->c_cpu;
char buf[256];
+ 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 */
- cpu_single_step(s->env, 0);
+ cpu_single_step(env, 0);
if (reason == EXCP_DEBUG) {
- if (s->env->watchpoint_hit) {
- snprintf(buf, sizeof(buf), "T%02xwatch:" TARGET_FMT_lx ";",
- SIGTRAP,
- s->env->watchpoint[s->env->watchpoint_hit - 1].vaddr);
+ if (env->watchpoint_hit) {
+ switch (env->watchpoint_hit->flags & BP_MEM_ACCESS) {
+ case BP_MEM_READ:
+ type = "r";
+ break;
+ case BP_MEM_ACCESS:
+ type = "a";
+ break;
+ default:
+ type = "";
+ break;
+ }
+ snprintf(buf, sizeof(buf),
+ "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
+ GDB_SIGNAL_TRAP, gdb_id(env), type,
+ env->watchpoint_hit->vaddr);
put_packet(s, buf);
- s->env->watchpoint_hit = 0;
+ env->watchpoint_hit = NULL;
return;
}
- tb_flush(s->env);
- ret = SIGTRAP;
- } else if (reason == EXCP_INTERRUPT) {
- ret = SIGINT;
+ tb_flush(env);
+ ret = GDB_SIGNAL_TRAP;
} else {
- ret = 0;
+ ret = GDB_SIGNAL_INT;
}
- snprintf(buf, sizeof(buf), "S%02x", ret);
+ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, gdb_id(env));
put_packet(s, buf);
}
#endif
@@ -1269,7 +2014,7 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
uint64_t i64;
GDBState *s;
- s = gdb_syscall_state;
+ s = gdbserver_state;
if (!s)
return;
gdb_current_syscall_cb = cb;
@@ -1314,15 +2059,14 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
va_end(va);
put_packet(s, buf);
#ifdef CONFIG_USER_ONLY
- gdb_handlesig(s->env, 0);
+ gdb_handlesig(s->c_cpu, 0);
#else
- cpu_interrupt(s->env, CPU_INTERRUPT_EXIT);
+ cpu_exit(s->c_cpu);
#endif
}
static void gdb_read_byte(GDBState *s, int ch)
{
- CPUState *env = s->env;
int i, csum;
uint8_t reply;
@@ -1388,7 +2132,7 @@ static void gdb_read_byte(GDBState *s, int ch)
} else {
reply = '+';
put_buffer(s, &reply, 1);
- s->state = gdb_handle_packet(s, env, s->line_buf);
+ s->state = gdb_handle_packet(s, s->line_buf);
}
break;
default:
@@ -1399,13 +2143,26 @@ 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;
char buf[256];
int n;
- s = &gdbserver_state;
+ s = gdbserver_state;
if (gdbserver_fd < 0 || s->fd < 0)
return sig;
@@ -1415,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
@@ -1453,7 +2210,7 @@ void gdb_exit(CPUState *env, int code)
GDBState *s;
char buf[4];
- s = &gdbserver_state;
+ s = gdbserver_state;
if (gdbserver_fd < 0 || s->fd < 0)
return;
@@ -1461,15 +2218,31 @@ 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 *opaque)
+static void gdb_accept(void)
{
GDBState *s;
- int fd;
+ struct sockaddr_in sockaddr;
+ socklen_t len;
+ int val, fd;
for(;;) {
- fd = socket_accept(gdbserver_fd, NULL);
- if (fd < 0) {
+ len = sizeof(sockaddr);
+ fd = accept(gdbserver_fd, (struct sockaddr *)&sockaddr, &len);
+ if (fd < 0 && errno != EINTR) {
perror("accept");
return;
} else if (fd >= 0) {
@@ -1478,42 +2251,46 @@ static void gdb_accept(void *opaque)
}
/* set short latency */
- socket_set_lowlatency(fd);
-
- s = &gdbserver_state;
- memset (s, 0, sizeof (GDBState));
- s->env = first_cpu; /* XXX: allow to change CPU */
+ val = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
+
+ s = qemu_mallocz(sizeof(GDBState));
+ s->c_cpu = first_cpu;
+ s->g_cpu = first_cpu;
s->fd = fd;
+ gdb_has_xml = 0;
- gdb_syscall_state = s;
+ gdbserver_state = s;
- socket_set_nonblock(fd);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
}
static int gdbserver_open(int port)
{
- SockAddress sockaddr;
+ struct sockaddr_in sockaddr;
int fd, val, ret;
- fd = socket_create_inet( SOCKET_STREAM );
+ fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return -1;
}
/* allow fast reuse */
- socket_set_xreuseaddr(fd);
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
- sock_address_init_inet( &sockaddr, SOCK_ADDRESS_INET_ANY, port );
- ret = socket_bind(fd, &sockaddr);
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(port);
+ sockaddr.sin_addr.s_addr = 0;
+ ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
if (ret < 0) {
perror("bind");
return -1;
}
- ret = socket_listen(fd, 0);
+ ret = listen(fd, 0);
if (ret < 0) {
perror("listen");
- socket_close(fd);
return -1;
}
return fd;
@@ -1525,22 +2302,35 @@ int gdbserver_start(int port)
if (gdbserver_fd < 0)
return -1;
/* accept connections */
- gdb_accept (NULL);
+ 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)
{
- return 1;
+ /* We can handle an arbitrarily large amount of data.
+ Pick the maximum packet size, which is as good as anything. */
+ return MAX_PACKET_LENGTH;
}
static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size)
{
- GDBState *s = opaque;
int i;
for (i = 0; i < size; i++) {
- gdb_read_byte(s, buf[i]);
+ gdb_read_byte(gdbserver_state, buf[i]);
}
}
@@ -1549,45 +2339,106 @@ static void gdb_chr_event(void *opaque, int event)
switch (event) {
case CHR_EVENT_RESET:
vm_stop(EXCP_INTERRUPT);
- gdb_syscall_state = opaque;
+ gdb_has_xml = 0;
break;
default:
break;
}
}
-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];
+
+ 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);
+}
- if (!port || !*port)
- return -1;
+static int gdb_monitor_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ const char *p = (const char *)buf;
+ int max_sz;
- 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;
+ 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(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->env = first_cpu; /* XXX: allow to change CPU */
+ s->c_cpu = first_cpu;
+ s->g_cpu = first_cpu;
s->chr = chr;
- qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive,
- gdb_chr_event, s);
- qemu_add_vm_stop_handler(gdb_vm_stopped, s);
+ s->state = chr ? RS_IDLE : RS_INACTIVE;
+ s->mon_chr = mon_chr;
+
return 0;
}
#endif