608 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <test_util.h>3#include <kvm_util.h>4#include <processor.h>5#include <linux/bitfield.h>6 7#define MDSCR_KDE (1 << 13)8#define MDSCR_MDE (1 << 15)9#define MDSCR_SS (1 << 0)10 11#define DBGBCR_LEN8 (0xff << 5)12#define DBGBCR_EXEC (0x0 << 3)13#define DBGBCR_EL1 (0x1 << 1)14#define DBGBCR_E (0x1 << 0)15#define DBGBCR_LBN_SHIFT 1616#define DBGBCR_BT_SHIFT 2017#define DBGBCR_BT_ADDR_LINK_CTX (0x1 << DBGBCR_BT_SHIFT)18#define DBGBCR_BT_CTX_LINK (0x3 << DBGBCR_BT_SHIFT)19 20#define DBGWCR_LEN8 (0xff << 5)21#define DBGWCR_RD (0x1 << 3)22#define DBGWCR_WR (0x2 << 3)23#define DBGWCR_EL1 (0x1 << 1)24#define DBGWCR_E (0x1 << 0)25#define DBGWCR_LBN_SHIFT 1626#define DBGWCR_WT_SHIFT 2027#define DBGWCR_WT_LINK (0x1 << DBGWCR_WT_SHIFT)28 29#define SPSR_D (1 << 9)30#define SPSR_SS (1 << 21)31 32extern unsigned char sw_bp, sw_bp2, hw_bp, hw_bp2, bp_svc, bp_brk, hw_wp, ss_start, hw_bp_ctx;33extern unsigned char iter_ss_begin, iter_ss_end;34static volatile uint64_t sw_bp_addr, hw_bp_addr;35static volatile uint64_t wp_addr, wp_data_addr;36static volatile uint64_t svc_addr;37static volatile uint64_t ss_addr[4], ss_idx;38#define PC(v) ((uint64_t)&(v))39 40#define GEN_DEBUG_WRITE_REG(reg_name) \41static void write_##reg_name(int num, uint64_t val) \42{ \43 switch (num) { \44 case 0: \45 write_sysreg(val, reg_name##0_el1); \46 break; \47 case 1: \48 write_sysreg(val, reg_name##1_el1); \49 break; \50 case 2: \51 write_sysreg(val, reg_name##2_el1); \52 break; \53 case 3: \54 write_sysreg(val, reg_name##3_el1); \55 break; \56 case 4: \57 write_sysreg(val, reg_name##4_el1); \58 break; \59 case 5: \60 write_sysreg(val, reg_name##5_el1); \61 break; \62 case 6: \63 write_sysreg(val, reg_name##6_el1); \64 break; \65 case 7: \66 write_sysreg(val, reg_name##7_el1); \67 break; \68 case 8: \69 write_sysreg(val, reg_name##8_el1); \70 break; \71 case 9: \72 write_sysreg(val, reg_name##9_el1); \73 break; \74 case 10: \75 write_sysreg(val, reg_name##10_el1); \76 break; \77 case 11: \78 write_sysreg(val, reg_name##11_el1); \79 break; \80 case 12: \81 write_sysreg(val, reg_name##12_el1); \82 break; \83 case 13: \84 write_sysreg(val, reg_name##13_el1); \85 break; \86 case 14: \87 write_sysreg(val, reg_name##14_el1); \88 break; \89 case 15: \90 write_sysreg(val, reg_name##15_el1); \91 break; \92 default: \93 GUEST_ASSERT(0); \94 } \95}96 97/* Define write_dbgbcr()/write_dbgbvr()/write_dbgwcr()/write_dbgwvr() */98GEN_DEBUG_WRITE_REG(dbgbcr)99GEN_DEBUG_WRITE_REG(dbgbvr)100GEN_DEBUG_WRITE_REG(dbgwcr)101GEN_DEBUG_WRITE_REG(dbgwvr)102 103static void reset_debug_state(void)104{105 uint8_t brps, wrps, i;106 uint64_t dfr0;107 108 asm volatile("msr daifset, #8");109 110 write_sysreg(0, osdlr_el1);111 write_sysreg(0, oslar_el1);112 isb();113 114 write_sysreg(0, mdscr_el1);115 write_sysreg(0, contextidr_el1);116 117 /* Reset all bcr/bvr/wcr/wvr registers */118 dfr0 = read_sysreg(id_aa64dfr0_el1);119 brps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_BRPs), dfr0);120 for (i = 0; i <= brps; i++) {121 write_dbgbcr(i, 0);122 write_dbgbvr(i, 0);123 }124 wrps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_WRPs), dfr0);125 for (i = 0; i <= wrps; i++) {126 write_dbgwcr(i, 0);127 write_dbgwvr(i, 0);128 }129 130 isb();131}132 133static void enable_os_lock(void)134{135 write_sysreg(1, oslar_el1);136 isb();137 138 GUEST_ASSERT(read_sysreg(oslsr_el1) & 2);139}140 141static void enable_monitor_debug_exceptions(void)142{143 uint32_t mdscr;144 145 asm volatile("msr daifclr, #8");146 147 mdscr = read_sysreg(mdscr_el1) | MDSCR_KDE | MDSCR_MDE;148 write_sysreg(mdscr, mdscr_el1);149 isb();150}151 152static void install_wp(uint8_t wpn, uint64_t addr)153{154 uint32_t wcr;155 156 wcr = DBGWCR_LEN8 | DBGWCR_RD | DBGWCR_WR | DBGWCR_EL1 | DBGWCR_E;157 write_dbgwcr(wpn, wcr);158 write_dbgwvr(wpn, addr);159 160 isb();161 162 enable_monitor_debug_exceptions();163}164 165static void install_hw_bp(uint8_t bpn, uint64_t addr)166{167 uint32_t bcr;168 169 bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E;170 write_dbgbcr(bpn, bcr);171 write_dbgbvr(bpn, addr);172 isb();173 174 enable_monitor_debug_exceptions();175}176 177static void install_wp_ctx(uint8_t addr_wp, uint8_t ctx_bp, uint64_t addr,178 uint64_t ctx)179{180 uint32_t wcr;181 uint64_t ctx_bcr;182 183 /* Setup a context-aware breakpoint for Linked Context ID Match */184 ctx_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E |185 DBGBCR_BT_CTX_LINK;186 write_dbgbcr(ctx_bp, ctx_bcr);187 write_dbgbvr(ctx_bp, ctx);188 189 /* Setup a linked watchpoint (linked to the context-aware breakpoint) */190 wcr = DBGWCR_LEN8 | DBGWCR_RD | DBGWCR_WR | DBGWCR_EL1 | DBGWCR_E |191 DBGWCR_WT_LINK | ((uint32_t)ctx_bp << DBGWCR_LBN_SHIFT);192 write_dbgwcr(addr_wp, wcr);193 write_dbgwvr(addr_wp, addr);194 isb();195 196 enable_monitor_debug_exceptions();197}198 199void install_hw_bp_ctx(uint8_t addr_bp, uint8_t ctx_bp, uint64_t addr,200 uint64_t ctx)201{202 uint32_t addr_bcr, ctx_bcr;203 204 /* Setup a context-aware breakpoint for Linked Context ID Match */205 ctx_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E |206 DBGBCR_BT_CTX_LINK;207 write_dbgbcr(ctx_bp, ctx_bcr);208 write_dbgbvr(ctx_bp, ctx);209 210 /*211 * Setup a normal breakpoint for Linked Address Match, and link it212 * to the context-aware breakpoint.213 */214 addr_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E |215 DBGBCR_BT_ADDR_LINK_CTX |216 ((uint32_t)ctx_bp << DBGBCR_LBN_SHIFT);217 write_dbgbcr(addr_bp, addr_bcr);218 write_dbgbvr(addr_bp, addr);219 isb();220 221 enable_monitor_debug_exceptions();222}223 224static void install_ss(void)225{226 uint32_t mdscr;227 228 asm volatile("msr daifclr, #8");229 230 mdscr = read_sysreg(mdscr_el1) | MDSCR_KDE | MDSCR_SS;231 write_sysreg(mdscr, mdscr_el1);232 isb();233}234 235static volatile char write_data;236 237static void guest_code(uint8_t bpn, uint8_t wpn, uint8_t ctx_bpn)238{239 uint64_t ctx = 0xabcdef; /* a random context number */240 241 /* Software-breakpoint */242 reset_debug_state();243 asm volatile("sw_bp: brk #0");244 GUEST_ASSERT_EQ(sw_bp_addr, PC(sw_bp));245 246 /* Hardware-breakpoint */247 reset_debug_state();248 install_hw_bp(bpn, PC(hw_bp));249 asm volatile("hw_bp: nop");250 GUEST_ASSERT_EQ(hw_bp_addr, PC(hw_bp));251 252 /* Hardware-breakpoint + svc */253 reset_debug_state();254 install_hw_bp(bpn, PC(bp_svc));255 asm volatile("bp_svc: svc #0");256 GUEST_ASSERT_EQ(hw_bp_addr, PC(bp_svc));257 GUEST_ASSERT_EQ(svc_addr, PC(bp_svc) + 4);258 259 /* Hardware-breakpoint + software-breakpoint */260 reset_debug_state();261 install_hw_bp(bpn, PC(bp_brk));262 asm volatile("bp_brk: brk #0");263 GUEST_ASSERT_EQ(sw_bp_addr, PC(bp_brk));264 GUEST_ASSERT_EQ(hw_bp_addr, PC(bp_brk));265 266 /* Watchpoint */267 reset_debug_state();268 install_wp(wpn, PC(write_data));269 write_data = 'x';270 GUEST_ASSERT_EQ(write_data, 'x');271 GUEST_ASSERT_EQ(wp_data_addr, PC(write_data));272 273 /* Single-step */274 reset_debug_state();275 install_ss();276 ss_idx = 0;277 asm volatile("ss_start:\n"278 "mrs x0, esr_el1\n"279 "add x0, x0, #1\n"280 "msr daifset, #8\n"281 : : : "x0");282 GUEST_ASSERT_EQ(ss_addr[0], PC(ss_start));283 GUEST_ASSERT_EQ(ss_addr[1], PC(ss_start) + 4);284 GUEST_ASSERT_EQ(ss_addr[2], PC(ss_start) + 8);285 286 /* OS Lock does not block software-breakpoint */287 reset_debug_state();288 enable_os_lock();289 sw_bp_addr = 0;290 asm volatile("sw_bp2: brk #0");291 GUEST_ASSERT_EQ(sw_bp_addr, PC(sw_bp2));292 293 /* OS Lock blocking hardware-breakpoint */294 reset_debug_state();295 enable_os_lock();296 install_hw_bp(bpn, PC(hw_bp2));297 hw_bp_addr = 0;298 asm volatile("hw_bp2: nop");299 GUEST_ASSERT_EQ(hw_bp_addr, 0);300 301 /* OS Lock blocking watchpoint */302 reset_debug_state();303 enable_os_lock();304 write_data = '\0';305 wp_data_addr = 0;306 install_wp(wpn, PC(write_data));307 write_data = 'x';308 GUEST_ASSERT_EQ(write_data, 'x');309 GUEST_ASSERT_EQ(wp_data_addr, 0);310 311 /* OS Lock blocking single-step */312 reset_debug_state();313 enable_os_lock();314 ss_addr[0] = 0;315 install_ss();316 ss_idx = 0;317 asm volatile("mrs x0, esr_el1\n\t"318 "add x0, x0, #1\n\t"319 "msr daifset, #8\n\t"320 : : : "x0");321 GUEST_ASSERT_EQ(ss_addr[0], 0);322 323 /* Linked hardware-breakpoint */324 hw_bp_addr = 0;325 reset_debug_state();326 install_hw_bp_ctx(bpn, ctx_bpn, PC(hw_bp_ctx), ctx);327 /* Set context id */328 write_sysreg(ctx, contextidr_el1);329 isb();330 asm volatile("hw_bp_ctx: nop");331 write_sysreg(0, contextidr_el1);332 GUEST_ASSERT_EQ(hw_bp_addr, PC(hw_bp_ctx));333 334 /* Linked watchpoint */335 reset_debug_state();336 install_wp_ctx(wpn, ctx_bpn, PC(write_data), ctx);337 /* Set context id */338 write_sysreg(ctx, contextidr_el1);339 isb();340 write_data = 'x';341 GUEST_ASSERT_EQ(write_data, 'x');342 GUEST_ASSERT_EQ(wp_data_addr, PC(write_data));343 344 GUEST_DONE();345}346 347static void guest_sw_bp_handler(struct ex_regs *regs)348{349 sw_bp_addr = regs->pc;350 regs->pc += 4;351}352 353static void guest_hw_bp_handler(struct ex_regs *regs)354{355 hw_bp_addr = regs->pc;356 regs->pstate |= SPSR_D;357}358 359static void guest_wp_handler(struct ex_regs *regs)360{361 wp_data_addr = read_sysreg(far_el1);362 wp_addr = regs->pc;363 regs->pstate |= SPSR_D;364}365 366static void guest_ss_handler(struct ex_regs *regs)367{368 __GUEST_ASSERT(ss_idx < 4, "Expected index < 4, got '%lu'", ss_idx);369 ss_addr[ss_idx++] = regs->pc;370 regs->pstate |= SPSR_SS;371}372 373static void guest_svc_handler(struct ex_regs *regs)374{375 svc_addr = regs->pc;376}377 378static void guest_code_ss(int test_cnt)379{380 uint64_t i;381 uint64_t bvr, wvr, w_bvr, w_wvr;382 383 for (i = 0; i < test_cnt; i++) {384 /* Bits [1:0] of dbg{b,w}vr are RES0 */385 w_bvr = i << 2;386 w_wvr = i << 2;387 388 /*389 * Enable Single Step execution. Note! This _must_ be a bare390 * ucall as the ucall() path uses atomic operations to manage391 * the ucall structures, and the built-in "atomics" are usually392 * implemented via exclusive access instructions. The exlusive393 * monitor is cleared on ERET, and so taking debug exceptions394 * during a LDREX=>STREX sequence will prevent forward progress395 * and hang the guest/test.396 */397 GUEST_UCALL_NONE();398 399 /*400 * The userspace will verify that the pc is as expected during401 * single step execution between iter_ss_begin and iter_ss_end.402 */403 asm volatile("iter_ss_begin:nop\n");404 405 write_sysreg(w_bvr, dbgbvr0_el1);406 write_sysreg(w_wvr, dbgwvr0_el1);407 bvr = read_sysreg(dbgbvr0_el1);408 wvr = read_sysreg(dbgwvr0_el1);409 410 /* Userspace disables Single Step when the end is nigh. */411 asm volatile("iter_ss_end:\n");412 413 GUEST_ASSERT_EQ(bvr, w_bvr);414 GUEST_ASSERT_EQ(wvr, w_wvr);415 }416 GUEST_DONE();417}418 419static int debug_version(uint64_t id_aa64dfr0)420{421 return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), id_aa64dfr0);422}423 424static void test_guest_debug_exceptions(uint8_t bpn, uint8_t wpn, uint8_t ctx_bpn)425{426 struct kvm_vcpu *vcpu;427 struct kvm_vm *vm;428 struct ucall uc;429 430 vm = vm_create_with_one_vcpu(&vcpu, guest_code);431 432 vm_init_descriptor_tables(vm);433 vcpu_init_descriptor_tables(vcpu);434 435 vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,436 ESR_EC_BRK_INS, guest_sw_bp_handler);437 vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,438 ESR_EC_HW_BP_CURRENT, guest_hw_bp_handler);439 vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,440 ESR_EC_WP_CURRENT, guest_wp_handler);441 vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,442 ESR_EC_SSTEP_CURRENT, guest_ss_handler);443 vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,444 ESR_EC_SVC64, guest_svc_handler);445 446 /* Specify bpn/wpn/ctx_bpn to be tested */447 vcpu_args_set(vcpu, 3, bpn, wpn, ctx_bpn);448 pr_debug("Use bpn#%d, wpn#%d and ctx_bpn#%d\n", bpn, wpn, ctx_bpn);449 450 vcpu_run(vcpu);451 switch (get_ucall(vcpu, &uc)) {452 case UCALL_ABORT:453 REPORT_GUEST_ASSERT(uc);454 break;455 case UCALL_DONE:456 goto done;457 default:458 TEST_FAIL("Unknown ucall %lu", uc.cmd);459 }460 461done:462 kvm_vm_free(vm);463}464 465void test_single_step_from_userspace(int test_cnt)466{467 struct kvm_vcpu *vcpu;468 struct kvm_vm *vm;469 struct ucall uc;470 struct kvm_run *run;471 uint64_t pc, cmd;472 uint64_t test_pc = 0;473 bool ss_enable = false;474 struct kvm_guest_debug debug = {};475 476 vm = vm_create_with_one_vcpu(&vcpu, guest_code_ss);477 run = vcpu->run;478 vcpu_args_set(vcpu, 1, test_cnt);479 480 while (1) {481 vcpu_run(vcpu);482 if (run->exit_reason != KVM_EXIT_DEBUG) {483 cmd = get_ucall(vcpu, &uc);484 if (cmd == UCALL_ABORT) {485 REPORT_GUEST_ASSERT(uc);486 /* NOT REACHED */487 } else if (cmd == UCALL_DONE) {488 break;489 }490 491 TEST_ASSERT(cmd == UCALL_NONE,492 "Unexpected ucall cmd 0x%lx", cmd);493 494 debug.control = KVM_GUESTDBG_ENABLE |495 KVM_GUESTDBG_SINGLESTEP;496 ss_enable = true;497 vcpu_guest_debug_set(vcpu, &debug);498 continue;499 }500 501 TEST_ASSERT(ss_enable, "Unexpected KVM_EXIT_DEBUG");502 503 /* Check if the current pc is expected. */504 vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc), &pc);505 TEST_ASSERT(!test_pc || pc == test_pc,506 "Unexpected pc 0x%lx (expected 0x%lx)",507 pc, test_pc);508 509 if ((pc + 4) == (uint64_t)&iter_ss_end) {510 test_pc = 0;511 debug.control = KVM_GUESTDBG_ENABLE;512 ss_enable = false;513 vcpu_guest_debug_set(vcpu, &debug);514 continue;515 }516 517 /*518 * If the current pc is between iter_ss_bgin and519 * iter_ss_end, the pc for the next KVM_EXIT_DEBUG should520 * be the current pc + 4.521 */522 if ((pc >= (uint64_t)&iter_ss_begin) &&523 (pc < (uint64_t)&iter_ss_end))524 test_pc = pc + 4;525 else526 test_pc = 0;527 }528 529 kvm_vm_free(vm);530}531 532/*533 * Run debug testing using the various breakpoint#, watchpoint# and534 * context-aware breakpoint# with the given ID_AA64DFR0_EL1 configuration.535 */536void test_guest_debug_exceptions_all(uint64_t aa64dfr0)537{538 uint8_t brp_num, wrp_num, ctx_brp_num, normal_brp_num, ctx_brp_base;539 int b, w, c;540 541 /* Number of breakpoints */542 brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_BRPs), aa64dfr0) + 1;543 __TEST_REQUIRE(brp_num >= 2, "At least two breakpoints are required");544 545 /* Number of watchpoints */546 wrp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_WRPs), aa64dfr0) + 1;547 548 /* Number of context aware breakpoints */549 ctx_brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_CTX_CMPs), aa64dfr0) + 1;550 551 pr_debug("%s brp_num:%d, wrp_num:%d, ctx_brp_num:%d\n", __func__,552 brp_num, wrp_num, ctx_brp_num);553 554 /* Number of normal (non-context aware) breakpoints */555 normal_brp_num = brp_num - ctx_brp_num;556 557 /* Lowest context aware breakpoint number */558 ctx_brp_base = normal_brp_num;559 560 /* Run tests with all supported breakpoints/watchpoints */561 for (c = ctx_brp_base; c < ctx_brp_base + ctx_brp_num; c++) {562 for (b = 0; b < normal_brp_num; b++) {563 for (w = 0; w < wrp_num; w++)564 test_guest_debug_exceptions(b, w, c);565 }566 }567}568 569static void help(char *name)570{571 puts("");572 printf("Usage: %s [-h] [-i iterations of the single step test]\n", name);573 puts("");574 exit(0);575}576 577int main(int argc, char *argv[])578{579 struct kvm_vcpu *vcpu;580 struct kvm_vm *vm;581 int opt;582 int ss_iteration = 10000;583 uint64_t aa64dfr0;584 585 vm = vm_create_with_one_vcpu(&vcpu, guest_code);586 vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64DFR0_EL1), &aa64dfr0);587 __TEST_REQUIRE(debug_version(aa64dfr0) >= 6,588 "Armv8 debug architecture not supported.");589 kvm_vm_free(vm);590 591 while ((opt = getopt(argc, argv, "i:")) != -1) {592 switch (opt) {593 case 'i':594 ss_iteration = atoi_positive("Number of iterations", optarg);595 break;596 case 'h':597 default:598 help(argv[0]);599 break;600 }601 }602 603 test_guest_debug_exceptions_all(aa64dfr0);604 test_single_step_from_userspace(ss_iteration);605 606 return 0;607}608