76 lines
2.0 KiB
C
76 lines
2.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2015 - ARM Ltd
|
|
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
|
*/
|
|
|
|
#ifndef __ARM64_KVM_HYP_FAULT_H__
|
|
#define __ARM64_KVM_HYP_FAULT_H__
|
|
|
|
#include <asm/kvm_asm.h>
|
|
#include <asm/kvm_emulate.h>
|
|
#include <asm/kvm_hyp.h>
|
|
#include <asm/kvm_mmu.h>
|
|
|
|
static inline bool __translate_far_to_hpfar(u64 far, u64 *hpfar)
|
|
{
|
|
u64 par, tmp;
|
|
|
|
/*
|
|
* Resolve the IPA the hard way using the guest VA.
|
|
*
|
|
* Stage-1 translation already validated the memory access
|
|
* rights. As such, we can use the EL1 translation regime, and
|
|
* don't have to distinguish between EL0 and EL1 access.
|
|
*
|
|
* We do need to save/restore PAR_EL1 though, as we haven't
|
|
* saved the guest context yet, and we may return early...
|
|
*/
|
|
par = read_sysreg_par();
|
|
if (!__kvm_at("s1e1r", far))
|
|
tmp = read_sysreg_par();
|
|
else
|
|
tmp = SYS_PAR_EL1_F; /* back to the guest */
|
|
write_sysreg(par, par_el1);
|
|
|
|
if (unlikely(tmp & SYS_PAR_EL1_F))
|
|
return false; /* Translation failed, back to guest */
|
|
|
|
/* Convert PAR to HPFAR format */
|
|
*hpfar = PAR_TO_HPFAR(tmp);
|
|
return true;
|
|
}
|
|
|
|
static inline bool __get_fault_info(u64 esr, struct kvm_vcpu_fault_info *fault)
|
|
{
|
|
u64 hpfar, far;
|
|
|
|
far = read_sysreg_el2(SYS_FAR);
|
|
|
|
/*
|
|
* The HPFAR can be invalid if the stage 2 fault did not
|
|
* happen during a stage 1 page table walk (the ESR_EL2.S1PTW
|
|
* bit is clear) and one of the two following cases are true:
|
|
* 1. The fault was due to a permission fault
|
|
* 2. The processor carries errata 834220
|
|
*
|
|
* Therefore, for all non S1PTW faults where we either have a
|
|
* permission fault or the errata workaround is enabled, we
|
|
* resolve the IPA using the AT instruction.
|
|
*/
|
|
if (!(esr & ESR_ELx_S1PTW) &&
|
|
(cpus_have_final_cap(ARM64_WORKAROUND_834220) ||
|
|
(esr & ESR_ELx_FSC_TYPE) == ESR_ELx_FSC_PERM)) {
|
|
if (!__translate_far_to_hpfar(far, &hpfar))
|
|
return false;
|
|
} else {
|
|
hpfar = read_sysreg(hpfar_el2);
|
|
}
|
|
|
|
fault->far_el2 = far;
|
|
fault->hpfar_el2 = hpfar;
|
|
return true;
|
|
}
|
|
|
|
#endif
|