kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sean Christopherson <seanjc@google.com>
To: Paolo Bonzini <pbonzini@redhat.com>
Cc: kvm@vger.kernel.org, Sean Christopherson <seanjc@google.com>
Subject: [kvm-unit-tests PATCH 13/39] x86/access: Pre-allocate all page tables at (sub)test init
Date: Thu, 25 Nov 2021 01:28:31 +0000	[thread overview]
Message-ID: <20211125012857.508243-14-seanjc@google.com> (raw)
In-Reply-To: <20211125012857.508243-1-seanjc@google.com>

Pre-allocate the page tables for each test instead of allocating page
tables on every. single. iteration.  In addition to being abysmally slow,
constantly allocating new page tables obliterates any hope of providing
meaningful test coverage for shadow paging, as using a new upper level
PTE for every iteration causes KVM to sync children, which prevents
exposing TLB flushing bugs in KVM.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 x86/access.c | 169 ++++++++++++++++++++++++++++++---------------------
 1 file changed, 101 insertions(+), 68 deletions(-)

diff --git a/x86/access.c b/x86/access.c
index 6c1e20e..abc6590 100644
--- a/x86/access.c
+++ b/x86/access.c
@@ -176,6 +176,9 @@ typedef struct {
 	int expected_fault;
 	unsigned expected_error;
 	int pt_levels;
+
+	/* 5-level paging, 1-based to avoid math. */
+	pt_element_t page_tables[6];
 } ac_test_t;
 
 typedef struct {
@@ -323,25 +326,25 @@ static pt_element_t ac_test_alloc_pt(ac_pt_env_t *pt_env)
 {
 	pt_element_t pt;
 
+	/*
+	 * Each test needs at most pt_levels-1 structures per virtual address,
+	 * and no existing scenario uses more than four addresses.
+	 */
+	assert(pt_env->pt_pool_current < (4 * (pt_env->pt_levels - 1)));
+
 	pt = pt_env->pt_pool_pa + (pt_env->pt_pool_current * PAGE_SIZE);
 	pt_env->pt_pool_current++;
 	memset(va(pt), 0, PAGE_SIZE);
 	return pt;
 }
 
-static _Bool ac_test_enough_room(ac_pt_env_t *pt_env)
+static void __ac_test_init(ac_test_t *at, unsigned long virt,
+			   ac_pt_env_t *pt_env, ac_test_t *buddy)
 {
-	/* '120' is completely arbitrary. */
-	return (pt_env->pt_pool_current + 5) < 120;
-}
+	unsigned long buddy_virt = buddy ? (unsigned long)buddy->virt : 0;
+	pt_element_t *root_pt = va(shadow_cr3 & PT_BASE_ADDR_MASK);
+	int i;
 
-static void ac_test_reset_pt_pool(ac_pt_env_t *pt_env)
-{
-	pt_env->pt_pool_current = 0;
-}
-
-static void ac_test_init(ac_test_t *at, unsigned long virt, ac_pt_env_t *pt_env)
-{
 	/*
 	 * The KUT infrastructure, e.g. this function, must use a different
 	 * top-level SPTE than the test, otherwise modifying SPTEs can affect
@@ -349,7 +352,7 @@ static void ac_test_init(ac_test_t *at, unsigned long virt, ac_pt_env_t *pt_env)
 	 * USER when CR4.SMEP=1.
 	 */
 	assert(PT_INDEX(virt, pt_env->pt_levels) !=
-	       PT_INDEX((unsigned long)ac_test_init, pt_env->pt_levels));
+	       PT_INDEX((unsigned long)__ac_test_init, pt_env->pt_levels));
 
 	set_efer_nx(1);
 	set_cr0_wp(1);
@@ -357,6 +360,33 @@ static void ac_test_init(ac_test_t *at, unsigned long virt, ac_pt_env_t *pt_env)
 	at->virt = (void *)virt;
 	at->phys = AT_CODE_DATA_PHYS;
 	at->pt_levels = pt_env->pt_levels;
+
+	at->page_tables[0] = -1ull;
+	at->page_tables[1] = -1ull;
+
+	/*
+	 * Zap the existing top-level PTE as it may be reused from a previous
+	 * sub-test.  This allows runtime PTE modification to assert that two
+	 * overlapping walks don't try to install different paging structures.
+	 */
+	root_pt[PT_INDEX(virt, pt_env->pt_levels)] = 0;
+
+	for (i = at->pt_levels; i > 1; i--) {
+		/*
+		 * Buddies can reuse any part of the walk that share the same
+		 * index.  This is weird, but intentional, as several tests
+		 * want different walks to merge at lower levels.
+		 */
+		if (buddy && PT_INDEX(virt, i) == PT_INDEX(buddy_virt, i))
+			at->page_tables[i] = buddy->page_tables[i];
+		else
+			at->page_tables[i] = ac_test_alloc_pt(pt_env);
+	}
+}
+
+static void ac_test_init(ac_test_t *at, unsigned long virt, ac_pt_env_t *pt_env)
+{
+	__ac_test_init(at, virt, pt_env, NULL);
 }
 
 static int ac_test_bump_one(ac_test_t *at)
@@ -372,6 +402,9 @@ static _Bool ac_test_legal(ac_test_t *at)
 	int flags = at->flags;
 	unsigned reserved;
 
+	if (F(AC_CPU_CR4_SMEP))
+		return false;
+
 	if (F(AC_ACCESS_FETCH) && F(AC_ACCESS_WRITE))
 		return false;
 
@@ -562,59 +595,60 @@ static void ac_set_expected_status(ac_test_t *at)
 	ac_emulate_access(at, at->flags);
 }
 
-static void __ac_setup_specific_pages(ac_test_t *at, ac_pt_env_t *pt_env, bool reuse,
-				      u64 pd_page, u64 pt_page)
+static pt_element_t ac_get_pt(ac_test_t *at, int i, pt_element_t *ptep)
+{
+	pt_element_t pte;
+
+	pte = *ptep;
+	if (pte && !(pte & PT_PAGE_SIZE_MASK) &&
+	    (pte & PT_BASE_ADDR_MASK) != at->page_tables[i]) {
+		printf("\nPT collision.  VA = 0x%lx, level = %d, index = %ld, found PT = 0x%lx, want PT = 0x%lx\n",
+			(unsigned long)at->virt, i,
+			PT_INDEX((unsigned long)at->virt, i),
+			pte, at->page_tables[i]);
+		abort();
+	}
+
+	pte = at->page_tables[i];
+	return pte;
+}
+
+static void __ac_setup_specific_pages(ac_test_t *at, u64 pd_page, u64 pt_page)
 {
 	unsigned long parent_pte = shadow_cr3;
 	int flags = at->flags;
-	bool skip = true;
-
-	if (!ac_test_enough_room(pt_env))
-		ac_test_reset_pt_pool(pt_env);
+	int i;
 
 	at->ptep = 0;
-	for (int i = at->pt_levels; i >= 1 && (i >= 2 || !F(AC_PDE_PSE)); --i) {
+	for (i = at->pt_levels; i >= 1 && (i >= 2 || !F(AC_PDE_PSE)); --i) {
 		pt_element_t *parent_pt = va(parent_pte & PT_BASE_ADDR_MASK);
 		unsigned index = PT_INDEX((unsigned long)at->virt, i);
 		pt_element_t *ptep = &parent_pt[index];
 		pt_element_t pte;
 
-		/*
-		 * Reuse existing page tables along the highest index, some
-		 * tests rely on sharing upper level paging structures between
-		 * two separate sub-tests.
-		 */
-		if (skip && i >= 2 && index == 511 && (*ptep & PT_PRESENT_MASK))
-			goto next;
-
-		skip = false;
-		if (reuse && *ptep) {
-			switch (i) {
-			case 2:
-				at->pdep = ptep;
-				break;
-			case 1:
-				at->ptep = ptep;
-				break;
-			}
-			goto next;
-		}
-
 		switch (i) {
 		case 5:
 		case 4:
-			pte = ac_test_alloc_pt(pt_env);
+			pte = ac_get_pt(at, i, ptep);
 			pte |= PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK;
 			break;
 		case 3:
-			pte = pd_page ? pd_page : ac_test_alloc_pt(pt_env);
+			if (pd_page)
+				pte = pd_page;
+			else
+				pte = ac_get_pt(at, i, ptep);
+
 			pte |= PT_PRESENT_MASK | PT_USER_MASK;
 			if (!F(AC_PDPTE_NO_WRITABLE))
 				pte |= PT_WRITABLE_MASK;
 			break;
 		case 2:
 			if (!F(AC_PDE_PSE)) {
-				pte = pt_page ? pt_page : ac_test_alloc_pt(pt_env);
+				if (pt_page)
+					pte = pt_page;
+				else
+					pte = ac_get_pt(at, i, ptep);
+
 				/* The protection key is ignored on non-leaf entries.  */
 				if (F(AC_PKU_PKEY))
 					pte |= 2ull << 59;
@@ -671,21 +705,20 @@ static void __ac_setup_specific_pages(ac_test_t *at, ac_pt_env_t *pt_env, bool r
 		}
 
 		*ptep = pte;
- next:
-		parent_pte = *ptep;
+
+		parent_pte = pte;
 	}
 	ac_set_expected_status(at);
 }
 
-static void ac_test_setup_pte(ac_test_t *at, ac_pt_env_t *pt_env)
+static void ac_test_setup_pte(ac_test_t *at)
 {
-	__ac_setup_specific_pages(at, pt_env, false, 0, 0);
+	__ac_setup_specific_pages(at, 0, 0);
 }
 
-static void ac_setup_specific_pages(ac_test_t *at, ac_pt_env_t *pt_env,
-				    u64 pd_page, u64 pt_page)
+static void ac_setup_specific_pages(ac_test_t *at, u64 pd_page, u64 pt_page)
 {
-	return __ac_setup_specific_pages(at, pt_env, false, pd_page, pt_page);
+	return __ac_setup_specific_pages(at, pd_page, pt_page);
 }
 
 static void __dump_pte(pt_element_t *ptep, int level, unsigned long virt)
@@ -874,15 +907,15 @@ static int corrupt_hugepage_triger(ac_pt_env_t *pt_env)
 	ac_test_t at1, at2;
 
 	ac_test_init(&at1, 0xffff923400000000ul, pt_env);
-	ac_test_init(&at2, 0xffffe66600000000ul, pt_env);
+	__ac_test_init(&at2, 0xffffe66600000000ul, pt_env, &at1);
 
 	at2.flags = AC_CPU_CR0_WP_MASK | AC_PDE_PSE_MASK | AC_PDE_PRESENT_MASK;
-	ac_test_setup_pte(&at2, pt_env);
+	ac_test_setup_pte(&at2);
 	if (!ac_test_do_access(&at2))
 		goto err;
 
 	at1.flags = at2.flags | AC_PDE_WRITABLE_MASK;
-	ac_test_setup_pte(&at1, pt_env);
+	ac_test_setup_pte(&at1);
 	if (!ac_test_do_access(&at1))
 		goto err;
 
@@ -912,13 +945,13 @@ static int check_pfec_on_prefetch_pte(ac_pt_env_t *pt_env)
 	ac_test_t at1, at2;
 
 	ac_test_init(&at1, 0xffff923406001000ul, pt_env);
-	ac_test_init(&at2, 0xffff923406003000ul, pt_env);
+	__ac_test_init(&at2, 0xffff923406003000ul, pt_env, &at1);
 
 	at1.flags = AC_PDE_PRESENT_MASK | AC_PTE_PRESENT_MASK;
-	ac_setup_specific_pages(&at1, pt_env, 30 * 1024 * 1024, 30 * 1024 * 1024);
+	ac_setup_specific_pages(&at1, 30 * 1024 * 1024, 30 * 1024 * 1024);
 
 	at2.flags = at1.flags | AC_PTE_NX_MASK;
-	ac_setup_specific_pages(&at2, pt_env, 30 * 1024 * 1024, 30 * 1024 * 1024);
+	ac_setup_specific_pages(&at2, 30 * 1024 * 1024, 30 * 1024 * 1024);
 
 	if (!ac_test_do_access(&at1)) {
 		printf("%s: prepare fail\n", __FUNCTION__);
@@ -957,17 +990,17 @@ static int check_large_pte_dirty_for_nowp(ac_pt_env_t *pt_env)
 	ac_test_t at1, at2;
 
 	ac_test_init(&at1, 0xffff923403000000ul, pt_env);
-	ac_test_init(&at2, 0xffffe66606000000ul, pt_env);
+	__ac_test_init(&at2, 0xffffe66606000000ul, pt_env, &at1);
 
 	at2.flags = AC_PDE_PRESENT_MASK | AC_PDE_PSE_MASK;
-	ac_test_setup_pte(&at2, pt_env);
+	ac_test_setup_pte(&at2);
 	if (!ac_test_do_access(&at2)) {
 		printf("%s: read on the first mapping fail.\n", __FUNCTION__);
 		goto err;
 	}
 
 	at1.flags = at2.flags | AC_ACCESS_WRITE_MASK;
-	ac_test_setup_pte(&at1, pt_env);
+	ac_test_setup_pte(&at1);
 	if (!ac_test_do_access(&at1)) {
 		printf("%s: write on the second mapping fail.\n", __FUNCTION__);
 		goto err;
@@ -1003,7 +1036,7 @@ static int check_smep_andnot_wp(ac_pt_env_t *pt_env)
 		    AC_CPU_CR4_SMEP_MASK |
 		    AC_CPU_CR0_WP_MASK |
 		    AC_ACCESS_WRITE_MASK;
-	ac_test_setup_pte(&at1, pt_env);
+	ac_test_setup_pte(&at1);
 
 	/*
 	 * Here we write the ro user page when
@@ -1062,19 +1095,19 @@ static int check_effective_sp_permissions(ac_pt_env_t *pt_env)
 		    AC_PDE_USER_MASK | AC_PTE_USER_MASK |
 		    AC_PDE_ACCESSED_MASK | AC_PTE_ACCESSED_MASK |
 		    AC_PTE_WRITABLE_MASK | AC_ACCESS_USER_MASK;
-	__ac_setup_specific_pages(&at1, pt_env, false, pmd, 0);
+	__ac_setup_specific_pages(&at1, pmd, 0);
 
-	ac_test_init(&at2, ptr2, pt_env);
+	__ac_test_init(&at2, ptr2, pt_env, &at1);
 	at2.flags = at1.flags | AC_PDE_WRITABLE_MASK | AC_PTE_DIRTY_MASK | AC_ACCESS_WRITE_MASK;
-	__ac_setup_specific_pages(&at2, pt_env, true, pmd, 0);
+	__ac_setup_specific_pages(&at2, pmd, 0);
 
-	ac_test_init(&at3, ptr3, pt_env);
+	__ac_test_init(&at3, ptr3, pt_env, &at1);
 	at3.flags = AC_PDPTE_NO_WRITABLE_MASK | at1.flags;
-	__ac_setup_specific_pages(&at3, pt_env, true, pmd, 0);
+	__ac_setup_specific_pages(&at3, pmd, 0);
 
-	ac_test_init(&at4, ptr4, pt_env);
+	__ac_test_init(&at4, ptr4, pt_env, &at2);
 	at4.flags = AC_PDPTE_NO_WRITABLE_MASK | at2.flags;
-	__ac_setup_specific_pages(&at4, pt_env, true, pmd, 0);
+	__ac_setup_specific_pages(&at4, pmd, 0);
 
 	err_read_at1 = ac_test_do_access(&at1);
 	if (!err_read_at1) {
@@ -1110,7 +1143,7 @@ static int ac_test_exec(ac_test_t *at, ac_pt_env_t *pt_env)
 	if (verbose) {
 		ac_test_show(at);
 	}
-	ac_test_setup_pte(at, pt_env);
+	ac_test_setup_pte(at);
 	r = ac_test_do_access(at);
 	return r;
 }
-- 
2.34.0.rc2.393.gf8c9666880-goog


  parent reply	other threads:[~2021-11-25  1:58 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-25  1:28 [kvm-unit-tests PATCH 00/39] x86/access: nVMX: Big overhaul Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 01/39] x86/access: Add proper defines for hardcoded addresses Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 02/39] x86/access: Cache CR3 to improve performance Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 03/39] x86/access: Use do-while loop for what is obviously a do-while loop Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 04/39] x86/access: Stop pretending the test is SMP friendly Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 05/39] x86/access: Refactor so called "page table pool" logic Sean Christopherson
2021-11-26 18:03   ` Paolo Bonzini
2021-11-25  1:28 ` [kvm-unit-tests PATCH 06/39] x86/access: Stash root page table level in test environment Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 07/39] x86/access: Hoist page table allocator helpers above "init" helper Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 08/39] x86/access: Rename variables in page table walkers Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 09/39] x86/access: Abort if page table insertion hits an unexpected level Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 10/39] x86/access: Make SMEP place nice with 5-level paging Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 11/39] x86/access: Use upper half of virtual address space Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 12/39] x86/access: Print the index when dumping PTEs Sean Christopherson
2021-11-25  1:28 ` Sean Christopherson [this message]
2021-11-26 18:15   ` [kvm-unit-tests PATCH 13/39] x86/access: Pre-allocate all page tables at (sub)test init Paolo Bonzini
2021-11-25  1:28 ` [kvm-unit-tests PATCH 14/39] x86/access: Don't write page tables if desired PTE is same as current PTE Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 15/39] x86/access: Preserve A/D bits when writing paging structure entries Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 16/39] x86/access: Make toggling of PRESENT bit a "higher order" action Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 17/39] x86/access: Manually override PMD in effective permissions sub-test Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 18/39] x86/access: Remove manual override of PUD/PMD in prefetch sub-test Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 19/39] x86/access: Remove PMD/PT target overrides Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 20/39] x86/access: Remove timeout overrides now that performance doesn't suck Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 21/39] nVMX: Skip EPT tests if INVEPT(SINGLE_CONTEXT) is unsupported Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 22/39] nVMX: Hoist assert macros to the top of vmx.h Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 23/39] nVMX: Add a non-reporting assertion macro Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 24/39] nVMX: Assert success in unchecked INVEPT/INVVPID helpers Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 25/39] nVMX: Drop less-than-useless ept_sync() wrapper Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 26/39] nVMX: Move EPT capability check helpers to vmx.h Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 27/39] nVMX: Drop unused and useless vpid_sync() helper Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 28/39] nVMX: Remove "v1" version of INVVPID test Sean Christopherson
2021-11-26 18:28   ` Paolo Bonzini
2021-11-25  1:28 ` [kvm-unit-tests PATCH 29/39] nVMX: Add helper to check if INVVPID type is supported Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 30/39] nVMX: Add helper to check if INVVPID " Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 31/39] nVMX: Add helper to get first supported INVVPID type Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 32/39] nVMX: Use helper to check for EPT A/D support Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 33/39] nVMX: Add helpers to check for 4/5-level EPT support Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 34/39] nVMX: Fix name of macro defining EPT execute only capability Sean Christopherson
2021-11-26 18:31   ` Paolo Bonzini
2021-11-25  1:28 ` [kvm-unit-tests PATCH 35/39] nVMX: Add helper to check if a memtype is supported for EPT structures Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 36/39] nVMX: Get rid of horribly named "ctrl" boolean in test_ept_eptp() Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 37/39] nVMX: Rename awful "ctrl" booleans to "is_ctrl_valid" Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 38/39] nVMX: Add helper to check if VPID is supported Sean Christopherson
2021-11-25  1:28 ` [kvm-unit-tests PATCH 39/39] x86/access: nVMX: Add "access" test variants to invalidate via (INV)VPID Sean Christopherson
2021-11-26 18:43 ` [kvm-unit-tests PATCH 00/39] x86/access: nVMX: Big overhaul Paolo Bonzini
2021-11-29 19:04   ` Sean Christopherson
2021-11-29 19:15     ` Paolo Bonzini

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=20211125012857.508243-14-seanjc@google.com \
    --to=seanjc@google.com \
    --cc=kvm@vger.kernel.org \
    --cc=pbonzini@redhat.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;
as well as URLs for NNTP newsgroup(s).