Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 156 lines 3.8 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * at - Test for KVM's AT emulation in the EL2&0 and EL1&0 translation regimes. 4 */ 5#include "kvm_util.h" 6#include "processor.h" 7#include "test_util.h" 8#include "ucall.h" 9 10#include <asm/sysreg.h> 11 12#define TEST_ADDR 0x80000000 13 14enum { 15 CLEAR_ACCESS_FLAG, 16}; 17 18static u64 *ptep_hva; 19 20#define copy_el2_to_el1(reg) \ 21 write_sysreg_s(read_sysreg_s(SYS_##reg##_EL1), SYS_##reg##_EL12) 22 23/* Yes, this is an ugly hack */ 24#define __at(op, addr) write_sysreg_s(addr, op) 25 26#define test_at_insn(op, expect_fault) \ 27do { \ 28 u64 par, fsc; \ 29 bool fault; \ 30 \ 31 GUEST_SYNC(CLEAR_ACCESS_FLAG); \ 32 \ 33 __at(OP_AT_##op, TEST_ADDR); \ 34 isb(); \ 35 par = read_sysreg(par_el1); \ 36 \ 37 fault = par & SYS_PAR_EL1_F; \ 38 fsc = FIELD_GET(SYS_PAR_EL1_FST, par); \ 39 \ 40 __GUEST_ASSERT((expect_fault) == fault, \ 41 "AT "#op": %sexpected fault (par: %lx)1", \ 42 (expect_fault) ? "" : "un", par); \ 43 if ((expect_fault)) { \ 44 __GUEST_ASSERT(fsc == ESR_ELx_FSC_ACCESS_L(3), \ 45 "AT "#op": expected access flag fault (par: %lx)", \ 46 par); \ 47 } else { \ 48 GUEST_ASSERT_EQ(FIELD_GET(SYS_PAR_EL1_ATTR, par), MAIR_ATTR_NORMAL); \ 49 GUEST_ASSERT_EQ(FIELD_GET(SYS_PAR_EL1_SH, par), PTE_SHARED >> 8); \ 50 GUEST_ASSERT_EQ(par & SYS_PAR_EL1_PA, TEST_ADDR); \ 51 } \ 52} while (0) 53 54static void test_at(bool expect_fault) 55{ 56 test_at_insn(S1E2R, expect_fault); 57 test_at_insn(S1E2W, expect_fault); 58 59 /* Reuse the stage-1 MMU context from EL2 at EL1 */ 60 copy_el2_to_el1(SCTLR); 61 copy_el2_to_el1(MAIR); 62 copy_el2_to_el1(TCR); 63 copy_el2_to_el1(TTBR0); 64 copy_el2_to_el1(TTBR1); 65 66 /* Disable stage-2 translation and enter a non-host context */ 67 write_sysreg(0, vtcr_el2); 68 write_sysreg(0, vttbr_el2); 69 sysreg_clear_set(hcr_el2, HCR_EL2_TGE | HCR_EL2_VM, 0); 70 isb(); 71 72 test_at_insn(S1E1R, expect_fault); 73 test_at_insn(S1E1W, expect_fault); 74} 75 76static void guest_code(void) 77{ 78 sysreg_clear_set(tcr_el1, TCR_HA, 0); 79 isb(); 80 81 test_at(true); 82 83 if (!SYS_FIELD_GET(ID_AA64MMFR1_EL1, HAFDBS, read_sysreg(id_aa64mmfr1_el1))) 84 GUEST_DONE(); 85 86 sysreg_clear_set(tcr_el1, 0, TCR_HA); 87 isb(); 88 test_at(false); 89 90 GUEST_DONE(); 91} 92 93static void handle_sync(struct kvm_vcpu *vcpu, struct ucall *uc) 94{ 95 switch (uc->args[1]) { 96 case CLEAR_ACCESS_FLAG: 97 /* 98 * Delete + reinstall the memslot to invalidate stage-2 99 * mappings of the stage-1 page tables, allowing KVM to 100 * potentially use the 'slow' AT emulation path. 101 * 102 * This and clearing the access flag from host userspace 103 * ensures that the access flag cannot be set speculatively 104 * and is reliably cleared at the time of the AT instruction. 105 */ 106 clear_bit(__ffs(PTE_AF), ptep_hva); 107 vm_mem_region_reload(vcpu->vm, vcpu->vm->memslots[MEM_REGION_PT]); 108 break; 109 default: 110 TEST_FAIL("Unexpected SYNC arg: %lu", uc->args[1]); 111 } 112} 113 114static void run_test(struct kvm_vcpu *vcpu) 115{ 116 struct ucall uc; 117 118 while (true) { 119 vcpu_run(vcpu); 120 switch (get_ucall(vcpu, &uc)) { 121 case UCALL_DONE: 122 return; 123 case UCALL_SYNC: 124 handle_sync(vcpu, &uc); 125 continue; 126 case UCALL_ABORT: 127 REPORT_GUEST_ASSERT(uc); 128 return; 129 default: 130 TEST_FAIL("Unexpected ucall: %lu", uc.cmd); 131 } 132 } 133} 134 135int main(void) 136{ 137 struct kvm_vcpu_init init; 138 struct kvm_vcpu *vcpu; 139 struct kvm_vm *vm; 140 141 TEST_REQUIRE(kvm_check_cap(KVM_CAP_ARM_EL2)); 142 143 vm = vm_create(1); 144 145 kvm_get_default_vcpu_target(vm, &init); 146 init.features[0] |= BIT(KVM_ARM_VCPU_HAS_EL2); 147 vcpu = aarch64_vcpu_add(vm, 0, &init, guest_code); 148 kvm_arch_vm_finalize_vcpus(vm); 149 150 virt_map(vm, TEST_ADDR, TEST_ADDR, 1); 151 ptep_hva = virt_get_pte_hva_at_level(vm, TEST_ADDR, 3); 152 run_test(vcpu); 153 154 kvm_vm_free(vm); 155 return 0; 156}