mirror of
https://github.com/torvalds/linux.git
synced 2026-09-27 11:02:03 +02:00
KVM: arm64: Skip cache maintenance for non-cacheable pKVM mappings
The pKVM flush path walks its own pkvm_mappings list and cleans the
data cache for every mapping, unlike the generic stage-2 walker it
shadows, which skips non-cacheable leaves. Cleaning the cacheable
alias of a non-cacheable mapping is pointless and can corrupt a
device endpoint. Record whether a mapping is non-cacheable in spare
bits of nr_pages and skip cache maintenance for it.
Fixes: e912efed48 ("KVM: arm64: Introduce the EL1 pKVM MMU")
Suggested-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Bradley Morgan <include@grrlz.net>
[tabba: use Marc's anonymous bitfield in place of the open-coded mask and helpers]
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
Reviewed-by: Vincent Donnefort <vdonnefort@google.com>
Tested-by: Bradley Morgan <include@grrlz.net> # On pixel 7, Android 17 CP2A.260705.006
Test: Bradley Morgan <include@grrlz.net> # On QEMU arm64 host
Link: https://patch.msgid.link/20260717130317.1953574-2-fuad.tabba@linux.dev
Signed-off-by: Oliver Upton <oupton@kernel.org>
This commit is contained in:
parent
734dc8c01c
commit
fff4eb33ff
|
|
@ -195,7 +195,10 @@ struct pkvm_mapping {
|
|||
struct rb_node node;
|
||||
u64 gfn;
|
||||
u64 pfn;
|
||||
u64 nr_pages;
|
||||
struct {
|
||||
u64 nr_pages:48;
|
||||
u64 nc:1;
|
||||
};
|
||||
u64 __subtree_last; /* Internal member for interval tree */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@ static int __pkvm_pgtable_stage2_unshare(struct kvm_pgtable *pgt, u64 start, u64
|
|||
|
||||
for_each_mapping_in_range_safe(pgt, start, end, mapping) {
|
||||
ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_guest, handle, mapping->gfn,
|
||||
mapping->nr_pages);
|
||||
(u64)mapping->nr_pages);
|
||||
if (WARN_ON(ret))
|
||||
return ret;
|
||||
pkvm_mapping_remove(mapping, &pgt->pkvm_mappings);
|
||||
|
|
@ -470,6 +470,7 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
|
|||
mapping->gfn = gfn;
|
||||
mapping->pfn = pfn;
|
||||
mapping->nr_pages = size / PAGE_SIZE;
|
||||
mapping->nc = !!(prot & (KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC));
|
||||
pkvm_mapping_insert(mapping, &pgt->pkvm_mappings);
|
||||
|
||||
return ret;
|
||||
|
|
@ -500,7 +501,7 @@ int pkvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size)
|
|||
lockdep_assert_held(&kvm->mmu_lock);
|
||||
for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
|
||||
ret = kvm_call_hyp_nvhe(__pkvm_host_wrprotect_guest, handle, mapping->gfn,
|
||||
mapping->nr_pages);
|
||||
(u64)mapping->nr_pages);
|
||||
if (WARN_ON(ret))
|
||||
break;
|
||||
}
|
||||
|
|
@ -514,9 +515,11 @@ int pkvm_pgtable_stage2_flush(struct kvm_pgtable *pgt, u64 addr, u64 size)
|
|||
struct pkvm_mapping *mapping;
|
||||
|
||||
lockdep_assert_held(&kvm->mmu_lock);
|
||||
for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping)
|
||||
__clean_dcache_guest_page(pfn_to_kaddr(mapping->pfn),
|
||||
PAGE_SIZE * mapping->nr_pages);
|
||||
for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
|
||||
if (!mapping->nc)
|
||||
__clean_dcache_guest_page(pfn_to_kaddr(mapping->pfn),
|
||||
PAGE_SIZE * mapping->nr_pages);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -534,7 +537,7 @@ bool pkvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr, u64
|
|||
lockdep_assert_held(&kvm->mmu_lock);
|
||||
for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping)
|
||||
young |= kvm_call_hyp_nvhe(__pkvm_host_test_clear_young_guest, handle, mapping->gfn,
|
||||
mapping->nr_pages, mkold);
|
||||
(u64)mapping->nr_pages, mkold);
|
||||
|
||||
return young;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user