diff options
Diffstat (limited to 'target/xtensa/gdbstub.c')
-rw-r--r-- | target/xtensa/gdbstub.c | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/target/xtensa/gdbstub.c b/target/xtensa/gdbstub.c new file mode 100644 index 000000000..b6696063e --- /dev/null +++ b/target/xtensa/gdbstub.c @@ -0,0 +1,182 @@ +/* + * 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.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 "qemu/log.h" + +enum { + xtRegisterTypeArRegfile = 1, /* Register File ar0..arXX. */ + xtRegisterTypeSpecialReg, /* CPU states, such as PS, Booleans, (rsr). */ + xtRegisterTypeUserReg, /* User defined registers (rur). */ + xtRegisterTypeTieRegfile, /* User define register files. */ + xtRegisterTypeTieState, /* TIE States (mapped on user regs). */ + xtRegisterTypeMapped, /* Mapped on Special Registers. */ + xtRegisterTypeUnmapped, /* Special case of masked registers. */ + xtRegisterTypeWindow, /* Live window registers (a0..a15). */ + xtRegisterTypeVirtual, /* PC, FP. */ + xtRegisterTypeUnknown +}; + +#define XTENSA_REGISTER_FLAGS_PRIVILEGED 0x0001 +#define XTENSA_REGISTER_FLAGS_READABLE 0x0002 +#define XTENSA_REGISTER_FLAGS_WRITABLE 0x0004 +#define XTENSA_REGISTER_FLAGS_VOLATILE 0x0008 + +void xtensa_count_regs(const XtensaConfig *config, + unsigned *n_regs, unsigned *n_core_regs) +{ + unsigned i; + bool count_core_regs = true; + + for (i = 0; config->gdb_regmap.reg[i].targno >= 0; ++i) { + if (config->gdb_regmap.reg[i].type != xtRegisterTypeTieState && + config->gdb_regmap.reg[i].type != xtRegisterTypeMapped && + config->gdb_regmap.reg[i].type != xtRegisterTypeUnmapped) { + ++*n_regs; + if (count_core_regs) { + if ((config->gdb_regmap.reg[i].flags & + XTENSA_REGISTER_FLAGS_PRIVILEGED) == 0) { + ++*n_core_regs; + } else { + count_core_regs = false; + } + } + } + } +} + +int xtensa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) +{ + XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = &cpu->env; + const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; +#ifdef CONFIG_USER_ONLY + int num_regs = env->config->gdb_regmap.num_core_regs; +#else + int num_regs = env->config->gdb_regmap.num_regs; +#endif + unsigned i; + + if (n < 0 || n >= num_regs) { + return 0; + } + + switch (reg->type) { + case xtRegisterTypeVirtual: /*pc*/ + return gdb_get_reg32(mem_buf, env->pc); + + case xtRegisterTypeArRegfile: /*ar*/ + xtensa_sync_phys_from_window(env); + return gdb_get_reg32(mem_buf, env->phys_regs[(reg->targno & 0xff) + % env->config->nareg]); + + case xtRegisterTypeSpecialReg: /*SR*/ + return gdb_get_reg32(mem_buf, env->sregs[reg->targno & 0xff]); + + case xtRegisterTypeUserReg: /*UR*/ + return gdb_get_reg32(mem_buf, env->uregs[reg->targno & 0xff]); + + case xtRegisterTypeTieRegfile: /*f*/ + i = reg->targno & 0x0f; + switch (reg->size) { + case 4: + return gdb_get_reg32(mem_buf, + float32_val(env->fregs[i].f32[FP_F32_LOW])); + case 8: + return gdb_get_reg64(mem_buf, float64_val(env->fregs[i].f64)); + default: + qemu_log_mask(LOG_UNIMP, "%s from reg %d of unsupported size %d\n", + __func__, n, reg->size); + return gdb_get_zeroes(mem_buf, reg->size); + } + + case xtRegisterTypeWindow: /*a*/ + return gdb_get_reg32(mem_buf, env->regs[reg->targno & 0x0f]); + + default: + qemu_log_mask(LOG_UNIMP, "%s from reg %d of unsupported type %d\n", + __func__, n, reg->type); + return gdb_get_zeroes(mem_buf, reg->size); + } +} + +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; +#ifdef CONFIG_USER_ONLY + int num_regs = env->config->gdb_regmap.num_core_regs; +#else + int num_regs = env->config->gdb_regmap.num_regs; +#endif + + if (n < 0 || n >= num_regs) { + return 0; + } + + tmp = ldl_p(mem_buf); + + switch (reg->type) { + case xtRegisterTypeVirtual: /*pc*/ + env->pc = tmp; + break; + + case xtRegisterTypeArRegfile: /*ar*/ + env->phys_regs[(reg->targno & 0xff) % env->config->nareg] = tmp; + xtensa_sync_window_from_phys(env); + break; + + case xtRegisterTypeSpecialReg: /*SR*/ + env->sregs[reg->targno & 0xff] = tmp; + break; + + case xtRegisterTypeUserReg: /*UR*/ + env->uregs[reg->targno & 0xff] = tmp; + break; + + case xtRegisterTypeTieRegfile: /*f*/ + switch (reg->size) { + case 4: + env->fregs[reg->targno & 0x0f].f32[FP_F32_LOW] = make_float32(tmp); + return 4; + case 8: + env->fregs[reg->targno & 0x0f].f64 = make_float64(tmp); + return 8; + default: + qemu_log_mask(LOG_UNIMP, "%s to reg %d of unsupported size %d\n", + __func__, n, reg->size); + return reg->size; + } + + case xtRegisterTypeWindow: /*a*/ + env->regs[reg->targno & 0x0f] = tmp; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s to reg %d of unsupported type %d\n", + __func__, n, reg->type); + return reg->size; + } + + return 4; +} |