According to ARM architecture specification (ARM DDI 0487 L.a, section
C5.4.3), Stage 2 translation should be skipped when VHE is active, or,
in other words, E2H bit is set. Fix the code by inverting both check
and comment.
Signed-off-by: Volodymyr Babchuk <volodymyr_babchuk@epam.com>
---
arch/arm64/kvm/at.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
index a25be111cd8f8..5e7c3fb01273c 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -1412,10 +1412,10 @@ void __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
return;
/*
- * If we only have a single stage of translation (E2H=0 or
+ * If we only have a single stage of translation (E2H=1 or
* TGE=1), exit early. Same thing if {VM,DC}=={0,0}.
*/
- if (!vcpu_el2_e2h_is_set(vcpu) || vcpu_el2_tge_is_set(vcpu) ||
+ if (vcpu_el2_e2h_is_set(vcpu) || vcpu_el2_tge_is_set(vcpu) ||
!(vcpu_read_sys_reg(vcpu, HCR_EL2) & (HCR_VM | HCR_DC)))
return;
--
2.50.1
Hi Volodymyr, Thanks for looking into this. On Wed, 06 Aug 2025 15:17:55 +0100, Volodymyr Babchuk <Volodymyr_Babchuk@epam.com> wrote: > > According to ARM architecture specification (ARM DDI 0487 L.a, section > C5.4.3), Stage 2 translation should be skipped when VHE is active, or, > in other words, E2H bit is set. Fix the code by inverting both check > and comment. > > Signed-off-by: Volodymyr Babchuk <volodymyr_babchuk@epam.com> > --- > arch/arm64/kvm/at.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c > index a25be111cd8f8..5e7c3fb01273c 100644 > --- a/arch/arm64/kvm/at.c > +++ b/arch/arm64/kvm/at.c > @@ -1412,10 +1412,10 @@ void __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) > return; > > /* > - * If we only have a single stage of translation (E2H=0 or > + * If we only have a single stage of translation (E2H=1 or > * TGE=1), exit early. Same thing if {VM,DC}=={0,0}. > */ > - if (!vcpu_el2_e2h_is_set(vcpu) || vcpu_el2_tge_is_set(vcpu) || > + if (vcpu_el2_e2h_is_set(vcpu) || vcpu_el2_tge_is_set(vcpu) || > !(vcpu_read_sys_reg(vcpu, HCR_EL2) & (HCR_VM | HCR_DC))) > return; The code we have here is clearly bogus, but what you are suggesting doesn't look correct to me either. Here's what the spec says: <quote> Performs stage 1 and 2 address translation, with permissions as if reading from the given virtual address from EL1, or from EL2 if the Effective value of HCR_EL2.{E2H, TGE} is {1, 1}, using the following translation regime: * When EL2 is implemented and enabled in the Security state described by the current Effective value of SCR_EL3.{NSE, NS}: - If the Effective value of HCR_EL2.{E2H, TGE} is not {1, 1}, the EL1&0 translation regime, accessed from EL1. - If the Effective value of HCR_EL2.{E2H, TGE} is {1, 1}, the EL2&0 translation regime, accessed from EL2. * Otherwise, the EL1&0 translation regime, accessed from EL1. </quote> We're obviously in the first bullet, so we need to work out whether this is applying to the EL1&0 or the EL2&0 translation regime. By the letter of the spec, we need to check for E2H+TGE being both set to elide the S2 final walk. I suspect what you really want is the hack below, though I think the DC handling has always been broken (who cares anyway?). Thanks, M. diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 0e56105339493..3e591979f947e 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -1420,10 +1420,10 @@ void __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) return; /* - * If we only have a single stage of translation (E2H=0 or - * TGE=1), exit early. Same thing if {VM,DC}=={0,0}. + * If we only have a single stage of translation ({E2H,TGE}={1,1}), + * exit early. Same thing if {VM,DC}=={0,0}. */ - if (!vcpu_el2_e2h_is_set(vcpu) || vcpu_el2_tge_is_set(vcpu) || + if ((vcpu_el2_e2h_is_set(vcpu) && vcpu_el2_tge_is_set(vcpu)) || !(vcpu_read_sys_reg(vcpu, HCR_EL2) & (HCR_VM | HCR_DC))) return; -- Without deviation from the norm, progress is not possible.
Hi Volodymyr, Thanks for catching this. On Wed, Aug 06, 2025 at 02:17:55PM +0000, Volodymyr Babchuk wrote: > According to ARM architecture specification (ARM DDI 0487 L.a, section > C5.4.3), Stage 2 translation should be skipped when VHE is active, or, > in other words, E2H bit is set. Fix the code by inverting both check > and comment. > > Signed-off-by: Volodymyr Babchuk <volodymyr_babchuk@epam.com> > --- > arch/arm64/kvm/at.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c > index a25be111cd8f8..5e7c3fb01273c 100644 > --- a/arch/arm64/kvm/at.c > +++ b/arch/arm64/kvm/at.c > @@ -1412,10 +1412,10 @@ void __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) > return; > > /* > - * If we only have a single stage of translation (E2H=0 or > + * If we only have a single stage of translation (E2H=1 or > * TGE=1), exit early. Same thing if {VM,DC}=={0,0}. > */ > - if (!vcpu_el2_e2h_is_set(vcpu) || vcpu_el2_tge_is_set(vcpu) || > + if (vcpu_el2_e2h_is_set(vcpu) || vcpu_el2_tge_is_set(vcpu) || The check should be HCR_EL2.<E2H,TGE> == '11'. Maybe instead: /* * Exit early if we only have a single stage of translation * either because we're in the EL2&0 translation regime or * stage-2 translation is disabled (i.e. HCR_EL2.{VM,DC}=={0,0}). */ if (compute_translation_regime(vcpu, op) == TR_EL20 || !(vcpu_read_sys_reg(vcpu, HCR_EL2) & (HCR_VM | HCR_DC))) return; Thanks, Oliver
On Wed, 06 Aug 2025 18:37:34 +0100, Oliver Upton <oliver.upton@linux.dev> wrote: > > Hi Volodymyr, > > Thanks for catching this. > > On Wed, Aug 06, 2025 at 02:17:55PM +0000, Volodymyr Babchuk wrote: > > According to ARM architecture specification (ARM DDI 0487 L.a, section > > C5.4.3), Stage 2 translation should be skipped when VHE is active, or, > > in other words, E2H bit is set. Fix the code by inverting both check > > and comment. > > > > Signed-off-by: Volodymyr Babchuk <volodymyr_babchuk@epam.com> > > --- > > arch/arm64/kvm/at.c | 4 ++-- > > 1 file changed, 2 insertions(+), 2 deletions(-) > > > > diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c > > index a25be111cd8f8..5e7c3fb01273c 100644 > > --- a/arch/arm64/kvm/at.c > > +++ b/arch/arm64/kvm/at.c > > @@ -1412,10 +1412,10 @@ void __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) > > return; > > > > /* > > - * If we only have a single stage of translation (E2H=0 or > > + * If we only have a single stage of translation (E2H=1 or > > * TGE=1), exit early. Same thing if {VM,DC}=={0,0}. > > */ > > - if (!vcpu_el2_e2h_is_set(vcpu) || vcpu_el2_tge_is_set(vcpu) || > > + if (vcpu_el2_e2h_is_set(vcpu) || vcpu_el2_tge_is_set(vcpu) || > > The check should be HCR_EL2.<E2H,TGE> == '11'. Maybe instead: > > /* > * Exit early if we only have a single stage of translation > * either because we're in the EL2&0 translation regime or > * stage-2 translation is disabled (i.e. HCR_EL2.{VM,DC}=={0,0}). > */ > if (compute_translation_regime(vcpu, op) == TR_EL20 || > !(vcpu_read_sys_reg(vcpu, HCR_EL2) & (HCR_VM | HCR_DC))) > return; Ah, same solution, just way better written! Thanks, M. -- Without deviation from the norm, progress is not possible.
© 2016 - 2025 Red Hat, Inc.