x86/sev: Add helper for validating pages in early enc attribute changes
early_set_memory_{encrypted,decrypted}() are used for changing the page state from decrypted (shared) to encrypted (private) and vice versa. When SEV-SNP is active, the page state transition needs to go through additional steps. If the page is transitioned from shared to private, then perform the following after the encryption attribute is set in the page table: 1. Issue the page state change VMGEXIT to add the page as a private in the RMP table. 2. Validate the page after its successfully added in the RMP table. To maintain the security guarantees, if the page is transitioned from private to shared, then perform the following before clearing the encryption attribute from the page table. 1. Invalidate the page. 2. Issue the page state change VMGEXIT to make the page shared in the RMP table. early_set_memory_{encrypted,decrypted}() can be called before the GHCB is setup so use the SNP page state MSR protocol VMGEXIT defined in the GHCB specification to request the page state change in the RMP table. While at it, add a helper snp_prep_memory() which will be used in probe_roms(), in a later patch. [ bp: Massage commit message. ] Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Signed-off-by: Borislav Petkov <bp@suse.de> Reviewed-by: Venu Busireddy <venu.busireddy@oracle.com> Link: https://lore.kernel.org/r/20220307213356.2797205-19-brijesh.singh@amd.com
This commit is contained in:
committed by
Borislav Petkov
parent
95d33bfaa3
commit
5e5ccff60a
@ -556,6 +556,105 @@ static u64 get_jump_table_addr(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pvalidate_pages(unsigned long vaddr, unsigned int npages, bool validate)
|
||||
{
|
||||
unsigned long vaddr_end;
|
||||
int rc;
|
||||
|
||||
vaddr = vaddr & PAGE_MASK;
|
||||
vaddr_end = vaddr + (npages << PAGE_SHIFT);
|
||||
|
||||
while (vaddr < vaddr_end) {
|
||||
rc = pvalidate(vaddr, RMP_PG_SIZE_4K, validate);
|
||||
if (WARN(rc, "Failed to validate address 0x%lx ret %d", vaddr, rc))
|
||||
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE);
|
||||
|
||||
vaddr = vaddr + PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
static void __init early_set_pages_state(unsigned long paddr, unsigned int npages, enum psc_op op)
|
||||
{
|
||||
unsigned long paddr_end;
|
||||
u64 val;
|
||||
|
||||
paddr = paddr & PAGE_MASK;
|
||||
paddr_end = paddr + (npages << PAGE_SHIFT);
|
||||
|
||||
while (paddr < paddr_end) {
|
||||
/*
|
||||
* Use the MSR protocol because this function can be called before
|
||||
* the GHCB is established.
|
||||
*/
|
||||
sev_es_wr_ghcb_msr(GHCB_MSR_PSC_REQ_GFN(paddr >> PAGE_SHIFT, op));
|
||||
VMGEXIT();
|
||||
|
||||
val = sev_es_rd_ghcb_msr();
|
||||
|
||||
if (WARN(GHCB_RESP_CODE(val) != GHCB_MSR_PSC_RESP,
|
||||
"Wrong PSC response code: 0x%x\n",
|
||||
(unsigned int)GHCB_RESP_CODE(val)))
|
||||
goto e_term;
|
||||
|
||||
if (WARN(GHCB_MSR_PSC_RESP_VAL(val),
|
||||
"Failed to change page state to '%s' paddr 0x%lx error 0x%llx\n",
|
||||
op == SNP_PAGE_STATE_PRIVATE ? "private" : "shared",
|
||||
paddr, GHCB_MSR_PSC_RESP_VAL(val)))
|
||||
goto e_term;
|
||||
|
||||
paddr = paddr + PAGE_SIZE;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
e_term:
|
||||
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
|
||||
}
|
||||
|
||||
void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr,
|
||||
unsigned int npages)
|
||||
{
|
||||
if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Ask the hypervisor to mark the memory pages as private in the RMP
|
||||
* table.
|
||||
*/
|
||||
early_set_pages_state(paddr, npages, SNP_PAGE_STATE_PRIVATE);
|
||||
|
||||
/* Validate the memory pages after they've been added in the RMP table. */
|
||||
pvalidate_pages(vaddr, npages, true);
|
||||
}
|
||||
|
||||
void __init early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr,
|
||||
unsigned int npages)
|
||||
{
|
||||
if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
|
||||
return;
|
||||
|
||||
/* Invalidate the memory pages before they are marked shared in the RMP table. */
|
||||
pvalidate_pages(vaddr, npages, false);
|
||||
|
||||
/* Ask hypervisor to mark the memory pages shared in the RMP table. */
|
||||
early_set_pages_state(paddr, npages, SNP_PAGE_STATE_SHARED);
|
||||
}
|
||||
|
||||
void __init snp_prep_memory(unsigned long paddr, unsigned int sz, enum psc_op op)
|
||||
{
|
||||
unsigned long vaddr, npages;
|
||||
|
||||
vaddr = (unsigned long)__va(paddr);
|
||||
npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
|
||||
|
||||
if (op == SNP_PAGE_STATE_PRIVATE)
|
||||
early_snp_set_memory_private(vaddr, paddr, npages);
|
||||
else if (op == SNP_PAGE_STATE_SHARED)
|
||||
early_snp_set_memory_shared(vaddr, paddr, npages);
|
||||
else
|
||||
WARN(1, "invalid memory op %d\n", op);
|
||||
}
|
||||
|
||||
int sev_es_setup_ap_jump_table(struct real_mode_header *rmh)
|
||||
{
|
||||
u16 startup_cs, startup_ip;
|
||||
|
Reference in New Issue
Block a user