xref: /linux/tools/testing/selftests/kvm/aarch64/mmio_abort.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * mmio_abort - Tests for userspace MMIO abort injection
4  *
5  * Copyright (c) 2024 Google LLC
6  */
7 #include "processor.h"
8 #include "test_util.h"
9 
10 #define MMIO_ADDR	0x8000000ULL
11 
12 static u64 expected_abort_pc;
13 
14 static void expect_sea_handler(struct ex_regs *regs)
15 {
16 	u64 esr = read_sysreg(esr_el1);
17 
18 	GUEST_ASSERT_EQ(regs->pc, expected_abort_pc);
19 	GUEST_ASSERT_EQ(ESR_ELx_EC(esr), ESR_ELx_EC_DABT_CUR);
20 	GUEST_ASSERT_EQ(esr & ESR_ELx_FSC_TYPE, ESR_ELx_FSC_EXTABT);
21 
22 	GUEST_DONE();
23 }
24 
25 static void unexpected_dabt_handler(struct ex_regs *regs)
26 {
27 	GUEST_FAIL("Unexpected data abort at PC: %lx\n", regs->pc);
28 }
29 
30 static struct kvm_vm *vm_create_with_dabt_handler(struct kvm_vcpu **vcpu, void *guest_code,
31 						  handler_fn dabt_handler)
32 {
33 	struct kvm_vm *vm = vm_create_with_one_vcpu(vcpu, guest_code);
34 
35 	vm_init_descriptor_tables(vm);
36 	vcpu_init_descriptor_tables(*vcpu);
37 	vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT, ESR_ELx_EC_DABT_CUR, dabt_handler);
38 
39 	virt_map(vm, MMIO_ADDR, MMIO_ADDR, 1);
40 
41 	return vm;
42 }
43 
44 static void vcpu_inject_extabt(struct kvm_vcpu *vcpu)
45 {
46 	struct kvm_vcpu_events events = {};
47 
48 	events.exception.ext_dabt_pending = true;
49 	vcpu_events_set(vcpu, &events);
50 }
51 
52 static void vcpu_run_expect_done(struct kvm_vcpu *vcpu)
53 {
54 	struct ucall uc;
55 
56 	vcpu_run(vcpu);
57 	switch (get_ucall(vcpu, &uc)) {
58 	case UCALL_ABORT:
59 		REPORT_GUEST_ASSERT(uc);
60 		break;
61 	case UCALL_DONE:
62 		break;
63 	default:
64 		TEST_FAIL("Unexpected ucall: %lu", uc.cmd);
65 	}
66 }
67 
68 extern char test_mmio_abort_insn;
69 
70 static void test_mmio_abort_guest(void)
71 {
72 	WRITE_ONCE(expected_abort_pc, (u64)&test_mmio_abort_insn);
73 
74 	asm volatile("test_mmio_abort_insn:\n\t"
75 		     "ldr x0, [%0]\n\t"
76 		     : : "r" (MMIO_ADDR) : "x0", "memory");
77 
78 	GUEST_FAIL("MMIO instruction should not retire");
79 }
80 
81 /*
82  * Test that KVM doesn't complete MMIO emulation when userspace has made an
83  * external abort pending for the instruction.
84  */
85 static void test_mmio_abort(void)
86 {
87 	struct kvm_vcpu *vcpu;
88 	struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_mmio_abort_guest,
89 							expect_sea_handler);
90 	struct kvm_run *run = vcpu->run;
91 
92 	vcpu_run(vcpu);
93 	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);
94 	TEST_ASSERT_EQ(run->mmio.phys_addr, MMIO_ADDR);
95 	TEST_ASSERT_EQ(run->mmio.len, sizeof(unsigned long));
96 	TEST_ASSERT(!run->mmio.is_write, "Expected MMIO read");
97 
98 	vcpu_inject_extabt(vcpu);
99 	vcpu_run_expect_done(vcpu);
100 	kvm_vm_free(vm);
101 }
102 
103 extern char test_mmio_nisv_insn;
104 
105 static void test_mmio_nisv_guest(void)
106 {
107 	WRITE_ONCE(expected_abort_pc, (u64)&test_mmio_nisv_insn);
108 
109 	asm volatile("test_mmio_nisv_insn:\n\t"
110 		     "ldr x0, [%0], #8\n\t"
111 		     : : "r" (MMIO_ADDR) : "x0", "memory");
112 
113 	GUEST_FAIL("MMIO instruction should not retire");
114 }
115 
116 /*
117  * Test that the KVM_RUN ioctl fails for ESR_EL2.ISV=0 MMIO aborts if userspace
118  * hasn't enabled KVM_CAP_ARM_NISV_TO_USER.
119  */
120 static void test_mmio_nisv(void)
121 {
122 	struct kvm_vcpu *vcpu;
123 	struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_mmio_nisv_guest,
124 							unexpected_dabt_handler);
125 
126 	TEST_ASSERT(_vcpu_run(vcpu), "Expected nonzero return code from KVM_RUN");
127 	TEST_ASSERT_EQ(errno, ENOSYS);
128 
129 	kvm_vm_free(vm);
130 }
131 
132 /*
133  * Test that ESR_EL2.ISV=0 MMIO aborts reach userspace and that an injected SEA
134  * reaches the guest.
135  */
136 static void test_mmio_nisv_abort(void)
137 {
138 	struct kvm_vcpu *vcpu;
139 	struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_mmio_nisv_guest,
140 							expect_sea_handler);
141 	struct kvm_run *run = vcpu->run;
142 
143 	vm_enable_cap(vm, KVM_CAP_ARM_NISV_TO_USER, 1);
144 
145 	vcpu_run(vcpu);
146 	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_ARM_NISV);
147 	TEST_ASSERT_EQ(run->arm_nisv.fault_ipa, MMIO_ADDR);
148 
149 	vcpu_inject_extabt(vcpu);
150 	vcpu_run_expect_done(vcpu);
151 	kvm_vm_free(vm);
152 }
153 
154 int main(void)
155 {
156 	test_mmio_abort();
157 	test_mmio_nisv();
158 	test_mmio_nisv_abort();
159 }
160