summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBradley Morgan <include@grrlz.net>2026-07-17 14:03:11 +0100
committerOliver Upton <oupton@kernel.org>2026-07-31 00:11:56 -0700
commitfff4eb33ffa082f1a106a5d696d035eac8d7842c (patch)
treec8ab3e48a837747092c23d82b114dfd71f9208a0
parent734dc8c01c838779ecda7d0d113839c2b68910be (diff)
downloadlinux-fff4eb33ffa082f1a106a5d696d035eac8d7842c.tar.gz
linux-fff4eb33ffa082f1a106a5d696d035eac8d7842c.zip
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: e912efed485a ("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>
-rw-r--r--arch/arm64/include/asm/kvm_pkvm.h5
-rw-r--r--arch/arm64/kvm/pkvm.c15
2 files changed, 13 insertions, 7 deletions
diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index 74fedd9c5ff0..57afb07d6b13 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -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 */
};
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 008766273912..a66105db8b11 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -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;
}