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