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