aboutsummaryrefslogtreecommitdiff
path: root/accel/hvf/hvf-accel-ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'accel/hvf/hvf-accel-ops.c')
-rw-r--r--accel/hvf/hvf-accel-ops.c109
1 files changed, 109 insertions, 0 deletions
diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c
index 24913ca9c4..92601b1369 100644
--- a/accel/hvf/hvf-accel-ops.c
+++ b/accel/hvf/hvf-accel-ops.c
@@ -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"),