[kvm-unit-tests RFC PATCH 12/13] x86 AMD SEV-SNP: Test-1: Perform Intermix to 2M Private PSCs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The test performs the following actions:
1. Allocates a 2M private page (512 4K entries) and converts the entire
   range to shared.
2. Performs a write operation on these un-encrypted pages.
3. Performs partial page state change conversions on first 256 4K
   entries and conducts a re-validation test on one of these now-private
   entries to determine whether the current page state is private or
   not.
4. Also conducts a write test on the 256 4K shared entries (with C-bit
   unset on PMD) to ensure these are in shared state.
5. Converts the whole 2M range from an intermixed state to private and
   runs a re-validation check on the now-private 2M page.

Since the test performs partial page state changes within 2M range,
pvalidate would result in a failure in the form of
PVALIDATE_FAIL_NOUPDATE since the test would perform same page state
change operation on the entries that are already in the same state. Do
not treat RMP entry validated bit unchanged as an error for this test.

The primary goal of this test is to determine whether a shared->private
conversion on a 2M range containing a mix of shared and private entries
is handled properly by the hypervisor or not. Such a PSC from an
intermixed state may never take place in a conventional SEV-SNP guest.

Suggested-by: Michael Roth <michael.roth@xxxxxxx>
Signed-off-by: Pavan Kumar Paluri <papaluri@xxxxxxx>
---
 x86/amd_sev.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 98 insertions(+), 3 deletions(-)

diff --git a/x86/amd_sev.c b/x86/amd_sev.c
index 6c6fe8e05adb..7b53ef9c44d0 100644
--- a/x86/amd_sev.c
+++ b/x86/amd_sev.c
@@ -22,8 +22,10 @@
 
 #define TESTDEV_IO_PORT 0xe0
 #define SNP_PSC_ALLOC_ORDER 10
+#define INTERMIX_PSC_ORDER 9
 
 static char st1[] = "abcdefghijklmnop";
+static bool allow_noupdate;
 
 static int test_sev_activation(void)
 {
@@ -136,15 +138,22 @@ static void pvalidate_pages(struct snp_psc_desc *desc)
 			for (; vaddr < vaddr_end; vaddr += PAGE_SIZE) {
 				pvalidate_result = pvalidate(vaddr, RMP_PG_SIZE_4K,
 							     validate);
-				if (pvalidate_result)
+				if (!allow_noupdate && pvalidate_result)
+					break;
+				else if (allow_noupdate &&
+					 (pvalidate_result &&
+					  pvalidate_result != PVALIDATE_FAIL_NOUPDATE))
 					break;
 			}
 		}
 
-		if (pvalidate_result) {
+		if (!allow_noupdate && pvalidate_result)
+			assert_msg(!pvalidate_result, "Failed to validate address: 0x%lx, ret: %d\n",
+				   vaddr, pvalidate_result);
+		else if (allow_noupdate &&
+			 (pvalidate_result && pvalidate_result != PVALIDATE_FAIL_NOUPDATE))
 			assert_msg(!pvalidate_result, "Failed to validate address: 0x%lx, ret: %d\n",
 				   vaddr, pvalidate_result);
-		}
 	}
 }
 
@@ -630,6 +639,91 @@ static void test_sev_psc_ghcb_nae(void)
 	free_pages_by_order(vm_pages, SNP_PSC_ALLOC_ORDER);
 }
 
+static void __test_sev_psc_private(unsigned long vaddr, struct ghcb *ghcb,
+				   bool large_page, pteval_t *pte)
+{
+	allow_noupdate = true;
+
+	set_pte_encrypted((unsigned long)vaddr, 1 << INTERMIX_PSC_ORDER);
+
+	/* Convert whole 2M range back to private */
+	sev_set_pages_state(vaddr, 512, SNP_PAGE_STATE_PRIVATE, ghcb,
+			    large_page);
+
+	allow_noupdate = false;
+
+	/* Test re-validation on the now-private 2M page */
+	report(is_validated_private_page(vaddr, large_page, 1),
+	       "Expected 2M page state: Private");
+}
+
+static void test_sev_psc_intermix(bool is_private)
+{
+	unsigned long *vm_page;
+	bool large_page = false;
+	pteval_t *pte;
+	struct ghcb *ghcb = (struct ghcb *)(rdmsr(SEV_ES_GHCB_MSR_INDEX));
+
+	vm_page = alloc_pages(INTERMIX_PSC_ORDER);
+	assert_msg(vm_page, "Page allocation failure");
+
+	pte = get_pte((pgd_t *)read_cr3(), (void *)vm_page);
+	assert_msg(pte, "Invalid PTE");
+
+	if (!pte && IS_ALIGNED((unsigned long)vm_page, LARGE_PAGE_SIZE)) {
+		install_large_page((pgd_t *)read_cr3(), (phys_addr_t)vm_page,
+				   (void *)(ulong)vm_page);
+		large_page = true;
+	}
+
+	pte = get_pte_level((pgd_t *)read_cr3(), (void *)vm_page, 1);
+	if (!pte)
+		report_info("Intermix test will have 2M mapping");
+
+	/* Convert the 2M range into shared */
+	sev_set_pages_state((unsigned long)vm_page, 512,
+			    SNP_PAGE_STATE_SHARED, ghcb,
+			    large_page);
+	set_pte_decrypted((unsigned long)vm_page, 1 << INTERMIX_PSC_ORDER);
+
+	report(!test_write((unsigned long)vm_page, 512),
+	       "Write to a 2M un-encrypted range");
+
+	set_pte_encrypted((unsigned long)vm_page, 1 << INTERMIX_PSC_ORDER);
+
+	/*
+	 * Convert half sub-pages into private and leave other
+	 * half in shared state.
+	 */
+	sev_set_pages_state((unsigned long)vm_page, 256,
+			    SNP_PAGE_STATE_PRIVATE, ghcb, false);
+
+	/* Test re-validation on a now-private 4k page */
+	report(is_validated_private_page((unsigned long)vm_page, false, 1),
+	       "Expected 4K page state: Private");
+
+	/*
+	 * Unset C-bit on 2M PMD before issuing read/write to these
+	 * 256 4K shared entries.
+	 */
+	set_pte_decrypted((unsigned long)vm_page, 1 << INTERMIX_PSC_ORDER);
+
+	report(!test_write((unsigned long)vm_page + 256 * PAGE_SIZE, 256),
+	       "Write to 256 4K shared pages within 2M un-encrypted page");
+
+	if (is_private)
+		__test_sev_psc_private((unsigned long)vm_page, ghcb,
+				       large_page, pte);
+
+	/* Cleanup */
+	free_pages_by_order(vm_page, INTERMIX_PSC_ORDER);
+}
+
+static void test_sev_psc_intermix_to_private(void)
+{
+	test_sev_psc_intermix(true);
+}
+
 int main(void)
 {
 	int rtn;
@@ -643,6 +737,7 @@ int main(void)
 	if (amd_sev_snp_enabled()) {
 		test_sev_psc_ghcb_msr();
 		test_sev_psc_ghcb_nae();
+		test_sev_psc_intermix_to_private();
 	}
 
 	return report_summary();
-- 
2.34.1





[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux