brintos

brintos / linux-shallow public Read only

0
0
Text · 14.8 KiB · 2582c49 Raw
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