* Lesser General Public License for more details.
*
* 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
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "qemu-common.h"
#include "qemu.h"
#else
+#include "monitor.h"
#include "qemu-char.h"
#include "sysemu.h"
#include "gdbstub.h"
#define MAX_PACKET_LENGTH 4096
+#include "cpu.h"
#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_QUIT = 3,
+ GDB_SIGNAL_TRAP = 5,
+ GDB_SIGNAL_ABRT = 6,
+ GDB_SIGNAL_ALRM = 14,
+ GDB_SIGNAL_IO = 23,
+ GDB_SIGNAL_XCPU = 24,
+ 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 {
} GDBRegisterState;
enum RSState {
+ RS_INACTIVE,
RS_IDLE,
RS_GETLINE,
RS_CHKSUM1,
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[MAX_PACKET_LENGTH];
int line_buf_index;
int running_state;
#else
CharDriverState *chr;
+ CharDriverState *mon_chr;
#endif
} 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. */
/* 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;
}
#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,
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;
}
8, 9, 10, 11, 12, 13, 14, 15
};
#else
-static const int gpr_map[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+#define gpr_map gpr_map32
#endif
+static const int gpr_map32[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
#define NUM_CORE_REGS (CPU_NB_REGS * 2 + 25)
+#define IDX_IP_REG CPU_NB_REGS
+#define IDX_FLAGS_REG (IDX_IP_REG + 1)
+#define IDX_SEG_REGS (IDX_FLAGS_REG + 1)
+#define IDX_FP_REGS (IDX_SEG_REGS + 6)
+#define IDX_XMM_REGS (IDX_FP_REGS + 16)
+#define IDX_MXCSR_REG (IDX_XMM_REGS + CPU_NB_REGS)
+
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. */
+ if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) {
+ GET_REG64(env->regs[gpr_map[n]]);
+ } else if (n < CPU_NB_REGS32) {
+ GET_REG32(env->regs[gpr_map32[n]]);
+ }
+ } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) {
#ifdef USE_X86LDOUBLE
- memcpy(mem_buf, &env->fpregs[n - (CPU_NB_REGS + 8)], 10);
+ /* FIXME: byteswap float values - after fixing fpregs layout. */
+ memcpy(mem_buf, &env->fpregs[n - IDX_FP_REGS], 10);
#else
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) {
+ } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) {
+ n -= IDX_XMM_REGS;
+ if (n < CPU_NB_REGS32 ||
+ (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK)) {
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. */
+ case IDX_IP_REG:
+ if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) {
+ GET_REG64(env->eip);
+ } else {
+ GET_REG32(env->eip);
+ }
+ case IDX_FLAGS_REG: GET_REG32(env->eflags);
+
+ case IDX_SEG_REGS: GET_REG32(env->segs[R_CS].selector);
+ case IDX_SEG_REGS + 1: GET_REG32(env->segs[R_SS].selector);
+ case IDX_SEG_REGS + 2: GET_REG32(env->segs[R_DS].selector);
+ case IDX_SEG_REGS + 3: GET_REG32(env->segs[R_ES].selector);
+ case IDX_SEG_REGS + 4: GET_REG32(env->segs[R_FS].selector);
+ case IDX_SEG_REGS + 5: GET_REG32(env->segs[R_GS].selector);
+
+ case IDX_FP_REGS + 8: GET_REG32(env->fpuc);
+ case IDX_FP_REGS + 9: GET_REG32((env->fpus & ~0x3800) |
+ (env->fpstt & 0x7) << 11);
+ case IDX_FP_REGS + 10: GET_REG32(0); /* ftag */
+ case IDX_FP_REGS + 11: GET_REG32(0); /* fiseg */
+ case IDX_FP_REGS + 12: GET_REG32(0); /* fioff */
+ case IDX_FP_REGS + 13: GET_REG32(0); /* foseg */
+ case IDX_FP_REGS + 14: GET_REG32(0); /* fooff */
+ case IDX_FP_REGS + 15: GET_REG32(0); /* fop */
+
+ case IDX_MXCSR_REG: GET_REG32(env->mxcsr);
}
}
return 0;
}
-static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int i)
+static int cpu_x86_gdb_load_seg(CPUState *env, int sreg, uint8_t *mem_buf)
+{
+ uint16_t selector = ldl_p(mem_buf);
+
+ if (selector != env->segs[sreg].selector) {
+#if defined(CONFIG_USER_ONLY)
+ cpu_x86_load_seg(env, sreg, selector);
+#else
+ unsigned int limit, flags;
+ target_ulong base;
+
+ if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
+ base = selector << 4;
+ limit = 0xffff;
+ flags = 0;
+ } else {
+ if (!cpu_x86_get_descr_debug(env, selector, &base, &limit, &flags))
+ return 4;
+ }
+ cpu_x86_load_seg_cache(env, sreg, selector, base, limit, flags);
+#endif
+ }
+ return 4;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
{
uint32_t tmp;
- 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;
+ if (n < CPU_NB_REGS) {
+ if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) {
+ env->regs[gpr_map[n]] = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ } else if (n < CPU_NB_REGS32) {
+ n = gpr_map32[n];
+ env->regs[n] &= ~0xffffffffUL;
+ env->regs[n] |= (uint32_t)ldl_p(mem_buf);
+ return 4;
+ }
+ } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) {
#ifdef USE_X86LDOUBLE
- memcpy(&env->fpregs[i], mem_buf, 10);
+ /* FIXME: byteswap float values - after fixing fpregs layout. */
+ memcpy(&env->fpregs[n - IDX_FP_REGS], mem_buf, 10);
#endif
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);
+ } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) {
+ n -= IDX_XMM_REGS;
+ if (n < CPU_NB_REGS32 ||
+ (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK)) {
+ env->xmm_regs[n].XMM_Q(0) = ldq_p(mem_buf);
+ env->xmm_regs[n].XMM_Q(1) = ldq_p(mem_buf + 8);
return 16;
- } else if (i == CPU_NB_REGS) {
- env->mxcsr = ldl_p(mem_buf);
- return 4;
}
} 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
- 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. */
+ switch (n) {
+ case IDX_IP_REG:
+ if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) {
+ env->eip = ldq_p(mem_buf);
+ return 8;
+ } else {
+ env->eip &= ~0xffffffffUL;
+ env->eip |= (uint32_t)ldl_p(mem_buf);
+ return 4;
+ }
+ case IDX_FLAGS_REG:
+ env->eflags = ldl_p(mem_buf);
+ return 4;
+
+ case IDX_SEG_REGS: return cpu_x86_gdb_load_seg(env, R_CS, mem_buf);
+ case IDX_SEG_REGS + 1: return cpu_x86_gdb_load_seg(env, R_SS, mem_buf);
+ case IDX_SEG_REGS + 2: return cpu_x86_gdb_load_seg(env, R_DS, mem_buf);
+ case IDX_SEG_REGS + 3: return cpu_x86_gdb_load_seg(env, R_ES, mem_buf);
+ case IDX_SEG_REGS + 4: return cpu_x86_gdb_load_seg(env, R_FS, mem_buf);
+ case IDX_SEG_REGS + 5: return cpu_x86_gdb_load_seg(env, R_GS, mem_buf);
+
+ case IDX_FP_REGS + 8:
+ env->fpuc = ldl_p(mem_buf);
+ return 4;
+ case IDX_FP_REGS + 9:
+ tmp = ldl_p(mem_buf);
+ env->fpstt = (tmp >> 11) & 7;
+ env->fpus = tmp & ~0x3800;
+ return 4;
+ case IDX_FP_REGS + 10: /* ftag */ return 4;
+ case IDX_FP_REGS + 11: /* fiseg */ return 4;
+ case IDX_FP_REGS + 12: /* fioff */ return 4;
+ case IDX_FP_REGS + 13: /* foseg */ return 4;
+ case IDX_FP_REGS + 14: /* fooff */ return 4;
+ case IDX_FP_REGS + 15: /* fop */ return 4;
+
+ case IDX_MXCSR_REG:
+ env->mxcsr = ldl_p(mem_buf);
+ return 4;
}
}
/* Unrecognised register. */
#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)
{
GET_REGL(env->gpr[n]);
} else if (n < 64) {
/* fprs */
- stfq_p(mem_buf, env->fpr[n]);
+ if (gdb_has_xml)
+ return 0;
+ stfq_p(mem_buf, env->fpr[n-32]);
return 8;
} else {
switch (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;
return sizeof(target_ulong);
} else if (n < 64) {
/* fprs */
- env->fpr[n] = ldfq_p(mem_buf);
+ if (gdb_has_xml)
+ return 0;
+ env->fpr[n-32] = ldfq_p(mem_buf);
return 8;
} else {
switch (n) {
return sizeof(target_ulong);
case 70:
/* fpscr */
+ if (gdb_has_xml)
+ return 0;
return 4;
}
}
#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
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
switch (n) {
case 64: GET_REGA(env->y);
- case 65: GET_REGA(GET_PSR(env));
+ case 65: GET_REGA(cpu_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 */
- case 72: GET_REGA(0);
+ default: GET_REGA(0);
}
#else
if (n < 64) {
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));
+ case 82: GET_REGL((cpu_get_ccr(env) << 32) |
+ ((env->asi & 0xff) << 24) |
+ ((env->pstate & 0xfff) << 8) |
+ cpu_get_cwp64(env));
case 83: GET_REGL(env->fsr);
case 84: GET_REGL(env->fprs);
case 85: GET_REGL(env->y);
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
switch (n) {
case 64: env->y = tmp; break;
- case 65: PUT_PSR(env, tmp); break;
+ case 65: cpu_put_psr(env, tmp); break;
case 66: env->wim = tmp; break;
case 67: env->tbr = tmp; break;
case 68: env->pc = tmp; break;
case 80: env->pc = tmp; break;
case 81: env->npc = tmp; break;
case 82:
- PUT_CCR(env, tmp >> 32);
+ cpu_put_ccr(env, tmp >> 32);
env->asi = (tmp >> 24) & 0xff;
env->pstate = (tmp >> 8) & 0xfff;
- PUT_CWP64(env, tmp & 0xff);
+ cpu_put_cwp64(env, tmp & 0xff);
break;
case 83: env->fsr = tmp; break;
case 84: env->fprs = tmp; break;
if (n < 8) {
/* D0-D7 */
env->dregs[n] = tmp;
- } else if (n < 8) {
+ } else if (n < 16) {
/* A0-A7 */
env->aregs[n - 8] = tmp;
} else {
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 37: GET_REGL(env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16));
case 72: GET_REGL(0); /* fp */
case 89: GET_REGL((int32_t)env->CP0_PRid);
}
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;
}
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 37:
+ env->active_tc.PC = tmp & ~(target_ulong)1;
+ if (tmp & 1) {
+ env->hflags |= MIPS_HFLAG_M16;
+ } else {
+ env->hflags &= ~(MIPS_HFLAG_M16);
+ }
+ break;
case 72: /* fp, ignored */ break;
default:
if (n > 89)
GET_REGL(env->gregs[n]);
}
} else if (n < 16) {
- GET_REGL(env->gregs[n - 8]);
+ GET_REGL(env->gregs[n]);
} else if (n >= 25 && n < 41) {
GET_REGL(env->fregs[(n - 25) + ((env->fpscr & FPSCR_FR) ? 16 : 0)]);
} else if (n >= 43 && n < 51) {
}
return 4;
} else if (n < 16) {
- env->gregs[n - 8] = tmp;
+ env->gregs[n] = tmp;
return 4;
} else if (n >= 25 && n < 41) {
env->fregs[(n - 25) + ((env->fpscr & FPSCR_FR) ? 16 : 0)] = tmp;
+ return 4;
} else if (n >= 43 && n < 51) {
env->gregs[n - 43] = tmp;
return 4;
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;
+ case 16: env->pc = tmp; break;
+ case 17: env->pr = tmp; break;
+ case 18: env->gbr = tmp; break;
+ case 19: env->vbr = tmp; break;
+ case 20: env->mach = tmp; break;
+ case 21: env->macl = tmp; break;
+ case 22: env->sr = tmp; break;
+ case 23: env->fpul = tmp; break;
+ case 24: env->fpscr = tmp; break;
+ case 41: env->ssr = tmp; break;
+ case 42: env->spc = tmp; break;
default: return 0;
}
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
+static int
+read_register_crisv10(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 15) {
+ GET_REG32(env->regs[n]);
+ }
+
+ if (n == 15) {
+ GET_REG32(env->pc);
+ }
+
+ if (n < 32) {
+ switch (n) {
+ case 16:
+ GET_REG8(env->pregs[n - 16]);
+ break;
+ case 17:
+ GET_REG8(env->pregs[n - 16]);
+ break;
+ case 20:
+ case 21:
+ GET_REG16(env->pregs[n - 16]);
+ break;
+ default:
+ if (n >= 23) {
+ GET_REG32(env->pregs[n - 16]);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
{
uint8_t srs;
+ if (env->pregs[PR_VR] < 32)
+ return read_register_crisv10(env, mem_buf, n);
+
srs = env->pregs[PR_SRS];
if (n < 16) {
GET_REG32(env->regs[n]);
return 4;
}
+#elif defined (TARGET_ALPHA)
+
+#define NUM_CORE_REGS 67
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint64_t val;
+ CPU_DoubleU d;
+
+ switch (n) {
+ case 0 ... 30:
+ val = env->ir[n];
+ break;
+ case 32 ... 62:
+ d.d = env->fir[n - 32];
+ val = d.ll;
+ break;
+ case 63:
+ val = cpu_alpha_load_fpcr(env);
+ break;
+ case 64:
+ val = env->pc;
+ break;
+ case 66:
+ val = env->unique;
+ break;
+ case 31:
+ case 65:
+ /* 31 really is the zero register; 65 is unassigned in the
+ gdb protocol, but is still required to occupy 8 bytes. */
+ val = 0;
+ break;
+ default:
+ return 0;
+ }
+ GET_REGL(val);
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ target_ulong tmp = ldtul_p(mem_buf);
+ CPU_DoubleU d;
+
+ switch (n) {
+ case 0 ... 30:
+ env->ir[n] = tmp;
+ break;
+ case 32 ... 62:
+ d.ll = tmp;
+ env->fir[n - 32] = d.d;
+ break;
+ case 63:
+ cpu_alpha_store_fpcr(env, tmp);
+ break;
+ case 64:
+ env->pc = tmp;
+ break;
+ case 66:
+ env->unique = tmp;
+ break;
+ case 31:
+ case 65:
+ /* 31 really is the zero register; 65 is unassigned in the
+ gdb protocol, but is still required to occupy 8 bytes. */
+ break;
+ default:
+ return 0;
+ }
+ return 8;
+}
+#elif defined (TARGET_S390X)
+
+#define NUM_CORE_REGS S390_NUM_TOTAL_REGS
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ switch (n) {
+ case S390_PSWM_REGNUM: GET_REGL(env->psw.mask); break;
+ case S390_PSWA_REGNUM: GET_REGL(env->psw.addr); break;
+ case S390_R0_REGNUM ... S390_R15_REGNUM:
+ GET_REGL(env->regs[n-S390_R0_REGNUM]); break;
+ case S390_A0_REGNUM ... S390_A15_REGNUM:
+ GET_REG32(env->aregs[n-S390_A0_REGNUM]); break;
+ case S390_FPC_REGNUM: GET_REG32(env->fpc); break;
+ case S390_F0_REGNUM ... S390_F15_REGNUM:
+ /* XXX */
+ break;
+ case S390_PC_REGNUM: GET_REGL(env->psw.addr); break;
+ case S390_CC_REGNUM:
+ env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst,
+ env->cc_vr);
+ GET_REG32(env->cc_op);
+ break;
+ }
+
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ target_ulong tmpl;
+ uint32_t tmp32;
+ int r = 8;
+ tmpl = ldtul_p(mem_buf);
+ tmp32 = ldl_p(mem_buf);
+
+ switch (n) {
+ case S390_PSWM_REGNUM: env->psw.mask = tmpl; break;
+ case S390_PSWA_REGNUM: env->psw.addr = tmpl; break;
+ case S390_R0_REGNUM ... S390_R15_REGNUM:
+ env->regs[n-S390_R0_REGNUM] = tmpl; break;
+ case S390_A0_REGNUM ... S390_A15_REGNUM:
+ env->aregs[n-S390_A0_REGNUM] = tmp32; r=4; break;
+ case S390_FPC_REGNUM: env->fpc = tmp32; r=4; break;
+ case S390_F0_REGNUM ... S390_F15_REGNUM:
+ /* XXX */
+ break;
+ case S390_PC_REGNUM: env->psw.addr = tmpl; break;
+ case S390_CC_REGNUM: env->cc_op = tmp32; r=4; break;
+ }
+
+ return r;
+}
+#elif defined (TARGET_LM32)
+
+#include "hw/lm32_pic.h"
+#define NUM_CORE_REGS (32 + 7)
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ GET_REG32(env->regs[n]);
+ } else {
+ switch (n) {
+ case 32:
+ GET_REG32(env->pc);
+ break;
+ /* FIXME: put in right exception ID */
+ case 33:
+ GET_REG32(0);
+ break;
+ case 34:
+ GET_REG32(env->eba);
+ break;
+ case 35:
+ GET_REG32(env->deba);
+ break;
+ case 36:
+ GET_REG32(env->ie);
+ break;
+ case 37:
+ GET_REG32(lm32_pic_get_im(env->pic_state));
+ break;
+ case 38:
+ GET_REG32(lm32_pic_get_ip(env->pic_state));
+ break;
+ }
+ }
+ 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 {
+ switch (n) {
+ case 32:
+ env->pc = tmp;
+ break;
+ case 34:
+ env->eba = tmp;
+ break;
+ case 35:
+ env->deba = tmp;
+ break;
+ case 36:
+ env->ie = tmp;
+ break;
+ case 37:
+ lm32_pic_set_im(env->pic_state, tmp);
+ break;
+ case 38:
+ lm32_pic_set_ip(env->pic_state, tmp);
+ break;
+ }
+ }
+ return 4;
+}
#else
#define NUM_CORE_REGS 0
return p - buf;
}
-const char *get_feature_xml(CPUState *env, 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;
int i;
const char *name;
"<xi:include href=\"%s\"/>",
GDB_CORE_XML);
- for (r = env->gdb_regs; r; r = r->next) {
- strcat(target_xml, "<xi:include href=\"");
- strcat(target_xml, r->xml);
- strcat(target_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), "\"/>");
}
- strcat(target_xml, "</target>");
+ pstrcat(target_xml, sizeof(target_xml), "</target>");
}
return target_xml;
}
}
}
-/* 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,
};
#endif
-static int gdb_breakpoint_insert(CPUState *env, target_ulong addr,
- target_ulong len, int type)
+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:
- return cpu_breakpoint_insert(env, addr, BP_GDB, NULL);
+ 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:
- return cpu_watchpoint_insert(env, addr, len, xlat_gdb_type[type],
- NULL);
+ 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_breakpoint_remove(CPUState *env, target_ulong addr,
- target_ulong len, int type)
+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:
- return cpu_breakpoint_remove(env, addr, BP_GDB);
+ 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:
- return cpu_watchpoint_remove(env, addr, len, xlat_gdb_type[type]);
+ 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(CPUState *env)
+static void gdb_breakpoint_remove_all(void)
{
- cpu_breakpoint_remove_all(env, BP_GDB);
+ 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);
+ cpu_watchpoint_remove_all(env, BP_GDB);
+#endif
+ }
+}
+
+static void gdb_set_cpu_pc(GDBState *s, target_ulong pc)
+{
+#if defined(TARGET_I386)
+ cpu_synchronize_state(s->c_cpu);
+ s->c_cpu->eip = pc;
+#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 & ~(target_ulong)1;
+ if (pc & 1) {
+ s->c_cpu->hflags |= MIPS_HFLAG_M16;
+ } else {
+ s->c_cpu->hflags &= ~(MIPS_HFLAG_M16);
+ }
+#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;
+#elif defined (TARGET_S390X)
+ cpu_synchronize_state(s->c_cpu);
+ s->c_cpu->psw.addr = pc;
+#elif defined (TARGET_LM32)
+ s->c_cpu->pc = pc;
+#endif
+}
+
+static inline int gdb_id(CPUState *env)
+{
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL)
+ return env->host_tid;
+#else
+ return env->cpu_index + 1;
#endif
}
-static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
+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;
+ uint32_t thread;
int ch, reg_size, type, res;
char buf[MAX_PACKET_LENGTH];
uint8_t mem_buf[MAX_PACKET_LENGTH];
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.
*/
- gdb_breakpoint_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 'v':
+ if (strncmp(p, "Cont", 4) == 0) {
+ int res_signal, res_thread;
+
+ p += 4;
+ if (*p == '?') {
+ put_packet(s, "vCont;c;C;s;S");
+ break;
+ }
+ res = 0;
+ res_signal = 0;
+ res_thread = 0;
+ while (*p) {
+ int action, signal;
+
+ if (*p++ != ';') {
+ res = 0;
+ break;
+ }
+ action = *p++;
+ signal = 0;
+ if (action == 'C' || action == 'S') {
+ signal = strtoul(p, (char **)&p, 16);
+ } else if (action != 'c' && action != 's') {
+ res = 0;
+ break;
+ }
+ thread = 0;
+ if (*p == ':') {
+ thread = strtoull(p+1, (char **)&p, 16);
+ }
+ action = tolower(action);
+ if (res == 0 || (res == 'c' && action == 's')) {
+ res = action;
+ res_signal = signal;
+ res_thread = thread;
+ }
+ }
+ if (res) {
+ if (res_thread != -1 && res_thread != 0) {
+ env = find_cpu(res_thread);
+ if (env == NULL) {
+ put_packet(s, "E22");
+ break;
+ }
+ s->c_cpu = env;
+ }
+ if (res == 's') {
+ cpu_single_step(s->c_cpu, sstep_flags);
+ }
+ s->signal = res_signal;
+ gdb_continue(s);
+ return RS_IDLE;
+ }
+ break;
+ } else {
+ goto unknown_command;
+ }
case 'k':
/* Kill the target */
fprintf(stderr, "\nQEMU: Terminated via GDBstub\n");
exit(0);
case 'D':
/* Detach packet */
- gdb_breakpoint_remove_all(env);
+ gdb_breakpoint_remove_all();
+ gdb_syscall_mode = GDB_SYS_DISABLED;
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':
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 {
}
break;
case 'g':
+ cpu_synchronize_state(s->g_cpu);
len = 0;
for (addr = 0; addr < num_g_regs; addr++) {
- reg_size = gdb_read_register(env, mem_buf + len, 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':
+ cpu_synchronize_state(s->g_cpu);
registers = mem_buf;
len = strlen(p) / 2;
hextomem((uint8_t *)registers, p, len);
for (addr = 0; addr < num_g_regs && len > 0; addr++) {
- reg_size = gdb_write_register(env, registers, addr);
+ reg_size = gdb_write_register(s->g_cpu, registers, addr);
len -= reg_size;
registers += reg_size;
}
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);
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");
if (!gdb_has_xml)
goto unknown_command;
addr = strtoull(p, (char **)&p, 16);
- reg_size = gdb_read_register(env, mem_buf, addr);
+ reg_size = gdb_read_register(s->g_cpu, mem_buf, addr);
if (reg_size) {
memtohex(buf, mem_buf, reg_size);
put_packet(s, buf);
p++;
reg_size = strlen(p) / 2;
hextomem(mem_buf, p, reg_size);
- gdb_write_register(env, mem_buf, addr);
+ gdb_write_register(s->g_cpu, mem_buf, addr);
put_packet(s, "OK");
break;
case 'Z':
p++;
len = strtoull(p, (char **)&p, 16);
if (ch == 'Z')
- res = gdb_breakpoint_insert(env, addr, len, type);
+ res = gdb_breakpoint_insert(addr, len, type);
else
- res = gdb_breakpoint_remove(env, addr, len, type);
+ res = gdb_breakpoint_remove(addr, len, type);
if (res >= 0)
put_packet(s, "OK");
else if (res == -ENOSYS)
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 'c':
+ s->c_cpu = env;
+ put_packet(s, "OK");
+ break;
+ case 'g':
+ s->g_cpu = env;
+ put_packet(s, "OK");
+ break;
+ default:
+ put_packet(s, "E22");
+ break;
+ }
+ break;
+ case 'T':
+ thread = strtoull(p, (char **)&p, 16);
+ env = find_cpu(thread);
+
+ if (env != NULL) {
+ put_packet(s, "OK");
+ } else {
+ put_packet(s, "E22");
+ }
+ break;
case 'q':
case 'Q':
/* parse any 'q' packets here */
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);
+ 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
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;
gdb_has_xml = 1;
p += 19;
- xml = get_feature_xml(env, p, &p);
+ xml = get_feature_xml(p, &p);
if (!xml) {
snprintf(buf, sizeof(buf), "E00");
put_packet(s, 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 || s->state == RS_INACTIVE || s->state == RS_SYSCALL) {
return;
-
- /* disable single step if it was enable */
- cpu_single_step(s->env, 0);
-
- if (reason == EXCP_DEBUG) {
- if (s->env->watchpoint_hit) {
- switch (s->env->watchpoint_hit->flags & BP_MEM_ACCESS) {
+ }
+ switch (reason) {
+ case VMSTOP_DEBUG:
+ if (env->watchpoint_hit) {
+ switch (env->watchpoint_hit->flags & BP_MEM_ACCESS) {
case BP_MEM_READ:
type = "r";
break;
type = "";
break;
}
- snprintf(buf, sizeof(buf), "T%02x%swatch:" TARGET_FMT_lx ";",
- SIGTRAP, type, s->env->watchpoint_hit->vaddr);
- put_packet(s, buf);
- s->env->watchpoint_hit = NULL;
- return;
+ snprintf(buf, sizeof(buf),
+ "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
+ GDB_SIGNAL_TRAP, gdb_id(env), type,
+ env->watchpoint_hit->vaddr);
+ env->watchpoint_hit = NULL;
+ goto send_packet;
}
- tb_flush(s->env);
- ret = SIGTRAP;
- } else if (reason == EXCP_INTERRUPT) {
- ret = SIGINT;
- } else {
- ret = 0;
+ tb_flush(env);
+ ret = GDB_SIGNAL_TRAP;
+ break;
+ case VMSTOP_USER:
+ ret = GDB_SIGNAL_INT;
+ break;
+ case VMSTOP_SHUTDOWN:
+ ret = GDB_SIGNAL_QUIT;
+ break;
+ case VMSTOP_DISKFULL:
+ ret = GDB_SIGNAL_IO;
+ break;
+ case VMSTOP_WATCHDOG:
+ ret = GDB_SIGNAL_ALRM;
+ break;
+ case VMSTOP_PANIC:
+ ret = GDB_SIGNAL_ABRT;
+ break;
+ case VMSTOP_SAVEVM:
+ case VMSTOP_LOADVM:
+ return;
+ case VMSTOP_MIGRATE:
+ ret = GDB_SIGNAL_XCPU;
+ break;
+ default:
+ ret = GDB_SIGNAL_UNKNOWN;
+ break;
}
- snprintf(buf, sizeof(buf), "S%02x", ret);
+ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, gdb_id(env));
+
+send_packet:
put_packet(s, buf);
+
+ /* disable single step if it was enabled */
+ cpu_single_step(env, 0);
}
#endif
uint64_t i64;
GDBState *s;
- s = gdb_syscall_state;
+ s = gdbserver_state;
if (!s)
return;
gdb_current_syscall_cb = cb;
s->state = RS_SYSCALL;
#ifndef CONFIG_USER_ONLY
- vm_stop(EXCP_DEBUG);
+ vm_stop(VMSTOP_DEBUG);
#endif
s->state = RS_IDLE;
va_start(va, 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;
if (vm_running) {
/* when the CPU is running, we cannot do anything except stop
it when receiving a char */
- vm_stop(EXCP_INTERRUPT);
+ vm_stop(VMSTOP_USER);
} else
#endif
{
} 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:
}
}
+/* Tell the remote gdb that the process has exited. */
+void gdb_exit(CPUState *env, int code)
+{
+ GDBState *s;
+ char buf[4];
+
+ s = gdbserver_state;
+ if (!s) {
+ return;
+ }
+#ifdef CONFIG_USER_ONLY
+ if (gdbserver_fd < 0 || s->fd < 0) {
+ return;
+ }
+#endif
+
+ snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
+ put_packet(s, buf);
+
+#ifndef CONFIG_USER_ONLY
+ if (s->chr) {
+ qemu_chr_close(s->chr);
+ }
+#endif
+}
+
#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)
{
char buf[256];
int n;
- s = &gdbserver_state;
+ s = gdbserver_state;
if (gdbserver_fd < 0 || s->fd < 0)
return 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
return sig;
}
-/* Tell the remote gdb that the process has exited. */
-void gdb_exit(CPUState *env, int code)
+/* 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;
+ s = gdbserver_state;
if (gdbserver_fd < 0 || s->fd < 0)
return;
- snprintf(buf, sizeof(buf), "W%02x", code);
+ 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;
struct sockaddr_in sockaddr;
perror("accept");
return;
} else if (fd >= 0) {
+#ifndef _WIN32
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
break;
}
}
val = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
- s = &gdbserver_state;
- memset (s, 0, sizeof (GDBState));
- s->env = first_cpu; /* XXX: allow to change CPU */
+ 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;
fcntl(fd, F_SETFL, O_NONBLOCK);
}
perror("socket");
return -1;
}
+#ifndef _WIN32
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
/* allow fast reuse */
val = 1;
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)
{
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]);
}
}
static void gdb_chr_event(void *opaque, int event)
{
switch (event) {
- case CHR_EVENT_RESET:
- vm_stop(EXCP_INTERRUPT);
- gdb_syscall_state = opaque;
+ case CHR_EVENT_OPENED:
+ vm_stop(VMSTOP_USER);
gdb_has_xml = 0;
break;
default:
}
}
-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(VMSTOP_USER);
+ }
+}
+#endif
+
+int gdbserver_start(const char *device)
+{
+ GDBState *s;
+ char gdbstub_device_name[128];
+ CharDriverState *chr = NULL;
+ CharDriverState *mon_chr;
- chr = qemu_chr_open("gdb", port);
- if (!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