xref: /linux/tools/testing/selftests/kvm/arm64/mmio_abort.c (revision 0410c6121529409b08e81a77ae3ee58c657e2243)
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