KVM: arm64: Make TLBI OS/Range UNDEF if not advertised to the guest
authorMarc Zyngier <maz@kernel.org>
Wed, 14 Feb 2024 13:18:22 +0000 (13:18 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Mon, 19 Feb 2024 17:13:01 +0000 (17:13 +0000)
Outer Shareable and Range TLBI instructions shouldn't be made available
to the guest if they are not advertised. Use FGU to disable those,
and set HCR_EL2.TLBIOS in the case the host doesn't have FGT. Note
that in that later case, we cannot efficiently disable TLBI Range
instructions, as this would require to trap all TLBIs.

Signed-off-by: Marc Zyngier <maz@kernel.org>
Reviewed-by: Joey Gouly <joey.gouly@arm.com>
Link: https://lore.kernel.org/r/20240214131827.2856277-22-maz@kernel.org
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kvm/sys_regs.c

index 39e7c7f7471754c8929972353c047527005ca671..f07ee7c89822f233fb07c82618b7d0f3dead8635 100644 (file)
@@ -3948,6 +3948,14 @@ void kvm_init_sysreg(struct kvm_vcpu *vcpu)
 
        mutex_lock(&kvm->arch.config_lock);
 
+       /*
+        * In the absence of FGT, we cannot independently trap TLBI
+        * Range instructions. This isn't great, but trapping all
+        * TLBIs would be far worse. Live with it...
+        */
+       if (!kvm_has_feat(kvm, ID_AA64ISAR0_EL1, TLB, OS))
+               vcpu->arch.hcr_el2 |= HCR_TTLBOS;
+
        if (test_bit(KVM_ARCH_FLAG_FGU_INITIALIZED, &kvm->arch.flags))
                goto out;
 
@@ -3960,6 +3968,32 @@ void kvm_init_sysreg(struct kvm_vcpu *vcpu)
                                       HFGxTR_EL2_nSMPRI_EL1_MASK       |
                                       HFGxTR_EL2_nTPIDR2_EL0_MASK);
 
+       if (!kvm_has_feat(kvm, ID_AA64ISAR0_EL1, TLB, OS))
+               kvm->arch.fgu[HFGITR_GROUP] |= (HFGITR_EL2_TLBIRVAALE1OS|
+                                               HFGITR_EL2_TLBIRVALE1OS |
+                                               HFGITR_EL2_TLBIRVAAE1OS |
+                                               HFGITR_EL2_TLBIRVAE1OS  |
+                                               HFGITR_EL2_TLBIVAALE1OS |
+                                               HFGITR_EL2_TLBIVALE1OS  |
+                                               HFGITR_EL2_TLBIVAAE1OS  |
+                                               HFGITR_EL2_TLBIASIDE1OS |
+                                               HFGITR_EL2_TLBIVAE1OS   |
+                                               HFGITR_EL2_TLBIVMALLE1OS);
+
+       if (!kvm_has_feat(kvm, ID_AA64ISAR0_EL1, TLB, RANGE))
+               kvm->arch.fgu[HFGITR_GROUP] |= (HFGITR_EL2_TLBIRVAALE1  |
+                                               HFGITR_EL2_TLBIRVALE1   |
+                                               HFGITR_EL2_TLBIRVAAE1   |
+                                               HFGITR_EL2_TLBIRVAE1    |
+                                               HFGITR_EL2_TLBIRVAALE1IS|
+                                               HFGITR_EL2_TLBIRVALE1IS |
+                                               HFGITR_EL2_TLBIRVAAE1IS |
+                                               HFGITR_EL2_TLBIRVAE1IS  |
+                                               HFGITR_EL2_TLBIRVAALE1OS|
+                                               HFGITR_EL2_TLBIRVALE1OS |
+                                               HFGITR_EL2_TLBIRVAAE1OS |
+                                               HFGITR_EL2_TLBIRVAE1OS);
+
        set_bit(KVM_ARCH_FLAG_FGU_INITIALIZED, &kvm->arch.flags);
 out:
        mutex_unlock(&kvm->arch.config_lock);