hvf: add breakpoint handlers
Required for guest debugging. The code has been structured like the KVM counterpart. Signed-off-by: Francesco Cagnin <fcagnin@quarkslab.com> Message-id: 20230601153107.81955-4-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
ce799a04b2
commit
f41520402c
@ -52,6 +52,7 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/hvf.h"
|
||||
#include "sysemu/hvf_int.h"
|
||||
@ -334,6 +335,8 @@ static int hvf_accel_init(MachineState *ms)
|
||||
s->slots[x].slot_id = x;
|
||||
}
|
||||
|
||||
QTAILQ_INIT(&s->hvf_sw_breakpoints);
|
||||
|
||||
hvf_state = s;
|
||||
memory_listener_register(&hvf_memory_listener, &address_space_memory);
|
||||
|
||||
@ -462,6 +465,108 @@ static void hvf_start_vcpu_thread(CPUState *cpu)
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
}
|
||||
|
||||
static int hvf_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp;
|
||||
int err;
|
||||
|
||||
if (type == GDB_BREAKPOINT_SW) {
|
||||
bp = hvf_find_sw_breakpoint(cpu, addr);
|
||||
if (bp) {
|
||||
bp->use_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bp = g_new(struct hvf_sw_breakpoint, 1);
|
||||
bp->pc = addr;
|
||||
bp->use_count = 1;
|
||||
err = hvf_arch_insert_sw_breakpoint(cpu, bp);
|
||||
if (err) {
|
||||
g_free(bp);
|
||||
return err;
|
||||
}
|
||||
|
||||
QTAILQ_INSERT_HEAD(&hvf_state->hvf_sw_breakpoints, bp, entry);
|
||||
} else {
|
||||
err = hvf_arch_insert_hw_breakpoint(addr, len, type);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
err = hvf_update_guest_debug(cpu);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hvf_remove_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp;
|
||||
int err;
|
||||
|
||||
if (type == GDB_BREAKPOINT_SW) {
|
||||
bp = hvf_find_sw_breakpoint(cpu, addr);
|
||||
if (!bp) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (bp->use_count > 1) {
|
||||
bp->use_count--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = hvf_arch_remove_sw_breakpoint(cpu, bp);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
QTAILQ_REMOVE(&hvf_state->hvf_sw_breakpoints, bp, entry);
|
||||
g_free(bp);
|
||||
} else {
|
||||
err = hvf_arch_remove_hw_breakpoint(addr, len, type);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
err = hvf_update_guest_debug(cpu);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hvf_remove_all_breakpoints(CPUState *cpu)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp, *next;
|
||||
CPUState *tmpcpu;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(bp, &hvf_state->hvf_sw_breakpoints, entry, next) {
|
||||
if (hvf_arch_remove_sw_breakpoint(cpu, bp) != 0) {
|
||||
/* Try harder to find a CPU that currently sees the breakpoint. */
|
||||
CPU_FOREACH(tmpcpu)
|
||||
{
|
||||
if (hvf_arch_remove_sw_breakpoint(tmpcpu, bp) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
QTAILQ_REMOVE(&hvf_state->hvf_sw_breakpoints, bp, entry);
|
||||
g_free(bp);
|
||||
}
|
||||
hvf_arch_remove_all_hw_breakpoints();
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
hvf_update_guest_debug(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
|
||||
@ -473,6 +578,10 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
ops->synchronize_post_init = hvf_cpu_synchronize_post_init;
|
||||
ops->synchronize_state = hvf_cpu_synchronize_state;
|
||||
ops->synchronize_pre_loadvm = hvf_cpu_synchronize_pre_loadvm;
|
||||
|
||||
ops->insert_breakpoint = hvf_insert_breakpoint;
|
||||
ops->remove_breakpoint = hvf_remove_breakpoint;
|
||||
ops->remove_all_breakpoints = hvf_remove_all_breakpoints;
|
||||
};
|
||||
static const TypeInfo hvf_accel_ops_type = {
|
||||
.name = ACCEL_OPS_NAME("hvf"),
|
||||
|
@ -44,3 +44,20 @@ void assert_hvf_ok(hv_return_t ret)
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, target_ulong pc)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp;
|
||||
|
||||
QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) {
|
||||
if (bp->pc == pc) {
|
||||
return bp;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hvf_sw_breakpoints_active(CPUState *cpu)
|
||||
{
|
||||
return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "qom/object.h"
|
||||
|
||||
#ifdef NEED_CPU_H
|
||||
#include "cpu.h"
|
||||
|
||||
#ifdef CONFIG_HVF
|
||||
uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
|
||||
@ -36,4 +37,25 @@ typedef struct HVFState HVFState;
|
||||
DECLARE_INSTANCE_CHECKER(HVFState, HVF_STATE,
|
||||
TYPE_HVF_ACCEL)
|
||||
|
||||
#ifdef NEED_CPU_H
|
||||
struct hvf_sw_breakpoint {
|
||||
target_ulong pc;
|
||||
target_ulong saved_insn;
|
||||
int use_count;
|
||||
QTAILQ_ENTRY(hvf_sw_breakpoint) entry;
|
||||
};
|
||||
|
||||
struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu,
|
||||
target_ulong pc);
|
||||
int hvf_sw_breakpoints_active(CPUState *cpu);
|
||||
|
||||
int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp);
|
||||
int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp);
|
||||
int hvf_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len,
|
||||
int type);
|
||||
int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len,
|
||||
int type);
|
||||
void hvf_arch_remove_all_hw_breakpoints(void);
|
||||
#endif /* NEED_CPU_H */
|
||||
|
||||
#endif
|
||||
|
@ -45,6 +45,7 @@ struct HVFState {
|
||||
|
||||
hvf_vcpu_caps *hvf_caps;
|
||||
uint64_t vtimer_offset;
|
||||
QTAILQ_HEAD(, hvf_sw_breakpoint) hvf_sw_breakpoints;
|
||||
};
|
||||
extern HVFState *hvf_state;
|
||||
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include "trace/trace-target_arm_hvf.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
#include "exec/gdbstub.h"
|
||||
|
||||
#define HVF_SYSREG(crn, crm, op0, op1, op2) \
|
||||
ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2)
|
||||
#define PL1_WRITE_MASK 0x4
|
||||
@ -1711,3 +1713,64 @@ int hvf_arch_init(void)
|
||||
qemu_add_vm_change_state_handler(hvf_vm_state_change, &vtimer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const uint32_t brk_insn = 0xd4200000;
|
||||
|
||||
int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp)
|
||||
{
|
||||
if (cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) ||
|
||||
cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&brk_insn, 4, 1)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp)
|
||||
{
|
||||
static uint32_t brk;
|
||||
|
||||
if (cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&brk, 4, 0) ||
|
||||
brk != brk_insn ||
|
||||
cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hvf_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, int type)
|
||||
{
|
||||
switch (type) {
|
||||
case GDB_BREAKPOINT_HW:
|
||||
return insert_hw_breakpoint(addr);
|
||||
case GDB_WATCHPOINT_READ:
|
||||
case GDB_WATCHPOINT_WRITE:
|
||||
case GDB_WATCHPOINT_ACCESS:
|
||||
return insert_hw_watchpoint(addr, len, type);
|
||||
default:
|
||||
return -ENOSYS;
|
||||
}
|
||||
}
|
||||
|
||||
int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, int type)
|
||||
{
|
||||
switch (type) {
|
||||
case GDB_BREAKPOINT_HW:
|
||||
return delete_hw_breakpoint(addr);
|
||||
case GDB_WATCHPOINT_READ:
|
||||
case GDB_WATCHPOINT_WRITE:
|
||||
case GDB_WATCHPOINT_ACCESS:
|
||||
return delete_hw_watchpoint(addr, len, type);
|
||||
default:
|
||||
return -ENOSYS;
|
||||
}
|
||||
}
|
||||
|
||||
void hvf_arch_remove_all_hw_breakpoints(void)
|
||||
{
|
||||
if (cur_hw_wps > 0) {
|
||||
g_array_remove_range(hw_watchpoints, 0, cur_hw_wps);
|
||||
}
|
||||
if (cur_hw_bps > 0) {
|
||||
g_array_remove_range(hw_breakpoints, 0, cur_hw_bps);
|
||||
}
|
||||
}
|
||||
|
@ -679,3 +679,27 @@ int hvf_vcpu_exec(CPUState *cpu)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int hvf_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, int type)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, int type)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void hvf_arch_remove_all_hw_breakpoints(void)
|
||||
{
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user