[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PULL 11/23] target-arm: Implement setting of watchpoints
From: |
Peter Maydell |
Subject: |
[Qemu-devel] [PULL 11/23] target-arm: Implement setting of watchpoints |
Date: |
Fri, 12 Sep 2014 14:23:42 +0100 |
Implement support for setting QEMU watchpoints based on the
values the guest writes to the ARM architected watchpoint
registers. (We do not yet report the firing of the watchpoints
to the guest, so they will just be ignored.)
Signed-off-by: Peter Maydell <address@hidden>
---
target-arm/cpu.c | 2 +
target-arm/cpu.h | 2 +
target-arm/helper.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++--
target-arm/internals.h | 10 ++++
target-arm/machine.c | 3 ++
5 files changed, 149 insertions(+), 3 deletions(-)
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index bdd23b4..6c40ecd 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -184,6 +184,8 @@ static void arm_cpu_reset(CPUState *s)
kvm_arm_reset_vcpu(cpu);
}
#endif
+
+ hw_watchpoint_update_all(cpu);
}
#ifndef CONFIG_USER_ONLY
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 51bedc8..d1e1ccb 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -323,6 +323,8 @@ typedef struct CPUARMState {
int eabi;
#endif
+ struct CPUWatchpoint *cpu_watchpoint[16];
+
CPU_COMMON
/* These fields after the common ones so they are preserved on reset. */
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 2b95f33..103dfb8 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -2279,6 +2279,131 @@ static const ARMCPRegInfo debug_lpae_cp_reginfo[] = {
REGINFO_SENTINEL
};
+void hw_watchpoint_update(ARMCPU *cpu, int n)
+{
+ CPUARMState *env = &cpu->env;
+ vaddr len = 0;
+ vaddr wvr = env->cp15.dbgwvr[n];
+ uint64_t wcr = env->cp15.dbgwcr[n];
+ int mask;
+ int flags = BP_CPU | BP_STOP_BEFORE_ACCESS;
+
+ if (env->cpu_watchpoint[n]) {
+ cpu_watchpoint_remove_by_ref(CPU(cpu), env->cpu_watchpoint[n]);
+ env->cpu_watchpoint[n] = NULL;
+ }
+
+ if (!extract64(wcr, 0, 1)) {
+ /* E bit clear : watchpoint disabled */
+ return;
+ }
+
+ switch (extract64(wcr, 3, 2)) {
+ case 0:
+ /* LSC 00 is reserved and must behave as if the wp is disabled */
+ return;
+ case 1:
+ flags |= BP_MEM_READ;
+ break;
+ case 2:
+ flags |= BP_MEM_WRITE;
+ break;
+ case 3:
+ flags |= BP_MEM_ACCESS;
+ break;
+ }
+
+ /* Attempts to use both MASK and BAS fields simultaneously are
+ * CONSTRAINED UNPREDICTABLE; we opt to ignore BAS in this case,
+ * thus generating a watchpoint for every byte in the masked region.
+ */
+ mask = extract64(wcr, 24, 4);
+ if (mask == 1 || mask == 2) {
+ /* Reserved values of MASK; we must act as if the mask value was
+ * some non-reserved value, or as if the watchpoint were disabled.
+ * We choose the latter.
+ */
+ return;
+ } else if (mask) {
+ /* Watchpoint covers an aligned area up to 2GB in size */
+ len = 1ULL << mask;
+ /* If masked bits in WVR are not zero it's CONSTRAINED UNPREDICTABLE
+ * whether the watchpoint fires when the unmasked bits match; we opt
+ * to generate the exceptions.
+ */
+ wvr &= ~(len - 1);
+ } else {
+ /* Watchpoint covers bytes defined by the byte address select bits */
+ int bas = extract64(wcr, 5, 8);
+ int basstart;
+
+ if (bas == 0) {
+ /* This must act as if the watchpoint is disabled */
+ return;
+ }
+
+ if (extract64(wvr, 2, 1)) {
+ /* Deprecated case of an only 4-aligned address. BAS[7:4] are
+ * ignored, and BAS[3:0] define which bytes to watch.
+ */
+ bas &= 0xf;
+ }
+ /* The BAS bits are supposed to be programmed to indicate a contiguous
+ * range of bytes. Otherwise it is CONSTRAINED UNPREDICTABLE whether
+ * we fire for each byte in the word/doubleword addressed by the WVR.
+ * We choose to ignore any non-zero bits after the first range of 1s.
+ */
+ basstart = ctz32(bas);
+ len = cto32(bas >> basstart);
+ wvr += basstart;
+ }
+
+ cpu_watchpoint_insert(CPU(cpu), wvr, len, flags,
+ &env->cpu_watchpoint[n]);
+}
+
+void hw_watchpoint_update_all(ARMCPU *cpu)
+{
+ int i;
+ CPUARMState *env = &cpu->env;
+
+ /* Completely clear out existing QEMU watchpoints and our array, to
+ * avoid possible stale entries following migration load.
+ */
+ cpu_watchpoint_remove_all(CPU(cpu), BP_CPU);
+ memset(env->cpu_watchpoint, 0, sizeof(env->cpu_watchpoint));
+
+ for (i = 0; i < ARRAY_SIZE(cpu->env.cpu_watchpoint); i++) {
+ hw_watchpoint_update(cpu, i);
+ }
+}
+
+static void dbgwvr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ ARMCPU *cpu = arm_env_get_cpu(env);
+ int i = ri->crm;
+
+ /* Bits [63:49] are hardwired to the value of bit [48]; that is, the
+ * register reads and behaves as if values written are sign extended.
+ * Bits [1:0] are RES0.
+ */
+ value = sextract64(value, 0, 49) & ~3ULL;
+
+ raw_write(env, ri, value);
+ hw_watchpoint_update(cpu, i);
+}
+
+static void dbgwcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ ARMCPU *cpu = arm_env_get_cpu(env);
+ int i = ri->crm;
+
+ raw_write(env, ri, value);
+ hw_watchpoint_update(cpu, i);
+}
+
static void define_debug_regs(ARMCPU *cpu)
{
/* Define v7 and v8 architectural debug registers.
@@ -2330,12 +2455,16 @@ static void define_debug_regs(ARMCPU *cpu)
{ .name = "DBGWVR", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6,
.access = PL1_RW,
- .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]) },
+ .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]),
+ .writefn = dbgwvr_write, .raw_writefn = raw_write
+ },
{ .name = "DBGWCR", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7,
.access = PL1_RW,
- .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]) },
- REGINFO_SENTINEL
+ .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]),
+ .writefn = dbgwcr_write, .raw_writefn = raw_write
+ },
+ REGINFO_SENTINEL
};
define_arm_cp_regs(cpu, dbgregs);
}
diff --git a/target-arm/internals.h b/target-arm/internals.h
index 53c2e3c..22f382c 100644
--- a/target-arm/internals.h
+++ b/target-arm/internals.h
@@ -296,4 +296,14 @@ static inline uint32_t syn_swstep(int same_el, int isv,
int ex)
| (isv << 24) | (ex << 6) | 0x22;
}
+/* Update a QEMU watchpoint based on the information the guest has set in the
+ * DBGWCR<n>_EL1 and DBGWVR<n>_EL1 registers.
+ */
+void hw_watchpoint_update(ARMCPU *cpu, int n);
+/* Update the QEMU watchpoints for every guest watchpoint. This does a
+ * complete delete-and-reinstate of the QEMU watchpoint list and so is
+ * suitable for use after migration or on reset.
+ */
+void hw_watchpoint_update_all(ARMCPU *cpu);
+
#endif
diff --git a/target-arm/machine.c b/target-arm/machine.c
index 3bcc7cc..8dfe87c 100644
--- a/target-arm/machine.c
+++ b/target-arm/machine.c
@@ -2,6 +2,7 @@
#include "hw/boards.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
+#include "internals.h"
static bool vfp_needed(void *opaque)
{
@@ -213,6 +214,8 @@ static int cpu_post_load(void *opaque, int version_id)
}
}
+ hw_watchpoint_update_all(cpu);
+
return 0;
}
--
1.9.1
- [Qemu-devel] [PULL 00/23] target-arm queue, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 17/23] target-arm: Push legacy wildcard TLB ops back into v6, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 09/23] exec.c: Record watchpoint fault address and direction, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 18/23] target-arm: Make *IS TLB maintenance ops affect all CPUs, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 23/23] hw/arm/boot: enable DTB support when booting ELF images, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 11/23] target-arm: Implement setting of watchpoints,
Peter Maydell <=
- [Qemu-devel] [PULL 14/23] target-arm: Set DBGDSCR.MOE for debug exceptions taken to AArch32, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 10/23] cpu-exec: Make debug_excp_handler a QOM CPU method, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 16/23] target-arm: Implement minimal DBGVCR, OSDLR_EL1, MDCCSR_EL0, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 15/23] target-arm: Remove comment about MDSCR_EL1 being dummy implementation, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 19/23] hw/arm/virt: fix pl011 and pl031 irq flags, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 05/23] target-arm: Fix broken indentation in arm_cpu_reest(), Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 07/23] exec.c: Relax restrictions on watchpoint length and alignment, Peter Maydell, 2014/09/12
- [Qemu-devel] [PULL 06/23] hw/arm/virt: Provide flash devices for boot ROMs, Peter Maydell, 2014/09/12