diff mbox series

[kvm-unit-tests,v2,15/16] x86 AMD SEV-SNP: Issue PSMASH/UNSMASH PSC requests on 2M ranges

Message ID 20240718124932.114121-16-papaluri@amd.com (mailing list archive)
State New, archived
Headers show
Series Introduce SEV-SNP support | expand

Commit Message

Paluri, PavanKumar July 18, 2024, 12:49 p.m. UTC
GHCB spec specifies that an SNP guest can submit PSMASH/UNSMASH hints
to hypervisor via PSC requests. Include a test to create such a PSC
request where KUT-SNP guest requests hypervisor to PSMASH/UNSMASH
2M ranges, to ensure hypervisor handles these requests without any
issues.

Signed-off-by: Pavan Kumar Paluri <papaluri@amd.com>
---
 lib/x86/amd_sev.c | 10 +++----
 lib/x86/amd_sev.h |  5 ++++
 x86/amd_sev.c     | 66 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 76 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/lib/x86/amd_sev.c b/lib/x86/amd_sev.c
index c2f2a3f43193..468ed9eef943 100644
--- a/lib/x86/amd_sev.c
+++ b/lib/x86/amd_sev.c
@@ -355,8 +355,8 @@  static bool pvalidate_failed(int result, bool allow_noupdate)
 	return false;
 }
 
-static void pvalidate_pages(struct snp_psc_desc *desc, unsigned long *vaddr_arr,
-			    bool allow_noupdate)
+void pvalidate_pages(struct snp_psc_desc *desc, unsigned long *vaddr_arr,
+		     bool allow_noupdate)
 {
 	struct psc_entry *entry;
 	int ret, i;
@@ -403,7 +403,7 @@  static int sev_ghcb_hv_call(struct ghcb *ghcb, u64 exit_code,
 	return verify_exception(ghcb);
 }
 
-static int vmgexit_psc(struct snp_psc_desc *desc, struct ghcb *ghcb)
+int vmgexit_psc(struct snp_psc_desc *desc, struct ghcb *ghcb)
 {
 	int cur_entry, end_entry, ret = 0;
 	struct snp_psc_desc *data;
@@ -457,8 +457,8 @@  static int vmgexit_psc(struct snp_psc_desc *desc, struct ghcb *ghcb)
 	return ret;
 }
 
-static void add_psc_entry(struct snp_psc_desc *desc, u8 idx, u8 op, unsigned long vaddr,
-			  bool large_entry, u16 cur_page_offset)
+void add_psc_entry(struct snp_psc_desc *desc, u8 idx, u8 op, unsigned long vaddr,
+		   bool large_entry, u16 cur_page_offset)
 {
 	struct psc_hdr *hdr = &desc->hdr;
 	struct psc_entry *entry = &desc->entries[idx];
diff --git a/lib/x86/amd_sev.h b/lib/x86/amd_sev.h
index e180a269fb63..8357a658d47d 100644
--- a/lib/x86/amd_sev.h
+++ b/lib/x86/amd_sev.h
@@ -247,6 +247,11 @@  unsigned long __sev_set_pages_state(struct snp_psc_desc *desc, unsigned long vad
 				    struct ghcb *ghcb, bool large_entry,
 				    bool allow_noupdate);
 void vc_ghcb_invalidate(struct ghcb *ghcb);
+void pvalidate_pages(struct snp_psc_desc *desc, unsigned long *vaddr_arr,
+		     bool allow_noupdate);
+int vmgexit_psc(struct snp_psc_desc *desc, struct ghcb *ghcb);
+void add_psc_entry(struct snp_psc_desc *desc, u8 idx, u8 op,
+		   unsigned long vaddr, bool large_entry, u16 offset);
 
 unsigned long long get_amd_sev_c_bit_mask(void);
 unsigned long long get_amd_sev_addr_upperbound(void);
diff --git a/x86/amd_sev.c b/x86/amd_sev.c
index ae19f8ad6cc8..bd369e5cada7 100644
--- a/x86/amd_sev.c
+++ b/x86/amd_sev.c
@@ -366,6 +366,71 @@  static void test_sev_psc_intermix_to_shared(void)
 	test_sev_psc_intermix(false);
 }
 
+static void test_sev_snp_psmash(void)
+{
+	int ret;
+	unsigned long vaddr, vaddr_arr[3];
+	struct snp_psc_desc desc = {0};
+	struct ghcb *ghcb = (struct ghcb *)(rdmsr(SEV_ES_GHCB_MSR_INDEX));
+
+	report_info("TEST: PSMASH and UNSMASH operations on 2M range");
+
+	vaddr = (unsigned long)vmalloc_pages(SEV_ALLOC_PAGE_COUNT,
+					     SEV_ALLOC_ORDER, RMP_PG_SIZE_2M);
+
+	/*
+	 * Create a PSC request for first PSC entry where:
+	 * - guest issues an UNSMASH on a 2M private range.
+	 * Hypervisor treats an UNSMASH hint from guest as a nop.
+	 * So it is expected that the state of pages after conversion to
+	 * be in the same state as before.
+	 */
+	vaddr_arr[0] = vaddr;
+	add_psc_entry(&desc, 0, SNP_PAGE_STATE_UNSMASH, vaddr_arr[0],
+		      true, 0);
+
+	/*
+	 * Create a PSC request for second PSC entry where:
+	 * - guest issues a PSMASH on the next 2M private range.
+	 * Hypervisor should also treat PSMASH hint from guest as a nop.
+	 */
+	vaddr_arr[1] = vaddr + LARGE_PAGE_SIZE;
+	add_psc_entry(&desc, 1, SNP_PAGE_STATE_PSMASH, vaddr_arr[1],
+		      true, 0);
+
+	/*
+	 * For 3rd PSC entry:
+	 * Perform an UNSMASH on the PSMASH'd entry where:
+	 * - guest now issues an UNSMASH on a 2M private PSMASH'd entry,
+	 * but since a PSMASH/UNSMASH are noops, states of these pages
+	 * should be in their original (private) states.
+	 */
+	vaddr_arr[2] = vaddr_arr[1];
+	add_psc_entry(&desc, 2, SNP_PAGE_STATE_UNSMASH, vaddr_arr[2],
+		      true, 0);
+
+	ret = vmgexit_psc(&desc, ghcb);
+
+	assert_msg(!ret, "VMGEXIT failed with ret value: %d", ret);
+
+	/*
+	 * Ensure the page states are still in the original (private)
+	 * state after hypervisor handled PSMASH/UNSMASH operations.
+	 */
+	report(is_validated_private_page(vaddr, RMP_PG_SIZE_2M),
+	       "Expected page state: Private");
+
+	report(is_validated_private_page(vaddr + LARGE_PAGE_SIZE,
+					 RMP_PG_SIZE_2M),
+	       "Expected page state: Private");
+
+	pvalidate_pages(&desc, vaddr_arr, true);
+
+	/* Free up all the used pages */
+	snp_free_pages(SEV_ALLOC_ORDER, SEV_ALLOC_PAGE_COUNT, vaddr,
+		       ghcb, true);
+}
+
 int main(void)
 {
 	int rtn;
@@ -387,6 +452,7 @@  int main(void)
 		test_sev_psc_ghcb_nae();
 		test_sev_psc_intermix_to_private();
 		test_sev_psc_intermix_to_shared();
+		test_sev_snp_psmash();
 	}
 
 	return report_summary();