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:
Francesco Cagnin 2023-06-06 10:19:30 +01:00 committed by Peter Maydell
parent f41520402c
commit eb2edc42b1
7 changed files with 520 additions and 2 deletions

View File

@ -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"),

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}