@@ -9,20 +9,31 @@
#define MDSCR_KDE (1 << 13)
#define MDSCR_MDE (1 << 15)
+#define MDSCR_SS (1 << 0)
#define DBGBCR_LEN8 (0xff << 5)
#define DBGBCR_EXEC (0x0 << 3)
#define DBGBCR_EL1 (0x1 << 1)
#define DBGBCR_E (0x1 << 0)
+#define DBGWCR_LEN8 (0xff << 5)
+#define DBGWCR_RD (0x1 << 3)
+#define DBGWCR_WR (0x2 << 3)
+#define DBGWCR_EL1 (0x1 << 1)
+#define DBGWCR_E (0x1 << 0)
+
#define SPSR_D (1 << 9)
#define ESR_EC_HW_BP_CURRENT 0x31
+#define ESR_EC_WP_CURRENT 0x35
#define ID_AA64DFR0_BRPS_SHIFT 12
#define ID_AA64DFR0_BRPS_MASK 0xf
+#define ID_AA64DFR0_WRPS_SHIFT 20
+#define ID_AA64DFR0_WRPS_MASK 0xf
static volatile uint64_t hw_bp_idx, hw_bp_addr[16];
+static volatile uint64_t wp_idx, wp_data_addr[16];
static void hw_bp_handler(struct pt_regs *regs, unsigned int esr)
{
@@ -30,6 +41,12 @@ static void hw_bp_handler(struct pt_regs *regs, unsigned int esr)
regs->pstate |= SPSR_D;
}
+static void wp_handler(struct pt_regs *regs, unsigned int esr)
+{
+ wp_data_addr[wp_idx++] = read_sysreg(far_el1);
+ regs->pstate |= SPSR_D;
+}
+
static int get_num_hw_bp(void)
{
uint64_t reg = read_sysreg(id_aa64dfr0_el1);
@@ -39,6 +56,15 @@ static int get_num_hw_bp(void)
return brps + 1;
}
+static int get_num_wp(void)
+{
+ uint64_t reg = read_sysreg(id_aa64dfr0_el1);
+ /* Number of watchpoints, minus 1 */
+ uint8_t wrps = (reg >> ID_AA64DFR0_WRPS_SHIFT) & ID_AA64DFR0_WRPS_MASK;
+
+ return wrps + 1;
+}
+
static void write_dbgbcr(int n, uint32_t bcr)
{
switch (n) {
@@ -119,9 +145,90 @@ static void write_dbgbvr(int n, uint64_t bvr)
}
}
+static void write_dbgwcr(int n, uint32_t wcr)
+{
+ switch (n) {
+ case 0:
+ write_sysreg(wcr, dbgwcr0_el1); break;
+ case 1:
+ write_sysreg(wcr, dbgwcr1_el1); break;
+ case 2:
+ write_sysreg(wcr, dbgwcr2_el1); break;
+ case 3:
+ write_sysreg(wcr, dbgwcr3_el1); break;
+ case 4:
+ write_sysreg(wcr, dbgwcr4_el1); break;
+ case 5:
+ write_sysreg(wcr, dbgwcr5_el1); break;
+ case 6:
+ write_sysreg(wcr, dbgwcr6_el1); break;
+ case 7:
+ write_sysreg(wcr, dbgwcr7_el1); break;
+ case 8:
+ write_sysreg(wcr, dbgwcr8_el1); break;
+ case 9:
+ write_sysreg(wcr, dbgwcr9_el1); break;
+ case 10:
+ write_sysreg(wcr, dbgwcr10_el1); break;
+ case 11:
+ write_sysreg(wcr, dbgwcr11_el1); break;
+ case 12:
+ write_sysreg(wcr, dbgwcr12_el1); break;
+ case 13:
+ write_sysreg(wcr, dbgwcr13_el1); break;
+ case 14:
+ write_sysreg(wcr, dbgwcr14_el1); break;
+ case 15:
+ write_sysreg(wcr, dbgwcr15_el1); break;
+ default:
+ report_abort("Invalid wcr");
+ }
+}
+
+static void write_dbgwvr(int n, uint64_t wvr)
+{
+ switch (n) {
+ case 0:
+ write_sysreg(wvr, dbgwvr0_el1); break;
+ case 1:
+ write_sysreg(wvr, dbgwvr1_el1); break;
+ case 2:
+ write_sysreg(wvr, dbgwvr2_el1); break;
+ case 3:
+ write_sysreg(wvr, dbgwvr3_el1); break;
+ case 4:
+ write_sysreg(wvr, dbgwvr4_el1); break;
+ case 5:
+ write_sysreg(wvr, dbgwvr5_el1); break;
+ case 6:
+ write_sysreg(wvr, dbgwvr6_el1); break;
+ case 7:
+ write_sysreg(wvr, dbgwvr7_el1); break;
+ case 8:
+ write_sysreg(wvr, dbgwvr8_el1); break;
+ case 9:
+ write_sysreg(wvr, dbgwvr9_el1); break;
+ case 10:
+ write_sysreg(wvr, dbgwvr10_el1); break;
+ case 11:
+ write_sysreg(wvr, dbgwvr11_el1); break;
+ case 12:
+ write_sysreg(wvr, dbgwvr12_el1); break;
+ case 13:
+ write_sysreg(wvr, dbgwvr13_el1); break;
+ case 14:
+ write_sysreg(wvr, dbgwvr14_el1); break;
+ case 15:
+ write_sysreg(wvr, dbgwvr15_el1); break;
+ default:
+ report_abort("invalid wvr");
+ }
+}
+
static void reset_debug_state(void)
{
int i, num_bp = get_num_hw_bp();
+ int num_wp = get_num_wp();
asm volatile("msr daifset, #8");
@@ -134,6 +241,10 @@ static void reset_debug_state(void)
write_dbgbvr(i, 0);
write_dbgbcr(i, 0);
}
+ for (i = 0; i < num_wp; i++) {
+ write_dbgwvr(i, 0);
+ write_dbgwcr(i, 0);
+ }
isb();
}
@@ -188,6 +299,51 @@ static void test_hw_bp(bool migrate)
report(hw_bp_addr[i] == addr, "hw breakpoint: %d", i);
}
+static volatile char write_data[16];
+
+static void test_wp(bool migrate)
+{
+ uint32_t wcr;
+ uint32_t mdscr;
+ int num_wp = get_num_wp();
+ int i;
+
+ install_exception_handler(EL1H_SYNC, ESR_EC_WP_CURRENT, wp_handler);
+
+ reset_debug_state();
+
+ wcr = DBGWCR_LEN8 | DBGWCR_RD | DBGWCR_WR | DBGWCR_EL1 | DBGWCR_E;
+ for (i = 0; i < num_wp; i++) {
+ write_dbgwcr(i, wcr);
+ write_dbgwvr(i, (uint64_t)&write_data[i]);
+ }
+ isb();
+
+ asm volatile("msr daifclr, #8");
+
+ mdscr = read_sysreg(mdscr_el1) | MDSCR_KDE | MDSCR_MDE;
+ write_sysreg(mdscr, mdscr_el1);
+ isb();
+
+ if (migrate) {
+ do_migrate();
+ report(num_wp == get_num_wp(), "wrps match after migrate");
+ }
+
+ wp_idx = 0;
+
+ for (i = 0; i < num_wp; i++) {
+ write_data[i] = i;
+ asm volatile("msr daifclr, #8");
+ }
+
+ for (i = 0; i < num_wp; i++) {
+ report(wp_data_addr[i] == (uint64_t)&write_data[i],
+ "watchpoint received: %d", i);
+ report(write_data[i] == i, "watchpoint data: %d", i);
+ }
+}
+
int main(int argc, char **argv)
{
if (argc < 2)
@@ -201,6 +357,14 @@ int main(int argc, char **argv)
report_prefix_push(argv[1]);
test_hw_bp(true);
report_prefix_pop();
+ } else if (strcmp(argv[1], "wp") == 0) {
+ report_prefix_push(argv[1]);
+ test_wp(false);
+ report_prefix_pop();
+ } else if (strcmp(argv[1], "wp-migration") == 0) {
+ report_prefix_push(argv[1]);
+ test_wp(true);
+ report_prefix_pop();
} else {
report_abort("Unknown subtest '%s'", argv[1]);
}
@@ -254,3 +254,15 @@ file = debug.flat
arch = arm64
extra_params = -append 'bp-migration'
groups = debug migration
+
+[debug-wp]
+file = debug.flat
+arch = arm64
+extra_params = -append 'wp'
+groups = debug
+
+[debug-wp-migration]
+file = debug.flat
+arch = arm64
+extra_params = -append 'wp-migration'
+groups = debug migration
Test the migration of watchpoint state. Program as many watchpoints as possible, migrate, and check that we get the corresponding exceptions. Signed-off-by: Ricardo Koller <ricarkol@google.com> --- arm/debug.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++ arm/unittests.cfg | 12 ++++ 2 files changed, 176 insertions(+)