]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: nv: Tag shadow S2 entries with guest's leaf S2 level
authorMarc Zyngier <maz@kernel.org>
Fri, 14 Jun 2024 14:45:48 +0000 (15:45 +0100)
committerOliver Upton <oliver.upton@linux.dev>
Wed, 19 Jun 2024 08:14:38 +0000 (08:14 +0000)
Populate bits [56:55] of the leaf entry with the level provided
by the guest's S2 translation. This will allow us to better scope
the invalidation by remembering the mapping size.

Of course, this assume that the guest will issue an invalidation
with an address that falls into the same leaf. If the guest doesn't,
we'll over-invalidate.

Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20240614144552.2773592-13-maz@kernel.org
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/include/asm/kvm_nested.h
arch/arm64/kvm/mmu.c

index fcb0de3a93fe5292f189fd94fca3295208e30601..971dbe533730f4f40d1fd4c86a5bdb4f34e022ca 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/bitfield.h>
 #include <linux/kvm_host.h>
 #include <asm/kvm_emulate.h>
+#include <asm/kvm_pgtable.h>
 
 static inline bool vcpu_has_nv(const struct kvm_vcpu *vcpu)
 {
@@ -195,4 +196,11 @@ static inline bool kvm_auth_eretax(struct kvm_vcpu *vcpu, u64 *elr)
 }
 #endif
 
+#define KVM_NV_GUEST_MAP_SZ    (KVM_PGTABLE_PROT_SW1 | KVM_PGTABLE_PROT_SW0)
+
+static inline u64 kvm_encode_nested_level(struct kvm_s2_trans *trans)
+{
+       return FIELD_PREP(KVM_NV_GUEST_MAP_SZ, trans->level);
+}
+
 #endif /* __ARM64_KVM_NESTED_H */
index 4ed93a384255ec27f80219b9298767cc609ff16e..6981b1bc094686e8bea69fc121247ff8150f892f 100644 (file)
@@ -1598,11 +1598,19 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
         * Potentially reduce shadow S2 permissions to match the guest's own
         * S2. For exec faults, we'd only reach this point if the guest
         * actually allowed it (see kvm_s2_handle_perm_fault).
+        *
+        * Also encode the level of the original translation in the SW bits
+        * of the leaf entry as a proxy for the span of that translation.
+        * This will be retrieved on TLB invalidation from the guest and
+        * used to limit the invalidation scope if a TTL hint or a range
+        * isn't provided.
         */
        if (nested) {
                writable &= kvm_s2_trans_writable(nested);
                if (!kvm_s2_trans_readable(nested))
                        prot &= ~KVM_PGTABLE_PROT_R;
+
+               prot |= kvm_encode_nested_level(nested);
        }
 
        read_lock(&kvm->mmu_lock);
@@ -1661,14 +1669,21 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
         * permissions only if vma_pagesize equals fault_granule. Otherwise,
         * kvm_pgtable_stage2_map() should be called to change block size.
         */
-       if (fault_is_perm && vma_pagesize == fault_granule)
+       if (fault_is_perm && vma_pagesize == fault_granule) {
+               /*
+                * Drop the SW bits in favour of those stored in the
+                * PTE, which will be preserved.
+                */
+               prot &= ~KVM_NV_GUEST_MAP_SZ;
                ret = kvm_pgtable_stage2_relax_perms(pgt, fault_ipa, prot);
-       else
+       } else {
                ret = kvm_pgtable_stage2_map(pgt, fault_ipa, vma_pagesize,
                                             __pfn_to_phys(pfn), prot,
                                             memcache,
                                             KVM_PGTABLE_WALK_HANDLE_FAULT |
                                             KVM_PGTABLE_WALK_SHARED);
+       }
+
 out_unlock:
        read_unlock(&kvm->mmu_lock);