All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] KVM: arm64: Fixes for resolving the fault IPA
@ 2025-04-01 22:42 Oliver Upton
  2025-04-01 22:42 ` [PATCH 1/3] KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid Oliver Upton
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Oliver Upton @ 2025-04-01 22:42 UTC (permalink / raw)
  To: kvmarm; +Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Jiaqi Yan,
	Oliver Upton

KVM's heuristics for determining the fault IPA are a bit shaky and don't
necessarily align with the letter of the architecture. On top of that,
HPFAR_EL2 is UNKNOWN if an SEA occurred during a table walk. Re-walking
the page tables will replay the SEA at EL2, becoming a panic in the hyp.

Oliver Upton (3):
  KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid
  arm64: Convert HPFAR_EL2 to sysreg table
  KVM: arm64: Don't translate FAR if invalid/unsafe

 arch/arm64/include/asm/esr.h           | 23 +++++++++
 arch/arm64/include/asm/kvm_emulate.h   |  7 ++-
 arch/arm64/include/asm/kvm_ras.h       |  2 +-
 arch/arm64/kvm/hyp/include/hyp/fault.h | 70 ++++++++++++++++++--------
 arch/arm64/kvm/hyp/nvhe/mem_protect.c  |  2 +-
 arch/arm64/kvm/mmu.c                   | 31 +++++++-----
 arch/arm64/tools/sysreg                |  7 +++
 7 files changed, 105 insertions(+), 37 deletions(-)


base-commit: 369c0122682c4468a69f2454614eded71c5348f3
-- 
2.39.5


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/3] KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid
  2025-04-01 22:42 [PATCH 0/3] KVM: arm64: Fixes for resolving the fault IPA Oliver Upton
@ 2025-04-01 22:42 ` Oliver Upton
  2025-04-02 11:15   ` Marc Zyngier
  2025-04-01 22:42 ` [PATCH 2/3] arm64: Convert HPFAR_EL2 to sysreg table Oliver Upton
  2025-04-01 22:42 ` [PATCH 3/3] KVM: arm64: Don't translate FAR if invalid/unsafe Oliver Upton
  2 siblings, 1 reply; 10+ messages in thread
From: Oliver Upton @ 2025-04-01 22:42 UTC (permalink / raw)
  To: kvmarm; +Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Jiaqi Yan,
	Oliver Upton

KVM's logic for deciding when HPFAR_EL2 is UNKNOWN doesn't align with
the architecture. Most notably, KVM assumes HPFAR_EL2 contains the
faulting IPA even in the case of an SEA.

Align the logic with the architecture rather than attempting to
paraphrase it. Additionally, take the opportunity to improve the
language around ARM erratum #834220 such that it actually describes the
bug.

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
 arch/arm64/include/asm/esr.h           |  1 +
 arch/arm64/kvm/hyp/include/hyp/fault.h | 46 ++++++++++++++++----------
 2 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index d1b1a33f9a8b..7b096ed87360 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -121,6 +121,7 @@
 #define ESR_ELx_FSC_SEA_TTW(n)	(0x14 + (n))
 #define ESR_ELx_FSC_SECC	(0x18)
 #define ESR_ELx_FSC_SECC_TTW(n)	(0x1c + (n))
+#define ESR_ELx_FSC_ADDRESS	(0x00)
 
 /* Status codes for individual page table levels */
 #define ESR_ELx_FSC_ACCESS_L(n)	(ESR_ELx_FSC_ACCESS + (n))
diff --git a/arch/arm64/kvm/hyp/include/hyp/fault.h b/arch/arm64/kvm/hyp/include/hyp/fault.h
index 17df94570f03..84d165e17bd0 100644
--- a/arch/arm64/kvm/hyp/include/hyp/fault.h
+++ b/arch/arm64/kvm/hyp/include/hyp/fault.h
@@ -44,31 +44,41 @@ static inline bool __translate_far_to_hpfar(u64 far, u64 *hpfar)
 	return true;
 }
 
+/*
+ * Checks for the conditions when HPFAR_EL2 is written, per DDI0487L.a D24.2.70.
+ */
+static inline bool __hpfar_valid(u64 esr)
+{
+	/*
+	 * CPUs affected by ARM erratum #834220 may incorrectly report a
+	 * stage-2 translation fault when a stage-1 permission fault occurs.
+	 *
+	 * Re-walk the page tables to determine if a stage-1 fault actually
+	 * occurred.
+	 */
+	if (cpus_have_final_cap(ARM64_WORKAROUND_834220) &&
+	    esr_fsc_is_translation_fault(esr))
+		return false;
+
+	if (esr_fsc_is_translation_fault(esr) || esr_fsc_is_access_flag_fault(esr))
+		return true;
+
+	if ((esr & ESR_ELx_S1PTW) && esr_fsc_is_permission_fault(esr))
+		return true;
+
+	return (esr & ESR_ELx_FSC) == ESR_ELx_FSC_ADDRESS;
+}
+
 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_fsc_is_permission_fault(esr))) {
-		if (!__translate_far_to_hpfar(far, &hpfar))
-			return false;
-	} else {
+	if (__hpfar_valid(esr))
 		hpfar = read_sysreg(hpfar_el2);
-	}
+	else if (!__translate_far_to_hpfar(far, &hpfar))
+		return false;
 
 	fault->far_el2 = far;
 	fault->hpfar_el2 = hpfar;
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/3] arm64: Convert HPFAR_EL2 to sysreg table
  2025-04-01 22:42 [PATCH 0/3] KVM: arm64: Fixes for resolving the fault IPA Oliver Upton
  2025-04-01 22:42 ` [PATCH 1/3] KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid Oliver Upton
@ 2025-04-01 22:42 ` Oliver Upton
  2025-04-01 22:42 ` [PATCH 3/3] KVM: arm64: Don't translate FAR if invalid/unsafe Oliver Upton
  2 siblings, 0 replies; 10+ messages in thread
From: Oliver Upton @ 2025-04-01 22:42 UTC (permalink / raw)
  To: kvmarm; +Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Jiaqi Yan,
	Oliver Upton

Switch over to the typical sysreg table for HPFAR_EL2 as we're about to
start using more fields in the register.

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
 arch/arm64/include/asm/kvm_emulate.h  | 2 +-
 arch/arm64/kvm/hyp/nvhe/mem_protect.c | 2 +-
 arch/arm64/tools/sysreg               | 7 +++++++
 3 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index d7cf66573aca..5d8c0832acbe 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -305,7 +305,7 @@ static __always_inline unsigned long kvm_vcpu_get_hfar(const struct kvm_vcpu *vc
 
 static __always_inline phys_addr_t kvm_vcpu_get_fault_ipa(const struct kvm_vcpu *vcpu)
 {
-	return ((phys_addr_t)vcpu->arch.fault.hpfar_el2 & HPFAR_MASK) << 8;
+	return ((phys_addr_t)vcpu->arch.fault.hpfar_el2 & HPFAR_EL2_FIPA) << 8;
 }
 
 static inline u64 kvm_vcpu_get_disr(const struct kvm_vcpu *vcpu)
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index f34f11c720d7..168893570703 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -578,7 +578,7 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt)
 		return;
 	}
 
-	addr = (fault.hpfar_el2 & HPFAR_MASK) << 8;
+	addr = (fault.hpfar_el2 & HPFAR_EL2_FIPA) << 8;
 	ret = host_stage2_idmap(addr);
 	BUG_ON(ret && ret != -EAGAIN);
 }
diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 2c63662c1a48..31ad9ce2b91c 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -3433,3 +3433,10 @@ Field	5	F
 Field	4	P
 Field	3:0	Align
 EndSysreg
+
+Sysreg	HPFAR_EL2	3	4	6	0	4
+Field	63	NS
+Res0	62:48
+Field	47:4	FIPA
+Res0	3:0
+EndSysreg
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/3] KVM: arm64: Don't translate FAR if invalid/unsafe
  2025-04-01 22:42 [PATCH 0/3] KVM: arm64: Fixes for resolving the fault IPA Oliver Upton
  2025-04-01 22:42 ` [PATCH 1/3] KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid Oliver Upton
  2025-04-01 22:42 ` [PATCH 2/3] arm64: Convert HPFAR_EL2 to sysreg table Oliver Upton
@ 2025-04-01 22:42 ` Oliver Upton
  2025-04-02 12:19   ` Marc Zyngier
  2 siblings, 1 reply; 10+ messages in thread
From: Oliver Upton @ 2025-04-01 22:42 UTC (permalink / raw)
  To: kvmarm; +Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Jiaqi Yan,
	Oliver Upton

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.

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
 arch/arm64/include/asm/esr.h           | 22 ++++++++++++++++++
 arch/arm64/include/asm/kvm_emulate.h   |  7 +++++-
 arch/arm64/include/asm/kvm_ras.h       |  2 +-
 arch/arm64/kvm/hyp/include/hyp/fault.h | 26 ++++++++++++++++-----
 arch/arm64/kvm/mmu.c                   | 31 ++++++++++++++++----------
 5 files changed, 69 insertions(+), 19 deletions(-)

diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 7b096ed87360..4b874ad95f2e 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -465,6 +465,28 @@ static inline bool esr_fsc_is_access_flag_fault(unsigned long esr)
 	       (esr == ESR_ELx_FSC_ACCESS_L(0));
 }
 
+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)
 {
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index 5d8c0832acbe..775e200a9be4 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -305,7 +305,12 @@ static __always_inline unsigned long kvm_vcpu_get_hfar(const struct kvm_vcpu *vc
 
 static __always_inline phys_addr_t kvm_vcpu_get_fault_ipa(const struct kvm_vcpu *vcpu)
 {
-	return ((phys_addr_t)vcpu->arch.fault.hpfar_el2 & HPFAR_EL2_FIPA) << 8;
+	u64 hpfar = vcpu->arch.fault.hpfar_el2;
+
+	if (unlikely(!(hpfar & HPFAR_EL2_NS)))
+		return INVALID_GPA;
+
+	return ((phys_addr_t)hpfar & HPFAR_EL2_FIPA) << 8;
 }
 
 static inline u64 kvm_vcpu_get_disr(const struct kvm_vcpu *vcpu)
diff --git a/arch/arm64/include/asm/kvm_ras.h b/arch/arm64/include/asm/kvm_ras.h
index 87e10d9a635b..9398ade632aa 100644
--- a/arch/arm64/include/asm/kvm_ras.h
+++ b/arch/arm64/include/asm/kvm_ras.h
@@ -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();
diff --git a/arch/arm64/kvm/hyp/include/hyp/fault.h b/arch/arm64/kvm/hyp/include/hyp/fault.h
index 84d165e17bd0..1ac9b23a382a 100644
--- a/arch/arm64/kvm/hyp/include/hyp/fault.h
+++ b/arch/arm64/kvm/hyp/include/hyp/fault.h
@@ -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;
 }
 
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 2feb6c6b63af..754f2fe0cc67 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1794,9 +1794,28 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 	gfn_t gfn;
 	int ret, idx;
 
+	/* Synchronous External Abort? */
+	if (kvm_vcpu_abt_issea(vcpu)) {
+		/*
+		 * For RAS the host kernel may handle this abort.
+		 * There is no need to pass the error into the guest.
+		 */
+		if (kvm_handle_guest_sea())
+			kvm_inject_vabt(vcpu);
+
+		return 1;
+	}
+
 	esr = kvm_vcpu_get_esr(vcpu);
 
+	/*
+	 * The fault IPA should be reliable at this point as we're not dealing
+	 * with an SEA.
+	 */
 	ipa = fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
+	if (KVM_BUG_ON(ipa == INVALID_GPA, vcpu->kvm))
+		return -EFAULT;
+
 	is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
 
 	if (esr_fsc_is_translation_fault(esr)) {
@@ -1818,18 +1837,6 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 		}
 	}
 
-	/* Synchronous External Abort? */
-	if (kvm_vcpu_abt_issea(vcpu)) {
-		/*
-		 * For RAS the host kernel may handle this abort.
-		 * There is no need to pass the error into the guest.
-		 */
-		if (kvm_handle_guest_sea(fault_ipa, kvm_vcpu_get_esr(vcpu)))
-			kvm_inject_vabt(vcpu);
-
-		return 1;
-	}
-
 	trace_kvm_guest_fault(*vcpu_pc(vcpu), kvm_vcpu_get_esr(vcpu),
 			      kvm_vcpu_get_hfar(vcpu), fault_ipa);
 
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/3] KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid
  2025-04-01 22:42 ` [PATCH 1/3] KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid Oliver Upton
@ 2025-04-02 11:15   ` Marc Zyngier
  2025-04-02 11:30     ` Marc Zyngier
  0 siblings, 1 reply; 10+ messages in thread
From: Marc Zyngier @ 2025-04-02 11:15 UTC (permalink / raw)
  To: Oliver Upton; +Cc: kvmarm, Joey Gouly, Suzuki K Poulose, Jiaqi Yan

On Tue, 01 Apr 2025 23:42:32 +0100,
Oliver Upton <oliver.upton@linux.dev> wrote:
> 
> KVM's logic for deciding when HPFAR_EL2 is UNKNOWN doesn't align with
> the architecture. Most notably, KVM assumes HPFAR_EL2 contains the
> faulting IPA even in the case of an SEA.
> 
> Align the logic with the architecture rather than attempting to
> paraphrase it. Additionally, take the opportunity to improve the
> language around ARM erratum #834220 such that it actually describes the
> bug.
> 
> Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
> ---
>  arch/arm64/include/asm/esr.h           |  1 +
>  arch/arm64/kvm/hyp/include/hyp/fault.h | 46 ++++++++++++++++----------
>  2 files changed, 29 insertions(+), 18 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
> index d1b1a33f9a8b..7b096ed87360 100644
> --- a/arch/arm64/include/asm/esr.h
> +++ b/arch/arm64/include/asm/esr.h
> @@ -121,6 +121,7 @@
>  #define ESR_ELx_FSC_SEA_TTW(n)	(0x14 + (n))
>  #define ESR_ELx_FSC_SECC	(0x18)
>  #define ESR_ELx_FSC_SECC_TTW(n)	(0x1c + (n))
> +#define ESR_ELx_FSC_ADDRESS	(0x00)

I think this should probably read "ADDRESS_SIZE", rather than just
"ADDRESS".

>
>  /* Status codes for individual page table levels */
>  #define ESR_ELx_FSC_ACCESS_L(n)	(ESR_ELx_FSC_ACCESS + (n))
> diff --git a/arch/arm64/kvm/hyp/include/hyp/fault.h b/arch/arm64/kvm/hyp/include/hyp/fault.h
> index 17df94570f03..84d165e17bd0 100644
> --- a/arch/arm64/kvm/hyp/include/hyp/fault.h
> +++ b/arch/arm64/kvm/hyp/include/hyp/fault.h
> @@ -44,31 +44,41 @@ static inline bool __translate_far_to_hpfar(u64 far, u64 *hpfar)
>  	return true;
>  }
>  
> +/*
> + * Checks for the conditions when HPFAR_EL2 is written, per DDI0487L.a D24.2.70.
> + */

You could also quote R_FKLWR, which was clarified in C23700 (Known
Issues L.a-03) by making the text clearer *and* adding a couple of
embarrassing typos (PFAR_EL2 instead of HPFAR_EL2). If anything, the
rule is more or less guaranteed to keep its reference, while the
numbering above will definitely move around.

> +static inline bool __hpfar_valid(u64 esr)
> +{
> +	/*
> +	 * CPUs affected by ARM erratum #834220 may incorrectly report a
> +	 * stage-2 translation fault when a stage-1 permission fault occurs.
> +	 *
> +	 * Re-walk the page tables to determine if a stage-1 fault actually
> +	 * occurred.
> +	 */
> +	if (cpus_have_final_cap(ARM64_WORKAROUND_834220) &&
> +	    esr_fsc_is_translation_fault(esr))
> +		return false;
> +
> +	if (esr_fsc_is_translation_fault(esr) || esr_fsc_is_access_flag_fault(esr))
> +		return true;
> +
> +	if ((esr & ESR_ELx_S1PTW) && esr_fsc_is_permission_fault(esr))
> +		return true;
> +
> +	return (esr & ESR_ELx_FSC) == ESR_ELx_FSC_ADDRESS;

Maybe add a esr_fsc_is_addr_sz_fault()?

> +}
> +
>  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_fsc_is_permission_fault(esr))) {
> -		if (!__translate_far_to_hpfar(far, &hpfar))
> -			return false;
> -	} else {
> +	if (__hpfar_valid(esr))
>  		hpfar = read_sysreg(hpfar_el2);
> -	}
> +	else if (!__translate_far_to_hpfar(far, &hpfar))
> +		return false;
>  
>  	fault->far_el2 = far;
>  	fault->hpfar_el2 = hpfar;

Otherwise looks OK to me.

	M.

-- 
Jazz isn't dead. It just smells funny.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/3] KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid
  2025-04-02 11:15   ` Marc Zyngier
@ 2025-04-02 11:30     ` Marc Zyngier
  2025-04-02 16:39       ` Oliver Upton
  0 siblings, 1 reply; 10+ messages in thread
From: Marc Zyngier @ 2025-04-02 11:30 UTC (permalink / raw)
  To: Oliver Upton; +Cc: kvmarm, Joey Gouly, Suzuki K Poulose, Jiaqi Yan

On Wed, 02 Apr 2025 12:15:52 +0100,
Marc Zyngier <maz@kernel.org> wrote:
> 
> On Tue, 01 Apr 2025 23:42:32 +0100,
> Oliver Upton <oliver.upton@linux.dev> wrote:
> > 
> > KVM's logic for deciding when HPFAR_EL2 is UNKNOWN doesn't align with
> > the architecture. Most notably, KVM assumes HPFAR_EL2 contains the
> > faulting IPA even in the case of an SEA.
> > 
> > Align the logic with the architecture rather than attempting to
> > paraphrase it. Additionally, take the opportunity to improve the
> > language around ARM erratum #834220 such that it actually describes the
> > bug.
> > 
> > Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
> > ---
> >  arch/arm64/include/asm/esr.h           |  1 +
> >  arch/arm64/kvm/hyp/include/hyp/fault.h | 46 ++++++++++++++++----------
> >  2 files changed, 29 insertions(+), 18 deletions(-)
> > 
> > diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
> > index d1b1a33f9a8b..7b096ed87360 100644
> > --- a/arch/arm64/include/asm/esr.h
> > +++ b/arch/arm64/include/asm/esr.h
> > @@ -121,6 +121,7 @@
> >  #define ESR_ELx_FSC_SEA_TTW(n)	(0x14 + (n))
> >  #define ESR_ELx_FSC_SECC	(0x18)
> >  #define ESR_ELx_FSC_SECC_TTW(n)	(0x1c + (n))
> > +#define ESR_ELx_FSC_ADDRESS	(0x00)
> 
> I think this should probably read "ADDRESS_SIZE", rather than just
> "ADDRESS".

Actually, we have

#define ESR_ELx_FSC_ADDRSZ	(0x00)

since

61e30b9eef7f ("KVM: arm64: nv: Implement nested Stage-2 page table walk logic")

It just isn't at the expected spot.

	M.

-- 
Jazz isn't dead. It just smells funny.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Don't translate FAR if invalid/unsafe
  2025-04-01 22:42 ` [PATCH 3/3] KVM: arm64: Don't translate FAR if invalid/unsafe Oliver Upton
@ 2025-04-02 12:19   ` Marc Zyngier
  2025-04-02 16:37     ` Oliver Upton
  0 siblings, 1 reply; 10+ messages in thread
From: Marc Zyngier @ 2025-04-02 12:19 UTC (permalink / raw)
  To: Oliver Upton; +Cc: kvmarm, Joey Gouly, Suzuki K Poulose, Jiaqi Yan

On Tue, 01 Apr 2025 23:42:34 +0100,
Oliver Upton <oliver.upton@linux.dev> wrote:
> 
> 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.
> 
> Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
> ---
>  arch/arm64/include/asm/esr.h           | 22 ++++++++++++++++++
>  arch/arm64/include/asm/kvm_emulate.h   |  7 +++++-
>  arch/arm64/include/asm/kvm_ras.h       |  2 +-
>  arch/arm64/kvm/hyp/include/hyp/fault.h | 26 ++++++++++++++++-----
>  arch/arm64/kvm/mmu.c                   | 31 ++++++++++++++++----------
>  5 files changed, 69 insertions(+), 19 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
> index 7b096ed87360..4b874ad95f2e 100644
> --- a/arch/arm64/include/asm/esr.h
> +++ b/arch/arm64/include/asm/esr.h
> @@ -465,6 +465,28 @@ static inline bool esr_fsc_is_access_flag_fault(unsigned long esr)
>  	       (esr == ESR_ELx_FSC_ACCESS_L(0));
>  }
>  
> +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)
>  {
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index 5d8c0832acbe..775e200a9be4 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -305,7 +305,12 @@ static __always_inline unsigned long kvm_vcpu_get_hfar(const struct kvm_vcpu *vc
>  
>  static __always_inline phys_addr_t kvm_vcpu_get_fault_ipa(const struct kvm_vcpu *vcpu)
>  {
> -	return ((phys_addr_t)vcpu->arch.fault.hpfar_el2 & HPFAR_EL2_FIPA) << 8;
> +	u64 hpfar = vcpu->arch.fault.hpfar_el2;
> +
> +	if (unlikely(!(hpfar & HPFAR_EL2_NS)))
> +		return INVALID_GPA;

Eh, why not ;-)

> +
> +	return ((phys_addr_t)hpfar & HPFAR_EL2_FIPA) << 8;

nit: as you are reworking this area, I'd rather see

	return FIELD_GET(HPFAR_EL2_FIPA, hpfar) << 12;

as it nicely shows what the FIPA field represents, instead of the
odd-looking 8 here. The compiler will nicely sort it out anyway.

Same thing in handle_host_mem_abort(), both of which could be fixed in
the previous patch.

>  }
>  
>  static inline u64 kvm_vcpu_get_disr(const struct kvm_vcpu *vcpu)
> diff --git a/arch/arm64/include/asm/kvm_ras.h b/arch/arm64/include/asm/kvm_ras.h
> index 87e10d9a635b..9398ade632aa 100644
> --- a/arch/arm64/include/asm/kvm_ras.h
> +++ b/arch/arm64/include/asm/kvm_ras.h
> @@ -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();
> diff --git a/arch/arm64/kvm/hyp/include/hyp/fault.h b/arch/arm64/kvm/hyp/include/hyp/fault.h
> index 84d165e17bd0..1ac9b23a382a 100644
> --- a/arch/arm64/kvm/hyp/include/hyp/fault.h
> +++ b/arch/arm64/kvm/hyp/include/hyp/fault.h
> @@ -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));
> +}

Ah, neat.

> +
>  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;

This may cause some unexpected damage, see below.

> +	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;
>  }
>  
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 2feb6c6b63af..754f2fe0cc67 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1794,9 +1794,28 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
>  	gfn_t gfn;
>  	int ret, idx;
>  
> +	/* Synchronous External Abort? */
> +	if (kvm_vcpu_abt_issea(vcpu)) {
> +		/*
> +		 * For RAS the host kernel may handle this abort.
> +		 * There is no need to pass the error into the guest.
> +		 */
> +		if (kvm_handle_guest_sea())
> +			kvm_inject_vabt(vcpu);
> +
> +		return 1;
> +	}
> +

Maybe add a line to the commit message to indicate that you're moving
this around?

>  	esr = kvm_vcpu_get_esr(vcpu);
>  
> +	/*
> +	 * The fault IPA should be reliable at this point as we're not dealing
> +	 * with an SEA.
> +	 */
>  	ipa = fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
> +	if (KVM_BUG_ON(ipa == INVALID_GPA, vcpu->kvm))
> +		return -EFAULT;
> +

I think you're missing something similar in handle_host_mem_abort(),
which will take fault.hpfar_el2 at face value even when it is unsafe
to translate. At least testing for HPFAR_EL2_NS seems necessary.

Thanks,

	M.

-- 
Jazz isn't dead. It just smells funny.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Don't translate FAR if invalid/unsafe
  2025-04-02 12:19   ` Marc Zyngier
@ 2025-04-02 16:37     ` Oliver Upton
  2025-04-02 17:01       ` Marc Zyngier
  0 siblings, 1 reply; 10+ messages in thread
From: Oliver Upton @ 2025-04-02 16:37 UTC (permalink / raw)
  To: Marc Zyngier; +Cc: kvmarm, Joey Gouly, Suzuki K Poulose, Jiaqi Yan

Hey,

On Wed, Apr 02, 2025 at 01:19:28PM +0100, Marc Zyngier wrote:
> On Tue, 01 Apr 2025 23:42:34 +0100,
> Oliver Upton <oliver.upton@linux.dev> wrote:
> >  static __always_inline phys_addr_t kvm_vcpu_get_fault_ipa(const struct kvm_vcpu *vcpu)
> >  {
> > -	return ((phys_addr_t)vcpu->arch.fault.hpfar_el2 & HPFAR_EL2_FIPA) << 8;
> > +	u64 hpfar = vcpu->arch.fault.hpfar_el2;
> > +
> > +	if (unlikely(!(hpfar & HPFAR_EL2_NS)))
> > +		return INVALID_GPA;
> 
> Eh, why not ;-)
> 
> > +
> > +	return ((phys_addr_t)hpfar & HPFAR_EL2_FIPA) << 8;
> 
> nit: as you are reworking this area, I'd rather see
> 
> 	return FIELD_GET(HPFAR_EL2_FIPA, hpfar) << 12;
> 
> as it nicely shows what the FIPA field represents, instead of the
> odd-looking 8 here. The compiler will nicely sort it out anyway.
> 
> Same thing in handle_host_mem_abort(), both of which could be fixed in
> the previous patch.

Agreed, this reads better.

> > diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> > index 2feb6c6b63af..754f2fe0cc67 100644
> > --- a/arch/arm64/kvm/mmu.c
> > +++ b/arch/arm64/kvm/mmu.c
> > @@ -1794,9 +1794,28 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> >  	gfn_t gfn;
> >  	int ret, idx;
> >  
> > +	/* Synchronous External Abort? */
> > +	if (kvm_vcpu_abt_issea(vcpu)) {
> > +		/*
> > +		 * For RAS the host kernel may handle this abort.
> > +		 * There is no need to pass the error into the guest.
> > +		 */
> > +		if (kvm_handle_guest_sea())
> > +			kvm_inject_vabt(vcpu);
> > +
> > +		return 1;
> > +	}
> > +
> 
> Maybe add a line to the commit message to indicate that you're moving
> this around?

I had this:

  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.

But I'm happy to expand a bit to make that more clear.

> >  	esr = kvm_vcpu_get_esr(vcpu);
> >  
> > +	/*
> > +	 * The fault IPA should be reliable at this point as we're not dealing
> > +	 * with an SEA.
> > +	 */
> >  	ipa = fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
> > +	if (KVM_BUG_ON(ipa == INVALID_GPA, vcpu->kvm))
> > +		return -EFAULT;
> > +
> 
> I think you're missing something similar in handle_host_mem_abort(),
> which will take fault.hpfar_el2 at face value even when it is unsafe
> to translate. At least testing for HPFAR_EL2_NS seems necessary.

Good point, I'll respin in a moment.

Thanks for having a look!

Best,
Oliver

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/3] KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid
  2025-04-02 11:30     ` Marc Zyngier
@ 2025-04-02 16:39       ` Oliver Upton
  0 siblings, 0 replies; 10+ messages in thread
From: Oliver Upton @ 2025-04-02 16:39 UTC (permalink / raw)
  To: Marc Zyngier; +Cc: kvmarm, Joey Gouly, Suzuki K Poulose, Jiaqi Yan

On Wed, Apr 02, 2025 at 12:30:29PM +0100, Marc Zyngier wrote:
> On Wed, 02 Apr 2025 12:15:52 +0100,
> Marc Zyngier <maz@kernel.org> wrote:
> > 
> > On Tue, 01 Apr 2025 23:42:32 +0100,
> > Oliver Upton <oliver.upton@linux.dev> wrote:
> > > 
> > > KVM's logic for deciding when HPFAR_EL2 is UNKNOWN doesn't align with
> > > the architecture. Most notably, KVM assumes HPFAR_EL2 contains the
> > > faulting IPA even in the case of an SEA.
> > > 
> > > Align the logic with the architecture rather than attempting to
> > > paraphrase it. Additionally, take the opportunity to improve the
> > > language around ARM erratum #834220 such that it actually describes the
> > > bug.
> > > 
> > > Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
> > > ---
> > >  arch/arm64/include/asm/esr.h           |  1 +
> > >  arch/arm64/kvm/hyp/include/hyp/fault.h | 46 ++++++++++++++++----------
> > >  2 files changed, 29 insertions(+), 18 deletions(-)
> > > 
> > > diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
> > > index d1b1a33f9a8b..7b096ed87360 100644
> > > --- a/arch/arm64/include/asm/esr.h
> > > +++ b/arch/arm64/include/asm/esr.h
> > > @@ -121,6 +121,7 @@
> > >  #define ESR_ELx_FSC_SEA_TTW(n)	(0x14 + (n))
> > >  #define ESR_ELx_FSC_SECC	(0x18)
> > >  #define ESR_ELx_FSC_SECC_TTW(n)	(0x1c + (n))
> > > +#define ESR_ELx_FSC_ADDRESS	(0x00)
> > 
> > I think this should probably read "ADDRESS_SIZE", rather than just
> > "ADDRESS".
> 
> Actually, we have
> 
> #define ESR_ELx_FSC_ADDRSZ	(0x00)
> 
> since
> 
> 61e30b9eef7f ("KVM: arm64: nv: Implement nested Stage-2 page table walk logic")
> 
> It just isn't at the expected spot.

Durrrrr :)

Thanks,
Oliver

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Don't translate FAR if invalid/unsafe
  2025-04-02 16:37     ` Oliver Upton
@ 2025-04-02 17:01       ` Marc Zyngier
  0 siblings, 0 replies; 10+ messages in thread
From: Marc Zyngier @ 2025-04-02 17:01 UTC (permalink / raw)
  To: Oliver Upton; +Cc: kvmarm, Joey Gouly, Suzuki K Poulose, Jiaqi Yan

On Wed, 02 Apr 2025 17:37:57 +0100,
Oliver Upton <oliver.upton@linux.dev> wrote:
> 
> > > diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> > > index 2feb6c6b63af..754f2fe0cc67 100644
> > > --- a/arch/arm64/kvm/mmu.c
> > > +++ b/arch/arm64/kvm/mmu.c
> > > @@ -1794,9 +1794,28 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> > >  	gfn_t gfn;
> > >  	int ret, idx;
> > >  
> > > +	/* Synchronous External Abort? */
> > > +	if (kvm_vcpu_abt_issea(vcpu)) {
> > > +		/*
> > > +		 * For RAS the host kernel may handle this abort.
> > > +		 * There is no need to pass the error into the guest.
> > > +		 */
> > > +		if (kvm_handle_guest_sea())
> > > +			kvm_inject_vabt(vcpu);
> > > +
> > > +		return 1;
> > > +	}
> > > +
> > 
> > Maybe add a line to the commit message to indicate that you're moving
> > this around?
> 
> I had this:
> 
>   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.
> 
> But I'm happy to expand a bit to make that more clear.

Ah, I somehow glanced over it. No need to change anything then.

Thanks,

	M.

-- 
Jazz isn't dead. It just smells funny.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-04-02 17:01 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-01 22:42 [PATCH 0/3] KVM: arm64: Fixes for resolving the fault IPA Oliver Upton
2025-04-01 22:42 ` [PATCH 1/3] KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid Oliver Upton
2025-04-02 11:15   ` Marc Zyngier
2025-04-02 11:30     ` Marc Zyngier
2025-04-02 16:39       ` Oliver Upton
2025-04-01 22:42 ` [PATCH 2/3] arm64: Convert HPFAR_EL2 to sysreg table Oliver Upton
2025-04-01 22:42 ` [PATCH 3/3] KVM: arm64: Don't translate FAR if invalid/unsafe Oliver Upton
2025-04-02 12:19   ` Marc Zyngier
2025-04-02 16:37     ` Oliver Upton
2025-04-02 17:01       ` Marc Zyngier

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.