diff options
-rw-r--r-- | target-arm/cpu.c | 2 | ||||
-rw-r--r-- | target-arm/cpu.h | 2 | ||||
-rw-r--r-- | target-arm/helper.c | 135 | ||||
-rw-r--r-- | target-arm/internals.h | 10 | ||||
-rw-r--r-- | 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 bdd23b42b6..6c40ecdeed 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 51bedc8262..d1e1ccb605 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 2b95f33872..103dfb8a9d 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 53c2e3cf3e..22f382c0c6 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 3bcc7cc833..8dfe87cb6b 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; } |