1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2015 - ARM Ltd 4 * Author: Marc Zyngier <marc.zyngier@arm.com> 5 */ 6 7 #ifndef __ARM64_KVM_HYP_FAULT_H__ 8 #define __ARM64_KVM_HYP_FAULT_H__ 9 10 #include <asm/kvm_asm.h> 11 #include <asm/kvm_emulate.h> 12 #include <asm/kvm_hyp.h> 13 #include <asm/kvm_mmu.h> 14 15 static inline bool __translate_far_to_hpfar(u64 far, u64 *hpfar) 16 { 17 int ret; 18 u64 par, tmp; 19 20 /* 21 * Resolve the IPA the hard way using the guest VA. 22 * 23 * Stage-1 translation already validated the memory access 24 * rights. As such, we can use the EL1 translation regime, and 25 * don't have to distinguish between EL0 and EL1 access. 26 * 27 * We do need to save/restore PAR_EL1 though, as we haven't 28 * saved the guest context yet, and we may return early... 29 */ 30 par = read_sysreg_par(); 31 ret = system_supports_poe() ? __kvm_at(OP_AT_S1E1A, far) : 32 __kvm_at(OP_AT_S1E1R, far); 33 if (!ret) 34 tmp = read_sysreg_par(); 35 else 36 tmp = SYS_PAR_EL1_F; /* back to the guest */ 37 write_sysreg(par, par_el1); 38 39 if (unlikely(tmp & SYS_PAR_EL1_F)) 40 return false; /* Translation failed, back to guest */ 41 42 /* Convert PAR to HPFAR format */ 43 *hpfar = PAR_TO_HPFAR(tmp); 44 return true; 45 } 46 47 static inline bool __get_fault_info(u64 esr, struct kvm_vcpu_fault_info *fault) 48 { 49 u64 hpfar, far; 50 51 far = read_sysreg_el2(SYS_FAR); 52 53 /* 54 * The HPFAR can be invalid if the stage 2 fault did not 55 * happen during a stage 1 page table walk (the ESR_EL2.S1PTW 56 * bit is clear) and one of the two following cases are true: 57 * 1. The fault was due to a permission fault 58 * 2. The processor carries errata 834220 59 * 60 * Therefore, for all non S1PTW faults where we either have a 61 * permission fault or the errata workaround is enabled, we 62 * resolve the IPA using the AT instruction. 63 */ 64 if (!(esr & ESR_ELx_S1PTW) && 65 (cpus_have_final_cap(ARM64_WORKAROUND_834220) || 66 esr_fsc_is_permission_fault(esr))) { 67 if (!__translate_far_to_hpfar(far, &hpfar)) 68 return false; 69 } else { 70 hpfar = read_sysreg(hpfar_el2); 71 } 72 73 fault->far_el2 = far; 74 fault->hpfar_el2 = hpfar; 75 return true; 76 } 77 78 #endif 79
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.