diff --git a/exec.c b/exec.c index 3ba9525bd3..c4f2894ea1 100644 --- a/exec.c +++ b/exec.c @@ -590,15 +590,14 @@ void cpu_breakpoint_remove_all(CPUArchState *env, int mask) void cpu_single_step(CPUState *cpu, int enabled) { #if defined(TARGET_HAS_ICE) - CPUArchState *env = cpu->env_ptr; - if (cpu->singlestep_enabled != enabled) { cpu->singlestep_enabled = enabled; if (kvm_enabled()) { - kvm_update_guest_debug(env, 0); + kvm_update_guest_debug(cpu, 0); } else { /* must flush all the translated code to avoid inconsistencies */ /* XXX: only flush what is necessary */ + CPUArchState *env = cpu->env_ptr; tb_flush(env); } } diff --git a/gdbstub.c b/gdbstub.c index 35ca7c2c1e..1af25a6fe6 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -40,7 +40,6 @@ #include "cpu.h" #include "qemu/sockets.h" #include "sysemu/kvm.h" -#include "qemu/bitops.h" static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, uint8_t *buf, int len, bool is_write) @@ -316,10 +315,7 @@ 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; +bool gdb_has_xml; #ifdef CONFIG_USER_ONLY /* XXX: This is not thread safe. Do we care? */ @@ -489,1319 +485,6 @@ static int put_packet(GDBState *s, const char *buf) 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 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 -}; -#else -#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(CPUX86State *env, uint8_t *mem_buf, int n) -{ - if (n < CPU_NB_REGS) { - 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 - /* 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 >= 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 { - switch (n) { - 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_x86_gdb_load_seg(CPUX86State *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(CPUX86State *env, uint8_t *mem_buf, int n) -{ - uint32_t tmp; - - 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 - /* FIXME: byteswap float values - after fixing fpregs layout. */ - memcpy(&env->fpregs[n - IDX_FP_REGS], mem_buf, 10); -#endif - return 10; - } 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 { - 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. */ - return 0; -} - -#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(CPUPPCState *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(env->fpscr); - } - } - } - return 0; -} - -static int cpu_gdb_write_register(CPUPPCState *env, uint8_t *mem_buf, int n) -{ - 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; - store_fpscr(env, ldtul_p(mem_buf), 0xffffffff); - return sizeof(target_ulong); - } - } - return 0; -} - -#elif defined (TARGET_SPARC) - -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) -#define NUM_CORE_REGS 86 -#else -#define NUM_CORE_REGS 72 -#endif - -#ifdef TARGET_ABI32 -#define GET_REGA(val) GET_REG32(val) -#else -#define GET_REGA(val) GET_REGL(val) -#endif - -static int cpu_gdb_read_register(CPUSPARCState *env, uint8_t *mem_buf, int n) -{ - if (n < 8) { - /* g0..g7 */ - GET_REGA(env->gregs[n]); - } - if (n < 32) { - /* register window */ - GET_REGA(env->regwptr[n - 8]); - } -#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64) - if (n < 64) { - /* fprs */ - if (n & 1) { - GET_REG32(env->fpr[(n - 32) / 2].l.lower); - } else { - GET_REG32(env->fpr[(n - 32) / 2].l.upper); - } - } - /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ - switch (n) { - case 64: GET_REGA(env->y); - 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 */ - default: GET_REGA(0); - } -#else - if (n < 64) { - /* f0-f31 */ - if (n & 1) { - GET_REG32(env->fpr[(n - 32) / 2].l.lower); - } else { - GET_REG32(env->fpr[(n - 32) / 2].l.upper); - } - } - if (n < 80) { - /* f32-f62 (double width, even numbers only) */ - GET_REG64(env->fpr[(n - 32) / 2].ll); - } - switch (n) { - case 80: GET_REGL(env->pc); - case 81: GET_REGL(env->npc); - 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); - } -#endif - return 0; -} - -static int cpu_gdb_write_register(CPUSPARCState *env, uint8_t *mem_buf, int n) -{ -#if defined(TARGET_ABI32) - abi_ulong tmp; - - tmp = ldl_p(mem_buf); -#else - target_ulong tmp; - - tmp = ldtul_p(mem_buf); -#endif - - if (n < 8) { - /* g0..g7 */ - env->gregs[n] = tmp; - } else if (n < 32) { - /* register window */ - env->regwptr[n - 8] = tmp; - } -#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64) - else if (n < 64) { - /* fprs */ - /* f0-f31 */ - if (n & 1) { - env->fpr[(n - 32) / 2].l.lower = tmp; - } else { - env->fpr[(n - 32) / 2].l.upper = tmp; - } - } else { - /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ - switch (n) { - case 64: env->y = 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 69: env->npc = tmp; break; - case 70: env->fsr = tmp; break; - default: return 0; - } - } - return 4; -#else - else if (n < 64) { - /* f0-f31 */ - tmp = ldl_p(mem_buf); - if (n & 1) { - env->fpr[(n - 32) / 2].l.lower = tmp; - } else { - env->fpr[(n - 32) / 2].l.upper = tmp; - } - return 4; - } else if (n < 80) { - /* f32-f62 (double width, even numbers only) */ - env->fpr[(n - 32) / 2].ll = tmp; - } else { - switch (n) { - case 80: env->pc = tmp; break; - case 81: env->npc = tmp; break; - case 82: - cpu_put_ccr(env, tmp >> 32); - env->asi = (tmp >> 24) & 0xff; - env->pstate = (tmp >> 8) & 0xfff; - cpu_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; - } - } - return 8; -#endif -} -#elif defined (TARGET_ARM) - -/* 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" - -static int cpu_gdb_read_register(CPUARMState *env, uint8_t *mem_buf, int n) -{ - if (n < 16) { - /* Core integer register. */ - GET_REG32(env->regs[n]); - } - if (n < 24) { - /* FPA registers. */ - if (gdb_has_xml) - return 0; - memset(mem_buf, 0, 12); - return 12; - } - switch (n) { - case 24: - /* FPA status register. */ - if (gdb_has_xml) - return 0; - GET_REG32(0); - case 25: - /* CPSR */ - GET_REG32(cpsr_read(env)); - } - /* Unknown register. */ - return 0; -} - -static int cpu_gdb_write_register(CPUARMState *env, uint8_t *mem_buf, int n) -{ - uint32_t tmp; - - tmp = ldl_p(mem_buf); - - /* 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; - - 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; -} - -#elif defined (TARGET_M68K) - -#define NUM_CORE_REGS 18 - -#define GDB_CORE_XML "cf-core.xml" - -static int cpu_gdb_read_register(CPUM68KState *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; -} - -static int cpu_gdb_write_register(CPUM68KState *env, uint8_t *mem_buf, int n) -{ - uint32_t tmp; - - tmp = ldl_p(mem_buf); - - if (n < 8) { - /* D0-D7 */ - env->dregs[n] = tmp; - } else if (n < 16) { - /* 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) - -#define NUM_CORE_REGS 73 - -static int cpu_gdb_read_register(CPUMIPSState *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 | !!(env->hflags & MIPS_HFLAG_M16)); - 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 0; -} - -/* convert MIPS rounding mode in FCR31 to IEEE library */ -static unsigned int ieee_rm[] = - { - float_round_nearest_even, - float_round_to_zero, - float_round_up, - float_round_down - }; -#define RESTORE_ROUNDING_MODE \ - set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], &env->active_fpu.fp_status) - -static int cpu_gdb_write_register(CPUMIPSState *env, uint8_t *mem_buf, int n) -{ - target_ulong tmp; - - tmp = ldtul_p(mem_buf); - - 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; - 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 & ~(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) - return 0; - /* Other registers are readonly. Ignore writes. */ - break; - } - - return sizeof(target_ulong); -} -#elif defined(TARGET_OPENRISC) - -#define NUM_CORE_REGS (32 + 3) - -static int cpu_gdb_read_register(CPUOpenRISCState *env, uint8_t *mem_buf, int n) -{ - if (n < 32) { - GET_REG32(env->gpr[n]); - } else { - switch (n) { - case 32: /* PPC */ - GET_REG32(env->ppc); - break; - - case 33: /* NPC */ - GET_REG32(env->npc); - break; - - case 34: /* SR */ - GET_REG32(env->sr); - break; - - default: - break; - } - } - return 0; -} - -static int cpu_gdb_write_register(CPUOpenRISCState *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->gpr[n] = tmp; - } else { - switch (n) { - case 32: /* PPC */ - env->ppc = tmp; - break; - - case 33: /* NPC */ - env->npc = tmp; - break; - - case 34: /* SR */ - env->sr = tmp; - break; - - default: - break; - } - } - return 4; -} -#elif defined (TARGET_SH4) - -/* Hint: Use "set architecture sh4" in GDB to see fpu registers */ -/* FIXME: We should use XML for this. */ - -#define NUM_CORE_REGS 59 - -static int cpu_gdb_read_register(CPUSH4State *env, uint8_t *mem_buf, int n) -{ - switch (n) { - case 0 ... 7: - if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) { - GET_REGL(env->gregs[n + 16]); - } else { - GET_REGL(env->gregs[n]); - } - case 8 ... 15: - GET_REGL(env->gregs[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 25 ... 40: - if (env->fpscr & FPSCR_FR) { - stfl_p(mem_buf, env->fregs[n - 9]); - } else { - stfl_p(mem_buf, env->fregs[n - 25]); - } - return 4; - case 41: - GET_REGL(env->ssr); - case 42: - GET_REGL(env->spc); - case 43 ... 50: - GET_REGL(env->gregs[n - 43]); - case 51 ... 58: - GET_REGL(env->gregs[n - (51 - 16)]); - } - - return 0; -} - -static int cpu_gdb_write_register(CPUSH4State *env, uint8_t *mem_buf, int n) -{ - switch (n) { - case 0 ... 7: - if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) { - env->gregs[n + 16] = ldl_p(mem_buf); - } else { - env->gregs[n] = ldl_p(mem_buf); - } - break; - case 8 ... 15: - env->gregs[n] = ldl_p(mem_buf); - break; - case 16: - env->pc = ldl_p(mem_buf); - break; - case 17: - env->pr = ldl_p(mem_buf); - break; - case 18: - env->gbr = ldl_p(mem_buf); - break; - case 19: - env->vbr = ldl_p(mem_buf); - break; - case 20: - env->mach = ldl_p(mem_buf); - break; - case 21: - env->macl = ldl_p(mem_buf); - break; - case 22: - env->sr = ldl_p(mem_buf); - break; - case 23: - env->fpul = ldl_p(mem_buf); - break; - case 24: - env->fpscr = ldl_p(mem_buf); - break; - case 25 ... 40: - if (env->fpscr & FPSCR_FR) { - env->fregs[n - 9] = ldfl_p(mem_buf); - } else { - env->fregs[n - 25] = ldfl_p(mem_buf); - } - break; - case 41: - env->ssr = ldl_p(mem_buf); - break; - case 42: - env->spc = ldl_p(mem_buf); - break; - case 43 ... 50: - env->gregs[n - 43] = ldl_p(mem_buf); - break; - case 51 ... 58: - env->gregs[n - (51 - 16)] = ldl_p(mem_buf); - break; - default: return 0; - } - - return 4; -} -#elif defined (TARGET_MICROBLAZE) - -#define NUM_CORE_REGS (32 + 5) - -static int cpu_gdb_read_register(CPUMBState *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(CPUMBState *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(CPUCRISState *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(CPUCRISState *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]); - } - - 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 cpu_gdb_write_register(CPUCRISState *env, uint8_t *mem_buf, int n) -{ - 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; -} -#elif defined (TARGET_ALPHA) - -#define NUM_CORE_REGS 67 - -static int cpu_gdb_read_register(CPUAlphaState *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(CPUAlphaState *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_REGS - -static int cpu_gdb_read_register(CPUS390XState *env, uint8_t *mem_buf, int n) -{ - uint64_t val; - int cc_op; - - switch (n) { - case S390_PSWM_REGNUM: - cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr); - val = deposit64(env->psw.mask, 44, 2, cc_op); - GET_REGL(val); - 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: - GET_REG64(env->fregs[n-S390_F0_REGNUM].ll); - break; - } - - return 0; -} - -static int cpu_gdb_write_register(CPUS390XState *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; - env->cc_op = extract64(tmpl, 44, 2); - 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: - env->fregs[n-S390_F0_REGNUM].ll = tmpl; - break; - default: - return 0; - } - return r; -} -#elif defined (TARGET_LM32) - -#include "hw/lm32/lm32_pic.h" -#define NUM_CORE_REGS (32 + 7) - -static int cpu_gdb_read_register(CPULM32State *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(CPULM32State *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; -} -#elif defined(TARGET_XTENSA) - -/* Use num_core_regs to see only non-privileged registers in an unmodified gdb. - * Use num_regs to see all registers. gdb modification is required for that: - * reset bit 0 in the 'flags' field of the registers definitions in the - * gdb/xtensa-config.c inside gdb source tree or inside gdb overlay. - */ -#define NUM_CORE_REGS (env->config->gdb_regmap.num_regs) -#define num_g_regs NUM_CORE_REGS - -static int cpu_gdb_read_register(CPUXtensaState *env, uint8_t *mem_buf, int n) -{ - const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; - - if (n < 0 || n >= env->config->gdb_regmap.num_regs) { - return 0; - } - - switch (reg->type) { - case 9: /*pc*/ - GET_REG32(env->pc); - break; - - case 1: /*ar*/ - xtensa_sync_phys_from_window(env); - GET_REG32(env->phys_regs[(reg->targno & 0xff) % env->config->nareg]); - break; - - case 2: /*SR*/ - GET_REG32(env->sregs[reg->targno & 0xff]); - break; - - case 3: /*UR*/ - GET_REG32(env->uregs[reg->targno & 0xff]); - break; - - case 4: /*f*/ - GET_REG32(float32_val(env->fregs[reg->targno & 0x0f])); - break; - - case 8: /*a*/ - GET_REG32(env->regs[reg->targno & 0x0f]); - break; - - default: - qemu_log("%s from reg %d of unsupported type %d\n", - __func__, n, reg->type); - return 0; - } -} - -static int cpu_gdb_write_register(CPUXtensaState *env, uint8_t *mem_buf, int n) -{ - uint32_t tmp; - const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; - - if (n < 0 || n >= env->config->gdb_regmap.num_regs) { - return 0; - } - - tmp = ldl_p(mem_buf); - - switch (reg->type) { - case 9: /*pc*/ - env->pc = tmp; - break; - - case 1: /*ar*/ - env->phys_regs[(reg->targno & 0xff) % env->config->nareg] = tmp; - xtensa_sync_window_from_phys(env); - break; - - case 2: /*SR*/ - env->sregs[reg->targno & 0xff] = tmp; - break; - - case 3: /*UR*/ - env->uregs[reg->targno & 0xff] = tmp; - break; - - case 4: /*f*/ - env->fregs[reg->targno & 0x0f] = make_float32(tmp); - break; - - case 8: /*a*/ - env->regs[reg->targno & 0x0f] = tmp; - break; - - default: - qemu_log("%s to reg %d of unsupported type %d\n", - __func__, n, reg->type); - return 0; - } - - return 4; -} -#else - -#define NUM_CORE_REGS 0 - -static int cpu_gdb_read_register(CPUArchState *env, uint8_t *mem_buf, int n) -{ - return 0; -} - -static int cpu_gdb_write_register(CPUArchState *env, uint8_t *mem_buf, int n) -{ - return 0; -} - -#endif - -#if !defined(TARGET_XTENSA) -static int num_g_regs = NUM_CORE_REGS; -#endif - -#ifdef GDB_CORE_XML /* Encode data using the encoding for 'x' packets. */ static int memtox(char *buf, const char *mem, int len) { @@ -1823,7 +506,8 @@ static int memtox(char *buf, const char *mem, int len) return p - buf; } -static const char *get_feature_xml(const char *p, const char **newp) +static const char *get_feature_xml(const char *p, const char **newp, + CPUClass *cc) { size_t len; int i; @@ -1847,7 +531,7 @@ static const char *get_feature_xml(const char *p, const char **newp) "" "" "", - GDB_CORE_XML); + cc->gdb_core_xml_file); for (r = cpu->gdb_regs; r; r = r->next) { pstrcat(target_xml, sizeof(target_xml), "env_ptr; GDBRegisterState *r; - if (reg < NUM_CORE_REGS) - return cpu_gdb_read_register(env, mem_buf, reg); + if (reg < cc->gdb_num_core_regs) { + return cc->gdb_read_register(cpu, mem_buf, reg); + } for (r = cpu->gdb_regs; r; r = r->next) { if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) { @@ -1885,11 +570,13 @@ static int gdb_read_register(CPUState *cpu, uint8_t *mem_buf, int reg) static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) { + CPUClass *cc = CPU_GET_CLASS(cpu); CPUArchState *env = cpu->env_ptr; GDBRegisterState *r; - if (reg < NUM_CORE_REGS) - return cpu_gdb_write_register(env, mem_buf, reg); + if (reg < cc->gdb_num_core_regs) { + return cc->gdb_write_register(cpu, mem_buf, reg); + } for (r = cpu->gdb_regs; r; r = r->next) { if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) { @@ -1899,7 +586,6 @@ static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) return 0; } -#if !defined(TARGET_XTENSA) /* 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 @@ -1912,7 +598,6 @@ void gdb_register_coprocessor(CPUState *cpu, { GDBRegisterState *s; GDBRegisterState **p; - static int last_reg = NUM_CORE_REGS; p = &cpu->gdb_regs; while (*p) { @@ -1923,25 +608,22 @@ void gdb_register_coprocessor(CPUState *cpu, } s = g_new0(GDBRegisterState, 1); - s->base_reg = last_reg; + s->base_reg = cpu->gdb_num_regs; s->num_regs = num_regs; s->get_reg = get_reg; s->set_reg = set_reg; s->xml = xml; /* Add to end of list. */ - last_reg += num_regs; + cpu->gdb_num_regs += 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; } } } -#endif #ifndef CONFIG_USER_ONLY static const int xlat_gdb_type[] = { @@ -2071,10 +753,8 @@ static CPUState *find_cpu(uint32_t thread_id) static int gdb_handle_packet(GDBState *s, const char *line_buf) { -#ifdef TARGET_XTENSA - CPUArchState *env; -#endif CPUState *cpu; + CPUClass *cc; const char *p; uint32_t thread; int ch, reg_size, type, res; @@ -2221,11 +901,8 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) break; case 'g': cpu_synchronize_state(s->g_cpu); -#ifdef TARGET_XTENSA - env = s->g_cpu->env_ptr; -#endif len = 0; - for (addr = 0; addr < num_g_regs; addr++) { + for (addr = 0; addr < s->g_cpu->gdb_num_regs; addr++) { reg_size = gdb_read_register(s->g_cpu, mem_buf + len, addr); len += reg_size; } @@ -2234,13 +911,10 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) break; case 'G': cpu_synchronize_state(s->g_cpu); -#ifdef TARGET_XTENSA - env = s->g_cpu->env_ptr; -#endif registers = mem_buf; len = strlen(p) / 2; hextomem((uint8_t *)registers, p, len); - for (addr = 0; addr < num_g_regs && len > 0; addr++) { + for (addr = 0; addr < s->g_cpu->gdb_num_regs && len > 0; addr++) { reg_size = gdb_write_register(s->g_cpu, registers, addr); len -= reg_size; registers += reg_size; @@ -2443,20 +1117,25 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) #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 + cc = CPU_GET_CLASS(first_cpu); + if (cc->gdb_core_xml_file != NULL) { + pstrcat(buf, sizeof(buf), ";qXfer:features:read+"); + } 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; + cc = CPU_GET_CLASS(first_cpu); + if (cc->gdb_core_xml_file == NULL) { + goto unknown_command; + } + + gdb_has_xml = true; p += 19; - xml = get_feature_xml(p, &p); + xml = get_feature_xml(p, &p, cc); if (!xml) { snprintf(buf, sizeof(buf), "E00"); put_packet(s, buf); @@ -2488,7 +1167,6 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet_binary(s, buf, len + 1); break; } -#endif /* Unrecognised 'q' command. */ goto unknown_command; @@ -2863,7 +1541,7 @@ static void gdb_accept(void) s->c_cpu = first_cpu; s->g_cpu = first_cpu; s->fd = fd; - gdb_has_xml = 0; + gdb_has_xml = false; gdbserver_state = s; @@ -2949,7 +1627,7 @@ static void gdb_chr_event(void *opaque, int event) switch (event) { case CHR_EVENT_OPENED: vm_stop(RUN_STATE_PAUSED); - gdb_has_xml = 0; + gdb_has_xml = false; break; default: break; diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 7ea1ad7f9c..a608a26c30 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -39,6 +39,43 @@ static inline int cpu_index(CPUState *cpu) #endif } +/* 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. + */ + +static inline int gdb_get_reg8(uint8_t *mem_buf, uint8_t val) +{ + stb_p(mem_buf, val); + return 1; +} + +static inline int gdb_get_reg16(uint8_t *mem_buf, uint16_t val) +{ + stw_p(mem_buf, val); + return 2; +} + +static inline int gdb_get_reg32(uint8_t *mem_buf, uint32_t val) +{ + stl_p(mem_buf, val); + return 4; +} + +static inline int gdb_get_reg64(uint8_t *mem_buf, uint64_t val) +{ + stq_p(mem_buf, val); + return 8; +} + +#if TARGET_LONG_BITS == 64 +#define gdb_get_regl(buf, val) gdb_get_reg64(buf, val) +#define ldtul_p(addr) ldq_p(addr) +#else +#define gdb_get_regl(buf, val) gdb_get_reg32(buf, val) +#define ldtul_p(addr) ldl_p(addr) +#endif + #endif #ifdef CONFIG_USER_ONLY @@ -47,6 +84,14 @@ int gdbserver_start(int); int gdbserver_start(const char *port); #endif +/** + * gdb_has_xml: + * 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. + */ +extern bool gdb_has_xml; + /* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */ extern const char *const xml_builtin[][2]; diff --git a/include/qom/cpu.h b/include/qom/cpu.h index daf1835c1a..0d6e95c0b6 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -80,7 +80,11 @@ struct TranslationBlock; * @synchronize_from_tb: Callback for synchronizing state from a TCG * #TranslationBlock. * @get_phys_page_debug: Callback for obtaining a physical address. + * @gdb_read_register: Callback for letting GDB read a register. + * @gdb_write_register: Callback for letting GDB write a register. * @vmsd: State description for migration. + * @gdb_num_core_regs: Number of core registers accessible to GDB. + * @gdb_core_xml_file: File name for core registers GDB XML description. * * Represents a CPU family or model. */ @@ -108,8 +112,9 @@ typedef struct CPUClass { void (*set_pc)(CPUState *cpu, vaddr value); void (*synchronize_from_tb)(CPUState *cpu, struct TranslationBlock *tb); hwaddr (*get_phys_page_debug)(CPUState *cpu, vaddr addr); + int (*gdb_read_register)(CPUState *cpu, uint8_t *buf, int reg); + int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg); - const struct VMStateDescription *vmsd; int (*write_elf64_note)(WriteCoreDumpFunction f, CPUState *cpu, int cpuid, void *opaque); int (*write_elf64_qemunote)(WriteCoreDumpFunction f, CPUState *cpu, @@ -118,6 +123,10 @@ typedef struct CPUClass { int cpuid, void *opaque); int (*write_elf32_qemunote)(WriteCoreDumpFunction f, CPUState *cpu, void *opaque); + + const struct VMStateDescription *vmsd; + int gdb_num_core_regs; + const char *gdb_core_xml_file; } CPUClass; struct KVMState; @@ -142,6 +151,7 @@ struct kvm_run; * @env_ptr: Pointer to subclass-specific CPUArchState field. * @current_tb: Currently executing TB. * @gdb_regs: Additional GDB registers. + * @gdb_num_regs: Number of total registers accessible to GDB. * @next_cpu: Next CPU sharing TB cache. * @kvm_fd: vCPU file descriptor for KVM. * @@ -177,6 +187,7 @@ struct CPUState { void *env_ptr; /* CPUArchState */ struct TranslationBlock *current_tb; struct GDBRegisterState *gdb_regs; + int gdb_num_regs; CPUState *next_cpu; int kvm_fd; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index f8ac448e0b..de74411f41 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -174,7 +174,7 @@ int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr, target_ulong len, int type); void kvm_remove_all_breakpoints(CPUState *cpu); -int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap); +int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap); #ifndef _WIN32 int kvm_set_signal_mask(CPUState *cpu, const sigset_t *sigset); #endif diff --git a/kvm-all.c b/kvm-all.c index 4fb4ccbeca..716860f617 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -1883,9 +1883,8 @@ static void kvm_invoke_set_guest_debug(void *data) &dbg_data->dbg); } -int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap) +int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) { - CPUState *cpu = ENV_GET_CPU(env); struct kvm_set_guest_debug_data data; data.dbg.control = reinject_trap; @@ -1935,9 +1934,7 @@ int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, } for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) { - CPUArchState *env = cpu->env_ptr; - - err = kvm_update_guest_debug(env, 0); + err = kvm_update_guest_debug(cpu, 0); if (err) { return err; } @@ -1977,9 +1974,7 @@ int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr, } for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) { - CPUArchState *env = cpu->env_ptr; - - err = kvm_update_guest_debug(env, 0); + err = kvm_update_guest_debug(cpu, 0); if (err) { return err; } @@ -2007,15 +2002,13 @@ void kvm_remove_all_breakpoints(CPUState *cpu) kvm_arch_remove_all_hw_breakpoints(); for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) { - CPUArchState *env = cpu->env_ptr; - - kvm_update_guest_debug(env, 0); + kvm_update_guest_debug(cpu, 0); } } #else /* !KVM_CAP_SET_GUEST_DEBUG */ -int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap) +int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) { return -EINVAL; } diff --git a/kvm-stub.c b/kvm-stub.c index 7b2233ae82..771360b3ca 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -78,7 +78,7 @@ void kvm_setup_guest_memory(void *start, size_t size) { } -int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap) +int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) { return -ENOSYS; } diff --git a/linux-user/main.c b/linux-user/main.c index 5dc09471e4..5309117034 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3637,7 +3637,7 @@ int main(int argc, char **argv, char **envp) exit(1); } cpu = ENV_GET_CPU(env); - cpu_reset(ENV_GET_CPU(env)); + cpu_reset(cpu); thread_cpu = cpu; diff --git a/qom/cpu.c b/qom/cpu.c index 5c45ab5333..dbc9fb6489 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -157,6 +157,17 @@ static int cpu_common_write_elf64_note(WriteCoreDumpFunction f, } +static int cpu_common_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg) +{ + return 0; +} + +static int cpu_common_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg) +{ + return 0; +} + + void cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags) { @@ -226,6 +237,14 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) } } +static void cpu_common_initfn(Object *obj) +{ + CPUState *cpu = CPU(obj); + CPUClass *cc = CPU_GET_CLASS(obj); + + cpu->gdb_num_regs = cc->gdb_num_core_regs; +} + static int64_t cpu_common_get_arch_id(CPUState *cpu) { return cpu->cpu_index; @@ -245,6 +264,8 @@ static void cpu_class_init(ObjectClass *klass, void *data) k->write_elf32_note = cpu_common_write_elf32_note; k->write_elf64_qemunote = cpu_common_write_elf64_qemunote; k->write_elf64_note = cpu_common_write_elf64_note; + k->gdb_read_register = cpu_common_gdb_read_register; + k->gdb_write_register = cpu_common_gdb_write_register; dc->realize = cpu_common_realizefn; dc->no_user = 1; } @@ -253,6 +274,7 @@ static const TypeInfo cpu_type_info = { .name = TYPE_CPU, .parent = TYPE_DEVICE, .instance_size = sizeof(CPUState), + .instance_init = cpu_common_initfn, .abstract = true, .class_size = sizeof(CPUClass), .class_init = cpu_class_init, diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 9b701b4714..f306cbada3 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -7,6 +7,7 @@ stub-obj-y += fdset-add-fd.o stub-obj-y += fdset-find-fd.o stub-obj-y += fdset-get-fd.o stub-obj-y += fdset-remove-fd.o +stub-obj-y += gdbstub.o stub-obj-y += get-fd.o stub-obj-y += get-vm-name.o stub-obj-y += iothread-lock.o diff --git a/stubs/gdbstub.c b/stubs/gdbstub.c new file mode 100644 index 0000000000..c1dbfe7fb7 --- /dev/null +++ b/stubs/gdbstub.c @@ -0,0 +1,5 @@ +#include "qemu-common.h" + +const char *const xml_builtin[][2] = { + { NULL, NULL } +}; diff --git a/target-alpha/Makefile.objs b/target-alpha/Makefile.objs index 590304cc61..b96c5da98d 100644 --- a/target-alpha/Makefile.objs +++ b/target-alpha/Makefile.objs @@ -1,3 +1,4 @@ obj-$(CONFIG_SOFTMMU) += machine.o obj-y += translate.o helper.o cpu.o obj-y += int_helper.o fpu_helper.o sys_helper.o mem_helper.o +obj-y += gdbstub.o diff --git a/target-alpha/cpu-qom.h b/target-alpha/cpu-qom.h index b2eeba36f3..2ebc9bcacb 100644 --- a/target-alpha/cpu-qom.h +++ b/target-alpha/cpu-qom.h @@ -82,5 +82,7 @@ void alpha_cpu_do_interrupt(CPUState *cpu); void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int alpha_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int alpha_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif diff --git a/target-alpha/cpu.c b/target-alpha/cpu.c index c8c8c2c861..64c70bc1e9 100644 --- a/target-alpha/cpu.c +++ b/target-alpha/cpu.c @@ -271,11 +271,14 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data) cc->do_interrupt = alpha_cpu_do_interrupt; cc->dump_state = alpha_cpu_dump_state; cc->set_pc = alpha_cpu_set_pc; + cc->gdb_read_register = alpha_cpu_gdb_read_register; + cc->gdb_write_register = alpha_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->do_unassigned_access = alpha_cpu_unassigned_access; cc->get_phys_page_debug = alpha_cpu_get_phys_page_debug; dc->vmsd = &vmstate_alpha_cpu; #endif + cc->gdb_num_core_regs = 67; } static const TypeInfo alpha_cpu_type_info = { diff --git a/target-alpha/gdbstub.c b/target-alpha/gdbstub.c new file mode 100644 index 0000000000..980f140e72 --- /dev/null +++ b/target-alpha/gdbstub.c @@ -0,0 +1,93 @@ +/* + * Alpha gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int alpha_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + AlphaCPU *cpu = ALPHA_CPU(cs); + CPUAlphaState *env = &cpu->env; + 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; + } + return gdb_get_regl(mem_buf, val); +} + +int alpha_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + AlphaCPU *cpu = ALPHA_CPU(cs); + CPUAlphaState *env = &cpu->env; + 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; +} diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index 4a6e52e528..2d9f77fa9b 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -4,3 +4,4 @@ obj-$(CONFIG_KVM) += kvm.o obj-$(CONFIG_NO_KVM) += kvm-stub.o obj-y += translate.o op_helper.o helper.o cpu.o obj-y += neon_helper.o iwmmxt_helper.o +obj-y += gdbstub.o diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 02162c9aba..cf3658714e 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -149,4 +149,7 @@ void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, hwaddr arm_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + #endif diff --git a/target-arm/cpu.c b/target-arm/cpu.c index d3906a4829..87d35c6bf2 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -824,10 +824,14 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->do_interrupt = arm_cpu_do_interrupt; cc->dump_state = arm_cpu_dump_state; cc->set_pc = arm_cpu_set_pc; + cc->gdb_read_register = arm_cpu_gdb_read_register; + cc->gdb_write_register = arm_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = arm_cpu_get_phys_page_debug; cc->vmsd = &vmstate_arm_cpu; #endif + cc->gdb_num_core_regs = 26; + cc->gdb_core_xml_file = "arm-core.xml"; } static void cpu_register(const ARMCPUInfo *info) diff --git a/target-arm/gdbstub.c b/target-arm/gdbstub.c new file mode 100644 index 0000000000..1c3439654f --- /dev/null +++ b/target-arm/gdbstub.c @@ -0,0 +1,102 @@ +/* + * ARM gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +/* 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. */ + +int arm_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + if (n < 16) { + /* Core integer register. */ + return gdb_get_reg32(mem_buf, env->regs[n]); + } + if (n < 24) { + /* FPA registers. */ + if (gdb_has_xml) { + return 0; + } + memset(mem_buf, 0, 12); + return 12; + } + switch (n) { + case 24: + /* FPA status register. */ + if (gdb_has_xml) { + return 0; + } + return gdb_get_reg32(mem_buf, 0); + case 25: + /* CPSR */ + return gdb_get_reg32(mem_buf, cpsr_read(env)); + } + /* Unknown register. */ + return 0; +} + +int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + uint32_t tmp; + + tmp = ldl_p(mem_buf); + + /* 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; + } + + 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; +} diff --git a/target-cris/Makefile.objs b/target-cris/Makefile.objs index afb87bcc80..7779227fc4 100644 --- a/target-cris/Makefile.objs +++ b/target-cris/Makefile.objs @@ -1,2 +1,3 @@ obj-y += translate.o op_helper.o helper.o cpu.o +obj-y += gdbstub.o obj-$(CONFIG_SOFTMMU) += mmu.o machine.o diff --git a/target-cris/cpu-qom.h b/target-cris/cpu-qom.h index d7baf0746a..75593667d6 100644 --- a/target-cris/cpu-qom.h +++ b/target-cris/cpu-qom.h @@ -81,4 +81,8 @@ void cris_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, hwaddr cris_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int crisv10_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int cris_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int cris_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + #endif diff --git a/target-cris/cpu.c b/target-cris/cpu.c index ba095e75a5..45f2d6bacf 100644 --- a/target-cris/cpu.c +++ b/target-cris/cpu.c @@ -175,6 +175,7 @@ static void crisv8_cpu_class_init(ObjectClass *oc, void *data) ccc->vr = 8; cc->do_interrupt = crisv10_cpu_do_interrupt; + cc->gdb_read_register = crisv10_cpu_gdb_read_register; } static void crisv9_cpu_class_init(ObjectClass *oc, void *data) @@ -184,6 +185,7 @@ static void crisv9_cpu_class_init(ObjectClass *oc, void *data) ccc->vr = 9; cc->do_interrupt = crisv10_cpu_do_interrupt; + cc->gdb_read_register = crisv10_cpu_gdb_read_register; } static void crisv10_cpu_class_init(ObjectClass *oc, void *data) @@ -193,6 +195,7 @@ static void crisv10_cpu_class_init(ObjectClass *oc, void *data) ccc->vr = 10; cc->do_interrupt = crisv10_cpu_do_interrupt; + cc->gdb_read_register = crisv10_cpu_gdb_read_register; } static void crisv11_cpu_class_init(ObjectClass *oc, void *data) @@ -202,6 +205,7 @@ static void crisv11_cpu_class_init(ObjectClass *oc, void *data) ccc->vr = 11; cc->do_interrupt = crisv10_cpu_do_interrupt; + cc->gdb_read_register = crisv10_cpu_gdb_read_register; } static void crisv32_cpu_class_init(ObjectClass *oc, void *data) @@ -255,9 +259,13 @@ static void cris_cpu_class_init(ObjectClass *oc, void *data) cc->do_interrupt = cris_cpu_do_interrupt; cc->dump_state = cris_cpu_dump_state; cc->set_pc = cris_cpu_set_pc; + cc->gdb_read_register = cris_cpu_gdb_read_register; + cc->gdb_write_register = cris_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = cris_cpu_get_phys_page_debug; #endif + + cc->gdb_num_core_regs = 49; } static const TypeInfo cris_cpu_type_info = { diff --git a/target-cris/gdbstub.c b/target-cris/gdbstub.c new file mode 100644 index 0000000000..5db3683ab6 --- /dev/null +++ b/target-cris/gdbstub.c @@ -0,0 +1,130 @@ +/* + * CRIS gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int crisv10_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + CRISCPU *cpu = CRIS_CPU(cs); + CPUCRISState *env = &cpu->env; + + if (n < 15) { + return gdb_get_reg32(mem_buf, env->regs[n]); + } + + if (n == 15) { + return gdb_get_reg32(mem_buf, env->pc); + } + + if (n < 32) { + switch (n) { + case 16: + return gdb_get_reg8(mem_buf, env->pregs[n - 16]); + case 17: + return gdb_get_reg8(mem_buf, env->pregs[n - 16]); + case 20: + case 21: + return gdb_get_reg16(mem_buf, env->pregs[n - 16]); + default: + if (n >= 23) { + return gdb_get_reg32(mem_buf, env->pregs[n - 16]); + } + break; + } + } + return 0; +} + +int cris_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + CRISCPU *cpu = CRIS_CPU(cs); + CPUCRISState *env = &cpu->env; + uint8_t srs; + + srs = env->pregs[PR_SRS]; + if (n < 16) { + return gdb_get_reg32(mem_buf, env->regs[n]); + } + + if (n >= 21 && n < 32) { + return gdb_get_reg32(mem_buf, env->pregs[n - 16]); + } + if (n >= 33 && n < 49) { + return gdb_get_reg32(mem_buf, env->sregs[srs][n - 33]); + } + switch (n) { + case 16: + return gdb_get_reg8(mem_buf, env->pregs[0]); + case 17: + return gdb_get_reg8(mem_buf, env->pregs[1]); + case 18: + return gdb_get_reg32(mem_buf, env->pregs[2]); + case 19: + return gdb_get_reg8(mem_buf, srs); + case 20: + return gdb_get_reg16(mem_buf, env->pregs[4]); + case 32: + return gdb_get_reg32(mem_buf, env->pc); + } + + return 0; +} + +int cris_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + CRISCPU *cpu = CRIS_CPU(cs); + CPUCRISState *env = &cpu->env; + 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; +} diff --git a/target-i386/Makefile.objs b/target-i386/Makefile.objs index c1d4f059da..3b629d4d39 100644 --- a/target-i386/Makefile.objs +++ b/target-i386/Makefile.objs @@ -1,6 +1,7 @@ obj-y += translate.o helper.o cpu.o obj-y += excp_helper.o fpu_helper.o cc_helper.o int_helper.o svm_helper.o obj-y += smm_helper.o misc_helper.o mem_helper.o seg_helper.o +obj-y += gdbstub.o obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o obj-$(CONFIG_KVM) += kvm.o hyperv.o obj-$(CONFIG_NO_KVM) += kvm-stub.o diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index d928562c53..60d2b5d772 100644 --- a/target-i386/cpu-qom.h +++ b/target-i386/cpu-qom.h @@ -106,4 +106,7 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, hwaddr x86_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int x86_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int x86_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + #endif diff --git a/target-i386/cpu.c b/target-i386/cpu.c index cd350cb8e4..2b59b7d7ef 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -2538,6 +2538,8 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) cc->dump_state = x86_cpu_dump_state; cc->set_pc = x86_cpu_set_pc; cc->synchronize_from_tb = x86_cpu_synchronize_from_tb; + cc->gdb_read_register = x86_cpu_gdb_read_register; + cc->gdb_write_register = x86_cpu_gdb_write_register; cc->get_arch_id = x86_cpu_get_arch_id; cc->get_paging_enabled = x86_cpu_get_paging_enabled; #ifndef CONFIG_USER_ONLY @@ -2549,6 +2551,7 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) cc->write_elf32_qemunote = x86_cpu_write_elf32_qemunote; cc->vmsd = &vmstate_x86_cpu; #endif + cc->gdb_num_core_regs = CPU_NB_REGS * 2 + 25; } static const TypeInfo x86_cpu_type_info = { diff --git a/target-i386/gdbstub.c b/target-i386/gdbstub.c new file mode 100644 index 0000000000..15bebeff89 --- /dev/null +++ b/target-i386/gdbstub.c @@ -0,0 +1,231 @@ +/* + * x86 gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +#ifdef TARGET_X86_64 +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 +}; +#else +#define gpr_map gpr_map32 +#endif +static const int gpr_map32[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + +#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) + +int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + if (n < CPU_NB_REGS) { + if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) { + return gdb_get_reg64(mem_buf, env->regs[gpr_map[n]]); + } else if (n < CPU_NB_REGS32) { + return gdb_get_reg32(mem_buf, env->regs[gpr_map32[n]]); + } + } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) { +#ifdef USE_X86LDOUBLE + /* 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 >= 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 { + switch (n) { + case IDX_IP_REG: + if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) { + return gdb_get_reg64(mem_buf, env->eip); + } else { + return gdb_get_reg32(mem_buf, env->eip); + } + case IDX_FLAGS_REG: + return gdb_get_reg32(mem_buf, env->eflags); + + case IDX_SEG_REGS: + return gdb_get_reg32(mem_buf, env->segs[R_CS].selector); + case IDX_SEG_REGS + 1: + return gdb_get_reg32(mem_buf, env->segs[R_SS].selector); + case IDX_SEG_REGS + 2: + return gdb_get_reg32(mem_buf, env->segs[R_DS].selector); + case IDX_SEG_REGS + 3: + return gdb_get_reg32(mem_buf, env->segs[R_ES].selector); + case IDX_SEG_REGS + 4: + return gdb_get_reg32(mem_buf, env->segs[R_FS].selector); + case IDX_SEG_REGS + 5: + return gdb_get_reg32(mem_buf, env->segs[R_GS].selector); + + case IDX_FP_REGS + 8: + return gdb_get_reg32(mem_buf, env->fpuc); + case IDX_FP_REGS + 9: + return gdb_get_reg32(mem_buf, (env->fpus & ~0x3800) | + (env->fpstt & 0x7) << 11); + case IDX_FP_REGS + 10: + return gdb_get_reg32(mem_buf, 0); /* ftag */ + case IDX_FP_REGS + 11: + return gdb_get_reg32(mem_buf, 0); /* fiseg */ + case IDX_FP_REGS + 12: + return gdb_get_reg32(mem_buf, 0); /* fioff */ + case IDX_FP_REGS + 13: + return gdb_get_reg32(mem_buf, 0); /* foseg */ + case IDX_FP_REGS + 14: + return gdb_get_reg32(mem_buf, 0); /* fooff */ + case IDX_FP_REGS + 15: + return gdb_get_reg32(mem_buf, 0); /* fop */ + + case IDX_MXCSR_REG: + return gdb_get_reg32(mem_buf, env->mxcsr); + } + } + return 0; +} + +static int x86_cpu_gdb_load_seg(X86CPU *cpu, int sreg, uint8_t *mem_buf) +{ + CPUX86State *env = &cpu->env; + 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; +} + +int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + uint32_t tmp; + + 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 + /* FIXME: byteswap float values - after fixing fpregs layout. */ + memcpy(&env->fpregs[n - IDX_FP_REGS], mem_buf, 10); +#endif + return 10; + } 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 { + 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 x86_cpu_gdb_load_seg(cpu, R_CS, mem_buf); + case IDX_SEG_REGS + 1: + return x86_cpu_gdb_load_seg(cpu, R_SS, mem_buf); + case IDX_SEG_REGS + 2: + return x86_cpu_gdb_load_seg(cpu, R_DS, mem_buf); + case IDX_SEG_REGS + 3: + return x86_cpu_gdb_load_seg(cpu, R_ES, mem_buf); + case IDX_SEG_REGS + 4: + return x86_cpu_gdb_load_seg(cpu, R_FS, mem_buf); + case IDX_SEG_REGS + 5: + return x86_cpu_gdb_load_seg(cpu, 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. */ + return 0; +} diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 3c9d10a762..376fc70ae3 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -1618,7 +1618,7 @@ static int kvm_guest_debug_workarounds(X86CPU *cpu) */ if (reinject_trap || (!kvm_has_robust_singlestep() && cs->singlestep_enabled)) { - ret = kvm_update_guest_debug(env, reinject_trap); + ret = kvm_update_guest_debug(cs, reinject_trap); } return ret; } diff --git a/target-lm32/Makefile.objs b/target-lm32/Makefile.objs index ca20f21443..40236876c8 100644 --- a/target-lm32/Makefile.objs +++ b/target-lm32/Makefile.objs @@ -1,2 +1,3 @@ obj-y += translate.o op_helper.o helper.o cpu.o +obj-y += gdbstub.o obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target-lm32/cpu-qom.h b/target-lm32/cpu-qom.h index 9e2732919d..723f6049c3 100644 --- a/target-lm32/cpu-qom.h +++ b/target-lm32/cpu-qom.h @@ -79,5 +79,7 @@ void lm32_cpu_do_interrupt(CPUState *cpu); void lm32_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr lm32_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int lm32_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int lm32_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif diff --git a/target-lm32/cpu.c b/target-lm32/cpu.c index ce55e4807d..962d553de0 100644 --- a/target-lm32/cpu.c +++ b/target-lm32/cpu.c @@ -87,10 +87,13 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data) cc->do_interrupt = lm32_cpu_do_interrupt; cc->dump_state = lm32_cpu_dump_state; cc->set_pc = lm32_cpu_set_pc; + cc->gdb_read_register = lm32_cpu_gdb_read_register; + cc->gdb_write_register = lm32_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = lm32_cpu_get_phys_page_debug; cc->vmsd = &vmstate_lm32_cpu; #endif + cc->gdb_num_core_regs = 32 + 7; } static const TypeInfo lm32_cpu_type_info = { diff --git a/target-lm32/gdbstub.c b/target-lm32/gdbstub.c new file mode 100644 index 0000000000..4979a98d74 --- /dev/null +++ b/target-lm32/gdbstub.c @@ -0,0 +1,92 @@ +/* + * LM32 gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" +#include "hw/lm32/lm32_pic.h" + +int lm32_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + LM32CPU *cpu = LM32_CPU(cs); + CPULM32State *env = &cpu->env; + + if (n < 32) { + return gdb_get_reg32(mem_buf, env->regs[n]); + } else { + switch (n) { + case 32: + return gdb_get_reg32(mem_buf, env->pc); + /* FIXME: put in right exception ID */ + case 33: + return gdb_get_reg32(mem_buf, 0); + case 34: + return gdb_get_reg32(mem_buf, env->eba); + case 35: + return gdb_get_reg32(mem_buf, env->deba); + case 36: + return gdb_get_reg32(mem_buf, env->ie); + case 37: + return gdb_get_reg32(mem_buf, lm32_pic_get_im(env->pic_state)); + case 38: + return gdb_get_reg32(mem_buf, lm32_pic_get_ip(env->pic_state)); + } + } + return 0; +} + +int lm32_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + LM32CPU *cpu = LM32_CPU(cs); + CPUClass *cc = CPU_GET_CLASS(cs); + CPULM32State *env = &cpu->env; + uint32_t tmp; + + if (n > cc->gdb_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; +} diff --git a/target-m68k/Makefile.objs b/target-m68k/Makefile.objs index 2e2b85044d..02cf616a78 100644 --- a/target-m68k/Makefile.objs +++ b/target-m68k/Makefile.objs @@ -1,2 +1,3 @@ obj-y += m68k-semi.o obj-y += translate.o op_helper.o helper.o cpu.o +obj-y += gdbstub.o diff --git a/target-m68k/cpu-qom.h b/target-m68k/cpu-qom.h index 7115707e91..7f388eda68 100644 --- a/target-m68k/cpu-qom.h +++ b/target-m68k/cpu-qom.h @@ -74,5 +74,7 @@ void m68k_cpu_do_interrupt(CPUState *cpu); void m68k_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int m68k_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c index 988f476257..c0bcb0dbce 100644 --- a/target-m68k/cpu.c +++ b/target-m68k/cpu.c @@ -190,10 +190,14 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) cc->do_interrupt = m68k_cpu_do_interrupt; cc->dump_state = m68k_cpu_dump_state; cc->set_pc = m68k_cpu_set_pc; + cc->gdb_read_register = m68k_cpu_gdb_read_register; + cc->gdb_write_register = m68k_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = m68k_cpu_get_phys_page_debug; #endif dc->vmsd = &vmstate_m68k_cpu; + cc->gdb_num_core_regs = 18; + cc->gdb_core_xml_file = "cf-core.xml"; } static void register_cpu_type(const M68kCPUInfo *info) diff --git a/target-m68k/gdbstub.c b/target-m68k/gdbstub.c new file mode 100644 index 0000000000..ae8179c016 --- /dev/null +++ b/target-m68k/gdbstub.c @@ -0,0 +1,75 @@ +/* + * m68k gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int m68k_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + + if (n < 8) { + /* D0-D7 */ + return gdb_get_reg32(mem_buf, env->dregs[n]); + } else if (n < 16) { + /* A0-A7 */ + return gdb_get_reg32(mem_buf, env->aregs[n - 8]); + } else { + switch (n) { + case 16: + return gdb_get_reg32(mem_buf, env->sr); + case 17: + return gdb_get_reg32(mem_buf, env->pc); + } + } + /* FP registers not included here because they vary between + ColdFire and m68k. Use XML bits for these. */ + return 0; +} + +int m68k_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + uint32_t tmp; + + tmp = ldl_p(mem_buf); + + if (n < 8) { + /* D0-D7 */ + env->dregs[n] = tmp; + } else if (n < 16) { + /* 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; +} diff --git a/target-microblaze/Makefile.objs b/target-microblaze/Makefile.objs index 985330eac5..f3d7b44c89 100644 --- a/target-microblaze/Makefile.objs +++ b/target-microblaze/Makefile.objs @@ -1,2 +1,3 @@ obj-y += translate.o op_helper.o helper.o cpu.o +obj-y += gdbstub.o obj-$(CONFIG_SOFTMMU) += mmu.o diff --git a/target-microblaze/cpu-qom.h b/target-microblaze/cpu-qom.h index 1318a36676..35a12b42a5 100644 --- a/target-microblaze/cpu-qom.h +++ b/target-microblaze/cpu-qom.h @@ -75,5 +75,7 @@ void mb_cpu_do_interrupt(CPUState *cs); void mb_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr mb_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int mb_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int mb_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif diff --git a/target-microblaze/cpu.c b/target-microblaze/cpu.c index 9f10c8c778..c75d1bd642 100644 --- a/target-microblaze/cpu.c +++ b/target-microblaze/cpu.c @@ -141,12 +141,15 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) cc->do_interrupt = mb_cpu_do_interrupt; cc->dump_state = mb_cpu_dump_state; cc->set_pc = mb_cpu_set_pc; + cc->gdb_read_register = mb_cpu_gdb_read_register; + cc->gdb_write_register = mb_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->do_unassigned_access = mb_cpu_unassigned_access; cc->get_phys_page_debug = mb_cpu_get_phys_page_debug; #endif dc->vmsd = &vmstate_mb_cpu; dc->props = mb_properties; + cc->gdb_num_core_regs = 32 + 5; } static const TypeInfo mb_cpu_type_info = { diff --git a/target-microblaze/gdbstub.c b/target-microblaze/gdbstub.c new file mode 100644 index 0000000000..a70e2ee3cb --- /dev/null +++ b/target-microblaze/gdbstub.c @@ -0,0 +1,56 @@ +/* + * MicroBlaze gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int mb_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + CPUMBState *env = &cpu->env; + + if (n < 32) { + return gdb_get_reg32(mem_buf, env->regs[n]); + } else { + return gdb_get_reg32(mem_buf, env->sregs[n - 32]); + } + return 0; +} + +int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + CPUClass *cc = CPU_GET_CLASS(cs); + CPUMBState *env = &cpu->env; + uint32_t tmp; + + if (n > cc->gdb_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; +} diff --git a/target-mips/Makefile.objs b/target-mips/Makefile.objs index 119c816518..0277d56e82 100644 --- a/target-mips/Makefile.objs +++ b/target-mips/Makefile.objs @@ -1,2 +1,3 @@ obj-y += translate.o dsp_helper.o op_helper.o lmi_helper.o helper.o cpu.o +obj-y += gdbstub.o obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target-mips/cpu-qom.h b/target-mips/cpu-qom.h index 7c8e616392..8877f813f7 100644 --- a/target-mips/cpu-qom.h +++ b/target-mips/cpu-qom.h @@ -78,5 +78,7 @@ void mips_cpu_do_interrupt(CPUState *cpu); void mips_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr mips_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int mips_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif diff --git a/target-mips/cpu.c b/target-mips/cpu.c index 4834c86d02..f81f9e9409 100644 --- a/target-mips/cpu.c +++ b/target-mips/cpu.c @@ -100,10 +100,14 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) cc->dump_state = mips_cpu_dump_state; cc->set_pc = mips_cpu_set_pc; cc->synchronize_from_tb = mips_cpu_synchronize_from_tb; + cc->gdb_read_register = mips_cpu_gdb_read_register; + cc->gdb_write_register = mips_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->do_unassigned_access = mips_cpu_unassigned_access; cc->get_phys_page_debug = mips_cpu_get_phys_page_debug; #endif + + cc->gdb_num_core_regs = 73; } static const TypeInfo mips_cpu_type_info = { diff --git a/target-mips/gdbstub.c b/target-mips/gdbstub.c new file mode 100644 index 0000000000..5b72d58a44 --- /dev/null +++ b/target-mips/gdbstub.c @@ -0,0 +1,155 @@ +/* + * MIPS gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; + + if (n < 32) { + return gdb_get_regl(mem_buf, env->active_tc.gpr[n]); + } + if (env->CP0_Config1 & (1 << CP0C1_FP)) { + if (n >= 38 && n < 70) { + if (env->CP0_Status & (1 << CP0St_FR)) { + return gdb_get_regl(mem_buf, + env->active_fpu.fpr[n - 38].d); + } else { + return gdb_get_regl(mem_buf, + env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX]); + } + } + switch (n) { + case 70: + return gdb_get_regl(mem_buf, (int32_t)env->active_fpu.fcr31); + case 71: + return gdb_get_regl(mem_buf, (int32_t)env->active_fpu.fcr0); + } + } + switch (n) { + case 32: + return gdb_get_regl(mem_buf, (int32_t)env->CP0_Status); + case 33: + return gdb_get_regl(mem_buf, env->active_tc.LO[0]); + case 34: + return gdb_get_regl(mem_buf, env->active_tc.HI[0]); + case 35: + return gdb_get_regl(mem_buf, env->CP0_BadVAddr); + case 36: + return gdb_get_regl(mem_buf, (int32_t)env->CP0_Cause); + case 37: + return gdb_get_regl(mem_buf, env->active_tc.PC | + !!(env->hflags & MIPS_HFLAG_M16)); + case 72: + return gdb_get_regl(mem_buf, 0); /* fp */ + case 89: + return gdb_get_regl(mem_buf, (int32_t)env->CP0_PRid); + } + if (n >= 73 && n <= 88) { + /* 16 embedded regs. */ + return gdb_get_regl(mem_buf, 0); + } + + return 0; +} + +/* convert MIPS rounding mode in FCR31 to IEEE library */ +static unsigned int ieee_rm[] = { + float_round_nearest_even, + float_round_to_zero, + float_round_up, + float_round_down +}; +#define RESTORE_ROUNDING_MODE \ + set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], \ + &env->active_fpu.fp_status) + +int mips_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; + target_ulong tmp; + + tmp = ldtul_p(mem_buf); + + 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; + 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 & ~(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) { + return 0; + } + /* Other registers are readonly. Ignore writes. */ + break; + } + + return sizeof(target_ulong); +} diff --git a/target-openrisc/Makefile.objs b/target-openrisc/Makefile.objs index 44dc5399df..397d01650e 100644 --- a/target-openrisc/Makefile.objs +++ b/target-openrisc/Makefile.objs @@ -2,3 +2,4 @@ obj-$(CONFIG_SOFTMMU) += machine.o obj-y += cpu.o exception.o interrupt.o mmu.o translate.o obj-y += exception_helper.o fpu_helper.o int_helper.o \ interrupt_helper.o mmu_helper.o sys_helper.o +obj-y += gdbstub.o diff --git a/target-openrisc/cpu.c b/target-openrisc/cpu.c index 7718820ecc..aa269fb7a6 100644 --- a/target-openrisc/cpu.c +++ b/target-openrisc/cpu.c @@ -155,10 +155,13 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) cc->do_interrupt = openrisc_cpu_do_interrupt; cc->dump_state = openrisc_cpu_dump_state; cc->set_pc = openrisc_cpu_set_pc; + cc->gdb_read_register = openrisc_cpu_gdb_read_register; + cc->gdb_write_register = openrisc_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = openrisc_cpu_get_phys_page_debug; dc->vmsd = &vmstate_openrisc_cpu; #endif + cc->gdb_num_core_regs = 32 + 3; } static void cpu_register(const OpenRISCCPUInfo *info) diff --git a/target-openrisc/cpu.h b/target-openrisc/cpu.h index 3ddb7674c7..8fd0bc0bf0 100644 --- a/target-openrisc/cpu.h +++ b/target-openrisc/cpu.h @@ -350,6 +350,8 @@ void openrisc_cpu_do_interrupt(CPUState *cpu); void openrisc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int openrisc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int openrisc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void openrisc_translate_init(void); int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env, target_ulong address, diff --git a/target-openrisc/gdbstub.c b/target-openrisc/gdbstub.c new file mode 100644 index 0000000000..18bcc46167 --- /dev/null +++ b/target-openrisc/gdbstub.c @@ -0,0 +1,83 @@ +/* + * OpenRISC gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int openrisc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + CPUOpenRISCState *env = &cpu->env; + + if (n < 32) { + return gdb_get_reg32(mem_buf, env->gpr[n]); + } else { + switch (n) { + case 32: /* PPC */ + return gdb_get_reg32(mem_buf, env->ppc); + + case 33: /* NPC */ + return gdb_get_reg32(mem_buf, env->npc); + + case 34: /* SR */ + return gdb_get_reg32(mem_buf, env->sr); + + default: + break; + } + } + return 0; +} + +int openrisc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + CPUClass *cc = CPU_GET_CLASS(cs); + CPUOpenRISCState *env = &cpu->env; + uint32_t tmp; + + if (n > cc->gdb_num_core_regs) { + return 0; + } + + tmp = ldl_p(mem_buf); + + if (n < 32) { + env->gpr[n] = tmp; + } else { + switch (n) { + case 32: /* PPC */ + env->ppc = tmp; + break; + + case 33: /* NPC */ + env->npc = tmp; + break; + + case 34: /* SR */ + env->sr = tmp; + break; + + default: + break; + } + } + return 4; +} diff --git a/target-ppc/Makefile.objs b/target-ppc/Makefile.objs index 6e78cb3624..f72e3993f7 100644 --- a/target-ppc/Makefile.objs +++ b/target-ppc/Makefile.objs @@ -13,3 +13,4 @@ obj-y += timebase_helper.o obj-y += misc_helper.o obj-y += mem_helper.o obj-$(CONFIG_USER_ONLY) += user_only_helper.o +obj-y += gdbstub.o diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index 3341c5151d..fc0d737880 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -106,5 +106,7 @@ void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, void ppc_cpu_dump_statistics(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int ppc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif diff --git a/target-ppc/gdbstub.c b/target-ppc/gdbstub.c new file mode 100644 index 0000000000..1c910902ea --- /dev/null +++ b/target-ppc/gdbstub.c @@ -0,0 +1,131 @@ +/* + * PowerPC gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +/* 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. + */ + +int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + if (n < 32) { + /* gprs */ + return gdb_get_regl(mem_buf, 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: + return gdb_get_regl(mem_buf, env->nip); + case 65: + return gdb_get_regl(mem_buf, env->msr); + case 66: + { + uint32_t cr = 0; + int i; + for (i = 0; i < 8; i++) { + cr |= env->crf[i] << (32 - ((i + 1) * 4)); + } + return gdb_get_reg32(mem_buf, cr); + } + case 67: + return gdb_get_regl(mem_buf, env->lr); + case 68: + return gdb_get_regl(mem_buf, env->ctr); + case 69: + return gdb_get_regl(mem_buf, env->xer); + case 70: + { + if (gdb_has_xml) { + return 0; + } + return gdb_get_reg32(mem_buf, env->fpscr); + } + } + } + return 0; +} + +int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + 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; + } + store_fpscr(env, ldtul_p(mem_buf), 0xffffffff); + return sizeof(target_ulong); + } + } + return 0; +} diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 0b0844f467..8215946e39 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8458,9 +8458,18 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) cc->dump_state = ppc_cpu_dump_state; cc->dump_statistics = ppc_cpu_dump_statistics; cc->set_pc = ppc_cpu_set_pc; + cc->gdb_read_register = ppc_cpu_gdb_read_register; + cc->gdb_write_register = ppc_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = ppc_cpu_get_phys_page_debug; #endif + + cc->gdb_num_core_regs = 71; +#if defined(TARGET_PPC64) + cc->gdb_core_xml_file = "power64-core.xml"; +#else + cc->gdb_core_xml_file = "power-core.xml"; +#endif } static const TypeInfo ppc_cpu_type_info = { diff --git a/target-s390x/Makefile.objs b/target-s390x/Makefile.objs index 4e634173a4..ab938e7ad8 100644 --- a/target-s390x/Makefile.objs +++ b/target-s390x/Makefile.objs @@ -1,4 +1,5 @@ obj-y += translate.o helper.o cpu.o interrupt.o obj-y += int_helper.o fpu_helper.o cc_helper.o mem_helper.o misc_helper.o +obj-y += gdbstub.o obj-$(CONFIG_SOFTMMU) += ioinst.o obj-$(CONFIG_KVM) += kvm.o diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h index a4fe8fb5fc..0d63b1cf20 100644 --- a/target-s390x/cpu-qom.h +++ b/target-s390x/cpu-qom.h @@ -75,5 +75,7 @@ void s390_cpu_do_interrupt(CPUState *cpu); void s390_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index cb89d1a46b..1d16da3787 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -173,10 +173,13 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->do_interrupt = s390_cpu_do_interrupt; cc->dump_state = s390_cpu_dump_state; cc->set_pc = s390_cpu_set_pc; + cc->gdb_read_register = s390_cpu_gdb_read_register; + cc->gdb_write_register = s390_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = s390_cpu_get_phys_page_debug; #endif dc->vmsd = &vmstate_s390_cpu; + cc->gdb_num_core_regs = S390_NUM_REGS; } static const TypeInfo s390_cpu_type_info = { diff --git a/target-s390x/gdbstub.c b/target-s390x/gdbstub.c new file mode 100644 index 0000000000..a129742e2f --- /dev/null +++ b/target-s390x/gdbstub.c @@ -0,0 +1,88 @@ +/* + * s390x gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" +#include "qemu/bitops.h" + +int s390_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + uint64_t val; + int cc_op; + + switch (n) { + case S390_PSWM_REGNUM: + cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr); + val = deposit64(env->psw.mask, 44, 2, cc_op); + return gdb_get_regl(mem_buf, val); + case S390_PSWA_REGNUM: + return gdb_get_regl(mem_buf, env->psw.addr); + case S390_R0_REGNUM ... S390_R15_REGNUM: + return gdb_get_regl(mem_buf, env->regs[n-S390_R0_REGNUM]); + case S390_A0_REGNUM ... S390_A15_REGNUM: + return gdb_get_reg32(mem_buf, env->aregs[n-S390_A0_REGNUM]); + case S390_FPC_REGNUM: + return gdb_get_reg32(mem_buf, env->fpc); + case S390_F0_REGNUM ... S390_F15_REGNUM: + return gdb_get_reg64(mem_buf, env->fregs[n-S390_F0_REGNUM].ll); + } + + return 0; +} + +int s390_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + 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; + env->cc_op = extract64(tmpl, 44, 2); + 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: + env->fregs[n-S390_F0_REGNUM].ll = tmpl; + break; + default: + return 0; + } + return r; +} diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 60e94f8ee8..85f01125de 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -345,12 +345,10 @@ void *kvm_arch_ram_alloc(ram_addr_t size) int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; - if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || - cpu_memory_rw_debug(env, bp->pc, (uint8_t *)diag_501, 4, 1)) { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)diag_501, 4, 1)) { return -EINVAL; } return 0; @@ -358,16 +356,14 @@ int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; uint8_t t[4]; static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; - if (cpu_memory_rw_debug(env, bp->pc, t, 4, 0)) { + if (cpu_memory_rw_debug(cs, bp->pc, t, 4, 0)) { return -EINVAL; } else if (memcmp(t, diag_501, 4)) { return -EINVAL; - } else if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) { + } else if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) { return -EINVAL; } diff --git a/target-sh4/Makefile.objs b/target-sh4/Makefile.objs index cb448a840f..a285358adf 100644 --- a/target-sh4/Makefile.objs +++ b/target-sh4/Makefile.objs @@ -1 +1,2 @@ obj-y += translate.o op_helper.o helper.o cpu.o +obj-y += gdbstub.o diff --git a/target-sh4/cpu-qom.h b/target-sh4/cpu-qom.h index 7c9160bab8..c04e78631b 100644 --- a/target-sh4/cpu-qom.h +++ b/target-sh4/cpu-qom.h @@ -87,5 +87,7 @@ void superh_cpu_do_interrupt(CPUState *cpu); void superh_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int superh_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int superh_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c index 51a77576fb..bda3c5112c 100644 --- a/target-sh4/cpu.c +++ b/target-sh4/cpu.c @@ -286,10 +286,13 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data) cc->dump_state = superh_cpu_dump_state; cc->set_pc = superh_cpu_set_pc; cc->synchronize_from_tb = superh_cpu_synchronize_from_tb; + cc->gdb_read_register = superh_cpu_gdb_read_register; + cc->gdb_write_register = superh_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = superh_cpu_get_phys_page_debug; #endif dc->vmsd = &vmstate_sh_cpu; + cc->gdb_num_core_regs = 59; } static const TypeInfo superh_cpu_type_info = { diff --git a/target-sh4/gdbstub.c b/target-sh4/gdbstub.c new file mode 100644 index 0000000000..df4fa2af76 --- /dev/null +++ b/target-sh4/gdbstub.c @@ -0,0 +1,146 @@ +/* + * SuperH gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +/* Hint: Use "set architecture sh4" in GDB to see fpu registers */ +/* FIXME: We should use XML for this. */ + +int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + SuperHCPU *cpu = SUPERH_CPU(cs); + CPUSH4State *env = &cpu->env; + + switch (n) { + case 0 ... 7: + if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) { + return gdb_get_regl(mem_buf, env->gregs[n + 16]); + } else { + return gdb_get_regl(mem_buf, env->gregs[n]); + } + case 8 ... 15: + return gdb_get_regl(mem_buf, env->gregs[n]); + case 16: + return gdb_get_regl(mem_buf, env->pc); + case 17: + return gdb_get_regl(mem_buf, env->pr); + case 18: + return gdb_get_regl(mem_buf, env->gbr); + case 19: + return gdb_get_regl(mem_buf, env->vbr); + case 20: + return gdb_get_regl(mem_buf, env->mach); + case 21: + return gdb_get_regl(mem_buf, env->macl); + case 22: + return gdb_get_regl(mem_buf, env->sr); + case 23: + return gdb_get_regl(mem_buf, env->fpul); + case 24: + return gdb_get_regl(mem_buf, env->fpscr); + case 25 ... 40: + if (env->fpscr & FPSCR_FR) { + stfl_p(mem_buf, env->fregs[n - 9]); + } else { + stfl_p(mem_buf, env->fregs[n - 25]); + } + return 4; + case 41: + return gdb_get_regl(mem_buf, env->ssr); + case 42: + return gdb_get_regl(mem_buf, env->spc); + case 43 ... 50: + return gdb_get_regl(mem_buf, env->gregs[n - 43]); + case 51 ... 58: + return gdb_get_regl(mem_buf, env->gregs[n - (51 - 16)]); + } + + return 0; +} + +int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + SuperHCPU *cpu = SUPERH_CPU(cs); + CPUSH4State *env = &cpu->env; + + switch (n) { + case 0 ... 7: + if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) { + env->gregs[n + 16] = ldl_p(mem_buf); + } else { + env->gregs[n] = ldl_p(mem_buf); + } + break; + case 8 ... 15: + env->gregs[n] = ldl_p(mem_buf); + break; + case 16: + env->pc = ldl_p(mem_buf); + break; + case 17: + env->pr = ldl_p(mem_buf); + break; + case 18: + env->gbr = ldl_p(mem_buf); + break; + case 19: + env->vbr = ldl_p(mem_buf); + break; + case 20: + env->mach = ldl_p(mem_buf); + break; + case 21: + env->macl = ldl_p(mem_buf); + break; + case 22: + env->sr = ldl_p(mem_buf); + break; + case 23: + env->fpul = ldl_p(mem_buf); + break; + case 24: + env->fpscr = ldl_p(mem_buf); + break; + case 25 ... 40: + if (env->fpscr & FPSCR_FR) { + env->fregs[n - 9] = ldfl_p(mem_buf); + } else { + env->fregs[n - 25] = ldfl_p(mem_buf); + } + break; + case 41: + env->ssr = ldl_p(mem_buf); + break; + case 42: + env->spc = ldl_p(mem_buf); + break; + case 43 ... 50: + env->gregs[n - 43] = ldl_p(mem_buf); + break; + case 51 ... 58: + env->gregs[n - (51 - 16)] = ldl_p(mem_buf); + break; + default: + return 0; + } + + return 4; +} diff --git a/target-sparc/Makefile.objs b/target-sparc/Makefile.objs index 9fc42ea9b0..1cd81cccc3 100644 --- a/target-sparc/Makefile.objs +++ b/target-sparc/Makefile.objs @@ -4,3 +4,4 @@ obj-y += fop_helper.o cc_helper.o win_helper.o mmu_helper.o ldst_helper.o obj-$(TARGET_SPARC) += int32_helper.o obj-$(TARGET_SPARC64) += int64_helper.o obj-$(TARGET_SPARC64) += vis_helper.o +obj-y += gdbstub.o diff --git a/target-sparc/cpu-qom.h b/target-sparc/cpu-qom.h index 39d975b5fc..8e3e0de277 100644 --- a/target-sparc/cpu-qom.h +++ b/target-sparc/cpu-qom.h @@ -79,5 +79,7 @@ void sparc_cpu_do_interrupt(CPUState *cpu); void sparc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int sparc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int sparc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c index d1d03396ef..c7b4a90663 100644 --- a/target-sparc/cpu.c +++ b/target-sparc/cpu.c @@ -787,10 +787,18 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) #endif cc->set_pc = sparc_cpu_set_pc; cc->synchronize_from_tb = sparc_cpu_synchronize_from_tb; + cc->gdb_read_register = sparc_cpu_gdb_read_register; + cc->gdb_write_register = sparc_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->do_unassigned_access = sparc_cpu_unassigned_access; cc->get_phys_page_debug = sparc_cpu_get_phys_page_debug; #endif + +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + cc->gdb_num_core_regs = 86; +#else + cc->gdb_num_core_regs = 72; +#endif } static const TypeInfo sparc_cpu_type_info = { diff --git a/target-sparc/gdbstub.c b/target-sparc/gdbstub.c new file mode 100644 index 0000000000..3de3242b29 --- /dev/null +++ b/target-sparc/gdbstub.c @@ -0,0 +1,208 @@ +/* + * SPARC gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +#ifdef TARGET_ABI32 +#define gdb_get_rega(buf, val) gdb_get_reg32(buf, val) +#else +#define gdb_get_rega(buf, val) gdb_get_regl(buf, val) +#endif + +int sparc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + SPARCCPU *cpu = SPARC_CPU(cs); + CPUSPARCState *env = &cpu->env; + + if (n < 8) { + /* g0..g7 */ + return gdb_get_rega(mem_buf, env->gregs[n]); + } + if (n < 32) { + /* register window */ + return gdb_get_rega(mem_buf, env->regwptr[n - 8]); + } +#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64) + if (n < 64) { + /* fprs */ + if (n & 1) { + return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.lower); + } else { + return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.upper); + } + } + /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ + switch (n) { + case 64: + return gdb_get_rega(mem_buf, env->y); + case 65: + return gdb_get_rega(mem_buf, cpu_get_psr(env)); + case 66: + return gdb_get_rega(mem_buf, env->wim); + case 67: + return gdb_get_rega(mem_buf, env->tbr); + case 68: + return gdb_get_rega(mem_buf, env->pc); + case 69: + return gdb_get_rega(mem_buf, env->npc); + case 70: + return gdb_get_rega(mem_buf, env->fsr); + case 71: + return gdb_get_rega(mem_buf, 0); /* csr */ + default: + return gdb_get_rega(mem_buf, 0); + } +#else + if (n < 64) { + /* f0-f31 */ + if (n & 1) { + return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.lower); + } else { + return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.upper); + } + } + if (n < 80) { + /* f32-f62 (double width, even numbers only) */ + return gdb_get_reg64(mem_buf, env->fpr[(n - 32) / 2].ll); + } + switch (n) { + case 80: + return gdb_get_regl(mem_buf, env->pc); + case 81: + return gdb_get_regl(mem_buf, env->npc); + case 82: + return gdb_get_regl(mem_buf, (cpu_get_ccr(env) << 32) | + ((env->asi & 0xff) << 24) | + ((env->pstate & 0xfff) << 8) | + cpu_get_cwp64(env)); + case 83: + return gdb_get_regl(mem_buf, env->fsr); + case 84: + return gdb_get_regl(mem_buf, env->fprs); + case 85: + return gdb_get_regl(mem_buf, env->y); + } +#endif + return 0; +} + +int sparc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + SPARCCPU *cpu = SPARC_CPU(cs); + CPUSPARCState *env = &cpu->env; +#if defined(TARGET_ABI32) + abi_ulong tmp; + + tmp = ldl_p(mem_buf); +#else + target_ulong tmp; + + tmp = ldtul_p(mem_buf); +#endif + + if (n < 8) { + /* g0..g7 */ + env->gregs[n] = tmp; + } else if (n < 32) { + /* register window */ + env->regwptr[n - 8] = tmp; + } +#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64) + else if (n < 64) { + /* fprs */ + /* f0-f31 */ + if (n & 1) { + env->fpr[(n - 32) / 2].l.lower = tmp; + } else { + env->fpr[(n - 32) / 2].l.upper = tmp; + } + } else { + /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ + switch (n) { + case 64: + env->y = 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 69: + env->npc = tmp; + break; + case 70: + env->fsr = tmp; + break; + default: + return 0; + } + } + return 4; +#else + else if (n < 64) { + /* f0-f31 */ + tmp = ldl_p(mem_buf); + if (n & 1) { + env->fpr[(n - 32) / 2].l.lower = tmp; + } else { + env->fpr[(n - 32) / 2].l.upper = tmp; + } + return 4; + } else if (n < 80) { + /* f32-f62 (double width, even numbers only) */ + env->fpr[(n - 32) / 2].ll = tmp; + } else { + switch (n) { + case 80: + env->pc = tmp; + break; + case 81: + env->npc = tmp; + break; + case 82: + cpu_put_ccr(env, tmp >> 32); + env->asi = (tmp >> 24) & 0xff; + env->pstate = (tmp >> 8) & 0xfff; + cpu_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; + } + } + return 8; +#endif +} diff --git a/target-xtensa/Makefile.objs b/target-xtensa/Makefile.objs index 644b7f99bb..5c150a870f 100644 --- a/target-xtensa/Makefile.objs +++ b/target-xtensa/Makefile.objs @@ -3,3 +3,4 @@ obj-y += core-dc232b.o obj-y += core-dc233c.o obj-y += core-fsf.o obj-y += translate.o op_helper.o helper.o cpu.o +obj-y += gdbstub.o diff --git a/target-xtensa/cpu-qom.h b/target-xtensa/cpu-qom.h index b9896f2647..c6cc2d91f4 100644 --- a/target-xtensa/cpu-qom.h +++ b/target-xtensa/cpu-qom.h @@ -45,6 +45,7 @@ * XtensaCPUClass: * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. + * @config: The CPU core configuration. * * An Xtensa CPU model. */ @@ -55,6 +56,8 @@ typedef struct XtensaCPUClass { DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); + + const XtensaConfig *config; } XtensaCPUClass; /** @@ -84,5 +87,7 @@ void xtensa_cpu_do_interrupt(CPUState *cpu); void xtensa_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int xtensa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int xtensa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c index d2bcfc69a2..e966aa0a79 100644 --- a/target-xtensa/cpu.c +++ b/target-xtensa/cpu.c @@ -64,10 +64,32 @@ static void xtensa_cpu_reset(CPUState *s) reset_mmu(env); } +static ObjectClass *xtensa_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + if (cpu_model == NULL) { + return NULL; + } + + typename = g_strdup_printf("%s-" TYPE_XTENSA_CPU, cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (oc == NULL || !object_class_dynamic_cast(oc, TYPE_XTENSA_CPU) || + object_class_is_abstract(oc)) { + return NULL; + } + return oc; +} + static void xtensa_cpu_realizefn(DeviceState *dev, Error **errp) { + CPUState *cs = CPU(dev); XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(dev); + cs->gdb_num_regs = xcc->config->gdb_regmap.num_regs; + xcc->parent_realize(dev, errp); } @@ -75,10 +97,12 @@ static void xtensa_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); XtensaCPU *cpu = XTENSA_CPU(obj); + XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(obj); CPUXtensaState *env = &cpu->env; static bool tcg_inited; cs->env_ptr = env; + env->config = xcc->config; cpu_exec_init(env); if (tcg_enabled() && !tcg_inited) { @@ -105,9 +129,12 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) xcc->parent_reset = cc->reset; cc->reset = xtensa_cpu_reset; + cc->class_by_name = xtensa_cpu_class_by_name; cc->do_interrupt = xtensa_cpu_do_interrupt; cc->dump_state = xtensa_cpu_dump_state; cc->set_pc = xtensa_cpu_set_pc; + cc->gdb_read_register = xtensa_cpu_gdb_read_register; + cc->gdb_write_register = xtensa_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = xtensa_cpu_get_phys_page_debug; #endif @@ -119,7 +146,7 @@ static const TypeInfo xtensa_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(XtensaCPU), .instance_init = xtensa_cpu_initfn, - .abstract = false, + .abstract = true, .class_size = sizeof(XtensaCPUClass), .class_init = xtensa_cpu_class_init, }; diff --git a/target-xtensa/gdbstub.c b/target-xtensa/gdbstub.c new file mode 100644 index 0000000000..9e13b20c46 --- /dev/null +++ b/target-xtensa/gdbstub.c @@ -0,0 +1,109 @@ +/* + * Xtensa gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = &cpu->env; + const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; + + if (n < 0 || n >= env->config->gdb_regmap.num_regs) { + return 0; + } + + switch (reg->type) { + case 9: /*pc*/ + return gdb_get_reg32(mem_buf, env->pc); + + case 1: /*ar*/ + xtensa_sync_phys_from_window(env); + return gdb_get_reg32(mem_buf, env->phys_regs[(reg->targno & 0xff) + % env->config->nareg]); + + case 2: /*SR*/ + return gdb_get_reg32(mem_buf, env->sregs[reg->targno & 0xff]); + + case 3: /*UR*/ + return gdb_get_reg32(mem_buf, env->uregs[reg->targno & 0xff]); + + case 4: /*f*/ + return gdb_get_reg32(mem_buf, float32_val(env->fregs[reg->targno + & 0x0f])); + + case 8: /*a*/ + return gdb_get_reg32(mem_buf, env->regs[reg->targno & 0x0f]); + + default: + qemu_log("%s from reg %d of unsupported type %d\n", + __func__, n, reg->type); + return 0; + } +} + +int xtensa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = &cpu->env; + uint32_t tmp; + const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; + + if (n < 0 || n >= env->config->gdb_regmap.num_regs) { + return 0; + } + + tmp = ldl_p(mem_buf); + + switch (reg->type) { + case 9: /*pc*/ + env->pc = tmp; + break; + + case 1: /*ar*/ + env->phys_regs[(reg->targno & 0xff) % env->config->nareg] = tmp; + xtensa_sync_window_from_phys(env); + break; + + case 2: /*SR*/ + env->sregs[reg->targno & 0xff] = tmp; + break; + + case 3: /*UR*/ + env->uregs[reg->targno & 0xff] = tmp; + break; + + case 4: /*f*/ + env->fregs[reg->targno & 0x0f] = make_float32(tmp); + break; + + case 8: /*a*/ + env->regs[reg->targno & 0x0f] = tmp; + break; + + default: + qemu_log("%s to reg %d of unsupported type %d\n", + __func__, n, reg->type); + return 0; + } + + return 4; +} diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c index de6cc3b7c5..a0f9993b2d 100644 --- a/target-xtensa/helper.c +++ b/target-xtensa/helper.c @@ -35,10 +35,35 @@ static struct XtensaConfigList *xtensa_cores; +static void xtensa_core_class_init(ObjectClass *oc, void *data) +{ + CPUClass *cc = CPU_CLASS(oc); + XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc); + const XtensaConfig *config = data; + + xcc->config = config; + + /* Use num_core_regs to see only non-privileged registers in an unmodified + * gdb. Use num_regs to see all registers. gdb modification is required + * for that: reset bit 0 in the 'flags' field of the registers definitions + * in the gdb/xtensa-config.c inside gdb source tree or inside gdb overlay. + */ + cc->gdb_num_core_regs = config->gdb_regmap.num_regs; +} + void xtensa_register_core(XtensaConfigList *node) { + TypeInfo type = { + .parent = TYPE_XTENSA_CPU, + .class_init = xtensa_core_class_init, + .class_data = (void *)node->config, + }; + node->next = xtensa_cores; xtensa_cores = node; + type.name = g_strdup_printf("%s-" TYPE_XTENSA_CPU, node->config->name); + type_register(&type); + g_free((gpointer)type.name); } static uint32_t check_hw_breakpoints(CPUXtensaState *env) @@ -72,24 +97,17 @@ void xtensa_breakpoint_handler(CPUXtensaState *env) XtensaCPU *cpu_xtensa_init(const char *cpu_model) { + ObjectClass *oc; XtensaCPU *cpu; CPUXtensaState *env; - const XtensaConfig *config = NULL; - XtensaConfigList *core = xtensa_cores; - for (; core; core = core->next) - if (strcmp(core->config->name, cpu_model) == 0) { - config = core->config; - break; - } - - if (config == NULL) { + oc = cpu_class_by_name(TYPE_XTENSA_CPU, cpu_model); + if (oc == NULL) { return NULL; } - cpu = XTENSA_CPU(object_new(TYPE_XTENSA_CPU)); + cpu = XTENSA_CPU(object_new(object_class_get_name(oc))); env = &cpu->env; - env->config = config; xtensa_irq_init(env);