]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
x86/boot/sev: Avoid shared GHCB page for early memory acceptance
authorArd Biesheuvel <ardb@kernel.org>
Thu, 17 Apr 2025 20:21:21 +0000 (22:21 +0200)
committerIngo Molnar <mingo@kernel.org>
Fri, 18 Apr 2025 12:30:30 +0000 (14:30 +0200)
Communicating with the hypervisor using the shared GHCB page requires
clearing the C bit in the mapping of that page. When executing in the
context of the EFI boot services, the page tables are owned by the
firmware, and this manipulation is not possible.

So switch to a different API for accepting memory in SEV-SNP guests, one
which is actually supported at the point during boot where the EFI stub
may need to accept memory, but the SEV-SNP init code has not executed
yet.

For simplicity, also switch the memory acceptance carried out by the
decompressor when not booting via EFI - this only involves the
allocation for the decompressed kernel, and is generally only called
after kexec, as normal boot will jump straight into the kernel from the
EFI stub.

Fixes: 6c3211796326 ("x86/sev: Add SNP-specific unaccepted memory support")
Tested-by: Tom Lendacky <thomas.lendacky@amd.com>
Co-developed-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: <stable@vger.kernel.org>
Cc: Dionna Amalie Glaze <dionnaglaze@google.com>
Cc: Kevin Loughlin <kevinloughlin@google.com>
Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: linux-efi@vger.kernel.org
Link: https://lore.kernel.org/r/20250404082921.2767593-8-ardb+git@google.com
Link: https://lore.kernel.org/r/20250410132850.3708703-2-ardb+git@google.com
Link: https://lore.kernel.org/r/20250417202120.1002102-2-ardb+git@google.com
arch/x86/boot/compressed/mem.c
arch/x86/boot/compressed/sev.c
arch/x86/boot/compressed/sev.h

index dbba332e4a12d744f29775dd4b5d6fd0eaa1cb03..f676156d9f3db41f9a6ba3b7bcdb62639772ff63 100644 (file)
@@ -34,11 +34,14 @@ static bool early_is_tdx_guest(void)
 
 void arch_accept_memory(phys_addr_t start, phys_addr_t end)
 {
+       static bool sevsnp;
+
        /* Platform-specific memory-acceptance call goes here */
        if (early_is_tdx_guest()) {
                if (!tdx_accept_memory(start, end))
                        panic("TDX: Failed to accept memory\n");
-       } else if (sev_snp_enabled()) {
+       } else if (sevsnp || (sev_get_status() & MSR_AMD64_SEV_SNP_ENABLED)) {
+               sevsnp = true;
                snp_accept_memory(start, end);
        } else {
                error("Cannot accept memory: unknown platform\n");
index bb55934c1cee7053726629e90e82b5b5b5825d21..89ba168f4f0f02b613e8886e88890637037b336b 100644 (file)
@@ -164,10 +164,7 @@ bool sev_snp_enabled(void)
 
 static void __page_state_change(unsigned long paddr, enum psc_op op)
 {
-       u64 val;
-
-       if (!sev_snp_enabled())
-               return;
+       u64 val, msr;
 
        /*
         * If private -> shared then invalidate the page before requesting the
@@ -176,6 +173,9 @@ static void __page_state_change(unsigned long paddr, enum psc_op op)
        if (op == SNP_PAGE_STATE_SHARED)
                pvalidate_4k_page(paddr, paddr, false);
 
+       /* Save the current GHCB MSR value */
+       msr = sev_es_rd_ghcb_msr();
+
        /* 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();
@@ -185,6 +185,9 @@ static void __page_state_change(unsigned long paddr, enum psc_op op)
        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);
 
+       /* Restore the GHCB MSR value */
+       sev_es_wr_ghcb_msr(msr);
+
        /*
         * Now that page state is changed in the RMP table, validate it so that it is
         * consistent with the RMP entry.
@@ -195,11 +198,17 @@ static void __page_state_change(unsigned long paddr, enum psc_op op)
 
 void snp_set_page_private(unsigned long paddr)
 {
+       if (!sev_snp_enabled())
+               return;
+
        __page_state_change(paddr, SNP_PAGE_STATE_PRIVATE);
 }
 
 void snp_set_page_shared(unsigned long paddr)
 {
+       if (!sev_snp_enabled())
+               return;
+
        __page_state_change(paddr, SNP_PAGE_STATE_SHARED);
 }
 
@@ -223,56 +232,10 @@ static bool early_setup_ghcb(void)
        return true;
 }
 
-static phys_addr_t __snp_accept_memory(struct snp_psc_desc *desc,
-                                      phys_addr_t pa, phys_addr_t pa_end)
-{
-       struct psc_hdr *hdr;
-       struct psc_entry *e;
-       unsigned int i;
-
-       hdr = &desc->hdr;
-       memset(hdr, 0, sizeof(*hdr));
-
-       e = desc->entries;
-
-       i = 0;
-       while (pa < pa_end && i < VMGEXIT_PSC_MAX_ENTRY) {
-               hdr->end_entry = i;
-
-               e->gfn = pa >> PAGE_SHIFT;
-               e->operation = SNP_PAGE_STATE_PRIVATE;
-               if (IS_ALIGNED(pa, PMD_SIZE) && (pa_end - pa) >= PMD_SIZE) {
-                       e->pagesize = RMP_PG_SIZE_2M;
-                       pa += PMD_SIZE;
-               } else {
-                       e->pagesize = RMP_PG_SIZE_4K;
-                       pa += PAGE_SIZE;
-               }
-
-               e++;
-               i++;
-       }
-
-       if (vmgexit_psc(boot_ghcb, desc))
-               sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
-
-       pvalidate_pages(desc);
-
-       return pa;
-}
-
 void snp_accept_memory(phys_addr_t start, phys_addr_t end)
 {
-       struct snp_psc_desc desc = {};
-       unsigned int i;
-       phys_addr_t pa;
-
-       if (!boot_ghcb && !early_setup_ghcb())
-               sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
-
-       pa = start;
-       while (pa < end)
-               pa = __snp_accept_memory(&desc, pa, end);
+       for (phys_addr_t pa = start; pa < end; pa += PAGE_SIZE)
+               __page_state_change(pa, SNP_PAGE_STATE_PRIVATE);
 }
 
 void sev_es_shutdown_ghcb(void)
index fc725a981b093b56b27207e40b911404de06374e..4e463f33186df47ee445a1024aad22e4bf8e17d7 100644 (file)
 
 bool sev_snp_enabled(void);
 void snp_accept_memory(phys_addr_t start, phys_addr_t end);
+u64 sev_get_status(void);
 
 #else
 
 static inline bool sev_snp_enabled(void) { return false; }
 static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
+static inline u64 sev_get_status(void) { return 0; }
 
 #endif