diff options
Diffstat (limited to 'target/ppc/gdbstub.c')
-rw-r--r-- | target/ppc/gdbstub.c | 628 |
1 files changed, 628 insertions, 0 deletions
diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c new file mode 100644 index 000000000..105c2f7dd --- /dev/null +++ b/target/ppc/gdbstub.c @@ -0,0 +1,628 @@ +/* + * 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.1 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 <http://www.gnu.org/licenses/>. + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/gdbstub.h" +#include "internal.h" + +static int ppc_gdb_register_len_apple(int n) +{ + switch (n) { + case 0 ... 31: + /* gprs */ + return 8; + case 32 ... 63: + /* fprs */ + return 8; + case 64 ... 95: + return 16; + case 64 + 32: /* nip */ + case 65 + 32: /* msr */ + case 67 + 32: /* lr */ + case 68 + 32: /* ctr */ + case 70 + 32: /* fpscr */ + return 8; + case 66 + 32: /* cr */ + case 69 + 32: /* xer */ + return 4; + default: + return 0; + } +} + +static int ppc_gdb_register_len(int n) +{ + switch (n) { + case 0 ... 31: + /* gprs */ + return sizeof(target_ulong); + case 32 ... 63: + /* fprs */ + if (gdb_has_xml) { + return 0; + } + return 8; + case 66: + /* cr */ + case 69: + /* xer */ + return 4; + case 64: + /* nip */ + case 65: + /* msr */ + case 67: + /* lr */ + case 68: + /* ctr */ + return sizeof(target_ulong); + case 70: + /* fpscr */ + if (gdb_has_xml) { + return 0; + } + return sizeof(target_ulong); + default: + return 0; + } +} + +/* + * We need to present the registers to gdb in the "current" memory + * ordering. For user-only mode we get this for free; + * TARGET_WORDS_BIGENDIAN is set to the proper ordering for the + * binary, and cannot be changed. For system mode, + * TARGET_WORDS_BIGENDIAN is always set, and we must check the current + * mode of the chip to see if we're running in little-endian. + */ +void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len) +{ +#ifndef CONFIG_USER_ONLY + if (!msr_le) { + /* do nothing */ + } else if (len == 4) { + bswap32s((uint32_t *)mem_buf); + } else if (len == 8) { + bswap64s((uint64_t *)mem_buf); + } else if (len == 16) { + bswap128s((Int128 *)mem_buf); + } else { + g_assert_not_reached(); + } +#endif +} + +/* + * 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, GByteArray *buf, int n) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + uint8_t *mem_buf; + int r = ppc_gdb_register_len(n); + + if (!r) { + return r; + } + + if (n < 32) { + /* gprs */ + gdb_get_regl(buf, env->gpr[n]); + } else if (n < 64) { + /* fprs */ + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n - 32)); + } else { + switch (n) { + case 64: + gdb_get_regl(buf, env->nip); + break; + case 65: + gdb_get_regl(buf, env->msr); + break; + case 66: + { + uint32_t cr = 0; + int i; + for (i = 0; i < 8; i++) { + cr |= env->crf[i] << (32 - ((i + 1) * 4)); + } + gdb_get_reg32(buf, cr); + break; + } + case 67: + gdb_get_regl(buf, env->lr); + break; + case 68: + gdb_get_regl(buf, env->ctr); + break; + case 69: + gdb_get_reg32(buf, cpu_read_xer(env)); + break; + case 70: + gdb_get_reg32(buf, env->fpscr); + break; + } + } + mem_buf = buf->data + buf->len - r; + ppc_maybe_bswap_register(env, mem_buf, r); + return r; +} + +int ppc_cpu_gdb_read_register_apple(CPUState *cs, GByteArray *buf, int n) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + uint8_t *mem_buf; + int r = ppc_gdb_register_len_apple(n); + + if (!r) { + return r; + } + + if (n < 32) { + /* gprs */ + gdb_get_reg64(buf, env->gpr[n]); + } else if (n < 64) { + /* fprs */ + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n - 32)); + } else if (n < 96) { + /* Altivec */ + gdb_get_reg64(buf, n - 64); + gdb_get_reg64(buf, 0); + } else { + switch (n) { + case 64 + 32: + gdb_get_reg64(buf, env->nip); + break; + case 65 + 32: + gdb_get_reg64(buf, env->msr); + break; + case 66 + 32: + { + uint32_t cr = 0; + int i; + for (i = 0; i < 8; i++) { + cr |= env->crf[i] << (32 - ((i + 1) * 4)); + } + gdb_get_reg32(buf, cr); + break; + } + case 67 + 32: + gdb_get_reg64(buf, env->lr); + break; + case 68 + 32: + gdb_get_reg64(buf, env->ctr); + break; + case 69 + 32: + gdb_get_reg32(buf, cpu_read_xer(env)); + break; + case 70 + 32: + gdb_get_reg64(buf, env->fpscr); + break; + } + } + mem_buf = buf->data + buf->len - r; + ppc_maybe_bswap_register(env, mem_buf, r); + return r; +} + +int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int r = ppc_gdb_register_len(n); + + if (!r) { + return r; + } + ppc_maybe_bswap_register(env, mem_buf, r); + if (n < 32) { + /* gprs */ + env->gpr[n] = ldtul_p(mem_buf); + } else if (n < 64) { + /* fprs */ + *cpu_fpr_ptr(env, n - 32) = ldq_p(mem_buf); + } else { + switch (n) { + case 64: + env->nip = ldtul_p(mem_buf); + break; + case 65: + ppc_store_msr(env, ldtul_p(mem_buf)); + break; + 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; + } + break; + } + case 67: + env->lr = ldtul_p(mem_buf); + break; + case 68: + env->ctr = ldtul_p(mem_buf); + break; + case 69: + cpu_write_xer(env, ldl_p(mem_buf)); + break; + case 70: + /* fpscr */ + ppc_store_fpscr(env, ldtul_p(mem_buf)); + break; + } + } + return r; +} +int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int r = ppc_gdb_register_len_apple(n); + + if (!r) { + return r; + } + ppc_maybe_bswap_register(env, mem_buf, r); + if (n < 32) { + /* gprs */ + env->gpr[n] = ldq_p(mem_buf); + } else if (n < 64) { + /* fprs */ + *cpu_fpr_ptr(env, n - 32) = ldq_p(mem_buf); + } else { + switch (n) { + case 64 + 32: + env->nip = ldq_p(mem_buf); + break; + case 65 + 32: + ppc_store_msr(env, ldq_p(mem_buf)); + break; + case 66 + 32: + { + uint32_t cr = ldl_p(mem_buf); + int i; + for (i = 0; i < 8; i++) { + env->crf[i] = (cr >> (32 - ((i + 1) * 4))) & 0xF; + } + break; + } + case 67 + 32: + env->lr = ldq_p(mem_buf); + break; + case 68 + 32: + env->ctr = ldq_p(mem_buf); + break; + case 69 + 32: + cpu_write_xer(env, ldl_p(mem_buf)); + break; + case 70 + 32: + /* fpscr */ + ppc_store_fpscr(env, ldq_p(mem_buf)); + break; + } + } + return r; +} + +#ifndef CONFIG_USER_ONLY +void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; + GString *xml; + char *spr_name; + unsigned int num_regs = 0; + int i; + + if (pcc->gdb_spr_xml) { + return; + } + + xml = g_string_new("<?xml version=\"1.0\"?>"); + g_string_append(xml, "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"); + g_string_append(xml, "<feature name=\"org.qemu.power.spr\">"); + + for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { + ppc_spr_t *spr = &env->spr_cb[i]; + + if (!spr->name) { + continue; + } + + spr_name = g_ascii_strdown(spr->name, -1); + g_string_append_printf(xml, "<reg name=\"%s\"", spr_name); + g_free(spr_name); + + g_string_append_printf(xml, " bitsize=\"%d\"", TARGET_LONG_BITS); + g_string_append(xml, " group=\"spr\"/>"); + + /* + * GDB identifies registers based on the order they are + * presented in the XML. These ids will not match QEMU's + * representation (which follows the PowerISA). + * + * Store the position of the current register description so + * we can make the correspondence later. + */ + spr->gdb_id = num_regs; + num_regs++; + } + + g_string_append(xml, "</feature>"); + + pcc->gdb_num_sprs = num_regs; + pcc->gdb_spr_xml = g_string_free(xml, false); +} + +const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); + + if (strcmp(xml_name, "power-spr.xml") == 0) { + return pcc->gdb_spr_xml; + } + return NULL; +} +#endif + +#if !defined(CONFIG_USER_ONLY) +static int gdb_find_spr_idx(CPUPPCState *env, int n) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { + ppc_spr_t *spr = &env->spr_cb[i]; + + if (spr->name && spr->gdb_id == n) { + return i; + } + } + return -1; +} + +static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n) +{ + int reg; + int len; + + reg = gdb_find_spr_idx(env, n); + if (reg < 0) { + return 0; + } + + len = TARGET_LONG_SIZE; + gdb_get_regl(buf, env->spr[reg]); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, len), len); + return len; +} + +static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + int reg; + int len; + + reg = gdb_find_spr_idx(env, n); + if (reg < 0) { + return 0; + } + + len = TARGET_LONG_SIZE; + ppc_maybe_bswap_register(env, mem_buf, len); + env->spr[reg] = ldn_p(mem_buf, len); + + return len; +} +#endif + +static int gdb_get_float_reg(CPUPPCState *env, GByteArray *buf, int n) +{ + uint8_t *mem_buf; + if (n < 32) { + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n)); + mem_buf = gdb_get_reg_ptr(buf, 8); + ppc_maybe_bswap_register(env, mem_buf, 8); + return 8; + } + if (n == 32) { + gdb_get_reg32(buf, env->fpscr); + mem_buf = gdb_get_reg_ptr(buf, 4); + ppc_maybe_bswap_register(env, mem_buf, 4); + return 4; + } + return 0; +} + +static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + ppc_maybe_bswap_register(env, mem_buf, 8); + *cpu_fpr_ptr(env, n) = ldq_p(mem_buf); + return 8; + } + if (n == 32) { + ppc_maybe_bswap_register(env, mem_buf, 4); + ppc_store_fpscr(env, ldl_p(mem_buf)); + return 4; + } + return 0; +} + +static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n) +{ + uint8_t *mem_buf; + + if (n < 32) { + ppc_avr_t *avr = cpu_avr_ptr(env, n); + gdb_get_reg128(buf, avr->VsrD(0), avr->VsrD(1)); + mem_buf = gdb_get_reg_ptr(buf, 16); + ppc_maybe_bswap_register(env, mem_buf, 16); + return 16; + } + if (n == 32) { + gdb_get_reg32(buf, ppc_get_vscr(env)); + mem_buf = gdb_get_reg_ptr(buf, 4); + ppc_maybe_bswap_register(env, mem_buf, 4); + return 4; + } + if (n == 33) { + gdb_get_reg32(buf, (uint32_t)env->spr[SPR_VRSAVE]); + mem_buf = gdb_get_reg_ptr(buf, 4); + ppc_maybe_bswap_register(env, mem_buf, 4); + return 4; + } + return 0; +} + +static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + ppc_avr_t *avr = cpu_avr_ptr(env, n); + ppc_maybe_bswap_register(env, mem_buf, 16); + avr->VsrD(0) = ldq_p(mem_buf); + avr->VsrD(1) = ldq_p(mem_buf + 8); + return 16; + } + if (n == 32) { + ppc_maybe_bswap_register(env, mem_buf, 4); + ppc_store_vscr(env, ldl_p(mem_buf)); + return 4; + } + if (n == 33) { + ppc_maybe_bswap_register(env, mem_buf, 4); + env->spr[SPR_VRSAVE] = (target_ulong)ldl_p(mem_buf); + return 4; + } + return 0; +} + +static int gdb_get_spe_reg(CPUPPCState *env, GByteArray *buf, int n) +{ + if (n < 32) { +#if defined(TARGET_PPC64) + gdb_get_reg32(buf, env->gpr[n] >> 32); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4); +#else + gdb_get_reg32(buf, env->gprh[n]); +#endif + return 4; + } + if (n == 32) { + gdb_get_reg64(buf, env->spe_acc); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8); + return 8; + } + if (n == 33) { + gdb_get_reg32(buf, env->spe_fscr); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4); + return 4; + } + return 0; +} + +static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { +#if defined(TARGET_PPC64) + target_ulong lo = (uint32_t)env->gpr[n]; + target_ulong hi; + + ppc_maybe_bswap_register(env, mem_buf, 4); + + hi = (target_ulong)ldl_p(mem_buf) << 32; + env->gpr[n] = lo | hi; +#else + env->gprh[n] = ldl_p(mem_buf); +#endif + return 4; + } + if (n == 32) { + ppc_maybe_bswap_register(env, mem_buf, 8); + env->spe_acc = ldq_p(mem_buf); + return 8; + } + if (n == 33) { + ppc_maybe_bswap_register(env, mem_buf, 4); + env->spe_fscr = ldl_p(mem_buf); + return 4; + } + return 0; +} + +static int gdb_get_vsx_reg(CPUPPCState *env, GByteArray *buf, int n) +{ + if (n < 32) { + gdb_get_reg64(buf, *cpu_vsrl_ptr(env, n)); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8); + return 8; + } + return 0; +} + +static int gdb_set_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + ppc_maybe_bswap_register(env, mem_buf, 8); + *cpu_vsrl_ptr(env, n) = ldq_p(mem_buf); + return 8; + } + return 0; +} + +gchar *ppc_gdb_arch_name(CPUState *cs) +{ +#if defined(TARGET_PPC64) + return g_strdup("powerpc:common64"); +#else + return g_strdup("powerpc:common"); +#endif +} + +void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *pcc) +{ + if (pcc->insns_flags & PPC_FLOAT) { + gdb_register_coprocessor(cs, gdb_get_float_reg, gdb_set_float_reg, + 33, "power-fpu.xml", 0); + } + if (pcc->insns_flags & PPC_ALTIVEC) { + gdb_register_coprocessor(cs, gdb_get_avr_reg, gdb_set_avr_reg, + 34, "power-altivec.xml", 0); + } + if (pcc->insns_flags & PPC_SPE) { + gdb_register_coprocessor(cs, gdb_get_spe_reg, gdb_set_spe_reg, + 34, "power-spe.xml", 0); + } + if (pcc->insns_flags2 & PPC2_VSX) { + gdb_register_coprocessor(cs, gdb_get_vsx_reg, gdb_set_vsx_reg, + 32, "power-vsx.xml", 0); + } +#ifndef CONFIG_USER_ONLY + gdb_register_coprocessor(cs, gdb_get_spr_reg, gdb_set_spr_reg, + pcc->gdb_num_sprs, "power-spr.xml", 0); +#endif +} |