KVM: arm64: Add helper computing the state of 52bit PA support
authorMarc Zyngier <maz@kernel.org>
Sat, 26 Jul 2025 10:38:09 +0000 (11:38 +0100)
committerMarc Zyngier <maz@kernel.org>
Sat, 20 Sep 2025 10:05:12 +0000 (11:05 +0100)
Track whether the guest is using 52bit PAs, either LPA or LPA2.
This further simplifies the handling of LVA for 4k and 16k pages,
as LPA2 implies LVA in this case.

Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/include/asm/kvm_nested.h
arch/arm64/kvm/at.c

index 7fd76f4..038e354 100644 (file)
@@ -299,6 +299,7 @@ struct s1_walk_info {
        bool                    pan;
        bool                    be;
        bool                    s2;
+       bool                    pa52bit;
 };
 
 struct s1_walk_result {
index d71ca4d..8e275ea 100644 (file)
@@ -31,6 +31,29 @@ static bool check_output_size(u64 ipa, struct s1_walk_info *wi)
        return wi->max_oa_bits < 48 && (ipa & GENMASK_ULL(47, wi->max_oa_bits));
 }
 
+static bool has_52bit_pa(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, u64 tcr)
+{
+       switch (BIT(wi->pgshift)) {
+       case SZ_64K:
+       default:                /* IMPDEF: treat any other value as 64k */
+               if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, PARANGE, 52))
+                       return false;
+               return ((wi->regime == TR_EL2 ?
+                        FIELD_GET(TCR_EL2_PS_MASK, tcr) :
+                        FIELD_GET(TCR_IPS_MASK, tcr)) == 0b0110);
+       case SZ_16K:
+               if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR0_EL1, TGRAN16, 52_BIT))
+                       return false;
+               break;
+       case SZ_4K:
+               if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR0_EL1, TGRAN4, 52_BIT))
+                       return false;
+               break;
+       }
+
+       return (tcr & (wi->regime == TR_EL2 ? TCR_EL2_DS : TCR_DS));
+}
+
 /* Return the translation regime that applies to an AT instruction */
 static enum trans_regime compute_translation_regime(struct kvm_vcpu *vcpu, u32 op)
 {
@@ -232,15 +255,13 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
                        goto transfault_l0;
        }
 
+       wi->pa52bit = has_52bit_pa(vcpu, wi, tcr);
+
        /* R_GTJBY, R_SXWGM */
        switch (BIT(wi->pgshift)) {
        case SZ_4K:
-               lva = kvm_has_feat(vcpu->kvm, ID_AA64MMFR0_EL1, TGRAN4, 52_BIT);
-               lva &= tcr & (wi->regime == TR_EL2 ? TCR_EL2_DS : TCR_DS);
-               break;
        case SZ_16K:
-               lva = kvm_has_feat(vcpu->kvm, ID_AA64MMFR0_EL1, TGRAN16, 52_BIT);
-               lva &= tcr & (wi->regime == TR_EL2 ? TCR_EL2_DS : TCR_DS);
+               lva = wi->pa52bit;
                break;
        case SZ_64K:
                lva = kvm_has_feat(vcpu->kvm, ID_AA64MMFR2_EL1, VARange, 52);