hvf: add guest debugging handlers for Apple Silicon hosts
Guests can now be debugged through the gdbstub. Support is added for single-stepping, software breakpoints, hardware breakpoints and watchpoints. The code has been structured like the KVM counterpart. While guest debugging is enabled, the guest can still read and write the DBG*_EL1 registers but they don't have any effect. Signed-off-by: Francesco Cagnin <fcagnin@quarkslab.com> Message-id: 20230601153107.81955-5-fcagnin@quarkslab.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
f41520402c
commit
eb2edc42b1
@ -343,12 +343,18 @@ static int hvf_accel_init(MachineState *ms)
|
|||||||
return hvf_arch_init();
|
return hvf_arch_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int hvf_gdbstub_sstep_flags(void)
|
||||||
|
{
|
||||||
|
return SSTEP_ENABLE | SSTEP_NOIRQ;
|
||||||
|
}
|
||||||
|
|
||||||
static void hvf_accel_class_init(ObjectClass *oc, void *data)
|
static void hvf_accel_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
AccelClass *ac = ACCEL_CLASS(oc);
|
AccelClass *ac = ACCEL_CLASS(oc);
|
||||||
ac->name = "HVF";
|
ac->name = "HVF";
|
||||||
ac->init_machine = hvf_accel_init;
|
ac->init_machine = hvf_accel_init;
|
||||||
ac->allowed = &hvf_allowed;
|
ac->allowed = &hvf_allowed;
|
||||||
|
ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo hvf_accel_type = {
|
static const TypeInfo hvf_accel_type = {
|
||||||
@ -398,6 +404,8 @@ static int hvf_init_vcpu(CPUState *cpu)
|
|||||||
cpu->vcpu_dirty = 1;
|
cpu->vcpu_dirty = 1;
|
||||||
assert_hvf_ok(r);
|
assert_hvf_ok(r);
|
||||||
|
|
||||||
|
cpu->hvf->guest_debug_enabled = false;
|
||||||
|
|
||||||
return hvf_arch_init_vcpu(cpu);
|
return hvf_arch_init_vcpu(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,6 +590,8 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
|
|||||||
ops->insert_breakpoint = hvf_insert_breakpoint;
|
ops->insert_breakpoint = hvf_insert_breakpoint;
|
||||||
ops->remove_breakpoint = hvf_remove_breakpoint;
|
ops->remove_breakpoint = hvf_remove_breakpoint;
|
||||||
ops->remove_all_breakpoints = hvf_remove_all_breakpoints;
|
ops->remove_all_breakpoints = hvf_remove_all_breakpoints;
|
||||||
|
ops->update_guest_debug = hvf_update_guest_debug;
|
||||||
|
ops->supports_guest_debug = hvf_arch_supports_guest_debug;
|
||||||
};
|
};
|
||||||
static const TypeInfo hvf_accel_ops_type = {
|
static const TypeInfo hvf_accel_ops_type = {
|
||||||
.name = ACCEL_OPS_NAME("hvf"),
|
.name = ACCEL_OPS_NAME("hvf"),
|
||||||
|
@ -61,3 +61,9 @@ int hvf_sw_breakpoints_active(CPUState *cpu)
|
|||||||
{
|
{
|
||||||
return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints);
|
return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int hvf_update_guest_debug(CPUState *cpu)
|
||||||
|
{
|
||||||
|
hvf_arch_update_guest_debug(cpu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -56,6 +56,21 @@ int hvf_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len,
|
|||||||
int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len,
|
int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len,
|
||||||
int type);
|
int type);
|
||||||
void hvf_arch_remove_all_hw_breakpoints(void);
|
void hvf_arch_remove_all_hw_breakpoints(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hvf_update_guest_debug:
|
||||||
|
* @cs: CPUState for the CPU to update
|
||||||
|
*
|
||||||
|
* Update guest to enable or disable debugging. Per-arch specifics will be
|
||||||
|
* handled by calling down to hvf_arch_update_guest_debug.
|
||||||
|
*/
|
||||||
|
int hvf_update_guest_debug(CPUState *cpu);
|
||||||
|
void hvf_arch_update_guest_debug(CPUState *cpu);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return whether the guest supports debugging.
|
||||||
|
*/
|
||||||
|
bool hvf_arch_supports_guest_debug(void);
|
||||||
#endif /* NEED_CPU_H */
|
#endif /* NEED_CPU_H */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -54,6 +54,7 @@ struct hvf_vcpu_state {
|
|||||||
void *exit;
|
void *exit;
|
||||||
bool vtimer_masked;
|
bool vtimer_masked;
|
||||||
sigset_t unblock_ipi_mask;
|
sigset_t unblock_ipi_mask;
|
||||||
|
bool guest_debug_enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
void assert_hvf_ok(hv_return_t ret);
|
void assert_hvf_ok(hv_return_t ret);
|
||||||
|
@ -33,6 +33,116 @@
|
|||||||
|
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
|
|
||||||
|
#define MDSCR_EL1_SS_SHIFT 0
|
||||||
|
#define MDSCR_EL1_MDE_SHIFT 15
|
||||||
|
|
||||||
|
static uint16_t dbgbcr_regs[] = {
|
||||||
|
HV_SYS_REG_DBGBCR0_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR1_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR2_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR3_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR4_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR5_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR6_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR7_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR8_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR9_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR10_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR11_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR12_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR13_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR14_EL1,
|
||||||
|
HV_SYS_REG_DBGBCR15_EL1,
|
||||||
|
};
|
||||||
|
static uint16_t dbgbvr_regs[] = {
|
||||||
|
HV_SYS_REG_DBGBVR0_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR1_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR2_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR3_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR4_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR5_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR6_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR7_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR8_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR9_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR10_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR11_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR12_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR13_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR14_EL1,
|
||||||
|
HV_SYS_REG_DBGBVR15_EL1,
|
||||||
|
};
|
||||||
|
static uint16_t dbgwcr_regs[] = {
|
||||||
|
HV_SYS_REG_DBGWCR0_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR1_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR2_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR3_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR4_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR5_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR6_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR7_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR8_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR9_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR10_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR11_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR12_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR13_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR14_EL1,
|
||||||
|
HV_SYS_REG_DBGWCR15_EL1,
|
||||||
|
};
|
||||||
|
static uint16_t dbgwvr_regs[] = {
|
||||||
|
HV_SYS_REG_DBGWVR0_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR1_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR2_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR3_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR4_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR5_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR6_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR7_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR8_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR9_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR10_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR11_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR12_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR13_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR14_EL1,
|
||||||
|
HV_SYS_REG_DBGWVR15_EL1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int hvf_arm_num_brps(hv_vcpu_config_t config)
|
||||||
|
{
|
||||||
|
uint64_t val;
|
||||||
|
hv_return_t ret;
|
||||||
|
ret = hv_vcpu_config_get_feature_reg(config, HV_FEATURE_REG_ID_AA64DFR0_EL1,
|
||||||
|
&val);
|
||||||
|
assert_hvf_ok(ret);
|
||||||
|
return FIELD_EX64(val, ID_AA64DFR0, BRPS) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hvf_arm_num_wrps(hv_vcpu_config_t config)
|
||||||
|
{
|
||||||
|
uint64_t val;
|
||||||
|
hv_return_t ret;
|
||||||
|
ret = hv_vcpu_config_get_feature_reg(config, HV_FEATURE_REG_ID_AA64DFR0_EL1,
|
||||||
|
&val);
|
||||||
|
assert_hvf_ok(ret);
|
||||||
|
return FIELD_EX64(val, ID_AA64DFR0, WRPS) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hvf_arm_init_debug(void)
|
||||||
|
{
|
||||||
|
hv_vcpu_config_t config;
|
||||||
|
config = hv_vcpu_config_create();
|
||||||
|
|
||||||
|
max_hw_bps = hvf_arm_num_brps(config);
|
||||||
|
hw_breakpoints =
|
||||||
|
g_array_sized_new(true, true, sizeof(HWBreakpoint), max_hw_bps);
|
||||||
|
|
||||||
|
max_hw_wps = hvf_arm_num_wrps(config);
|
||||||
|
hw_watchpoints =
|
||||||
|
g_array_sized_new(true, true, sizeof(HWWatchpoint), max_hw_wps);
|
||||||
|
}
|
||||||
|
|
||||||
#define HVF_SYSREG(crn, crm, op0, op1, op2) \
|
#define HVF_SYSREG(crn, crm, op0, op1, op2) \
|
||||||
ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2)
|
ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2)
|
||||||
#define PL1_WRITE_MASK 0x4
|
#define PL1_WRITE_MASK 0x4
|
||||||
@ -465,6 +575,92 @@ int hvf_get_registers(CPUState *cpu)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cpu->hvf->guest_debug_enabled) {
|
||||||
|
/* Handle debug registers */
|
||||||
|
switch (hvf_sreg_match[i].reg) {
|
||||||
|
case HV_SYS_REG_DBGBVR0_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR0_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR0_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR0_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR1_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR1_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR1_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR1_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR2_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR2_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR2_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR2_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR3_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR3_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR3_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR3_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR4_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR4_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR4_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR4_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR5_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR5_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR5_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR5_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR6_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR6_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR6_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR6_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR7_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR7_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR7_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR7_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR8_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR8_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR8_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR8_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR9_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR9_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR9_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR9_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR10_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR10_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR10_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR10_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR11_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR11_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR11_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR11_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR12_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR12_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR12_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR12_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR13_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR13_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR13_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR13_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR14_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR14_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR14_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR14_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR15_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR15_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR15_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR15_EL1: {
|
||||||
|
/*
|
||||||
|
* If the guest is being debugged, the vCPU's debug registers
|
||||||
|
* are holding the gdbstub's view of the registers (set in
|
||||||
|
* hvf_arch_update_guest_debug()).
|
||||||
|
* Since the environment is used to store only the guest's view
|
||||||
|
* of the registers, don't update it with the values from the
|
||||||
|
* vCPU but simply keep the values from the previous
|
||||||
|
* environment.
|
||||||
|
*/
|
||||||
|
const ARMCPRegInfo *ri;
|
||||||
|
ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_sreg_match[i].key);
|
||||||
|
val = read_raw_cp_reg(env, ri);
|
||||||
|
|
||||||
|
arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx] = val;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = hv_vcpu_get_sys_reg(cpu->hvf->fd, hvf_sreg_match[i].reg, &val);
|
ret = hv_vcpu_get_sys_reg(cpu->hvf->fd, hvf_sreg_match[i].reg, &val);
|
||||||
assert_hvf_ok(ret);
|
assert_hvf_ok(ret);
|
||||||
|
|
||||||
@ -516,6 +712,82 @@ int hvf_put_registers(CPUState *cpu)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cpu->hvf->guest_debug_enabled) {
|
||||||
|
/* Handle debug registers */
|
||||||
|
switch (hvf_sreg_match[i].reg) {
|
||||||
|
case HV_SYS_REG_DBGBVR0_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR0_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR0_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR0_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR1_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR1_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR1_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR1_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR2_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR2_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR2_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR2_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR3_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR3_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR3_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR3_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR4_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR4_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR4_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR4_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR5_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR5_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR5_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR5_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR6_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR6_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR6_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR6_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR7_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR7_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR7_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR7_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR8_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR8_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR8_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR8_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR9_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR9_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR9_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR9_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR10_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR10_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR10_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR10_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR11_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR11_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR11_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR11_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR12_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR12_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR12_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR12_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR13_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR13_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR13_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR13_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR14_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR14_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR14_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR14_EL1:
|
||||||
|
case HV_SYS_REG_DBGBVR15_EL1:
|
||||||
|
case HV_SYS_REG_DBGBCR15_EL1:
|
||||||
|
case HV_SYS_REG_DBGWVR15_EL1:
|
||||||
|
case HV_SYS_REG_DBGWCR15_EL1:
|
||||||
|
/*
|
||||||
|
* If the guest is being debugged, the vCPU's debug registers
|
||||||
|
* are already holding the gdbstub's view of the registers (set
|
||||||
|
* in hvf_arch_update_guest_debug()).
|
||||||
|
*/
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val = arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx];
|
val = arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx];
|
||||||
ret = hv_vcpu_set_sys_reg(cpu->hvf->fd, hvf_sreg_match[i].reg, val);
|
ret = hv_vcpu_set_sys_reg(cpu->hvf->fd, hvf_sreg_match[i].reg, val);
|
||||||
assert_hvf_ok(ret);
|
assert_hvf_ok(ret);
|
||||||
@ -1532,11 +1804,13 @@ int hvf_vcpu_exec(CPUState *cpu)
|
|||||||
{
|
{
|
||||||
ARMCPU *arm_cpu = ARM_CPU(cpu);
|
ARMCPU *arm_cpu = ARM_CPU(cpu);
|
||||||
CPUARMState *env = &arm_cpu->env;
|
CPUARMState *env = &arm_cpu->env;
|
||||||
|
int ret;
|
||||||
hv_vcpu_exit_t *hvf_exit = cpu->hvf->exit;
|
hv_vcpu_exit_t *hvf_exit = cpu->hvf->exit;
|
||||||
hv_return_t r;
|
hv_return_t r;
|
||||||
bool advance_pc = false;
|
bool advance_pc = false;
|
||||||
|
|
||||||
if (hvf_inject_interrupts(cpu)) {
|
if (!(cpu->singlestep_enabled & SSTEP_NOIRQ) &&
|
||||||
|
hvf_inject_interrupts(cpu)) {
|
||||||
return EXCP_INTERRUPT;
|
return EXCP_INTERRUPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1554,6 +1828,7 @@ int hvf_vcpu_exec(CPUState *cpu)
|
|||||||
uint64_t syndrome = hvf_exit->exception.syndrome;
|
uint64_t syndrome = hvf_exit->exception.syndrome;
|
||||||
uint32_t ec = syn_get_ec(syndrome);
|
uint32_t ec = syn_get_ec(syndrome);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
qemu_mutex_lock_iothread();
|
qemu_mutex_lock_iothread();
|
||||||
switch (exit_reason) {
|
switch (exit_reason) {
|
||||||
case HV_EXIT_REASON_EXCEPTION:
|
case HV_EXIT_REASON_EXCEPTION:
|
||||||
@ -1573,6 +1848,49 @@ int hvf_vcpu_exec(CPUState *cpu)
|
|||||||
hvf_sync_vtimer(cpu);
|
hvf_sync_vtimer(cpu);
|
||||||
|
|
||||||
switch (ec) {
|
switch (ec) {
|
||||||
|
case EC_SOFTWARESTEP: {
|
||||||
|
ret = EXCP_DEBUG;
|
||||||
|
|
||||||
|
if (!cpu->singlestep_enabled) {
|
||||||
|
error_report("EC_SOFTWARESTEP but single-stepping not enabled");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EC_AA64_BKPT: {
|
||||||
|
ret = EXCP_DEBUG;
|
||||||
|
|
||||||
|
cpu_synchronize_state(cpu);
|
||||||
|
|
||||||
|
if (!hvf_find_sw_breakpoint(cpu, env->pc)) {
|
||||||
|
/* Re-inject into the guest */
|
||||||
|
ret = 0;
|
||||||
|
hvf_raise_exception(cpu, EXCP_BKPT, syn_aa64_bkpt(0));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EC_BREAKPOINT: {
|
||||||
|
ret = EXCP_DEBUG;
|
||||||
|
|
||||||
|
cpu_synchronize_state(cpu);
|
||||||
|
|
||||||
|
if (!find_hw_breakpoint(cpu, env->pc)) {
|
||||||
|
error_report("EC_BREAKPOINT but unknown hw breakpoint");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EC_WATCHPOINT: {
|
||||||
|
ret = EXCP_DEBUG;
|
||||||
|
|
||||||
|
cpu_synchronize_state(cpu);
|
||||||
|
|
||||||
|
CPUWatchpoint *wp =
|
||||||
|
find_hw_watchpoint(cpu, hvf_exit->exception.virtual_address);
|
||||||
|
if (!wp) {
|
||||||
|
error_report("EXCP_DEBUG but unknown hw watchpoint");
|
||||||
|
}
|
||||||
|
cpu->watchpoint_hit = wp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case EC_DATAABORT: {
|
case EC_DATAABORT: {
|
||||||
bool isv = syndrome & ARM_EL_ISV;
|
bool isv = syndrome & ARM_EL_ISV;
|
||||||
bool iswrite = (syndrome >> 6) & 1;
|
bool iswrite = (syndrome >> 6) & 1;
|
||||||
@ -1677,9 +1995,14 @@ int hvf_vcpu_exec(CPUState *cpu)
|
|||||||
pc += 4;
|
pc += 4;
|
||||||
r = hv_vcpu_set_reg(cpu->hvf->fd, HV_REG_PC, pc);
|
r = hv_vcpu_set_reg(cpu->hvf->fd, HV_REG_PC, pc);
|
||||||
assert_hvf_ok(r);
|
assert_hvf_ok(r);
|
||||||
|
|
||||||
|
/* Handle single-stepping over instructions which trigger a VM exit */
|
||||||
|
if (cpu->singlestep_enabled) {
|
||||||
|
ret = EXCP_DEBUG;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_hvf_vtimer = {
|
static const VMStateDescription vmstate_hvf_vtimer = {
|
||||||
@ -1711,6 +2034,9 @@ int hvf_arch_init(void)
|
|||||||
hvf_state->vtimer_offset = mach_absolute_time();
|
hvf_state->vtimer_offset = mach_absolute_time();
|
||||||
vmstate_register(NULL, 0, &vmstate_hvf_vtimer, &vtimer);
|
vmstate_register(NULL, 0, &vmstate_hvf_vtimer, &vtimer);
|
||||||
qemu_add_vm_change_state_handler(hvf_vm_state_change, &vtimer);
|
qemu_add_vm_change_state_handler(hvf_vm_state_change, &vtimer);
|
||||||
|
|
||||||
|
hvf_arm_init_debug();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1774,3 +2100,147 @@ void hvf_arch_remove_all_hw_breakpoints(void)
|
|||||||
g_array_remove_range(hw_breakpoints, 0, cur_hw_bps);
|
g_array_remove_range(hw_breakpoints, 0, cur_hw_bps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the vCPU with the gdbstub's view of debug registers. This view
|
||||||
|
* consists of all hardware breakpoints and watchpoints inserted so far while
|
||||||
|
* debugging the guest.
|
||||||
|
*/
|
||||||
|
static void hvf_put_gdbstub_debug_registers(CPUState *cpu)
|
||||||
|
{
|
||||||
|
hv_return_t r = HV_SUCCESS;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cur_hw_bps; i++) {
|
||||||
|
HWBreakpoint *bp = get_hw_bp(i);
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbcr_regs[i], bp->bcr);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbvr_regs[i], bp->bvr);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
}
|
||||||
|
for (i = cur_hw_bps; i < max_hw_bps; i++) {
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbcr_regs[i], 0);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbvr_regs[i], 0);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < cur_hw_wps; i++) {
|
||||||
|
HWWatchpoint *wp = get_hw_wp(i);
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwcr_regs[i], wp->wcr);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwvr_regs[i], wp->wvr);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
}
|
||||||
|
for (i = cur_hw_wps; i < max_hw_wps; i++) {
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwcr_regs[i], 0);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwvr_regs[i], 0);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the vCPU with the guest's view of debug registers. This view is kept
|
||||||
|
* in the environment at all times.
|
||||||
|
*/
|
||||||
|
static void hvf_put_guest_debug_registers(CPUState *cpu)
|
||||||
|
{
|
||||||
|
ARMCPU *arm_cpu = ARM_CPU(cpu);
|
||||||
|
CPUARMState *env = &arm_cpu->env;
|
||||||
|
hv_return_t r = HV_SUCCESS;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < max_hw_bps; i++) {
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbcr_regs[i],
|
||||||
|
env->cp15.dbgbcr[i]);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbvr_regs[i],
|
||||||
|
env->cp15.dbgbvr[i]);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < max_hw_wps; i++) {
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwcr_regs[i],
|
||||||
|
env->cp15.dbgwcr[i]);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwvr_regs[i],
|
||||||
|
env->cp15.dbgwvr[i]);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool hvf_arm_hw_debug_active(CPUState *cpu)
|
||||||
|
{
|
||||||
|
return ((cur_hw_wps > 0) || (cur_hw_bps > 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hvf_arch_set_traps(void)
|
||||||
|
{
|
||||||
|
CPUState *cpu;
|
||||||
|
bool should_enable_traps = false;
|
||||||
|
hv_return_t r = HV_SUCCESS;
|
||||||
|
|
||||||
|
/* Check whether guest debugging is enabled for at least one vCPU; if it
|
||||||
|
* is, enable exiting the guest on all vCPUs */
|
||||||
|
CPU_FOREACH(cpu) {
|
||||||
|
should_enable_traps |= cpu->hvf->guest_debug_enabled;
|
||||||
|
}
|
||||||
|
CPU_FOREACH(cpu) {
|
||||||
|
/* Set whether debug exceptions exit the guest */
|
||||||
|
r = hv_vcpu_set_trap_debug_exceptions(cpu->hvf->fd,
|
||||||
|
should_enable_traps);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
|
||||||
|
/* Set whether accesses to debug registers exit the guest */
|
||||||
|
r = hv_vcpu_set_trap_debug_reg_accesses(cpu->hvf->fd,
|
||||||
|
should_enable_traps);
|
||||||
|
assert_hvf_ok(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hvf_arch_update_guest_debug(CPUState *cpu)
|
||||||
|
{
|
||||||
|
ARMCPU *arm_cpu = ARM_CPU(cpu);
|
||||||
|
CPUARMState *env = &arm_cpu->env;
|
||||||
|
|
||||||
|
/* Check whether guest debugging is enabled */
|
||||||
|
cpu->hvf->guest_debug_enabled = cpu->singlestep_enabled ||
|
||||||
|
hvf_sw_breakpoints_active(cpu) ||
|
||||||
|
hvf_arm_hw_debug_active(cpu);
|
||||||
|
|
||||||
|
/* Update debug registers */
|
||||||
|
if (cpu->hvf->guest_debug_enabled) {
|
||||||
|
hvf_put_gdbstub_debug_registers(cpu);
|
||||||
|
} else {
|
||||||
|
hvf_put_guest_debug_registers(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_synchronize_state(cpu);
|
||||||
|
|
||||||
|
/* Enable/disable single-stepping */
|
||||||
|
if (cpu->singlestep_enabled) {
|
||||||
|
env->cp15.mdscr_el1 =
|
||||||
|
deposit64(env->cp15.mdscr_el1, MDSCR_EL1_SS_SHIFT, 1, 1);
|
||||||
|
pstate_write(env, pstate_read(env) | PSTATE_SS);
|
||||||
|
} else {
|
||||||
|
env->cp15.mdscr_el1 =
|
||||||
|
deposit64(env->cp15.mdscr_el1, MDSCR_EL1_SS_SHIFT, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable/disable Breakpoint exceptions */
|
||||||
|
if (hvf_arm_hw_debug_active(cpu)) {
|
||||||
|
env->cp15.mdscr_el1 =
|
||||||
|
deposit64(env->cp15.mdscr_el1, MDSCR_EL1_MDE_SHIFT, 1, 1);
|
||||||
|
} else {
|
||||||
|
env->cp15.mdscr_el1 =
|
||||||
|
deposit64(env->cp15.mdscr_el1, MDSCR_EL1_MDE_SHIFT, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
hvf_arch_set_traps();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool hvf_arch_supports_guest_debug(void)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -13,6 +13,13 @@
|
|||||||
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hvf_arm_init_debug() - initialize guest debug capabilities
|
||||||
|
*
|
||||||
|
* Should be called only once before using guest debug capabilities.
|
||||||
|
*/
|
||||||
|
void hvf_arm_init_debug(void);
|
||||||
|
|
||||||
void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu);
|
void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -703,3 +703,12 @@ int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, int type)
|
|||||||
void hvf_arch_remove_all_hw_breakpoints(void)
|
void hvf_arch_remove_all_hw_breakpoints(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hvf_arch_update_guest_debug(CPUState *cpu)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool hvf_arch_supports_guest_debug(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user