linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 00/13] Direct Map Removal Support for guest_memfd
@ 2025-12-05 16:57 Kalyazin, Nikita
  2025-12-05 16:57 ` [PATCH v8 01/13] x86: export set_direct_map_valid_noflush to KVM module Kalyazin, Nikita
                   ` (12 more replies)
  0 siblings, 13 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:57 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

[ based on kvm/next ]

Unmapping virtual machine guest memory from the host kernel's direct map
is a successful mitigation against Spectre-style transient execution
issues: if the kernel page tables do not contain entries pointing to
guest memory, then any attempted speculative read through the direct map
will necessarily be blocked by the MMU before any observable
microarchitectural side-effects happen.  This means that Spectre-gadgets
and similar cannot be used to target virtual machine memory.  Roughly
60% of speculative execution issues fall into this category [1, Table
1].

This patch series extends guest_memfd with the ability to remove its
memory from the host kernel's direct map, to be able to attain the above
protection for KVM guests running inside guest_memfd.

Additionally, a Firecracker branch with support for these VMs can be
found on GitHub [2].

For more details, please refer to the v5 cover letter.  No substantial
changes in design have taken place since.

See also related write() syscall support in guest_memfd [3] where
the interoperation between the two features is described.

Changes since v7:
 - David: separate patches for adding x86 and ARM support
 - Dave/Will: drop support for disabling TLB flushes

v7: https://lore.kernel.org/kvm/20250924151101.2225820-1-patrick.roy@campus.lmu.de
v6: https://lore.kernel.org/kvm/20250912091708.17502-1-roypat@amazon.co.uk
v5: https://lore.kernel.org/kvm/20250828093902.2719-1-roypat@amazon.co.uk
v4: https://lore.kernel.org/kvm/20250221160728.1584559-1-roypat@amazon.co.uk
RFCv3: https://lore.kernel.org/kvm/20241030134912.515725-1-roypat@amazon.co.uk
RFCv2: https://lore.kernel.org/kvm/20240910163038.1298452-1-roypat@amazon.co.uk
RFCv1: https://lore.kernel.org/kvm/20240709132041.3625501-1-roypat@amazon.co.uk

[1] https://download.vusec.net/papers/quarantine_raid23.pdf
[2] https://github.com/firecracker-microvm/firecracker/tree/feature/secret-hiding
[3] https://lore.kernel.org/kvm/20251114151828.98165-1-kalyazin@amazon.com

Patrick Roy (13):
  x86: export set_direct_map_valid_noflush to KVM module
  x86/tlb: export flush_tlb_kernel_range to KVM module
  mm: introduce AS_NO_DIRECT_MAP
  KVM: guest_memfd: Add stub for kvm_arch_gmem_invalidate
  KVM: guest_memfd: Add flag to remove from direct map
  KVM: x86: define kvm_arch_gmem_supports_no_direct_map()
  KVM: arm64: define kvm_arch_gmem_supports_no_direct_map()
  KVM: selftests: load elf via bounce buffer
  KVM: selftests: set KVM_MEM_GUEST_MEMFD in vm_mem_add() if guest_memfd
    != -1
  KVM: selftests: Add guest_memfd based vm_mem_backing_src_types
  KVM: selftests: cover GUEST_MEMFD_FLAG_NO_DIRECT_MAP in existing
    selftests
  KVM: selftests: stuff vm_mem_backing_src_type into vm_shape
  KVM: selftests: Test guest execution from direct map removed gmem

 Documentation/virt/kvm/api.rst                | 22 ++++---
 arch/arm64/include/asm/kvm_host.h             | 13 ++++
 arch/x86/include/asm/kvm_host.h               |  9 +++
 arch/x86/include/asm/tlbflush.h               |  3 +-
 arch/x86/mm/pat/set_memory.c                  |  1 +
 arch/x86/mm/tlb.c                             |  1 +
 include/linux/kvm_host.h                      | 14 ++++
 include/linux/pagemap.h                       | 16 +++++
 include/linux/secretmem.h                     | 18 ------
 include/uapi/linux/kvm.h                      |  1 +
 lib/buildid.c                                 |  4 +-
 mm/gup.c                                      | 19 ++----
 mm/mlock.c                                    |  2 +-
 mm/secretmem.c                                |  8 +--
 .../testing/selftests/kvm/guest_memfd_test.c  | 17 ++++-
 .../testing/selftests/kvm/include/kvm_util.h  | 37 ++++++++---
 .../testing/selftests/kvm/include/test_util.h |  8 +++
 tools/testing/selftests/kvm/lib/elf.c         |  8 +--
 tools/testing/selftests/kvm/lib/io.c          | 23 +++++++
 tools/testing/selftests/kvm/lib/kvm_util.c    | 59 +++++++++--------
 tools/testing/selftests/kvm/lib/test_util.c   |  8 +++
 tools/testing/selftests/kvm/lib/x86/sev.c     |  1 +
 .../selftests/kvm/pre_fault_memory_test.c     |  1 +
 .../selftests/kvm/set_memory_region_test.c    | 52 +++++++++++++--
 .../kvm/x86/private_mem_conversions_test.c    |  7 +-
 virt/kvm/guest_memfd.c                        | 64 +++++++++++++++++--
 26 files changed, 314 insertions(+), 102 deletions(-)


base-commit: e0c26d47def7382d7dbd9cad58bc653aed75737a
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 01/13] x86: export set_direct_map_valid_noflush to KVM module
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
@ 2025-12-05 16:57 ` Kalyazin, Nikita
  2025-12-05 17:26   ` Dave Hansen
  2025-12-05 16:58 ` [PATCH v8 02/13] x86/tlb: export flush_tlb_kernel_range " Kalyazin, Nikita
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:57 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

Use the per-module export functionality to allow KVM (and only KVM)
access to set_direct_map_valid_noflush(). This allows guest_memfd to
remove its memory from the direct map, even if KVM is built as a module.

Only do this on x86, as only x86 and arm64 support guest_memfd, and
arm64 does not support building KVM as a module.

Direct map removal gives guest_memfd the same protection that
memfd_secret enjoys, such as hardening against Spectre-like attacks
through in-kernel gadgets.

Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 arch/x86/mm/pat/set_memory.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c
index 970981893c9b..a7a88b598d99 100644
--- a/arch/x86/mm/pat/set_memory.c
+++ b/arch/x86/mm/pat/set_memory.c
@@ -2655,6 +2655,7 @@ int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
 
 	return __set_pages_np(page, nr);
 }
+EXPORT_SYMBOL_FOR_MODULES(set_direct_map_valid_noflush, "kvm");
 
 #ifdef CONFIG_DEBUG_PAGEALLOC
 void __kernel_map_pages(struct page *page, int numpages, int enable)
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 02/13] x86/tlb: export flush_tlb_kernel_range to KVM module
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
  2025-12-05 16:57 ` [PATCH v8 01/13] x86: export set_direct_map_valid_noflush to KVM module Kalyazin, Nikita
@ 2025-12-05 16:58 ` Kalyazin, Nikita
  2025-12-05 16:58 ` [PATCH v8 03/13] mm: introduce AS_NO_DIRECT_MAP Kalyazin, Nikita
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:58 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

After direct map removal, a TLB flush must be done to ensure that the
just-unmapped memory cannot be accessed through stale TLB entries.

This export is only needed on x86, as arm64 (the only other architecture
supporting guest_memfd currently) does not allow building KVM as a
module.

Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 arch/x86/include/asm/tlbflush.h | 3 ++-
 arch/x86/mm/tlb.c               | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index 00daedfefc1b..6f57f7eb621b 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -317,7 +317,6 @@ extern void flush_tlb_all(void);
 extern void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
 				unsigned long end, unsigned int stride_shift,
 				bool freed_tables);
-extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
 
 static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long a)
 {
@@ -483,6 +482,8 @@ static inline void cpu_tlbstate_update_lam(unsigned long lam, u64 untag_mask)
 #endif
 #endif /* !MODULE */
 
+extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
+
 static inline void __native_tlb_flush_global(unsigned long cr4)
 {
 	native_write_cr4(cr4 ^ X86_CR4_PGE);
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 5d221709353e..cce591d26e4c 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -1561,6 +1561,7 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end)
 
 	put_flush_tlb_info();
 }
+EXPORT_SYMBOL_FOR_MODULES(flush_tlb_kernel_range, "kvm");
 
 /*
  * This can be used from process context to figure out what the value of
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 03/13] mm: introduce AS_NO_DIRECT_MAP
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
  2025-12-05 16:57 ` [PATCH v8 01/13] x86: export set_direct_map_valid_noflush to KVM module Kalyazin, Nikita
  2025-12-05 16:58 ` [PATCH v8 02/13] x86/tlb: export flush_tlb_kernel_range " Kalyazin, Nikita
@ 2025-12-05 16:58 ` Kalyazin, Nikita
  2025-12-05 18:35   ` John Hubbard
  2025-12-05 16:58 ` [PATCH v8 04/13] KVM: guest_memfd: Add stub for kvm_arch_gmem_invalidate Kalyazin, Nikita
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:58 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

Add AS_NO_DIRECT_MAP for mappings where direct map entries of folios are
set to not present. Currently, mappings that match this description are
secretmem mappings (memfd_secret()). Later, some guest_memfd
configurations will also fall into this category.

Reject this new type of mappings in all locations that currently reject
secretmem mappings, on the assumption that if secretmem mappings are
rejected somewhere, it is precisely because of an inability to deal with
folios without direct map entries, and then make memfd_secret() use
AS_NO_DIRECT_MAP on its address_space to drop its special
vma_is_secretmem()/secretmem_mapping() checks.

This drops a optimization in gup_fast_folio_allowed() where
secretmem_mapping() was only called if CONFIG_SECRETMEM=y. secretmem is
enabled by default since commit b758fe6df50d ("mm/secretmem: make it on
by default"), so the secretmem check did not actually end up elided in
most cases anymore anyway.

Use a new flag instead of overloading AS_INACCESSIBLE (which is already
set by guest_memfd) because not all guest_memfd mappings will end up
being direct map removed (e.g. in pKVM setups, parts of guest_memfd that
can be mapped to userspace should also be GUP-able, and generally not
have restrictions on who can access it).

Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Acked-by: David Hildenbrand (Red Hat)" <david@kernel.org>
Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 include/linux/pagemap.h   | 16 ++++++++++++++++
 include/linux/secretmem.h | 18 ------------------
 lib/buildid.c             |  4 ++--
 mm/gup.c                  | 19 +++++--------------
 mm/mlock.c                |  2 +-
 mm/secretmem.c            |  8 ++------
 6 files changed, 26 insertions(+), 41 deletions(-)

diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index a17fabbc0269..d51e0c0404e2 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -213,6 +213,7 @@ enum mapping_flags {
 	AS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM = 9,
 	AS_KERNEL_FILE = 10,	/* mapping for a fake kernel file that shouldn't
 				   account usage to user cgroups */
+	AS_NO_DIRECT_MAP = 11,	/* Folios in the mapping are not in the direct map */
 	/* Bits 16-25 are used for FOLIO_ORDER */
 	AS_FOLIO_ORDER_BITS = 5,
 	AS_FOLIO_ORDER_MIN = 16,
@@ -348,6 +349,21 @@ static inline bool mapping_writeback_may_deadlock_on_reclaim(const struct addres
 	return test_bit(AS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM, &mapping->flags);
 }
 
+static inline void mapping_set_no_direct_map(struct address_space *mapping)
+{
+	set_bit(AS_NO_DIRECT_MAP, &mapping->flags);
+}
+
+static inline bool mapping_no_direct_map(const struct address_space *mapping)
+{
+	return test_bit(AS_NO_DIRECT_MAP, &mapping->flags);
+}
+
+static inline bool vma_has_no_direct_map(const struct vm_area_struct *vma)
+{
+	return vma->vm_file && mapping_no_direct_map(vma->vm_file->f_mapping);
+}
+
 static inline gfp_t mapping_gfp_mask(const struct address_space *mapping)
 {
 	return mapping->gfp_mask;
diff --git a/include/linux/secretmem.h b/include/linux/secretmem.h
index e918f96881f5..0ae1fb057b3d 100644
--- a/include/linux/secretmem.h
+++ b/include/linux/secretmem.h
@@ -4,28 +4,10 @@
 
 #ifdef CONFIG_SECRETMEM
 
-extern const struct address_space_operations secretmem_aops;
-
-static inline bool secretmem_mapping(struct address_space *mapping)
-{
-	return mapping->a_ops == &secretmem_aops;
-}
-
-bool vma_is_secretmem(struct vm_area_struct *vma);
 bool secretmem_active(void);
 
 #else
 
-static inline bool vma_is_secretmem(struct vm_area_struct *vma)
-{
-	return false;
-}
-
-static inline bool secretmem_mapping(struct address_space *mapping)
-{
-	return false;
-}
-
 static inline bool secretmem_active(void)
 {
 	return false;
diff --git a/lib/buildid.c b/lib/buildid.c
index c4b0f376fb34..89e567954284 100644
--- a/lib/buildid.c
+++ b/lib/buildid.c
@@ -65,8 +65,8 @@ static int freader_get_folio(struct freader *r, loff_t file_off)
 
 	freader_put_folio(r);
 
-	/* reject secretmem folios created with memfd_secret() */
-	if (secretmem_mapping(r->file->f_mapping))
+	/* reject folios without direct map entries (e.g. from memfd_secret() or guest_memfd()) */
+	if (mapping_no_direct_map(r->file->f_mapping))
 		return -EFAULT;
 
 	r->folio = filemap_get_folio(r->file->f_mapping, file_off >> PAGE_SHIFT);
diff --git a/mm/gup.c b/mm/gup.c
index a8ba5112e4d0..719e4dbecc09 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -11,7 +11,6 @@
 #include <linux/rmap.h>
 #include <linux/swap.h>
 #include <linux/swapops.h>
-#include <linux/secretmem.h>
 
 #include <linux/sched/signal.h>
 #include <linux/rwsem.h>
@@ -1216,7 +1215,7 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)
 	if ((gup_flags & FOLL_SPLIT_PMD) && is_vm_hugetlb_page(vma))
 		return -EOPNOTSUPP;
 
-	if (vma_is_secretmem(vma))
+	if (vma_has_no_direct_map(vma))
 		return -EFAULT;
 
 	if (write) {
@@ -2724,7 +2723,7 @@ EXPORT_SYMBOL(get_user_pages_unlocked);
  * This call assumes the caller has pinned the folio, that the lowest page table
  * level still points to this folio, and that interrupts have been disabled.
  *
- * GUP-fast must reject all secretmem folios.
+ * GUP-fast must reject all folios without direct map entries (such as secretmem).
  *
  * Writing to pinned file-backed dirty tracked folios is inherently problematic
  * (see comment describing the writable_file_mapping_allowed() function). We
@@ -2739,7 +2738,6 @@ static bool gup_fast_folio_allowed(struct folio *folio, unsigned int flags)
 {
 	bool reject_file_backed = false;
 	struct address_space *mapping;
-	bool check_secretmem = false;
 	unsigned long mapping_flags;
 
 	/*
@@ -2751,18 +2749,10 @@ static bool gup_fast_folio_allowed(struct folio *folio, unsigned int flags)
 		reject_file_backed = true;
 
 	/* We hold a folio reference, so we can safely access folio fields. */
-
-	/* secretmem folios are always order-0 folios. */
-	if (IS_ENABLED(CONFIG_SECRETMEM) && !folio_test_large(folio))
-		check_secretmem = true;
-
-	if (!reject_file_backed && !check_secretmem)
-		return true;
-
 	if (WARN_ON_ONCE(folio_test_slab(folio)))
 		return false;
 
-	/* hugetlb neither requires dirty-tracking nor can be secretmem. */
+	/* hugetlb neither requires dirty-tracking nor can be without direct map. */
 	if (folio_test_hugetlb(folio))
 		return true;
 
@@ -2800,8 +2790,9 @@ static bool gup_fast_folio_allowed(struct folio *folio, unsigned int flags)
 	 * At this point, we know the mapping is non-null and points to an
 	 * address_space object.
 	 */
-	if (check_secretmem && secretmem_mapping(mapping))
+	if (mapping_no_direct_map(mapping))
 		return false;
+
 	/* The only remaining allowed file system is shmem. */
 	return !reject_file_backed || shmem_mapping(mapping);
 }
diff --git a/mm/mlock.c b/mm/mlock.c
index bb0776f5ef7c..506452ee8cad 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -474,7 +474,7 @@ static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_struct *vma,
 
 	if (newflags == oldflags || (oldflags & VM_SPECIAL) ||
 	    is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm) ||
-	    vma_is_dax(vma) || vma_is_secretmem(vma) || (oldflags & VM_DROPPABLE))
+	    vma_is_dax(vma) || vma_has_no_direct_map(vma) || (oldflags & VM_DROPPABLE))
 		/* don't set VM_LOCKED or VM_LOCKONFAULT and don't count */
 		goto out;
 
diff --git a/mm/secretmem.c b/mm/secretmem.c
index b59350daffe3..c1eafe29c3e8 100644
--- a/mm/secretmem.c
+++ b/mm/secretmem.c
@@ -134,11 +134,6 @@ static int secretmem_mmap_prepare(struct vm_area_desc *desc)
 	return 0;
 }
 
-bool vma_is_secretmem(struct vm_area_struct *vma)
-{
-	return vma->vm_ops == &secretmem_vm_ops;
-}
-
 static const struct file_operations secretmem_fops = {
 	.release	= secretmem_release,
 	.mmap_prepare	= secretmem_mmap_prepare,
@@ -156,7 +151,7 @@ static void secretmem_free_folio(struct folio *folio)
 	folio_zero_segment(folio, 0, folio_size(folio));
 }
 
-const struct address_space_operations secretmem_aops = {
+static const struct address_space_operations secretmem_aops = {
 	.dirty_folio	= noop_dirty_folio,
 	.free_folio	= secretmem_free_folio,
 	.migrate_folio	= secretmem_migrate_folio,
@@ -205,6 +200,7 @@ static struct file *secretmem_file_create(unsigned long flags)
 
 	mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
 	mapping_set_unevictable(inode->i_mapping);
+	mapping_set_no_direct_map(inode->i_mapping);
 
 	inode->i_op = &secretmem_iops;
 	inode->i_mapping->a_ops = &secretmem_aops;
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 04/13] KVM: guest_memfd: Add stub for kvm_arch_gmem_invalidate
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
                   ` (2 preceding siblings ...)
  2025-12-05 16:58 ` [PATCH v8 03/13] mm: introduce AS_NO_DIRECT_MAP Kalyazin, Nikita
@ 2025-12-05 16:58 ` Kalyazin, Nikita
  2025-12-05 16:58 ` [PATCH v8 05/13] KVM: guest_memfd: Add flag to remove from direct map Kalyazin, Nikita
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:58 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

Add a no-op stub for kvm_arch_gmem_invalidate if
CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE=n. This allows defining
kvm_gmem_free_folio without ifdef-ery, which allows more cleanly using
guest_memfd's free_folio callback for non-arch-invalidation related
code.

Acked-by: David Hildenbrand (Red Hat)" <david@kernel.org
Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 include/linux/kvm_host.h | 2 ++
 virt/kvm/guest_memfd.c   | 4 ----
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index d93f75b05ae2..27796a09d29b 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2589,6 +2589,8 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t gfn, void __user *src, long npages
 
 #ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
 void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);
+#else
+static inline void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end) { }
 #endif
 
 #ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index fdaea3422c30..92e7f8c1f303 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -527,7 +527,6 @@ static int kvm_gmem_error_folio(struct address_space *mapping, struct folio *fol
 	return MF_DELAYED;
 }
 
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
 static void kvm_gmem_free_folio(struct folio *folio)
 {
 	struct page *page = folio_page(folio, 0);
@@ -536,15 +535,12 @@ static void kvm_gmem_free_folio(struct folio *folio)
 
 	kvm_arch_gmem_invalidate(pfn, pfn + (1ul << order));
 }
-#endif
 
 static const struct address_space_operations kvm_gmem_aops = {
 	.dirty_folio = noop_dirty_folio,
 	.migrate_folio	= kvm_gmem_migrate_folio,
 	.error_remove_folio = kvm_gmem_error_folio,
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
 	.free_folio = kvm_gmem_free_folio,
-#endif
 };
 
 static int kvm_gmem_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 05/13] KVM: guest_memfd: Add flag to remove from direct map
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
                   ` (3 preceding siblings ...)
  2025-12-05 16:58 ` [PATCH v8 04/13] KVM: guest_memfd: Add stub for kvm_arch_gmem_invalidate Kalyazin, Nikita
@ 2025-12-05 16:58 ` Kalyazin, Nikita
  2025-12-05 17:30   ` Dave Hansen
  2025-12-05 16:58 ` [PATCH v8 06/13] KVM: x86: define kvm_arch_gmem_supports_no_direct_map() Kalyazin, Nikita
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:58 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

Add GUEST_MEMFD_FLAG_NO_DIRECT_MAP flag for KVM_CREATE_GUEST_MEMFD()
ioctl. When set, guest_memfd folios will be removed from the direct map
after preparation, with direct map entries only restored when the folios
are freed.

To ensure these folios do not end up in places where the kernel cannot
deal with them, set AS_NO_DIRECT_MAP on the guest_memfd's struct
address_space if GUEST_MEMFD_FLAG_NO_DIRECT_MAP is requested.

Note that this flag causes removal of direct map entries for all
guest_memfd folios independent of whether they are "shared" or "private"
(although current guest_memfd only supports either all folios in the
"shared" state, or all folios in the "private" state if
GUEST_MEMFD_FLAG_MMAP is not set). The usecase for removing direct map
entries of also the shared parts of guest_memfd are a special type of
non-CoCo VM where, host userspace is trusted to have access to all of
guest memory, but where Spectre-style transient execution attacks
through the host kernel's direct map should still be mitigated.  In this
setup, KVM retains access to guest memory via userspace mappings of
guest_memfd, which are reflected back into KVM's memslots via
userspace_addr. This is needed for things like MMIO emulation on x86_64
to work.

Direct map entries are zapped right before guest or userspace mappings
of gmem folios are set up, e.g. in kvm_gmem_fault_user_mapping() or
kvm_gmem_get_pfn() [called from the KVM MMU code]. The only place where
a gmem folio can be allocated without being mapped anywhere is
kvm_gmem_populate(), where handling potential failures of direct map
removal is not possible (by the time direct map removal is attempted,
the folio is already marked as prepared, meaning attempting to re-try
kvm_gmem_populate() would just result in -EEXIST without fixing up the
direct map state). These folios are then removed form the direct map
upon kvm_gmem_get_pfn(), e.g. when they are mapped into the guest later.

Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 Documentation/virt/kvm/api.rst | 22 ++++++++-----
 include/linux/kvm_host.h       | 12 +++++++
 include/uapi/linux/kvm.h       |  1 +
 virt/kvm/guest_memfd.c         | 60 ++++++++++++++++++++++++++++++++++
 4 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 01a3abef8abb..c5f54f1370c8 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -6440,15 +6440,19 @@ a single guest_memfd file, but the bound ranges must not overlap).
 The capability KVM_CAP_GUEST_MEMFD_FLAGS enumerates the `flags` that can be
 specified via KVM_CREATE_GUEST_MEMFD.  Currently defined flags:
 
-  ============================ ================================================
-  GUEST_MEMFD_FLAG_MMAP        Enable using mmap() on the guest_memfd file
-                               descriptor.
-  GUEST_MEMFD_FLAG_INIT_SHARED Make all memory in the file shared during
-                               KVM_CREATE_GUEST_MEMFD (memory files created
-                               without INIT_SHARED will be marked private).
-                               Shared memory can be faulted into host userspace
-                               page tables. Private memory cannot.
-  ============================ ================================================
+  ============================== ================================================
+  GUEST_MEMFD_FLAG_MMAP          Enable using mmap() on the guest_memfd file
+                                 descriptor.
+  GUEST_MEMFD_FLAG_INIT_SHARED   Make all memory in the file shared during
+                                 KVM_CREATE_GUEST_MEMFD (memory files created
+                                 without INIT_SHARED will be marked private).
+                                 Shared memory can be faulted into host userspace
+                                 page tables. Private memory cannot.
+  GUEST_MEMFD_FLAG_NO_DIRECT_MAP The guest_memfd instance will behave similarly
+                                 to memfd_secret, and unmaps the memory backing
+                                 it from the kernel's address space before
+                                 being passed off to userspace or the guest.
+  ============================== ================================================
 
 When the KVM MMU performs a PFN lookup to service a guest fault and the backing
 guest_memfd has the GUEST_MEMFD_FLAG_MMAP set, then the fault will always be
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 27796a09d29b..d4d5306075bf 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -738,10 +738,22 @@ static inline u64 kvm_gmem_get_supported_flags(struct kvm *kvm)
 	if (!kvm || kvm_arch_supports_gmem_init_shared(kvm))
 		flags |= GUEST_MEMFD_FLAG_INIT_SHARED;
 
+	if (kvm_arch_gmem_supports_no_direct_map())
+		flags |= GUEST_MEMFD_FLAG_NO_DIRECT_MAP;
+
 	return flags;
 }
 #endif
 
+#ifdef CONFIG_KVM_GUEST_MEMFD
+#ifndef kvm_arch_gmem_supports_no_direct_map
+static inline bool kvm_arch_gmem_supports_no_direct_map(void)
+{
+	return false;
+}
+#endif
+#endif /* CONFIG_KVM_GUEST_MEMFD */
+
 #ifndef kvm_arch_has_readonly_mem
 static inline bool kvm_arch_has_readonly_mem(struct kvm *kvm)
 {
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index dddb781b0507..60341e1ba1be 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1612,6 +1612,7 @@ struct kvm_memory_attributes {
 #define KVM_CREATE_GUEST_MEMFD	_IOWR(KVMIO,  0xd4, struct kvm_create_guest_memfd)
 #define GUEST_MEMFD_FLAG_MMAP		(1ULL << 0)
 #define GUEST_MEMFD_FLAG_INIT_SHARED	(1ULL << 1)
+#define GUEST_MEMFD_FLAG_NO_DIRECT_MAP	(1ULL << 2)
 
 struct kvm_create_guest_memfd {
 	__u64 size;
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 92e7f8c1f303..ec4966a47d5e 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -7,6 +7,9 @@
 #include <linux/mempolicy.h>
 #include <linux/pseudo_fs.h>
 #include <linux/pagemap.h>
+#include <linux/set_memory.h>
+
+#include <asm/tlbflush.h>
 
 #include "kvm_mm.h"
 
@@ -76,6 +79,49 @@ static int __kvm_gmem_prepare_folio(struct kvm *kvm, struct kvm_memory_slot *slo
 	return 0;
 }
 
+#define KVM_GMEM_FOLIO_NO_DIRECT_MAP BIT(0)
+
+static bool kvm_gmem_folio_no_direct_map(struct folio *folio)
+{
+	return ((u64) folio->private) & KVM_GMEM_FOLIO_NO_DIRECT_MAP;
+}
+
+static int kvm_gmem_folio_zap_direct_map(struct folio *folio)
+{
+	int r = 0;
+	unsigned long addr = (unsigned long) folio_address(folio);
+	u64 gmem_flags = GMEM_I(folio_inode(folio))->flags;
+
+	if (kvm_gmem_folio_no_direct_map(folio) || !(gmem_flags & GUEST_MEMFD_FLAG_NO_DIRECT_MAP))
+		goto out;
+
+	r = set_direct_map_valid_noflush(folio_page(folio, 0), folio_nr_pages(folio),
+					 false);
+
+	if (r)
+		goto out;
+
+	folio->private = (void *) KVM_GMEM_FOLIO_NO_DIRECT_MAP;
+	flush_tlb_kernel_range(addr, addr + folio_size(folio));
+
+out:
+	return r;
+}
+
+static void kvm_gmem_folio_restore_direct_map(struct folio *folio)
+{
+	/*
+	 * Direct map restoration cannot fail, as the only error condition
+	 * for direct map manipulation is failure to allocate page tables
+	 * when splitting huge pages, but this split would have already
+	 * happened in set_direct_map_invalid_noflush() in kvm_gmem_folio_zap_direct_map().
+	 * Thus set_direct_map_valid_noflush() here only updates prot bits.
+	 */
+	if (kvm_gmem_folio_no_direct_map(folio))
+		set_direct_map_valid_noflush(folio_page(folio, 0), folio_nr_pages(folio),
+					 true);
+}
+
 static inline void kvm_gmem_mark_prepared(struct folio *folio)
 {
 	folio_mark_uptodate(folio);
@@ -398,6 +444,7 @@ static vm_fault_t kvm_gmem_fault_user_mapping(struct vm_fault *vmf)
 	struct inode *inode = file_inode(vmf->vma->vm_file);
 	struct folio *folio;
 	vm_fault_t ret = VM_FAULT_LOCKED;
+	int err;
 
 	if (((loff_t)vmf->pgoff << PAGE_SHIFT) >= i_size_read(inode))
 		return VM_FAULT_SIGBUS;
@@ -423,6 +470,12 @@ static vm_fault_t kvm_gmem_fault_user_mapping(struct vm_fault *vmf)
 		kvm_gmem_mark_prepared(folio);
 	}
 
+	err = kvm_gmem_folio_zap_direct_map(folio);
+	if (err) {
+		ret = vmf_error(err);
+		goto out_folio;
+	}
+
 	vmf->page = folio_file_page(folio, vmf->pgoff);
 
 out_folio:
@@ -533,6 +586,8 @@ static void kvm_gmem_free_folio(struct folio *folio)
 	kvm_pfn_t pfn = page_to_pfn(page);
 	int order = folio_order(folio);
 
+	kvm_gmem_folio_restore_direct_map(folio);
+
 	kvm_arch_gmem_invalidate(pfn, pfn + (1ul << order));
 }
 
@@ -596,6 +651,9 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
 	/* Unmovable mappings are supposed to be marked unevictable as well. */
 	WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping));
 
+	if (flags & GUEST_MEMFD_FLAG_NO_DIRECT_MAP)
+		mapping_set_no_direct_map(inode->i_mapping);
+
 	GMEM_I(inode)->flags = flags;
 
 	file = alloc_file_pseudo(inode, kvm_gmem_mnt, name, O_RDWR, &kvm_gmem_fops);
@@ -807,6 +865,8 @@ int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
 	if (!is_prepared)
 		r = kvm_gmem_prepare_folio(kvm, slot, gfn, folio);
 
+	kvm_gmem_folio_zap_direct_map(folio);
+
 	folio_unlock(folio);
 
 	if (!r)
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 06/13] KVM: x86: define kvm_arch_gmem_supports_no_direct_map()
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
                   ` (4 preceding siblings ...)
  2025-12-05 16:58 ` [PATCH v8 05/13] KVM: guest_memfd: Add flag to remove from direct map Kalyazin, Nikita
@ 2025-12-05 16:58 ` Kalyazin, Nikita
  2025-12-05 16:59 ` [PATCH v8 07/13] KVM: arm64: " Kalyazin, Nikita
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:58 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

x86 supports GUEST_MEMFD_FLAG_NO_DIRECT_MAP whenever direct map
modifications are possible (which is always the case).

Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 arch/x86/include/asm/kvm_host.h | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 5a3bfa293e8b..68bd29a52f24 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -28,6 +28,7 @@
 #include <linux/sched/vhost_task.h>
 #include <linux/call_once.h>
 #include <linux/atomic.h>
+#include <linux/set_memory.h>
 
 #include <asm/apic.h>
 #include <asm/pvclock-abi.h>
@@ -2481,4 +2482,12 @@ static inline bool kvm_arch_has_irq_bypass(void)
 	return enable_device_posted_irqs;
 }
 
+#ifdef CONFIG_KVM_GUEST_MEMFD
+static inline bool kvm_arch_gmem_supports_no_direct_map(void)
+{
+	return can_set_direct_map();
+}
+#define kvm_arch_gmem_supports_no_direct_map kvm_arch_gmem_supports_no_direct_map
+#endif /* CONFIG_KVM_GUEST_MEMFD */
+
 #endif /* _ASM_X86_KVM_HOST_H */
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 07/13] KVM: arm64: define kvm_arch_gmem_supports_no_direct_map()
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
                   ` (5 preceding siblings ...)
  2025-12-05 16:58 ` [PATCH v8 06/13] KVM: x86: define kvm_arch_gmem_supports_no_direct_map() Kalyazin, Nikita
@ 2025-12-05 16:59 ` Kalyazin, Nikita
  2025-12-05 16:59 ` [PATCH v8 08/13] KVM: selftests: load elf via bounce buffer Kalyazin, Nikita
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:59 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

Support for GUEST_MEMFD_FLAG_NO_DIRECT_MAP on arm64 depends on 1) direct
map manipulations at 4k granularity being possible, and 2) FEAT_S2FWB.

1) is met whenever the direct map is set up at 4k granularity (e.g. not
 with huge/gigantic pages) at boottime, as due to ARM's
break-before-make semantics, breaking huge mappings into 4k mappings in
the direct map is not possible (BBM would require temporary invalidation
of the entire huge mapping, even if only a 4k subrange should be zapped,
which will probably crash the kernel). However, current defconfigs
select for example CONFIG_RO_DATA_FULL_DEFAULT_ENABLED, which forces a 4k
direct map.

2) is required to allow KVM to elide cache coherency operations when
installing stage 2 page tables, which require the direct map to be
entry for the newly mapped memory to be present (which it will not be,
as guest_memfd would have removed direct map entries in
kvm_gmem_get_pfn()).

Cc: Will Deacon <will@kernel.org>
Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 arch/arm64/include/asm/kvm_host.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index ac7f970c7883..d431ca7d4fc9 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -19,6 +19,7 @@
 #include <linux/maple_tree.h>
 #include <linux/percpu.h>
 #include <linux/psci.h>
+#include <linux/set_memory.h>
 #include <asm/arch_gicv3.h>
 #include <asm/barrier.h>
 #include <asm/cpufeature.h>
@@ -1654,5 +1655,17 @@ static __always_inline enum fgt_group_id __fgt_reg_to_group_id(enum vcpu_sysreg
 									\
 		p;							\
 	})
+#ifdef CONFIG_KVM_GUEST_MEMFD
+static inline bool kvm_arch_gmem_supports_no_direct_map(void)
+{
+	/*
+	 * Without FWB, direct map access is needed in kvm_pgtable_stage2_map(),
+	 * as it calls dcache_clean_inval_poc().
+	 */
+	return can_set_direct_map() && cpus_have_final_cap(ARM64_HAS_STAGE2_FWB);
+}
+#define kvm_arch_gmem_supports_no_direct_map kvm_arch_gmem_supports_no_direct_map
+#endif /* CONFIG_KVM_GUEST_MEMFD */
+
 
 #endif /* __ARM64_KVM_HOST_H__ */
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 08/13] KVM: selftests: load elf via bounce buffer
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
                   ` (6 preceding siblings ...)
  2025-12-05 16:59 ` [PATCH v8 07/13] KVM: arm64: " Kalyazin, Nikita
@ 2025-12-05 16:59 ` Kalyazin, Nikita
  2025-12-05 16:59 ` [PATCH v8 09/13] KVM: selftests: set KVM_MEM_GUEST_MEMFD in vm_mem_add() if guest_memfd != -1 Kalyazin, Nikita
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:59 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

If guest memory is backed using a VMA that does not allow GUP (e.g. a
userspace mapping of guest_memfd when the fd was allocated using
GUEST_MEMFD_FLAG_NO_DIRECT_MAP), then directly loading the test ELF
binary into it via read(2) potentially does not work. To nevertheless
support loading binaries in this cases, do the read(2) syscall using a
bounce buffer, and then memcpy from the bounce buffer into guest memory.

Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 .../testing/selftests/kvm/include/test_util.h |  1 +
 tools/testing/selftests/kvm/lib/elf.c         |  8 +++----
 tools/testing/selftests/kvm/lib/io.c          | 23 +++++++++++++++++++
 3 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
index b4872ba8ed12..8140e59b59e5 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -48,6 +48,7 @@ do {								\
 
 ssize_t test_write(int fd, const void *buf, size_t count);
 ssize_t test_read(int fd, void *buf, size_t count);
+ssize_t test_read_bounce(int fd, void *buf, size_t count);
 int test_seq_read(const char *path, char **bufp, size_t *sizep);
 
 void __printf(5, 6) test_assert(bool exp, const char *exp_str,
diff --git a/tools/testing/selftests/kvm/lib/elf.c b/tools/testing/selftests/kvm/lib/elf.c
index f34d926d9735..e829fbe0a11e 100644
--- a/tools/testing/selftests/kvm/lib/elf.c
+++ b/tools/testing/selftests/kvm/lib/elf.c
@@ -31,7 +31,7 @@ static void elfhdr_get(const char *filename, Elf64_Ehdr *hdrp)
 	 * the real size of the ELF header.
 	 */
 	unsigned char ident[EI_NIDENT];
-	test_read(fd, ident, sizeof(ident));
+	test_read_bounce(fd, ident, sizeof(ident));
 	TEST_ASSERT((ident[EI_MAG0] == ELFMAG0) && (ident[EI_MAG1] == ELFMAG1)
 		&& (ident[EI_MAG2] == ELFMAG2) && (ident[EI_MAG3] == ELFMAG3),
 		"ELF MAGIC Mismatch,\n"
@@ -79,7 +79,7 @@ static void elfhdr_get(const char *filename, Elf64_Ehdr *hdrp)
 	offset_rv = lseek(fd, 0, SEEK_SET);
 	TEST_ASSERT(offset_rv == 0, "Seek to ELF header failed,\n"
 		"  rv: %zi expected: %i", offset_rv, 0);
-	test_read(fd, hdrp, sizeof(*hdrp));
+	test_read_bounce(fd, hdrp, sizeof(*hdrp));
 	TEST_ASSERT(hdrp->e_phentsize == sizeof(Elf64_Phdr),
 		"Unexpected physical header size,\n"
 		"  hdrp->e_phentsize: %x\n"
@@ -146,7 +146,7 @@ void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename)
 
 		/* Read in the program header. */
 		Elf64_Phdr phdr;
-		test_read(fd, &phdr, sizeof(phdr));
+		test_read_bounce(fd, &phdr, sizeof(phdr));
 
 		/* Skip if this header doesn't describe a loadable segment. */
 		if (phdr.p_type != PT_LOAD)
@@ -187,7 +187,7 @@ void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename)
 				"  expected: 0x%jx",
 				n1, errno, (intmax_t) offset_rv,
 				(intmax_t) phdr.p_offset);
-			test_read(fd, addr_gva2hva(vm, phdr.p_vaddr),
+			test_read_bounce(fd, addr_gva2hva(vm, phdr.p_vaddr),
 				phdr.p_filesz);
 		}
 	}
diff --git a/tools/testing/selftests/kvm/lib/io.c b/tools/testing/selftests/kvm/lib/io.c
index fedb2a741f0b..74419becc8bc 100644
--- a/tools/testing/selftests/kvm/lib/io.c
+++ b/tools/testing/selftests/kvm/lib/io.c
@@ -155,3 +155,26 @@ ssize_t test_read(int fd, void *buf, size_t count)
 
 	return num_read;
 }
+
+/* Test read via intermediary buffer
+ *
+ * Same as test_read, except read(2)s happen into a bounce buffer that is memcpy'd
+ * to buf. For use with buffers that cannot be GUP'd (e.g. guest_memfd VMAs if
+ * guest_memfd was created with GUEST_MEMFD_FLAG_NO_DIRECT_MAP).
+ */
+ssize_t test_read_bounce(int fd, void *buf, size_t count)
+{
+	void *bounce_buffer;
+	ssize_t num_read;
+
+	TEST_ASSERT(count >= 0, "Unexpected count, count: %li", count);
+
+	bounce_buffer = malloc(count);
+	TEST_ASSERT(bounce_buffer != NULL, "Failed to allocate bounce buffer");
+
+	num_read = test_read(fd, bounce_buffer, count);
+	memcpy(buf, bounce_buffer, num_read);
+	free(bounce_buffer);
+
+	return num_read;
+}
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 09/13] KVM: selftests: set KVM_MEM_GUEST_MEMFD in vm_mem_add() if guest_memfd != -1
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
                   ` (7 preceding siblings ...)
  2025-12-05 16:59 ` [PATCH v8 08/13] KVM: selftests: load elf via bounce buffer Kalyazin, Nikita
@ 2025-12-05 16:59 ` Kalyazin, Nikita
  2025-12-05 16:59 ` [PATCH v8 10/13] KVM: selftests: Add guest_memfd based vm_mem_backing_src_types Kalyazin, Nikita
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:59 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

Have vm_mem_add() always set KVM_MEM_GUEST_MEMFD in the memslot flags if
a guest_memfd is passed in as an argument. This eliminates the
possibility where a guest_memfd instance is passed to vm_mem_add(), but
it ends up being ignored because the flags argument does not specify
KVM_MEM_GUEST_MEMFD at the same time.

This makes it easy to support more scenarios in which no vm_mem_add() is
not passed a guest_memfd instance, but is expected to allocate one.
Currently, this only happens if guest_memfd == -1 but flags &
KVM_MEM_GUEST_MEMFD != 0, but later vm_mem_add() will gain support for
loading the test code itself into guest_memfd (via
GUEST_MEMFD_FLAG_MMAP) if requested via a special
vm_mem_backing_src_type, at which point having to make sure the src_type
and flags are in-sync becomes cumbersome.

Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 tools/testing/selftests/kvm/lib/kvm_util.c | 24 +++++++++++++---------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 8279b6ced8d2..56ddbca91850 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1057,21 +1057,25 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
 
 	region->backing_src_type = src_type;
 
-	if (flags & KVM_MEM_GUEST_MEMFD) {
-		if (guest_memfd < 0) {
+	if (guest_memfd < 0) {
+		if (flags & KVM_MEM_GUEST_MEMFD) {
 			uint32_t guest_memfd_flags = 0;
 			TEST_ASSERT(!guest_memfd_offset,
 				    "Offset must be zero when creating new guest_memfd");
 			guest_memfd = vm_create_guest_memfd(vm, mem_size, guest_memfd_flags);
-		} else {
-			/*
-			 * Install a unique fd for each memslot so that the fd
-			 * can be closed when the region is deleted without
-			 * needing to track if the fd is owned by the framework
-			 * or by the caller.
-			 */
-			guest_memfd = kvm_dup(guest_memfd);
 		}
+	} else {
+		/*
+		 * Install a unique fd for each memslot so that the fd
+		 * can be closed when the region is deleted without
+		 * needing to track if the fd is owned by the framework
+		 * or by the caller.
+		 */
+		guest_memfd = kvm_dup(guest_memfd);
+	}
+
+	if (guest_memfd > 0) {
+		flags |= KVM_MEM_GUEST_MEMFD;
 
 		region->region.guest_memfd = guest_memfd;
 		region->region.guest_memfd_offset = guest_memfd_offset;
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 10/13] KVM: selftests: Add guest_memfd based vm_mem_backing_src_types
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
                   ` (8 preceding siblings ...)
  2025-12-05 16:59 ` [PATCH v8 09/13] KVM: selftests: set KVM_MEM_GUEST_MEMFD in vm_mem_add() if guest_memfd != -1 Kalyazin, Nikita
@ 2025-12-05 16:59 ` Kalyazin, Nikita
  2025-12-05 16:59 ` [PATCH v8 11/13] KVM: selftests: cover GUEST_MEMFD_FLAG_NO_DIRECT_MAP in existing selftests Kalyazin, Nikita
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:59 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

Allow selftests to configure their memslots such that userspace_addr is
set to a MAP_SHARED mapping of the guest_memfd that's associated with
the memslot. This setup is the configuration for non-CoCo VMs, where all
guest memory is backed by a guest_memfd whose folios are all marked
shared, but KVM is still able to access guest memory to provide
functionality such as MMIO emulation on x86.

Add backing types for normal guest_memfd, as well as direct map removed
guest_memfd.

Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 .../testing/selftests/kvm/include/kvm_util.h  | 18 ++++++
 .../testing/selftests/kvm/include/test_util.h |  7 +++
 tools/testing/selftests/kvm/lib/kvm_util.c    | 61 ++++++++++---------
 tools/testing/selftests/kvm/lib/test_util.c   |  8 +++
 4 files changed, 65 insertions(+), 29 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 81f4355ff28a..6689b43810c1 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -641,6 +641,24 @@ static inline bool is_smt_on(void)
 
 void vm_create_irqchip(struct kvm_vm *vm);
 
+static inline uint32_t backing_src_guest_memfd_flags(enum vm_mem_backing_src_type t)
+{
+	uint32_t flags = 0;
+
+	switch (t) {
+	case VM_MEM_SRC_GUEST_MEMFD_NO_DIRECT_MAP:
+		flags |= GUEST_MEMFD_FLAG_NO_DIRECT_MAP;
+		fallthrough;
+	case VM_MEM_SRC_GUEST_MEMFD:
+		flags |= GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_INIT_SHARED;
+		break;
+	default:
+		break;
+	}
+
+	return flags;
+}
+
 static inline int __vm_create_guest_memfd(struct kvm_vm *vm, uint64_t size,
 					uint64_t flags)
 {
diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
index 8140e59b59e5..ea6de20ce8ef 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -152,6 +152,8 @@ enum vm_mem_backing_src_type {
 	VM_MEM_SRC_ANONYMOUS_HUGETLB_16GB,
 	VM_MEM_SRC_SHMEM,
 	VM_MEM_SRC_SHARED_HUGETLB,
+	VM_MEM_SRC_GUEST_MEMFD,
+	VM_MEM_SRC_GUEST_MEMFD_NO_DIRECT_MAP,
 	NUM_SRC_TYPES,
 };
 
@@ -184,6 +186,11 @@ static inline bool backing_src_is_shared(enum vm_mem_backing_src_type t)
 	return vm_mem_backing_src_alias(t)->flag & MAP_SHARED;
 }
 
+static inline bool backing_src_is_guest_memfd(enum vm_mem_backing_src_type t)
+{
+	return t == VM_MEM_SRC_GUEST_MEMFD || t == VM_MEM_SRC_GUEST_MEMFD_NO_DIRECT_MAP;
+}
+
 static inline bool backing_src_can_be_huge(enum vm_mem_backing_src_type t)
 {
 	return t != VM_MEM_SRC_ANONYMOUS && t != VM_MEM_SRC_SHMEM;
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 56ddbca91850..28ee51253909 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1013,6 +1013,33 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
 	alignment = 1;
 #endif
 
+	if (guest_memfd < 0) {
+		if ((flags & KVM_MEM_GUEST_MEMFD) || backing_src_is_guest_memfd(src_type)) {
+			uint32_t guest_memfd_flags = backing_src_guest_memfd_flags(src_type);
+
+			TEST_ASSERT(!guest_memfd_offset,
+				    "Offset must be zero when creating new guest_memfd");
+			guest_memfd = vm_create_guest_memfd(vm, mem_size, guest_memfd_flags);
+		}
+	} else {
+		/*
+		 * Install a unique fd for each memslot so that the fd
+		 * can be closed when the region is deleted without
+		 * needing to track if the fd is owned by the framework
+		 * or by the caller.
+		 */
+		guest_memfd = kvm_dup(guest_memfd);
+	}
+
+	if (guest_memfd > 0) {
+		flags |= KVM_MEM_GUEST_MEMFD;
+
+		region->region.guest_memfd = guest_memfd;
+		region->region.guest_memfd_offset = guest_memfd_offset;
+	} else {
+		region->region.guest_memfd = -1;
+	}
+
 	/*
 	 * When using THP mmap is not guaranteed to returned a hugepage aligned
 	 * address so we have to pad the mmap. Padding is not needed for HugeTLB
@@ -1028,10 +1055,13 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
 	if (alignment > 1)
 		region->mmap_size += alignment;
 
-	region->fd = -1;
-	if (backing_src_is_shared(src_type))
+	if (backing_src_is_guest_memfd(src_type))
+		region->fd = guest_memfd;
+	else if (backing_src_is_shared(src_type))
 		region->fd = kvm_memfd_alloc(region->mmap_size,
 					     src_type == VM_MEM_SRC_SHARED_HUGETLB);
+	else
+		region->fd = -1;
 
 	region->mmap_start = kvm_mmap(region->mmap_size, PROT_READ | PROT_WRITE,
 				      vm_mem_backing_src_alias(src_type)->flag,
@@ -1056,33 +1086,6 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
 	}
 
 	region->backing_src_type = src_type;
-
-	if (guest_memfd < 0) {
-		if (flags & KVM_MEM_GUEST_MEMFD) {
-			uint32_t guest_memfd_flags = 0;
-			TEST_ASSERT(!guest_memfd_offset,
-				    "Offset must be zero when creating new guest_memfd");
-			guest_memfd = vm_create_guest_memfd(vm, mem_size, guest_memfd_flags);
-		}
-	} else {
-		/*
-		 * Install a unique fd for each memslot so that the fd
-		 * can be closed when the region is deleted without
-		 * needing to track if the fd is owned by the framework
-		 * or by the caller.
-		 */
-		guest_memfd = kvm_dup(guest_memfd);
-	}
-
-	if (guest_memfd > 0) {
-		flags |= KVM_MEM_GUEST_MEMFD;
-
-		region->region.guest_memfd = guest_memfd;
-		region->region.guest_memfd_offset = guest_memfd_offset;
-	} else {
-		region->region.guest_memfd = -1;
-	}
-
 	region->unused_phy_pages = sparsebit_alloc();
 	if (vm_arch_has_protected_memory(vm))
 		region->protected_phy_pages = sparsebit_alloc();
diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c
index 8a1848586a85..ce9fe0271515 100644
--- a/tools/testing/selftests/kvm/lib/test_util.c
+++ b/tools/testing/selftests/kvm/lib/test_util.c
@@ -306,6 +306,14 @@ const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(uint32_t i)
 			 */
 			.flag = MAP_SHARED,
 		},
+		[VM_MEM_SRC_GUEST_MEMFD] = {
+			.name = "guest_memfd",
+			.flag = MAP_SHARED,
+		},
+		[VM_MEM_SRC_GUEST_MEMFD_NO_DIRECT_MAP] = {
+			.name = "guest_memfd_no_direct_map",
+			.flag = MAP_SHARED,
+		}
 	};
 	_Static_assert(ARRAY_SIZE(aliases) == NUM_SRC_TYPES,
 		       "Missing new backing src types?");
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 11/13] KVM: selftests: cover GUEST_MEMFD_FLAG_NO_DIRECT_MAP in existing selftests
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
                   ` (9 preceding siblings ...)
  2025-12-05 16:59 ` [PATCH v8 10/13] KVM: selftests: Add guest_memfd based vm_mem_backing_src_types Kalyazin, Nikita
@ 2025-12-05 16:59 ` Kalyazin, Nikita
  2025-12-05 17:00 ` [PATCH v8 12/13] KVM: selftests: stuff vm_mem_backing_src_type into vm_shape Kalyazin, Nikita
  2025-12-05 17:00 ` [PATCH v8 13/13] KVM: selftests: Test guest execution from direct map removed gmem Kalyazin, Nikita
  12 siblings, 0 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 16:59 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

Extend mem conversion selftests to cover the scenario that the guest can
fault in and write gmem-backed guest memory even if its direct map
removed. Also cover the new flag in guest_memfd_test.c tests.

Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 tools/testing/selftests/kvm/guest_memfd_test.c  | 17 ++++++++++++++++-
 .../kvm/x86/private_mem_conversions_test.c      |  7 ++++---
 2 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 618c937f3c90..9615018a1a67 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -403,6 +403,17 @@ static void test_guest_memfd(unsigned long vm_type)
 		__test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP |
 				       GUEST_MEMFD_FLAG_INIT_SHARED);
 
+	if (flags & GUEST_MEMFD_FLAG_NO_DIRECT_MAP) {
+		__test_guest_memfd(vm, GUEST_MEMFD_FLAG_NO_DIRECT_MAP);
+		if (flags & GUEST_MEMFD_FLAG_MMAP)
+			__test_guest_memfd(vm, GUEST_MEMFD_FLAG_NO_DIRECT_MAP |
+					       GUEST_MEMFD_FLAG_MMAP);
+		if (flags & GUEST_MEMFD_FLAG_INIT_SHARED)
+			__test_guest_memfd(vm, GUEST_MEMFD_FLAG_NO_DIRECT_MAP |
+					       GUEST_MEMFD_FLAG_MMAP |
+					       GUEST_MEMFD_FLAG_INIT_SHARED);
+	}
+
 	kvm_vm_free(vm);
 }
 
@@ -445,10 +456,14 @@ static void test_guest_memfd_guest(void)
 	TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_INIT_SHARED,
 		    "Default VM type should support INIT_SHARED, supported flags = 0x%x",
 		    vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS));
+	TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_NO_DIRECT_MAP,
+		    "Default VM type should support INIT_SHARED, supported flags = 0x%x",
+		    vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS));
 
 	size = vm->page_size;
 	fd = vm_create_guest_memfd(vm, size, GUEST_MEMFD_FLAG_MMAP |
-					     GUEST_MEMFD_FLAG_INIT_SHARED);
+					     GUEST_MEMFD_FLAG_INIT_SHARED |
+					     GUEST_MEMFD_FLAG_NO_DIRECT_MAP);
 	vm_set_user_memory_region2(vm, slot, KVM_MEM_GUEST_MEMFD, gpa, size, NULL, fd, 0);
 
 	mem = kvm_mmap(size, PROT_READ | PROT_WRITE, MAP_SHARED, fd);
diff --git a/tools/testing/selftests/kvm/x86/private_mem_conversions_test.c b/tools/testing/selftests/kvm/x86/private_mem_conversions_test.c
index 1969f4ab9b28..8767cb4a037e 100644
--- a/tools/testing/selftests/kvm/x86/private_mem_conversions_test.c
+++ b/tools/testing/selftests/kvm/x86/private_mem_conversions_test.c
@@ -367,7 +367,7 @@ static void *__test_mem_conversions(void *__vcpu)
 }
 
 static void test_mem_conversions(enum vm_mem_backing_src_type src_type, uint32_t nr_vcpus,
-				 uint32_t nr_memslots)
+				 uint32_t nr_memslots, uint64_t gmem_flags)
 {
 	/*
 	 * Allocate enough memory so that each vCPU's chunk of memory can be
@@ -394,7 +394,7 @@ static void test_mem_conversions(enum vm_mem_backing_src_type src_type, uint32_t
 
 	vm_enable_cap(vm, KVM_CAP_EXIT_HYPERCALL, (1 << KVM_HC_MAP_GPA_RANGE));
 
-	memfd = vm_create_guest_memfd(vm, memfd_size, 0);
+	memfd = vm_create_guest_memfd(vm, memfd_size, gmem_flags);
 
 	for (i = 0; i < nr_memslots; i++)
 		vm_mem_add(vm, src_type, BASE_DATA_GPA + slot_size * i,
@@ -474,7 +474,8 @@ int main(int argc, char *argv[])
 		}
 	}
 
-	test_mem_conversions(src_type, nr_vcpus, nr_memslots);
+	test_mem_conversions(src_type, nr_vcpus, nr_memslots, 0);
+	test_mem_conversions(src_type, nr_vcpus, nr_memslots, GUEST_MEMFD_FLAG_NO_DIRECT_MAP);
 
 	return 0;
 }
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 12/13] KVM: selftests: stuff vm_mem_backing_src_type into vm_shape
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
                   ` (10 preceding siblings ...)
  2025-12-05 16:59 ` [PATCH v8 11/13] KVM: selftests: cover GUEST_MEMFD_FLAG_NO_DIRECT_MAP in existing selftests Kalyazin, Nikita
@ 2025-12-05 17:00 ` Kalyazin, Nikita
  2025-12-05 17:00 ` [PATCH v8 13/13] KVM: selftests: Test guest execution from direct map removed gmem Kalyazin, Nikita
  12 siblings, 0 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 17:00 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

Use one of the padding fields in struct vm_shape to carry an enum
vm_mem_backing_src_type value, to give the option to overwrite the
default of VM_MEM_SRC_ANONYMOUS in __vm_create().

Overwriting this default will allow tests to create VMs where the test
code is backed by mmap'd guest_memfd instead of anonymous memory.

Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 .../testing/selftests/kvm/include/kvm_util.h  | 19 ++++++++++---------
 tools/testing/selftests/kvm/lib/kvm_util.c    |  2 +-
 tools/testing/selftests/kvm/lib/x86/sev.c     |  1 +
 .../selftests/kvm/pre_fault_memory_test.c     |  1 +
 4 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 6689b43810c1..4bc4af9a40cf 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -192,7 +192,7 @@ enum vm_guest_mode {
 struct vm_shape {
 	uint32_t type;
 	uint8_t  mode;
-	uint8_t  pad0;
+	uint8_t  src_type;
 	uint16_t pad1;
 };
 
@@ -200,14 +200,15 @@ kvm_static_assert(sizeof(struct vm_shape) == sizeof(uint64_t));
 
 #define VM_TYPE_DEFAULT			0
 
-#define VM_SHAPE(__mode)			\
-({						\
-	struct vm_shape shape = {		\
-		.mode = (__mode),		\
-		.type = VM_TYPE_DEFAULT		\
-	};					\
-						\
-	shape;					\
+#define VM_SHAPE(__mode)				\
+({							\
+	struct vm_shape shape = {			\
+		.mode	  = (__mode),			\
+		.type	  = VM_TYPE_DEFAULT,		\
+		.src_type = VM_MEM_SRC_ANONYMOUS	\
+	};						\
+							\
+	shape;						\
 })
 
 #if defined(__aarch64__)
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 28ee51253909..268a4520633b 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -467,7 +467,7 @@ struct kvm_vm *__vm_create(struct vm_shape shape, uint32_t nr_runnable_vcpus,
 	if (is_guest_memfd_required(shape))
 		flags |= KVM_MEM_GUEST_MEMFD;
 
-	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags);
+	vm_userspace_mem_region_add(vm, shape.src_type, 0, 0, nr_pages, flags);
 	for (i = 0; i < NR_MEM_REGIONS; i++)
 		vm->memslots[i] = 0;
 
diff --git a/tools/testing/selftests/kvm/lib/x86/sev.c b/tools/testing/selftests/kvm/lib/x86/sev.c
index c3a9838f4806..d920880e4fc0 100644
--- a/tools/testing/selftests/kvm/lib/x86/sev.c
+++ b/tools/testing/selftests/kvm/lib/x86/sev.c
@@ -164,6 +164,7 @@ struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t type, void *guest_code,
 	struct vm_shape shape = {
 		.mode = VM_MODE_DEFAULT,
 		.type = type,
+		.src_type = VM_MEM_SRC_ANONYMOUS,
 	};
 	struct kvm_vm *vm;
 	struct kvm_vcpu *cpus[1];
diff --git a/tools/testing/selftests/kvm/pre_fault_memory_test.c b/tools/testing/selftests/kvm/pre_fault_memory_test.c
index 93e603d91311..8a4d5af53fab 100644
--- a/tools/testing/selftests/kvm/pre_fault_memory_test.c
+++ b/tools/testing/selftests/kvm/pre_fault_memory_test.c
@@ -165,6 +165,7 @@ static void __test_pre_fault_memory(unsigned long vm_type, bool private)
 	const struct vm_shape shape = {
 		.mode = VM_MODE_DEFAULT,
 		.type = vm_type,
+		.src_type = VM_MEM_SRC_ANONYMOUS,
 	};
 	struct kvm_vcpu *vcpu;
 	struct kvm_run *run;
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v8 13/13] KVM: selftests: Test guest execution from direct map removed gmem
  2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
                   ` (11 preceding siblings ...)
  2025-12-05 17:00 ` [PATCH v8 12/13] KVM: selftests: stuff vm_mem_backing_src_type into vm_shape Kalyazin, Nikita
@ 2025-12-05 17:00 ` Kalyazin, Nikita
  12 siblings, 0 replies; 17+ messages in thread
From: Kalyazin, Nikita @ 2025-12-05 17:00 UTC (permalink / raw)
  To: kvm, linux-doc, linux-kernel, kvmarm, linux-fsdevel, linux-mm,
	bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco, Kalyazin, Nikita

From: Patrick Roy <patrick.roy@linux.dev>

Add a selftest that loads itself into guest_memfd (via
GUEST_MEMFD_FLAG_MMAP) and triggers an MMIO exit when executed. This
exercises x86 MMIO emulation code inside KVM for guest_memfd-backed
memslots where the guest_memfd folios are direct map removed.
Particularly, it validates that x86 MMIO emulation code (guest page
table walks + instruction fetch) correctly accesses gmem through the VMA
that's been reflected into the memslot's userspace_addr field (instead
of trying to do direct map accesses).

Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
---
 .../selftests/kvm/set_memory_region_test.c    | 52 +++++++++++++++++--
 1 file changed, 48 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c
index 7fe427ff9b38..6c57fb036b20 100644
--- a/tools/testing/selftests/kvm/set_memory_region_test.c
+++ b/tools/testing/selftests/kvm/set_memory_region_test.c
@@ -602,6 +602,41 @@ static void test_mmio_during_vectoring(void)
 
 	kvm_vm_free(vm);
 }
+
+static void guest_code_trigger_mmio(void)
+{
+	/*
+	 * Read some GPA that is not backed by a memslot. KVM consider this
+	 * as MMIO and tell userspace to emulate the read.
+	 */
+	READ_ONCE(*((uint64_t *)MEM_REGION_GPA));
+
+	GUEST_DONE();
+}
+
+static void test_guest_memfd_mmio(void)
+{
+	struct kvm_vm *vm;
+	struct kvm_vcpu *vcpu;
+	struct vm_shape shape = {
+		.mode = VM_MODE_DEFAULT,
+		.src_type = VM_MEM_SRC_GUEST_MEMFD_NO_DIRECT_MAP,
+	};
+	pthread_t vcpu_thread;
+
+	pr_info("Testing MMIO emulation for instructions in gmem\n");
+
+	vm = __vm_create_shape_with_one_vcpu(shape, &vcpu, 0, guest_code_trigger_mmio);
+
+	virt_map(vm, MEM_REGION_GPA, MEM_REGION_GPA, 1);
+
+	pthread_create(&vcpu_thread, NULL, vcpu_worker, vcpu);
+
+	/* If the MMIO read was successfully emulated, the vcpu thread will exit */
+	pthread_join(vcpu_thread, NULL);
+
+	kvm_vm_free(vm);
+}
 #endif
 
 int main(int argc, char *argv[])
@@ -625,10 +660,19 @@ int main(int argc, char *argv[])
 	test_add_max_memory_regions();
 
 #ifdef __x86_64__
-	if (kvm_has_cap(KVM_CAP_GUEST_MEMFD) &&
-	    (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SW_PROTECTED_VM))) {
-		test_add_private_memory_region();
-		test_add_overlapping_private_memory_regions();
+	if (kvm_has_cap(KVM_CAP_GUEST_MEMFD)) {
+		uint64_t valid_flags = kvm_check_cap(KVM_CAP_GUEST_MEMFD_FLAGS);
+
+		if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SW_PROTECTED_VM)) {
+			test_add_private_memory_region();
+			test_add_overlapping_private_memory_regions();
+		}
+
+		if ((valid_flags & GUEST_MEMFD_FLAG_MMAP)
+			&& (valid_flags & GUEST_MEMFD_FLAG_NO_DIRECT_MAP))
+			test_guest_memfd_mmio();
+		else
+			pr_info("Skipping tests requiring GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_NO_DIRECT_MAP");
 	} else {
 		pr_info("Skipping tests for KVM_MEM_GUEST_MEMFD memory regions\n");
 	}
-- 
2.50.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v8 01/13] x86: export set_direct_map_valid_noflush to KVM module
  2025-12-05 16:57 ` [PATCH v8 01/13] x86: export set_direct_map_valid_noflush to KVM module Kalyazin, Nikita
@ 2025-12-05 17:26   ` Dave Hansen
  0 siblings, 0 replies; 17+ messages in thread
From: Dave Hansen @ 2025-12-05 17:26 UTC (permalink / raw)
  To: Kalyazin, Nikita, kvm, linux-doc, linux-kernel, kvmarm,
	linux-fsdevel, linux-mm, bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco

On 12/5/25 08:57, Kalyazin, Nikita wrote:
...
> +EXPORT_SYMBOL_FOR_MODULES(set_direct_map_valid_noflush, "kvm");

I think this export is a bad idea. The interface is abhorrent enough,
but exporting it just invites abuse and bugs.

I think what this really needs is a pair of helpers that are just:

	folio_zap_direct_map()
and
	folio_restore_direct_map()

that take a folio. *Those* can get exported to modules and those should
internally flush the TLB and not expect callers to do it.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v8 05/13] KVM: guest_memfd: Add flag to remove from direct map
  2025-12-05 16:58 ` [PATCH v8 05/13] KVM: guest_memfd: Add flag to remove from direct map Kalyazin, Nikita
@ 2025-12-05 17:30   ` Dave Hansen
  0 siblings, 0 replies; 17+ messages in thread
From: Dave Hansen @ 2025-12-05 17:30 UTC (permalink / raw)
  To: Kalyazin, Nikita, kvm, linux-doc, linux-kernel, kvmarm,
	linux-fsdevel, linux-mm, bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, jhubbard,
	peterx, jannh, pfalcato, shuah, riel, baohua, ryan.roberts,
	jgross, yu-cheng.yu, kas, coxu, kevin.brodsky, ackerleytng,
	maobibo, prsampat, mlevitsk, isaku.yamahata, jmattson,
	jthoughton, linux-arm-kernel, vannapurve, jackmanb, aneesh.kumar,
	patrick.roy, Thomson, Jack, Itazuri, Takahiro, Manwaring, Derek,
	Cali, Marco

On 12/5/25 08:58, Kalyazin, Nikita wrote:
> +static void kvm_gmem_folio_restore_direct_map(struct folio *folio)
> +{
> +	/*
> +	 * Direct map restoration cannot fail, as the only error condition
> +	 * for direct map manipulation is failure to allocate page tables
> +	 * when splitting huge pages, but this split would have already
> +	 * happened in set_direct_map_invalid_noflush() in kvm_gmem_folio_zap_direct_map().
> +	 * Thus set_direct_map_valid_noflush() here only updates prot bits.
> +	 */
> +	if (kvm_gmem_folio_no_direct_map(folio))
> +		set_direct_map_valid_noflush(folio_page(folio, 0), folio_nr_pages(folio),
> +					 true);
> +}

This is rather hopeful programming.

I can think of a number of ways that this assumption might become invalid.
This at *least* check for set_direct_map_valid_noflush() failures (or
whatever interface you end up using)?

A WARN_ON_ONCE() would be fine.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v8 03/13] mm: introduce AS_NO_DIRECT_MAP
  2025-12-05 16:58 ` [PATCH v8 03/13] mm: introduce AS_NO_DIRECT_MAP Kalyazin, Nikita
@ 2025-12-05 18:35   ` John Hubbard
  0 siblings, 0 replies; 17+ messages in thread
From: John Hubbard @ 2025-12-05 18:35 UTC (permalink / raw)
  To: Kalyazin, Nikita, kvm, linux-doc, linux-kernel, kvmarm,
	linux-fsdevel, linux-mm, bpf, linux-kselftest
  Cc: pbonzini, corbet, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, seanjc, tglx, mingo, bp,
	dave.hansen, x86, hpa, luto, peterz, willy, akpm, david,
	lorenzo.stoakes, Liam.Howlett, vbabka, rppt, surenb, mhocko, ast,
	daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, jgg, peterx, jannh,
	pfalcato, shuah, riel, baohua, ryan.roberts, jgross, yu-cheng.yu,
	kas, coxu, kevin.brodsky, ackerleytng, maobibo, prsampat,
	mlevitsk, isaku.yamahata, jmattson, jthoughton, linux-arm-kernel,
	vannapurve, jackmanb, aneesh.kumar, patrick.roy, Thomson, Jack,
	Itazuri, Takahiro, Manwaring, Derek, Cali, Marco

On 12/5/25 8:58 AM, Kalyazin, Nikita wrote:
> From: Patrick Roy <patrick.roy@linux.dev>
> 
> Add AS_NO_DIRECT_MAP for mappings where direct map entries of folios are
> set to not present. Currently, mappings that match this description are
> secretmem mappings (memfd_secret()). Later, some guest_memfd
> configurations will also fall into this category.
> 
> Reject this new type of mappings in all locations that currently reject
> secretmem mappings, on the assumption that if secretmem mappings are
> rejected somewhere, it is precisely because of an inability to deal with
> folios without direct map entries, and then make memfd_secret() use
> AS_NO_DIRECT_MAP on its address_space to drop its special
> vma_is_secretmem()/secretmem_mapping() checks.
> 
> This drops a optimization in gup_fast_folio_allowed() where
> secretmem_mapping() was only called if CONFIG_SECRETMEM=y. secretmem is
> enabled by default since commit b758fe6df50d ("mm/secretmem: make it on
> by default"), so the secretmem check did not actually end up elided in
> most cases anymore anyway.

The above paragraph can be part (most) of a commit description for
a separate patch for the gup aspects. Let's split it out that way,
because it is a distinct change, and the diffs can stand alone
from this patch here.

thanks,
-- 
John Hubbard

> 
> Use a new flag instead of overloading AS_INACCESSIBLE (which is already
> set by guest_memfd) because not all guest_memfd mappings will end up
> being direct map removed (e.g. in pKVM setups, parts of guest_memfd that
> can be mapped to userspace should also be GUP-able, and generally not
> have restrictions on who can access it).
> 
> Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> Acked-by: David Hildenbrand (Red Hat)" <david@kernel.org>
> Signed-off-by: Patrick Roy <patrick.roy@linux.dev>
> Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
> ---
>   include/linux/pagemap.h   | 16 ++++++++++++++++
>   include/linux/secretmem.h | 18 ------------------
>   lib/buildid.c             |  4 ++--
>   mm/gup.c                  | 19 +++++--------------
>   mm/mlock.c                |  2 +-
>   mm/secretmem.c            |  8 ++------
>   6 files changed, 26 insertions(+), 41 deletions(-)
> 
> diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
> index a17fabbc0269..d51e0c0404e2 100644
> --- a/include/linux/pagemap.h
> +++ b/include/linux/pagemap.h
> @@ -213,6 +213,7 @@ enum mapping_flags {
>   	AS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM = 9,
>   	AS_KERNEL_FILE = 10,	/* mapping for a fake kernel file that shouldn't
>   				   account usage to user cgroups */
> +	AS_NO_DIRECT_MAP = 11,	/* Folios in the mapping are not in the direct map */
>   	/* Bits 16-25 are used for FOLIO_ORDER */
>   	AS_FOLIO_ORDER_BITS = 5,
>   	AS_FOLIO_ORDER_MIN = 16,
> @@ -348,6 +349,21 @@ static inline bool mapping_writeback_may_deadlock_on_reclaim(const struct addres
>   	return test_bit(AS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM, &mapping->flags);
>   }
>   
> +static inline void mapping_set_no_direct_map(struct address_space *mapping)
> +{
> +	set_bit(AS_NO_DIRECT_MAP, &mapping->flags);
> +}
> +
> +static inline bool mapping_no_direct_map(const struct address_space *mapping)
> +{
> +	return test_bit(AS_NO_DIRECT_MAP, &mapping->flags);
> +}
> +
> +static inline bool vma_has_no_direct_map(const struct vm_area_struct *vma)
> +{
> +	return vma->vm_file && mapping_no_direct_map(vma->vm_file->f_mapping);
> +}
> +
>   static inline gfp_t mapping_gfp_mask(const struct address_space *mapping)
>   {
>   	return mapping->gfp_mask;
> diff --git a/include/linux/secretmem.h b/include/linux/secretmem.h
> index e918f96881f5..0ae1fb057b3d 100644
> --- a/include/linux/secretmem.h
> +++ b/include/linux/secretmem.h
> @@ -4,28 +4,10 @@
>   
>   #ifdef CONFIG_SECRETMEM
>   
> -extern const struct address_space_operations secretmem_aops;
> -
> -static inline bool secretmem_mapping(struct address_space *mapping)
> -{
> -	return mapping->a_ops == &secretmem_aops;
> -}
> -
> -bool vma_is_secretmem(struct vm_area_struct *vma);
>   bool secretmem_active(void);
>   
>   #else
>   
> -static inline bool vma_is_secretmem(struct vm_area_struct *vma)
> -{
> -	return false;
> -}
> -
> -static inline bool secretmem_mapping(struct address_space *mapping)
> -{
> -	return false;
> -}
> -
>   static inline bool secretmem_active(void)
>   {
>   	return false;
> diff --git a/lib/buildid.c b/lib/buildid.c
> index c4b0f376fb34..89e567954284 100644
> --- a/lib/buildid.c
> +++ b/lib/buildid.c
> @@ -65,8 +65,8 @@ static int freader_get_folio(struct freader *r, loff_t file_off)
>   
>   	freader_put_folio(r);
>   
> -	/* reject secretmem folios created with memfd_secret() */
> -	if (secretmem_mapping(r->file->f_mapping))
> +	/* reject folios without direct map entries (e.g. from memfd_secret() or guest_memfd()) */
> +	if (mapping_no_direct_map(r->file->f_mapping))
>   		return -EFAULT;
>   
>   	r->folio = filemap_get_folio(r->file->f_mapping, file_off >> PAGE_SHIFT);
> diff --git a/mm/gup.c b/mm/gup.c
> index a8ba5112e4d0..719e4dbecc09 100644
> --- a/mm/gup.c
> +++ b/mm/gup.c
> @@ -11,7 +11,6 @@
>   #include <linux/rmap.h>
>   #include <linux/swap.h>
>   #include <linux/swapops.h>
> -#include <linux/secretmem.h>
>   
>   #include <linux/sched/signal.h>
>   #include <linux/rwsem.h>
> @@ -1216,7 +1215,7 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)
>   	if ((gup_flags & FOLL_SPLIT_PMD) && is_vm_hugetlb_page(vma))
>   		return -EOPNOTSUPP;
>   
> -	if (vma_is_secretmem(vma))
> +	if (vma_has_no_direct_map(vma))
>   		return -EFAULT;
>   
>   	if (write) {
> @@ -2724,7 +2723,7 @@ EXPORT_SYMBOL(get_user_pages_unlocked);
>    * This call assumes the caller has pinned the folio, that the lowest page table
>    * level still points to this folio, and that interrupts have been disabled.
>    *
> - * GUP-fast must reject all secretmem folios.
> + * GUP-fast must reject all folios without direct map entries (such as secretmem).
>    *
>    * Writing to pinned file-backed dirty tracked folios is inherently problematic
>    * (see comment describing the writable_file_mapping_allowed() function). We
> @@ -2739,7 +2738,6 @@ static bool gup_fast_folio_allowed(struct folio *folio, unsigned int flags)
>   {
>   	bool reject_file_backed = false;
>   	struct address_space *mapping;
> -	bool check_secretmem = false;
>   	unsigned long mapping_flags;
>   
>   	/*
> @@ -2751,18 +2749,10 @@ static bool gup_fast_folio_allowed(struct folio *folio, unsigned int flags)
>   		reject_file_backed = true;
>   
>   	/* We hold a folio reference, so we can safely access folio fields. */
> -
> -	/* secretmem folios are always order-0 folios. */
> -	if (IS_ENABLED(CONFIG_SECRETMEM) && !folio_test_large(folio))
> -		check_secretmem = true;
> -
> -	if (!reject_file_backed && !check_secretmem)
> -		return true;
> -
>   	if (WARN_ON_ONCE(folio_test_slab(folio)))
>   		return false;
>   
> -	/* hugetlb neither requires dirty-tracking nor can be secretmem. */
> +	/* hugetlb neither requires dirty-tracking nor can be without direct map. */
>   	if (folio_test_hugetlb(folio))
>   		return true;
>   
> @@ -2800,8 +2790,9 @@ static bool gup_fast_folio_allowed(struct folio *folio, unsigned int flags)
>   	 * At this point, we know the mapping is non-null and points to an
>   	 * address_space object.
>   	 */
> -	if (check_secretmem && secretmem_mapping(mapping))
> +	if (mapping_no_direct_map(mapping))
>   		return false;
> +
>   	/* The only remaining allowed file system is shmem. */
>   	return !reject_file_backed || shmem_mapping(mapping);
>   }
> diff --git a/mm/mlock.c b/mm/mlock.c
> index bb0776f5ef7c..506452ee8cad 100644
> --- a/mm/mlock.c
> +++ b/mm/mlock.c
> @@ -474,7 +474,7 @@ static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_struct *vma,
>   
>   	if (newflags == oldflags || (oldflags & VM_SPECIAL) ||
>   	    is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm) ||
> -	    vma_is_dax(vma) || vma_is_secretmem(vma) || (oldflags & VM_DROPPABLE))
> +	    vma_is_dax(vma) || vma_has_no_direct_map(vma) || (oldflags & VM_DROPPABLE))
>   		/* don't set VM_LOCKED or VM_LOCKONFAULT and don't count */
>   		goto out;
>   
> diff --git a/mm/secretmem.c b/mm/secretmem.c
> index b59350daffe3..c1eafe29c3e8 100644
> --- a/mm/secretmem.c
> +++ b/mm/secretmem.c
> @@ -134,11 +134,6 @@ static int secretmem_mmap_prepare(struct vm_area_desc *desc)
>   	return 0;
>   }
>   
> -bool vma_is_secretmem(struct vm_area_struct *vma)
> -{
> -	return vma->vm_ops == &secretmem_vm_ops;
> -}
> -
>   static const struct file_operations secretmem_fops = {
>   	.release	= secretmem_release,
>   	.mmap_prepare	= secretmem_mmap_prepare,
> @@ -156,7 +151,7 @@ static void secretmem_free_folio(struct folio *folio)
>   	folio_zero_segment(folio, 0, folio_size(folio));
>   }
>   
> -const struct address_space_operations secretmem_aops = {
> +static const struct address_space_operations secretmem_aops = {
>   	.dirty_folio	= noop_dirty_folio,
>   	.free_folio	= secretmem_free_folio,
>   	.migrate_folio	= secretmem_migrate_folio,
> @@ -205,6 +200,7 @@ static struct file *secretmem_file_create(unsigned long flags)
>   
>   	mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
>   	mapping_set_unevictable(inode->i_mapping);
> +	mapping_set_no_direct_map(inode->i_mapping);
>   
>   	inode->i_op = &secretmem_iops;
>   	inode->i_mapping->a_ops = &secretmem_aops;




^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2025-12-05 18:35 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-05 16:57 [PATCH v8 00/13] Direct Map Removal Support for guest_memfd Kalyazin, Nikita
2025-12-05 16:57 ` [PATCH v8 01/13] x86: export set_direct_map_valid_noflush to KVM module Kalyazin, Nikita
2025-12-05 17:26   ` Dave Hansen
2025-12-05 16:58 ` [PATCH v8 02/13] x86/tlb: export flush_tlb_kernel_range " Kalyazin, Nikita
2025-12-05 16:58 ` [PATCH v8 03/13] mm: introduce AS_NO_DIRECT_MAP Kalyazin, Nikita
2025-12-05 18:35   ` John Hubbard
2025-12-05 16:58 ` [PATCH v8 04/13] KVM: guest_memfd: Add stub for kvm_arch_gmem_invalidate Kalyazin, Nikita
2025-12-05 16:58 ` [PATCH v8 05/13] KVM: guest_memfd: Add flag to remove from direct map Kalyazin, Nikita
2025-12-05 17:30   ` Dave Hansen
2025-12-05 16:58 ` [PATCH v8 06/13] KVM: x86: define kvm_arch_gmem_supports_no_direct_map() Kalyazin, Nikita
2025-12-05 16:59 ` [PATCH v8 07/13] KVM: arm64: " Kalyazin, Nikita
2025-12-05 16:59 ` [PATCH v8 08/13] KVM: selftests: load elf via bounce buffer Kalyazin, Nikita
2025-12-05 16:59 ` [PATCH v8 09/13] KVM: selftests: set KVM_MEM_GUEST_MEMFD in vm_mem_add() if guest_memfd != -1 Kalyazin, Nikita
2025-12-05 16:59 ` [PATCH v8 10/13] KVM: selftests: Add guest_memfd based vm_mem_backing_src_types Kalyazin, Nikita
2025-12-05 16:59 ` [PATCH v8 11/13] KVM: selftests: cover GUEST_MEMFD_FLAG_NO_DIRECT_MAP in existing selftests Kalyazin, Nikita
2025-12-05 17:00 ` [PATCH v8 12/13] KVM: selftests: stuff vm_mem_backing_src_type into vm_shape Kalyazin, Nikita
2025-12-05 17:00 ` [PATCH v8 13/13] KVM: selftests: Test guest execution from direct map removed gmem Kalyazin, Nikita

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox