On 2021-12-10 09:43:01 -0600, Brijesh Singh wrote: > Many of the integrity guarantees of SEV-SNP are enforced through the > Reverse Map Table (RMP). Each RMP entry contains the GPA at which a > particular page of DRAM should be mapped. The VMs can request the > hypervisor to add pages in the RMP table via the Page State Change VMGEXIT > defined in the GHCB specification. Inside each RMP entry is a Validated > flag; this flag is automatically cleared to 0 by the CPU hardware when a > new RMP entry is created for a guest. Each VM page can be either > validated or invalidated, as indicated by the Validated flag in the RMP > entry. Memory access to a private page that is not validated generates > a #VC. A VM must use PVALIDATE instruction to validate the private page > before using it. > > To maintain the security guarantee of SEV-SNP guests, when transitioning > pages from private to shared, the guest must invalidate the pages before > asking the hypervisor to change the page state to shared in the RMP table. > > After the pages are mapped private in the page table, the guest must issue > a page state change VMGEXIT to make the pages private in the RMP table and > validate it. > > On boot, BIOS should have validated the entire system memory. During > the kernel decompression stage, the VC handler uses the > set_memory_decrypted() to make the GHCB page shared (i.e clear encryption > attribute). And while exiting from the decompression, it calls the > set_page_encrypted() to make the page private. > > Add sev_snp_set_page_{private,shared}() helper that is used by the Since the functions being added are snp_set_page_{private,shared}(), s/sev_snp_set_page_/snp_set_page_/ Also, s/helper that is/helpers that are/ > set_memory_{decrypt,encrypt}() to change the page state in the RMP table. s/decrypt,encrypt/decrypted,encrypted/ > > Signed-off-by: Brijesh Singh <brijesh.singh@xxxxxxx> > --- > arch/x86/boot/compressed/ident_map_64.c | 18 +++++++++- > arch/x86/boot/compressed/misc.h | 4 +++ > arch/x86/boot/compressed/sev.c | 46 +++++++++++++++++++++++++ > arch/x86/include/asm/sev-common.h | 26 ++++++++++++++ > 4 files changed, 93 insertions(+), 1 deletion(-) > > diff --git a/arch/x86/boot/compressed/ident_map_64.c b/arch/x86/boot/compressed/ident_map_64.c > index f7213d0943b8..ef77453cc629 100644 > --- a/arch/x86/boot/compressed/ident_map_64.c > +++ b/arch/x86/boot/compressed/ident_map_64.c > @@ -275,15 +275,31 @@ static int set_clr_page_flags(struct x86_mapping_info *info, > * Changing encryption attributes of a page requires to flush it from > * the caches. > */ > - if ((set | clr) & _PAGE_ENC) > + if ((set | clr) & _PAGE_ENC) { > clflush_page(address); > > + /* > + * If the encryption attribute is being cleared, then change > + * the page state to shared in the RMP table. > + */ > + if (clr) This function is also called by set_page_non_present() with clr set to _PAGE_PRESENT. Do we want to change the page state to shared even when the page is not present? If not, shouldn't the check be (clr & _PAGE_ENC)? > + snp_set_page_shared(pte_pfn(*ptep) << PAGE_SHIFT); > + } > + > /* Update PTE */ > pte = *ptep; > pte = pte_set_flags(pte, set); > pte = pte_clear_flags(pte, clr); > set_pte(ptep, pte); > > + /* > + * If the encryption attribute is being set, then change the page state to > + * private in the RMP entry. The page state must be done after the PTE > + * is updated. > + */ > + if (set & _PAGE_ENC) > + snp_set_page_private(__pa(address & PAGE_MASK)); > + > /* Flush TLB after changing encryption attribute */ > write_cr3(top_level_pgt); > > diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h > index 23e0e395084a..01cc13c12059 100644 > --- a/arch/x86/boot/compressed/misc.h > +++ b/arch/x86/boot/compressed/misc.h > @@ -124,6 +124,8 @@ static inline void console_init(void) > void sev_enable(struct boot_params *bp); > void sev_es_shutdown_ghcb(void); > extern bool sev_es_check_ghcb_fault(unsigned long address); > +void snp_set_page_private(unsigned long paddr); > +void snp_set_page_shared(unsigned long paddr); > #else > static inline void sev_enable(struct boot_params *bp) { } > static inline void sev_es_shutdown_ghcb(void) { } > @@ -131,6 +133,8 @@ static inline bool sev_es_check_ghcb_fault(unsigned long address) > { > return false; > } > +static inline void snp_set_page_private(unsigned long paddr) { } > +static inline void snp_set_page_shared(unsigned long paddr) { } > #endif > > /* acpi.c */ > diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c > index 9be369f72299..12a93acc94ba 100644 > --- a/arch/x86/boot/compressed/sev.c > +++ b/arch/x86/boot/compressed/sev.c > @@ -119,6 +119,52 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt, > /* Include code for early handlers */ > #include "../../kernel/sev-shared.c" > > +static inline bool sev_snp_enabled(void) > +{ > + return sev_status & MSR_AMD64_SEV_SNP_ENABLED; > +} > + > +static void __page_state_change(unsigned long paddr, enum psc_op op) > +{ > + u64 val; > + > + if (!sev_snp_enabled()) > + return; > + > + /* > + * If private -> shared then invalidate the page before requesting the This comment is confusing. We don't know what the present state is, right? If we don't, shouldn't we just say: If the operation is SNP_PAGE_STATE_SHARED, invalidate the page before requesting the state change in the RMP table. > + * state change in the RMP table. > + */ > + if (op == SNP_PAGE_STATE_SHARED && pvalidate(paddr, RMP_PG_SIZE_4K, 0)) > + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE); > + > + /* Issue VMGEXIT to change the page state in RMP table. */ > + sev_es_wr_ghcb_msr(GHCB_MSR_PSC_REQ_GFN(paddr >> PAGE_SHIFT, op)); > + VMGEXIT(); > + > + /* Read the response of the VMGEXIT. */ > + val = sev_es_rd_ghcb_msr(); > + if ((GHCB_RESP_CODE(val) != GHCB_MSR_PSC_RESP) || GHCB_MSR_PSC_RESP_VAL(val)) > + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC); > + > + /* > + * Now that page is added in the RMP table, validate it so that it is > + * consistent with the RMP entry. The page is not "added", right? Shouldn't we just say: Validate the page so that it is consistent with the RMP entry. Venu