162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <test_util.h> 362306a36Sopenharmony_ci#include <kvm_util.h> 462306a36Sopenharmony_ci#include <processor.h> 562306a36Sopenharmony_ci#include <linux/bitfield.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define MDSCR_KDE (1 << 13) 862306a36Sopenharmony_ci#define MDSCR_MDE (1 << 15) 962306a36Sopenharmony_ci#define MDSCR_SS (1 << 0) 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define DBGBCR_LEN8 (0xff << 5) 1262306a36Sopenharmony_ci#define DBGBCR_EXEC (0x0 << 3) 1362306a36Sopenharmony_ci#define DBGBCR_EL1 (0x1 << 1) 1462306a36Sopenharmony_ci#define DBGBCR_E (0x1 << 0) 1562306a36Sopenharmony_ci#define DBGBCR_LBN_SHIFT 16 1662306a36Sopenharmony_ci#define DBGBCR_BT_SHIFT 20 1762306a36Sopenharmony_ci#define DBGBCR_BT_ADDR_LINK_CTX (0x1 << DBGBCR_BT_SHIFT) 1862306a36Sopenharmony_ci#define DBGBCR_BT_CTX_LINK (0x3 << DBGBCR_BT_SHIFT) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define DBGWCR_LEN8 (0xff << 5) 2162306a36Sopenharmony_ci#define DBGWCR_RD (0x1 << 3) 2262306a36Sopenharmony_ci#define DBGWCR_WR (0x2 << 3) 2362306a36Sopenharmony_ci#define DBGWCR_EL1 (0x1 << 1) 2462306a36Sopenharmony_ci#define DBGWCR_E (0x1 << 0) 2562306a36Sopenharmony_ci#define DBGWCR_LBN_SHIFT 16 2662306a36Sopenharmony_ci#define DBGWCR_WT_SHIFT 20 2762306a36Sopenharmony_ci#define DBGWCR_WT_LINK (0x1 << DBGWCR_WT_SHIFT) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define SPSR_D (1 << 9) 3062306a36Sopenharmony_ci#define SPSR_SS (1 << 21) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciextern unsigned char sw_bp, sw_bp2, hw_bp, hw_bp2, bp_svc, bp_brk, hw_wp, ss_start, hw_bp_ctx; 3362306a36Sopenharmony_ciextern unsigned char iter_ss_begin, iter_ss_end; 3462306a36Sopenharmony_cistatic volatile uint64_t sw_bp_addr, hw_bp_addr; 3562306a36Sopenharmony_cistatic volatile uint64_t wp_addr, wp_data_addr; 3662306a36Sopenharmony_cistatic volatile uint64_t svc_addr; 3762306a36Sopenharmony_cistatic volatile uint64_t ss_addr[4], ss_idx; 3862306a36Sopenharmony_ci#define PC(v) ((uint64_t)&(v)) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define GEN_DEBUG_WRITE_REG(reg_name) \ 4162306a36Sopenharmony_cistatic void write_##reg_name(int num, uint64_t val) \ 4262306a36Sopenharmony_ci{ \ 4362306a36Sopenharmony_ci switch (num) { \ 4462306a36Sopenharmony_ci case 0: \ 4562306a36Sopenharmony_ci write_sysreg(val, reg_name##0_el1); \ 4662306a36Sopenharmony_ci break; \ 4762306a36Sopenharmony_ci case 1: \ 4862306a36Sopenharmony_ci write_sysreg(val, reg_name##1_el1); \ 4962306a36Sopenharmony_ci break; \ 5062306a36Sopenharmony_ci case 2: \ 5162306a36Sopenharmony_ci write_sysreg(val, reg_name##2_el1); \ 5262306a36Sopenharmony_ci break; \ 5362306a36Sopenharmony_ci case 3: \ 5462306a36Sopenharmony_ci write_sysreg(val, reg_name##3_el1); \ 5562306a36Sopenharmony_ci break; \ 5662306a36Sopenharmony_ci case 4: \ 5762306a36Sopenharmony_ci write_sysreg(val, reg_name##4_el1); \ 5862306a36Sopenharmony_ci break; \ 5962306a36Sopenharmony_ci case 5: \ 6062306a36Sopenharmony_ci write_sysreg(val, reg_name##5_el1); \ 6162306a36Sopenharmony_ci break; \ 6262306a36Sopenharmony_ci case 6: \ 6362306a36Sopenharmony_ci write_sysreg(val, reg_name##6_el1); \ 6462306a36Sopenharmony_ci break; \ 6562306a36Sopenharmony_ci case 7: \ 6662306a36Sopenharmony_ci write_sysreg(val, reg_name##7_el1); \ 6762306a36Sopenharmony_ci break; \ 6862306a36Sopenharmony_ci case 8: \ 6962306a36Sopenharmony_ci write_sysreg(val, reg_name##8_el1); \ 7062306a36Sopenharmony_ci break; \ 7162306a36Sopenharmony_ci case 9: \ 7262306a36Sopenharmony_ci write_sysreg(val, reg_name##9_el1); \ 7362306a36Sopenharmony_ci break; \ 7462306a36Sopenharmony_ci case 10: \ 7562306a36Sopenharmony_ci write_sysreg(val, reg_name##10_el1); \ 7662306a36Sopenharmony_ci break; \ 7762306a36Sopenharmony_ci case 11: \ 7862306a36Sopenharmony_ci write_sysreg(val, reg_name##11_el1); \ 7962306a36Sopenharmony_ci break; \ 8062306a36Sopenharmony_ci case 12: \ 8162306a36Sopenharmony_ci write_sysreg(val, reg_name##12_el1); \ 8262306a36Sopenharmony_ci break; \ 8362306a36Sopenharmony_ci case 13: \ 8462306a36Sopenharmony_ci write_sysreg(val, reg_name##13_el1); \ 8562306a36Sopenharmony_ci break; \ 8662306a36Sopenharmony_ci case 14: \ 8762306a36Sopenharmony_ci write_sysreg(val, reg_name##14_el1); \ 8862306a36Sopenharmony_ci break; \ 8962306a36Sopenharmony_ci case 15: \ 9062306a36Sopenharmony_ci write_sysreg(val, reg_name##15_el1); \ 9162306a36Sopenharmony_ci break; \ 9262306a36Sopenharmony_ci default: \ 9362306a36Sopenharmony_ci GUEST_ASSERT(0); \ 9462306a36Sopenharmony_ci } \ 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* Define write_dbgbcr()/write_dbgbvr()/write_dbgwcr()/write_dbgwvr() */ 9862306a36Sopenharmony_ciGEN_DEBUG_WRITE_REG(dbgbcr) 9962306a36Sopenharmony_ciGEN_DEBUG_WRITE_REG(dbgbvr) 10062306a36Sopenharmony_ciGEN_DEBUG_WRITE_REG(dbgwcr) 10162306a36Sopenharmony_ciGEN_DEBUG_WRITE_REG(dbgwvr) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void reset_debug_state(void) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci uint8_t brps, wrps, i; 10662306a36Sopenharmony_ci uint64_t dfr0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci asm volatile("msr daifset, #8"); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci write_sysreg(0, osdlr_el1); 11162306a36Sopenharmony_ci write_sysreg(0, oslar_el1); 11262306a36Sopenharmony_ci isb(); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci write_sysreg(0, mdscr_el1); 11562306a36Sopenharmony_ci write_sysreg(0, contextidr_el1); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Reset all bcr/bvr/wcr/wvr registers */ 11862306a36Sopenharmony_ci dfr0 = read_sysreg(id_aa64dfr0_el1); 11962306a36Sopenharmony_ci brps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_BRPS), dfr0); 12062306a36Sopenharmony_ci for (i = 0; i <= brps; i++) { 12162306a36Sopenharmony_ci write_dbgbcr(i, 0); 12262306a36Sopenharmony_ci write_dbgbvr(i, 0); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci wrps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_WRPS), dfr0); 12562306a36Sopenharmony_ci for (i = 0; i <= wrps; i++) { 12662306a36Sopenharmony_ci write_dbgwcr(i, 0); 12762306a36Sopenharmony_ci write_dbgwvr(i, 0); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci isb(); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void enable_os_lock(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci write_sysreg(1, oslar_el1); 13662306a36Sopenharmony_ci isb(); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci GUEST_ASSERT(read_sysreg(oslsr_el1) & 2); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void enable_monitor_debug_exceptions(void) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci uint32_t mdscr; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci asm volatile("msr daifclr, #8"); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci mdscr = read_sysreg(mdscr_el1) | MDSCR_KDE | MDSCR_MDE; 14862306a36Sopenharmony_ci write_sysreg(mdscr, mdscr_el1); 14962306a36Sopenharmony_ci isb(); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void install_wp(uint8_t wpn, uint64_t addr) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci uint32_t wcr; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci wcr = DBGWCR_LEN8 | DBGWCR_RD | DBGWCR_WR | DBGWCR_EL1 | DBGWCR_E; 15762306a36Sopenharmony_ci write_dbgwcr(wpn, wcr); 15862306a36Sopenharmony_ci write_dbgwvr(wpn, addr); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci isb(); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci enable_monitor_debug_exceptions(); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void install_hw_bp(uint8_t bpn, uint64_t addr) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci uint32_t bcr; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E; 17062306a36Sopenharmony_ci write_dbgbcr(bpn, bcr); 17162306a36Sopenharmony_ci write_dbgbvr(bpn, addr); 17262306a36Sopenharmony_ci isb(); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci enable_monitor_debug_exceptions(); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void install_wp_ctx(uint8_t addr_wp, uint8_t ctx_bp, uint64_t addr, 17862306a36Sopenharmony_ci uint64_t ctx) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci uint32_t wcr; 18162306a36Sopenharmony_ci uint64_t ctx_bcr; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Setup a context-aware breakpoint for Linked Context ID Match */ 18462306a36Sopenharmony_ci ctx_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E | 18562306a36Sopenharmony_ci DBGBCR_BT_CTX_LINK; 18662306a36Sopenharmony_ci write_dbgbcr(ctx_bp, ctx_bcr); 18762306a36Sopenharmony_ci write_dbgbvr(ctx_bp, ctx); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Setup a linked watchpoint (linked to the context-aware breakpoint) */ 19062306a36Sopenharmony_ci wcr = DBGWCR_LEN8 | DBGWCR_RD | DBGWCR_WR | DBGWCR_EL1 | DBGWCR_E | 19162306a36Sopenharmony_ci DBGWCR_WT_LINK | ((uint32_t)ctx_bp << DBGWCR_LBN_SHIFT); 19262306a36Sopenharmony_ci write_dbgwcr(addr_wp, wcr); 19362306a36Sopenharmony_ci write_dbgwvr(addr_wp, addr); 19462306a36Sopenharmony_ci isb(); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci enable_monitor_debug_exceptions(); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_civoid install_hw_bp_ctx(uint8_t addr_bp, uint8_t ctx_bp, uint64_t addr, 20062306a36Sopenharmony_ci uint64_t ctx) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci uint32_t addr_bcr, ctx_bcr; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Setup a context-aware breakpoint for Linked Context ID Match */ 20562306a36Sopenharmony_ci ctx_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E | 20662306a36Sopenharmony_ci DBGBCR_BT_CTX_LINK; 20762306a36Sopenharmony_ci write_dbgbcr(ctx_bp, ctx_bcr); 20862306a36Sopenharmony_ci write_dbgbvr(ctx_bp, ctx); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * Setup a normal breakpoint for Linked Address Match, and link it 21262306a36Sopenharmony_ci * to the context-aware breakpoint. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci addr_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E | 21562306a36Sopenharmony_ci DBGBCR_BT_ADDR_LINK_CTX | 21662306a36Sopenharmony_ci ((uint32_t)ctx_bp << DBGBCR_LBN_SHIFT); 21762306a36Sopenharmony_ci write_dbgbcr(addr_bp, addr_bcr); 21862306a36Sopenharmony_ci write_dbgbvr(addr_bp, addr); 21962306a36Sopenharmony_ci isb(); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci enable_monitor_debug_exceptions(); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void install_ss(void) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci uint32_t mdscr; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci asm volatile("msr daifclr, #8"); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci mdscr = read_sysreg(mdscr_el1) | MDSCR_KDE | MDSCR_SS; 23162306a36Sopenharmony_ci write_sysreg(mdscr, mdscr_el1); 23262306a36Sopenharmony_ci isb(); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic volatile char write_data; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void guest_code(uint8_t bpn, uint8_t wpn, uint8_t ctx_bpn) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci uint64_t ctx = 0xabcdef; /* a random context number */ 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* Software-breakpoint */ 24262306a36Sopenharmony_ci reset_debug_state(); 24362306a36Sopenharmony_ci asm volatile("sw_bp: brk #0"); 24462306a36Sopenharmony_ci GUEST_ASSERT_EQ(sw_bp_addr, PC(sw_bp)); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Hardware-breakpoint */ 24762306a36Sopenharmony_ci reset_debug_state(); 24862306a36Sopenharmony_ci install_hw_bp(bpn, PC(hw_bp)); 24962306a36Sopenharmony_ci asm volatile("hw_bp: nop"); 25062306a36Sopenharmony_ci GUEST_ASSERT_EQ(hw_bp_addr, PC(hw_bp)); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Hardware-breakpoint + svc */ 25362306a36Sopenharmony_ci reset_debug_state(); 25462306a36Sopenharmony_ci install_hw_bp(bpn, PC(bp_svc)); 25562306a36Sopenharmony_ci asm volatile("bp_svc: svc #0"); 25662306a36Sopenharmony_ci GUEST_ASSERT_EQ(hw_bp_addr, PC(bp_svc)); 25762306a36Sopenharmony_ci GUEST_ASSERT_EQ(svc_addr, PC(bp_svc) + 4); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* Hardware-breakpoint + software-breakpoint */ 26062306a36Sopenharmony_ci reset_debug_state(); 26162306a36Sopenharmony_ci install_hw_bp(bpn, PC(bp_brk)); 26262306a36Sopenharmony_ci asm volatile("bp_brk: brk #0"); 26362306a36Sopenharmony_ci GUEST_ASSERT_EQ(sw_bp_addr, PC(bp_brk)); 26462306a36Sopenharmony_ci GUEST_ASSERT_EQ(hw_bp_addr, PC(bp_brk)); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Watchpoint */ 26762306a36Sopenharmony_ci reset_debug_state(); 26862306a36Sopenharmony_ci install_wp(wpn, PC(write_data)); 26962306a36Sopenharmony_ci write_data = 'x'; 27062306a36Sopenharmony_ci GUEST_ASSERT_EQ(write_data, 'x'); 27162306a36Sopenharmony_ci GUEST_ASSERT_EQ(wp_data_addr, PC(write_data)); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Single-step */ 27462306a36Sopenharmony_ci reset_debug_state(); 27562306a36Sopenharmony_ci install_ss(); 27662306a36Sopenharmony_ci ss_idx = 0; 27762306a36Sopenharmony_ci asm volatile("ss_start:\n" 27862306a36Sopenharmony_ci "mrs x0, esr_el1\n" 27962306a36Sopenharmony_ci "add x0, x0, #1\n" 28062306a36Sopenharmony_ci "msr daifset, #8\n" 28162306a36Sopenharmony_ci : : : "x0"); 28262306a36Sopenharmony_ci GUEST_ASSERT_EQ(ss_addr[0], PC(ss_start)); 28362306a36Sopenharmony_ci GUEST_ASSERT_EQ(ss_addr[1], PC(ss_start) + 4); 28462306a36Sopenharmony_ci GUEST_ASSERT_EQ(ss_addr[2], PC(ss_start) + 8); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* OS Lock does not block software-breakpoint */ 28762306a36Sopenharmony_ci reset_debug_state(); 28862306a36Sopenharmony_ci enable_os_lock(); 28962306a36Sopenharmony_ci sw_bp_addr = 0; 29062306a36Sopenharmony_ci asm volatile("sw_bp2: brk #0"); 29162306a36Sopenharmony_ci GUEST_ASSERT_EQ(sw_bp_addr, PC(sw_bp2)); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* OS Lock blocking hardware-breakpoint */ 29462306a36Sopenharmony_ci reset_debug_state(); 29562306a36Sopenharmony_ci enable_os_lock(); 29662306a36Sopenharmony_ci install_hw_bp(bpn, PC(hw_bp2)); 29762306a36Sopenharmony_ci hw_bp_addr = 0; 29862306a36Sopenharmony_ci asm volatile("hw_bp2: nop"); 29962306a36Sopenharmony_ci GUEST_ASSERT_EQ(hw_bp_addr, 0); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* OS Lock blocking watchpoint */ 30262306a36Sopenharmony_ci reset_debug_state(); 30362306a36Sopenharmony_ci enable_os_lock(); 30462306a36Sopenharmony_ci write_data = '\0'; 30562306a36Sopenharmony_ci wp_data_addr = 0; 30662306a36Sopenharmony_ci install_wp(wpn, PC(write_data)); 30762306a36Sopenharmony_ci write_data = 'x'; 30862306a36Sopenharmony_ci GUEST_ASSERT_EQ(write_data, 'x'); 30962306a36Sopenharmony_ci GUEST_ASSERT_EQ(wp_data_addr, 0); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* OS Lock blocking single-step */ 31262306a36Sopenharmony_ci reset_debug_state(); 31362306a36Sopenharmony_ci enable_os_lock(); 31462306a36Sopenharmony_ci ss_addr[0] = 0; 31562306a36Sopenharmony_ci install_ss(); 31662306a36Sopenharmony_ci ss_idx = 0; 31762306a36Sopenharmony_ci asm volatile("mrs x0, esr_el1\n\t" 31862306a36Sopenharmony_ci "add x0, x0, #1\n\t" 31962306a36Sopenharmony_ci "msr daifset, #8\n\t" 32062306a36Sopenharmony_ci : : : "x0"); 32162306a36Sopenharmony_ci GUEST_ASSERT_EQ(ss_addr[0], 0); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Linked hardware-breakpoint */ 32462306a36Sopenharmony_ci hw_bp_addr = 0; 32562306a36Sopenharmony_ci reset_debug_state(); 32662306a36Sopenharmony_ci install_hw_bp_ctx(bpn, ctx_bpn, PC(hw_bp_ctx), ctx); 32762306a36Sopenharmony_ci /* Set context id */ 32862306a36Sopenharmony_ci write_sysreg(ctx, contextidr_el1); 32962306a36Sopenharmony_ci isb(); 33062306a36Sopenharmony_ci asm volatile("hw_bp_ctx: nop"); 33162306a36Sopenharmony_ci write_sysreg(0, contextidr_el1); 33262306a36Sopenharmony_ci GUEST_ASSERT_EQ(hw_bp_addr, PC(hw_bp_ctx)); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Linked watchpoint */ 33562306a36Sopenharmony_ci reset_debug_state(); 33662306a36Sopenharmony_ci install_wp_ctx(wpn, ctx_bpn, PC(write_data), ctx); 33762306a36Sopenharmony_ci /* Set context id */ 33862306a36Sopenharmony_ci write_sysreg(ctx, contextidr_el1); 33962306a36Sopenharmony_ci isb(); 34062306a36Sopenharmony_ci write_data = 'x'; 34162306a36Sopenharmony_ci GUEST_ASSERT_EQ(write_data, 'x'); 34262306a36Sopenharmony_ci GUEST_ASSERT_EQ(wp_data_addr, PC(write_data)); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci GUEST_DONE(); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void guest_sw_bp_handler(struct ex_regs *regs) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci sw_bp_addr = regs->pc; 35062306a36Sopenharmony_ci regs->pc += 4; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic void guest_hw_bp_handler(struct ex_regs *regs) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci hw_bp_addr = regs->pc; 35662306a36Sopenharmony_ci regs->pstate |= SPSR_D; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void guest_wp_handler(struct ex_regs *regs) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci wp_data_addr = read_sysreg(far_el1); 36262306a36Sopenharmony_ci wp_addr = regs->pc; 36362306a36Sopenharmony_ci regs->pstate |= SPSR_D; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void guest_ss_handler(struct ex_regs *regs) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci __GUEST_ASSERT(ss_idx < 4, "Expected index < 4, got '%u'", ss_idx); 36962306a36Sopenharmony_ci ss_addr[ss_idx++] = regs->pc; 37062306a36Sopenharmony_ci regs->pstate |= SPSR_SS; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void guest_svc_handler(struct ex_regs *regs) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci svc_addr = regs->pc; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void guest_code_ss(int test_cnt) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci uint64_t i; 38162306a36Sopenharmony_ci uint64_t bvr, wvr, w_bvr, w_wvr; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci for (i = 0; i < test_cnt; i++) { 38462306a36Sopenharmony_ci /* Bits [1:0] of dbg{b,w}vr are RES0 */ 38562306a36Sopenharmony_ci w_bvr = i << 2; 38662306a36Sopenharmony_ci w_wvr = i << 2; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* 38962306a36Sopenharmony_ci * Enable Single Step execution. Note! This _must_ be a bare 39062306a36Sopenharmony_ci * ucall as the ucall() path uses atomic operations to manage 39162306a36Sopenharmony_ci * the ucall structures, and the built-in "atomics" are usually 39262306a36Sopenharmony_ci * implemented via exclusive access instructions. The exlusive 39362306a36Sopenharmony_ci * monitor is cleared on ERET, and so taking debug exceptions 39462306a36Sopenharmony_ci * during a LDREX=>STREX sequence will prevent forward progress 39562306a36Sopenharmony_ci * and hang the guest/test. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci GUEST_UCALL_NONE(); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* 40062306a36Sopenharmony_ci * The userspace will verify that the pc is as expected during 40162306a36Sopenharmony_ci * single step execution between iter_ss_begin and iter_ss_end. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci asm volatile("iter_ss_begin:nop\n"); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci write_sysreg(w_bvr, dbgbvr0_el1); 40662306a36Sopenharmony_ci write_sysreg(w_wvr, dbgwvr0_el1); 40762306a36Sopenharmony_ci bvr = read_sysreg(dbgbvr0_el1); 40862306a36Sopenharmony_ci wvr = read_sysreg(dbgwvr0_el1); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Userspace disables Single Step when the end is nigh. */ 41162306a36Sopenharmony_ci asm volatile("iter_ss_end:\n"); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci GUEST_ASSERT_EQ(bvr, w_bvr); 41462306a36Sopenharmony_ci GUEST_ASSERT_EQ(wvr, w_wvr); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci GUEST_DONE(); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int debug_version(uint64_t id_aa64dfr0) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), id_aa64dfr0); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void test_guest_debug_exceptions(uint8_t bpn, uint8_t wpn, uint8_t ctx_bpn) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 42762306a36Sopenharmony_ci struct kvm_vm *vm; 42862306a36Sopenharmony_ci struct ucall uc; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci vm = vm_create_with_one_vcpu(&vcpu, guest_code); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci vm_init_descriptor_tables(vm); 43362306a36Sopenharmony_ci vcpu_init_descriptor_tables(vcpu); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT, 43662306a36Sopenharmony_ci ESR_EC_BRK_INS, guest_sw_bp_handler); 43762306a36Sopenharmony_ci vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT, 43862306a36Sopenharmony_ci ESR_EC_HW_BP_CURRENT, guest_hw_bp_handler); 43962306a36Sopenharmony_ci vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT, 44062306a36Sopenharmony_ci ESR_EC_WP_CURRENT, guest_wp_handler); 44162306a36Sopenharmony_ci vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT, 44262306a36Sopenharmony_ci ESR_EC_SSTEP_CURRENT, guest_ss_handler); 44362306a36Sopenharmony_ci vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT, 44462306a36Sopenharmony_ci ESR_EC_SVC64, guest_svc_handler); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* Specify bpn/wpn/ctx_bpn to be tested */ 44762306a36Sopenharmony_ci vcpu_args_set(vcpu, 3, bpn, wpn, ctx_bpn); 44862306a36Sopenharmony_ci pr_debug("Use bpn#%d, wpn#%d and ctx_bpn#%d\n", bpn, wpn, ctx_bpn); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci vcpu_run(vcpu); 45162306a36Sopenharmony_ci switch (get_ucall(vcpu, &uc)) { 45262306a36Sopenharmony_ci case UCALL_ABORT: 45362306a36Sopenharmony_ci REPORT_GUEST_ASSERT(uc); 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci case UCALL_DONE: 45662306a36Sopenharmony_ci goto done; 45762306a36Sopenharmony_ci default: 45862306a36Sopenharmony_ci TEST_FAIL("Unknown ucall %lu", uc.cmd); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cidone: 46262306a36Sopenharmony_ci kvm_vm_free(vm); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_civoid test_single_step_from_userspace(int test_cnt) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 46862306a36Sopenharmony_ci struct kvm_vm *vm; 46962306a36Sopenharmony_ci struct ucall uc; 47062306a36Sopenharmony_ci struct kvm_run *run; 47162306a36Sopenharmony_ci uint64_t pc, cmd; 47262306a36Sopenharmony_ci uint64_t test_pc = 0; 47362306a36Sopenharmony_ci bool ss_enable = false; 47462306a36Sopenharmony_ci struct kvm_guest_debug debug = {}; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci vm = vm_create_with_one_vcpu(&vcpu, guest_code_ss); 47762306a36Sopenharmony_ci run = vcpu->run; 47862306a36Sopenharmony_ci vcpu_args_set(vcpu, 1, test_cnt); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci while (1) { 48162306a36Sopenharmony_ci vcpu_run(vcpu); 48262306a36Sopenharmony_ci if (run->exit_reason != KVM_EXIT_DEBUG) { 48362306a36Sopenharmony_ci cmd = get_ucall(vcpu, &uc); 48462306a36Sopenharmony_ci if (cmd == UCALL_ABORT) { 48562306a36Sopenharmony_ci REPORT_GUEST_ASSERT(uc); 48662306a36Sopenharmony_ci /* NOT REACHED */ 48762306a36Sopenharmony_ci } else if (cmd == UCALL_DONE) { 48862306a36Sopenharmony_ci break; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci TEST_ASSERT(cmd == UCALL_NONE, 49262306a36Sopenharmony_ci "Unexpected ucall cmd 0x%lx", cmd); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci debug.control = KVM_GUESTDBG_ENABLE | 49562306a36Sopenharmony_ci KVM_GUESTDBG_SINGLESTEP; 49662306a36Sopenharmony_ci ss_enable = true; 49762306a36Sopenharmony_ci vcpu_guest_debug_set(vcpu, &debug); 49862306a36Sopenharmony_ci continue; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci TEST_ASSERT(ss_enable, "Unexpected KVM_EXIT_DEBUG"); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* Check if the current pc is expected. */ 50462306a36Sopenharmony_ci vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc), &pc); 50562306a36Sopenharmony_ci TEST_ASSERT(!test_pc || pc == test_pc, 50662306a36Sopenharmony_ci "Unexpected pc 0x%lx (expected 0x%lx)", 50762306a36Sopenharmony_ci pc, test_pc); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if ((pc + 4) == (uint64_t)&iter_ss_end) { 51062306a36Sopenharmony_ci test_pc = 0; 51162306a36Sopenharmony_ci debug.control = KVM_GUESTDBG_ENABLE; 51262306a36Sopenharmony_ci ss_enable = false; 51362306a36Sopenharmony_ci vcpu_guest_debug_set(vcpu, &debug); 51462306a36Sopenharmony_ci continue; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* 51862306a36Sopenharmony_ci * If the current pc is between iter_ss_bgin and 51962306a36Sopenharmony_ci * iter_ss_end, the pc for the next KVM_EXIT_DEBUG should 52062306a36Sopenharmony_ci * be the current pc + 4. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci if ((pc >= (uint64_t)&iter_ss_begin) && 52362306a36Sopenharmony_ci (pc < (uint64_t)&iter_ss_end)) 52462306a36Sopenharmony_ci test_pc = pc + 4; 52562306a36Sopenharmony_ci else 52662306a36Sopenharmony_ci test_pc = 0; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci kvm_vm_free(vm); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/* 53362306a36Sopenharmony_ci * Run debug testing using the various breakpoint#, watchpoint# and 53462306a36Sopenharmony_ci * context-aware breakpoint# with the given ID_AA64DFR0_EL1 configuration. 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_civoid test_guest_debug_exceptions_all(uint64_t aa64dfr0) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci uint8_t brp_num, wrp_num, ctx_brp_num, normal_brp_num, ctx_brp_base; 53962306a36Sopenharmony_ci int b, w, c; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Number of breakpoints */ 54262306a36Sopenharmony_ci brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_BRPS), aa64dfr0) + 1; 54362306a36Sopenharmony_ci __TEST_REQUIRE(brp_num >= 2, "At least two breakpoints are required"); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* Number of watchpoints */ 54662306a36Sopenharmony_ci wrp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_WRPS), aa64dfr0) + 1; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Number of context aware breakpoints */ 54962306a36Sopenharmony_ci ctx_brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_CTX_CMPS), aa64dfr0) + 1; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci pr_debug("%s brp_num:%d, wrp_num:%d, ctx_brp_num:%d\n", __func__, 55262306a36Sopenharmony_ci brp_num, wrp_num, ctx_brp_num); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Number of normal (non-context aware) breakpoints */ 55562306a36Sopenharmony_ci normal_brp_num = brp_num - ctx_brp_num; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* Lowest context aware breakpoint number */ 55862306a36Sopenharmony_ci ctx_brp_base = normal_brp_num; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Run tests with all supported breakpoints/watchpoints */ 56162306a36Sopenharmony_ci for (c = ctx_brp_base; c < ctx_brp_base + ctx_brp_num; c++) { 56262306a36Sopenharmony_ci for (b = 0; b < normal_brp_num; b++) { 56362306a36Sopenharmony_ci for (w = 0; w < wrp_num; w++) 56462306a36Sopenharmony_ci test_guest_debug_exceptions(b, w, c); 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic void help(char *name) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci puts(""); 57262306a36Sopenharmony_ci printf("Usage: %s [-h] [-i iterations of the single step test]\n", name); 57362306a36Sopenharmony_ci puts(""); 57462306a36Sopenharmony_ci exit(0); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ciint main(int argc, char *argv[]) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 58062306a36Sopenharmony_ci struct kvm_vm *vm; 58162306a36Sopenharmony_ci int opt; 58262306a36Sopenharmony_ci int ss_iteration = 10000; 58362306a36Sopenharmony_ci uint64_t aa64dfr0; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci vm = vm_create_with_one_vcpu(&vcpu, guest_code); 58662306a36Sopenharmony_ci vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64DFR0_EL1), &aa64dfr0); 58762306a36Sopenharmony_ci __TEST_REQUIRE(debug_version(aa64dfr0) >= 6, 58862306a36Sopenharmony_ci "Armv8 debug architecture not supported."); 58962306a36Sopenharmony_ci kvm_vm_free(vm); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci while ((opt = getopt(argc, argv, "i:")) != -1) { 59262306a36Sopenharmony_ci switch (opt) { 59362306a36Sopenharmony_ci case 'i': 59462306a36Sopenharmony_ci ss_iteration = atoi_positive("Number of iterations", optarg); 59562306a36Sopenharmony_ci break; 59662306a36Sopenharmony_ci case 'h': 59762306a36Sopenharmony_ci default: 59862306a36Sopenharmony_ci help(argv[0]); 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci test_guest_debug_exceptions_all(aa64dfr0); 60462306a36Sopenharmony_ci test_single_step_from_userspace(ss_iteration); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 608