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@xxxxxxx> --- 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 --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(); -- 2.34.1