qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Peter Maydell <peter.maydell@linaro.org>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PULL 11/23] target-arm: Implement setting of watchpoints
Date: Fri, 12 Sep 2014 14:23:42 +0100	[thread overview]
Message-ID: <1410528234-13545-12-git-send-email-peter.maydell@linaro.org> (raw)
In-Reply-To: <1410528234-13545-1-git-send-email-peter.maydell@linaro.org>

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 <peter.maydell@linaro.org>
---
 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

  parent reply	other threads:[~2014-09-12 13:24 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-09-12 13:23 [Qemu-devel] [PULL 00/23] target-arm queue Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 01/23] hw/arm/virt: add linux, stdout-path to /chosen DT node Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 02/23] cpu-exec.c: Allow disabling of IRQs on ARM Cortex-M CPUs Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 03/23] pl061: implement input interrupt logic Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 04/23] target-arm: Fix resetting issues on ARMv7-M CPUs Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 05/23] target-arm: Fix broken indentation in arm_cpu_reest() Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 06/23] hw/arm/virt: Provide flash devices for boot ROMs Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 07/23] exec.c: Relax restrictions on watchpoint length and alignment Peter Maydell
2014-09-18  4:48   ` Max Filippov
2014-09-18  4:58     ` Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 08/23] exec.c: Provide full set of dummy wp remove functions in user-mode Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 09/23] exec.c: Record watchpoint fault address and direction Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 10/23] cpu-exec: Make debug_excp_handler a QOM CPU method Peter Maydell
2014-09-12 13:23 ` Peter Maydell [this message]
2014-09-12 13:23 ` [Qemu-devel] [PULL 12/23] target-arm: Move extended_addresses_enabled() to internals.h Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 13/23] target-arm: Implement handling of fired watchpoints Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 14/23] target-arm: Set DBGDSCR.MOE for debug exceptions taken to AArch32 Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 15/23] target-arm: Remove comment about MDSCR_EL1 being dummy implementation Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 16/23] target-arm: Implement minimal DBGVCR, OSDLR_EL1, MDCCSR_EL0 Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 17/23] target-arm: Push legacy wildcard TLB ops back into v6 Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 18/23] target-arm: Make *IS TLB maintenance ops affect all CPUs Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 19/23] hw/arm/virt: fix pl011 and pl031 irq flags Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 20/23] hw/arm/boot: load DTB as a ROM image Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 21/23] hw/arm/boot: pass an address limit to and return size from load_dtb() Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 22/23] hw/arm/boot: load device tree to base of DRAM if no -kernel option was passed Peter Maydell
2014-09-12 13:23 ` [Qemu-devel] [PULL 23/23] hw/arm/boot: enable DTB support when booting ELF images Peter Maydell
2014-09-12 15:55 ` [Qemu-devel] [PULL 00/23] target-arm queue Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1410528234-13545-12-git-send-email-peter.maydell@linaro.org \
    --to=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).