mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Yosry Ahmed <yosry.ahmed@linux.dev>
To: Sean Christopherson <seanjc@google.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
	Jim Mattson <jmattson@google.com>,
	kvm@vger.kernel.org, linux-kernel@vger.kernel.org,
	Yosry Ahmed <yosry.ahmed@linux.dev>
Subject: [PATCH v2 16/23] KVM: selftests: Use __virt_pg_map() for nested EPTs
Date: Tue, 21 Oct 2025 07:47:29 +0000	[thread overview]
Message-ID: <20251021074736.1324328-17-yosry.ahmed@linux.dev> (raw)
In-Reply-To: <20251021074736.1324328-1-yosry.ahmed@linux.dev>

__nested_pg_map() bears a lot of resemblence to __virt_pg_map(). The
main difference is using the EPT struct overlay instead of the PTE
masks. Now that PTE masks are passed into __virt_pg_map() as a struct,
define a similar struct for EPTs and use __virt_pg_map() instead of
__nested_pg_map().

EPTs have no 'present' or 'user' bits, so use the 'readable' bit instead
like shadow_{present/user}_mask, ignoring the fact that entries can be
present and not readable if the CPU has VMX_EPT_EXECUTE_ONLY_BIT. This
is simple and sufficient for testing.

Opportunistically drop nested_pg_map() since it has no callers.

Add an executable bitmask to struct pte_masks, and update
__virt_pg_map() and friends to set the bit on newly created entries to
match the EPT behavior. It's a noop for x86 page tables.

Another benefit of reusing the code is having separate handling for
upper-level PTEs vs 4K PTEs, which avoids some quirks like setting the
large bit on a 4K PTE.

Signed-off-by: Yosry Ahmed <yosry.ahmed@linux.dev>
Suggested-by: Sean Christopherson <seanjc@google.com>
---
 .../selftests/kvm/include/x86/processor.h     |   1 +
 tools/testing/selftests/kvm/include/x86/vmx.h |   4 +-
 .../testing/selftests/kvm/lib/x86/processor.c |   9 +-
 tools/testing/selftests/kvm/lib/x86/vmx.c     | 113 +++---------------
 4 files changed, 27 insertions(+), 100 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 2f0d83b6e5952..0d6d335d309ef 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -1447,6 +1447,7 @@ struct pte_masks {
 	uint64_t dirty;
 	uint64_t large;
 	uint64_t nx;
+	uint64_t x;
 };
 
 extern const struct pte_masks x86_pte_masks;
diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h
index 96e2b4c630a9b..5aa14ceed050a 100644
--- a/tools/testing/selftests/kvm/include/x86/vmx.h
+++ b/tools/testing/selftests/kvm/include/x86/vmx.h
@@ -99,6 +99,8 @@
 #define VMX_EPT_VPID_CAP_1G_PAGES		0x00020000
 #define VMX_EPT_VPID_CAP_AD_BITS		0x00200000
 
+extern const struct pte_masks ept_pte_masks;
+
 #define EXIT_REASON_FAILED_VMENTRY	0x80000000
 
 enum vmcs_field {
@@ -559,8 +561,6 @@ bool load_vmcs(struct vmx_pages *vmx);
 
 bool ept_1g_pages_supported(void);
 
-void nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm,
-		   uint64_t nested_paddr, uint64_t paddr);
 void nested_map(struct vmx_pages *vmx, struct kvm_vm *vm,
 		 uint64_t nested_paddr, uint64_t paddr, uint64_t size);
 void nested_map_memslot(struct vmx_pages *vmx, struct kvm_vm *vm,
diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c
index 13e9376d5f545..caad8a9b3f067 100644
--- a/tools/testing/selftests/kvm/lib/x86/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86/processor.c
@@ -177,6 +177,7 @@ const struct pte_masks x86_pte_masks = {
 	.dirty		=	BIT_ULL(6),
 	.large		=	BIT_ULL(7),
 	.nx		=	BIT_ULL(63),
+	.x		=	0,
 };
 
 static void *virt_get_pte(struct kvm_vm *vm, vm_paddr_t root_gpa,
@@ -209,7 +210,7 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *vm,
 	paddr = vm_untag_gpa(vm, paddr);
 
 	if (!(*pte & masks->present)) {
-		*pte = masks->present | masks->writeable;
+		*pte = masks->present | masks->writeable | masks->x;
 		if (current_level == target_level)
 			*pte |= masks->large | (paddr & PHYSICAL_PAGE_MASK);
 		else
@@ -256,6 +257,9 @@ void __virt_pg_map(struct kvm_vm *vm, vm_paddr_t root_gpa, uint64_t vaddr,
 	TEST_ASSERT(vm_untag_gpa(vm, paddr) == paddr,
 		    "Unexpected bits in paddr: %lx", paddr);
 
+	TEST_ASSERT(!masks->x || !masks->nx,
+		    "X and NX bit masks cannot be used simultaneously");
+
 	/*
 	 * Allocate upper level page tables, if not already present.  Return
 	 * early if a hugepage was created.
@@ -271,7 +275,8 @@ void __virt_pg_map(struct kvm_vm *vm, vm_paddr_t root_gpa, uint64_t vaddr,
 	pte = virt_get_pte(vm, root_gpa, pte, vaddr, PG_LEVEL_4K, masks);
 	TEST_ASSERT(!(*pte & masks->present),
 		    "PTE already present for 4k page at vaddr: 0x%lx", vaddr);
-	*pte = masks->present | masks->writeable | (paddr & PHYSICAL_PAGE_MASK);
+	*pte = masks->present | masks->writeable | masks->x
+		| (paddr & PHYSICAL_PAGE_MASK);
 
 	/*
 	 * Neither SEV nor TDX supports shared page tables, so only the final
diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c
index 36e60016fa7b2..46a491eb083c9 100644
--- a/tools/testing/selftests/kvm/lib/x86/vmx.c
+++ b/tools/testing/selftests/kvm/lib/x86/vmx.c
@@ -19,19 +19,21 @@ bool enable_evmcs;
 struct hv_enlightened_vmcs *current_evmcs;
 struct hv_vp_assist_page *current_vp_assist;
 
-struct eptPageTableEntry {
-	uint64_t readable:1;
-	uint64_t writable:1;
-	uint64_t executable:1;
-	uint64_t memory_type:3;
-	uint64_t ignore_pat:1;
-	uint64_t page_size:1;
-	uint64_t accessed:1;
-	uint64_t dirty:1;
-	uint64_t ignored_11_10:2;
-	uint64_t address:40;
-	uint64_t ignored_62_52:11;
-	uint64_t suppress_ve:1;
+const struct pte_masks ept_pte_masks = {
+	/*
+	 * EPTs do not have 'present' or 'user' bits, instead bit 0 is the
+	 * 'readable' bit. In some cases, EPTs can be execute-only and an entry
+	 * is present but not readable. However, for the purposes of testing we
+	 * assume present == user == readable for simplicity.
+	 */
+	.present	=	BIT_ULL(0),
+	.user		=	BIT_ULL(0),
+	.writeable	=	BIT_ULL(1),
+	.x		=	BIT_ULL(2),
+	.accessed	=	BIT_ULL(5),
+	.dirty		=	BIT_ULL(6),
+	.large		=	BIT_ULL(7),
+	.nx		=	0,
 };
 
 struct eptPageTablePointer {
@@ -362,88 +364,6 @@ void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip, void *guest_rsp)
 	init_vmcs_guest_state(guest_rip, guest_rsp);
 }
 
-static void nested_create_pte(struct kvm_vm *vm,
-			      struct eptPageTableEntry *pte,
-			      uint64_t nested_paddr,
-			      uint64_t paddr,
-			      int current_level,
-			      int target_level)
-{
-	if (!pte->readable) {
-		pte->writable = true;
-		pte->readable = true;
-		pte->executable = true;
-		pte->page_size = (current_level == target_level);
-		if (pte->page_size)
-			pte->address = paddr >> vm->page_shift;
-		else
-			pte->address = vm_alloc_page_table(vm) >> vm->page_shift;
-	} else {
-		/*
-		 * Entry already present.  Assert that the caller doesn't want
-		 * a hugepage at this level, and that there isn't a hugepage at
-		 * this level.
-		 */
-		TEST_ASSERT(current_level != target_level,
-			    "Cannot create hugepage at level: %u, nested_paddr: 0x%lx",
-			    current_level, nested_paddr);
-		TEST_ASSERT(!pte->page_size,
-			    "Cannot create page table at level: %u, nested_paddr: 0x%lx",
-			    current_level, nested_paddr);
-	}
-}
-
-
-void __nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm,
-		     uint64_t nested_paddr, uint64_t paddr, int target_level)
-{
-	const uint64_t page_size = PG_LEVEL_SIZE(target_level);
-	struct eptPageTableEntry *pt = vmx->eptp_hva, *pte;
-	uint16_t index;
-
-	TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K ||
-		    vm->mode == VM_MODE_PXXV57_4K,
-		    "Unknown or unsupported guest mode: 0x%x", vm->mode);
-
-	TEST_ASSERT((nested_paddr >> 48) == 0,
-		    "Nested physical address 0x%lx is > 48-bits and requires 5-level EPT",
-		    nested_paddr);
-	TEST_ASSERT((nested_paddr % page_size) == 0,
-		    "Nested physical address not on page boundary,\n"
-		    "  nested_paddr: 0x%lx page_size: 0x%lx",
-		    nested_paddr, page_size);
-	TEST_ASSERT((nested_paddr >> vm->page_shift) <= vm->max_gfn,
-		    "Physical address beyond beyond maximum supported,\n"
-		    "  nested_paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x",
-		    paddr, vm->max_gfn, vm->page_size);
-	TEST_ASSERT((paddr % page_size) == 0,
-		    "Physical address not on page boundary,\n"
-		    "  paddr: 0x%lx page_size: 0x%lx",
-		    paddr, page_size);
-	TEST_ASSERT((paddr >> vm->page_shift) <= vm->max_gfn,
-		    "Physical address beyond beyond maximum supported,\n"
-		    "  paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x",
-		    paddr, vm->max_gfn, vm->page_size);
-
-	for (int level = PG_LEVEL_512G; level >= PG_LEVEL_4K; level--) {
-		index = (nested_paddr >> PG_LEVEL_SHIFT(level)) & 0x1ffu;
-		pte = &pt[index];
-
-		nested_create_pte(vm, pte, nested_paddr, paddr, level, target_level);
-
-		if (pte->page_size)
-			break;
-
-		pt = addr_gpa2hva(vm, pte->address * vm->page_size);
-	}
-}
-
-void nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm,
-		   uint64_t nested_paddr, uint64_t paddr)
-{
-	__nested_pg_map(vmx, vm, nested_paddr, paddr, PG_LEVEL_4K);
-}
-
 /*
  * Map a range of EPT guest physical addresses to the VM's physical address
  *
@@ -472,7 +392,8 @@ void __nested_map(struct vmx_pages *vmx, struct kvm_vm *vm,
 	TEST_ASSERT(paddr + size > paddr, "Paddr overflow");
 
 	while (npages--) {
-		__nested_pg_map(vmx, vm, nested_paddr, paddr, level);
+		__virt_pg_map(vm, vmx->eptp_gpa, nested_paddr, paddr,
+			      level, &ept_pte_masks);
 		nested_paddr += page_size;
 		paddr += page_size;
 	}
-- 
2.51.0.869.ge66316f041-goog


  parent reply	other threads:[~2025-10-21  7:48 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-21  7:47 [PATCH v2 00/23] Extend test coverage for nested SVM Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 01/23] KVM: selftests: Minor improvements to asserts in test_vmx_nested_state() Yosry Ahmed
2025-11-20 23:40   ` Sean Christopherson
2025-11-20 23:46     ` Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 02/23] KVM: selftests: Extend vmx_set_nested_state_test to cover SVM Yosry Ahmed
2025-11-20 23:52   ` Sean Christopherson
2025-10-21  7:47 ` [PATCH v2 03/23] KVM: selftests: Extend vmx_close_while_nested_test " Yosry Ahmed
2025-11-20 23:53   ` Sean Christopherson
2025-10-21  7:47 ` [PATCH v2 04/23] KVM: selftests: Extend vmx_nested_tsc_scaling_test " Yosry Ahmed
2025-11-20 23:54   ` Sean Christopherson
2025-10-21  7:47 ` [PATCH v2 05/23] KVM: selftests: Move nested invalid CR3 check to its own test Yosry Ahmed
2025-11-20 23:55   ` Sean Christopherson
2025-10-21  7:47 ` [PATCH v2 06/23] KVM: selftests: Extend nested_invalid_cr3_test to cover SVM Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 07/23] KVM: selftests: Extend vmx_tsc_adjust_test " Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 08/23] KVM: selftests: Stop hardcoding PAGE_SIZE in x86 selftests Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 09/23] KVM: selftests: Remove the unused argument to prepare_eptp() Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 10/23] KVM: selftests: Stop using __virt_pg_map() directly in tests Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 11/23] KVM: selftests: Make sure vm->vpages_mapped is always up-to-date Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 12/23] KVM: selftests: Parameterize the PTE bitmasks for virt mapping functions Yosry Ahmed
2025-11-21  0:07   ` Sean Christopherson
2025-11-21  0:18     ` Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 13/23] KVM: selftests: Pass the root GPA into virt_get_pte() Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 14/23] KVM: selftests: Pass the root GPA into __virt_pg_map() Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 15/23] KVM: selftests: Stop setting AD bits on nested EPTs on creation Yosry Ahmed
2025-10-21  7:47 ` Yosry Ahmed [this message]
2025-10-21  7:47 ` [PATCH v2 17/23] KVM: selftests: Kill eptPageTablePointer Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 18/23] KVM: selftests: Generalize nested mapping functions Yosry Ahmed
2025-11-21  0:10   ` Sean Christopherson
2025-11-21  0:20     ` Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 19/23] KVM: selftests: Move nested MMU mapping functions outside of vmx.c Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 20/23] KVM: selftests: Stop passing a memslot to nested_map_memslot() Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 21/23] KVM: selftests: Allow kvm_cpu_has_ept() to be called on AMD CPUs Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 22/23] KVM: selftests: Set the user bit on nested MMU PTEs Yosry Ahmed
2025-10-21  7:47 ` [PATCH v2 23/23] KVM: selftests: Extend vmx_dirty_log_test to cover SVM Yosry Ahmed
2025-10-29 14:24 ` [PATCH v2 00/23] Extend test coverage for nested SVM Yosry Ahmed
2025-11-18 22:25 ` Yosry Ahmed
2025-11-18 23:00   ` Jim Mattson
2025-11-18 23:22     ` Yosry Ahmed
2025-11-18 23:49       ` Sean Christopherson
2025-11-19  0:01         ` Yosry Ahmed
2025-11-19  0:07           ` Sean Christopherson
2025-11-20 23:51           ` Sean Christopherson
2025-11-20 23:23 ` Sean Christopherson
2025-11-20 23:32   ` Yosry Ahmed
2025-11-20 23:50 ` Sean Christopherson
2025-11-21  0:05   ` Yosry Ahmed
2025-11-21  0:24     ` Sean Christopherson
2025-11-21  0:30       ` Yosry Ahmed
2025-11-21 18:55 ` Sean Christopherson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251021074736.1324328-17-yosry.ahmed@linux.dev \
    --to=yosry.ahmed@linux.dev \
    --cc=jmattson@google.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=seanjc@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®