Commit 26fbdf36 authored by Oliver Upton's avatar Oliver Upton
Browse files

KVM: arm64: Don't translate FAR if invalid/unsafe



Don't re-walk the page tables if an SEA occurred during the faulting
page table walk to avoid taking a fatal exception in the hyp.
Additionally, check that FAR_EL2 is valid for SEAs not taken on PTW
as the architecture doesn't guarantee it contains the fault VA.

Finally, fix up the rest of the abort path by checking for SEAs early
and bugging the VM if we get further along with an UNKNOWN fault IPA.

Reviewed-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20250402201725.2963645-4-oliver.upton@linux.dev


Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parent 1cf3e126
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -482,6 +482,28 @@ static inline bool esr_fsc_is_addr_sz_fault(unsigned long esr)
	       (esr == ESR_ELx_FSC_ADDRSZ_L(-1));
}

static inline bool esr_fsc_is_sea_ttw(unsigned long esr)
{
	esr = esr & ESR_ELx_FSC;

	return (esr == ESR_ELx_FSC_SEA_TTW(3)) ||
	       (esr == ESR_ELx_FSC_SEA_TTW(2)) ||
	       (esr == ESR_ELx_FSC_SEA_TTW(1)) ||
	       (esr == ESR_ELx_FSC_SEA_TTW(0)) ||
	       (esr == ESR_ELx_FSC_SEA_TTW(-1));
}

static inline bool esr_fsc_is_secc_ttw(unsigned long esr)
{
	esr = esr & ESR_ELx_FSC;

	return (esr == ESR_ELx_FSC_SECC_TTW(3)) ||
	       (esr == ESR_ELx_FSC_SECC_TTW(2)) ||
	       (esr == ESR_ELx_FSC_SECC_TTW(1)) ||
	       (esr == ESR_ELx_FSC_SECC_TTW(0)) ||
	       (esr == ESR_ELx_FSC_SECC_TTW(-1));
}

/* Indicate whether ESR.EC==0x1A is for an ERETAx instruction */
static inline bool esr_iss_is_eretax(unsigned long esr)
{
+3 −0
Original line number Diff line number Diff line
@@ -307,6 +307,9 @@ static __always_inline phys_addr_t kvm_vcpu_get_fault_ipa(const struct kvm_vcpu
{
	u64 hpfar = vcpu->arch.fault.hpfar_el2;

	if (unlikely(!(hpfar & HPFAR_EL2_NS)))
		return INVALID_GPA;

	return FIELD_GET(HPFAR_EL2_FIPA, hpfar) << 12;
}

+1 −1
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
 * Was this synchronous external abort a RAS notification?
 * Returns '0' for errors handled by some RAS subsystem, or -ENOENT.
 */
static inline int kvm_handle_guest_sea(phys_addr_t addr, u64 esr)
static inline int kvm_handle_guest_sea(void)
{
	/* apei_claim_sea(NULL) expects to mask interrupts itself */
	lockdep_assert_irqs_enabled();
+21 −5
Original line number Diff line number Diff line
@@ -12,6 +12,16 @@
#include <asm/kvm_hyp.h>
#include <asm/kvm_mmu.h>

static inline bool __fault_safe_to_translate(u64 esr)
{
	u64 fsc = esr & ESR_ELx_FSC;

	if (esr_fsc_is_sea_ttw(esr) || esr_fsc_is_secc_ttw(esr))
		return false;

	return !(fsc == ESR_ELx_FSC_EXTABT && (esr & ESR_ELx_FnV));
}

static inline bool __translate_far_to_hpfar(u64 far, u64 *hpfar)
{
	int ret;
@@ -71,17 +81,23 @@ static inline bool __hpfar_valid(u64 esr)

static inline bool __get_fault_info(u64 esr, struct kvm_vcpu_fault_info *fault)
{
	u64 hpfar, far;
	u64 hpfar;

	far = read_sysreg_el2(SYS_FAR);
	fault->far_el2		= read_sysreg_el2(SYS_FAR);
	fault->hpfar_el2	= 0;

	if (__hpfar_valid(esr))
		hpfar = read_sysreg(hpfar_el2);
	else if (!__translate_far_to_hpfar(far, &hpfar))
	else if (unlikely(!__fault_safe_to_translate(esr)))
		return true;
	else if (!__translate_far_to_hpfar(fault->far_el2, &hpfar))
		return false;

	fault->far_el2 = far;
	fault->hpfar_el2 = hpfar;
	/*
	 * Hijack HPFAR_EL2.NS (RES0 in Non-secure) to indicate a valid
	 * HPFAR value.
	 */
	fault->hpfar_el2 = hpfar | HPFAR_EL2_NS;
	return true;
}

+7 −0
Original line number Diff line number Diff line
@@ -578,7 +578,14 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt)
		return;
	}


	/*
	 * Yikes, we couldn't resolve the fault IPA. This should reinject an
	 * abort into the host when we figure out how to do that.
	 */
	BUG_ON(!(fault.hpfar_el2 & HPFAR_EL2_NS));
	addr = FIELD_GET(HPFAR_EL2_FIPA, fault.hpfar_el2) << 12;

	ret = host_stage2_idmap(addr);
	BUG_ON(ret && ret != -EAGAIN);
}
Loading