Share pages before the page allocator is available, after both their
direct-map and kernel-image mappings have been made shared. Preserve
initial contents in a private scratch page, matching the AMD early helper,
and use the existing MapGPA path and shared-page accounting.

UP per-CPU data has an image alias. Drop shared image mappings before
kexec converts their backing pages to private through the direct map.

Suggested-by: Kiryl Shutsemau <[email protected]>
Link: https://lore.kernel.org/r/aqqGUAX65s4LdJkr@thinkstation
Signed-off-by: Zack Rusin <[email protected]>
---
 arch/x86/coco/tdx/tdx.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
index f904a636d449..4a683e9eafc3 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -4,6 +4,7 @@
 #undef pr_fmt
 #define pr_fmt(fmt)     "tdx: " fmt
 
+#include <linux/cacheflush.h>
 #include <linux/cpufeature.h>
 #include <linux/export.h>
 #include <linux/io.h>
@@ -18,6 +19,7 @@
 #include <asm/paravirt_types.h>
 #include <asm/pgtable.h>
+#include <asm/sections.h>
 #include <asm/set_memory.h>
 #include <asm/traps.h>
 
 /* MMIO direction */
@@ -1006,6 +1008,23 @@ static int tdx_enc_status_change_finish(unsigned long 
vaddr, int numpages,
        return 0;
 }
 
+static char tdx_early_buffer[PAGE_SIZE] __initdata __aligned(PAGE_SIZE);
+
+static int __init tdx_early_decrypt_page(unsigned long addr, unsigned long 
alias)
+{
+       void *buffer = __va(__pa_symbol(tdx_early_buffer));
+       int ret;
+
+       memcpy(buffer, (void *)addr, PAGE_SIZE);
+       clflush_cache_range((void *)addr, PAGE_SIZE);
+       early_set_page_decrypted(addr, alias);
+       ret = tdx_enc_status_change_finish(addr, 1, false);
+       if (ret)
+               return ret;
+       memcpy((void *)addr, buffer, PAGE_SIZE);
+       return 0;
+}
+
 /* Stop new private<->shared conversions */
 static void tdx_kexec_begin(void)
 {
@@ -1033,6 +1052,18 @@ static void tdx_kexec_finish(void)
 
        lockdep_assert_irqs_disabled();
 
+       /* Drop image aliases before the direct-map walk makes pages private. */
+       addr = (unsigned long)_text;
+       while (addr < _brk_end) {
+               unsigned int level;
+               pte_t *pte = lookup_address(addr, &level);
+
+               if (pte && pte_decrypted(*pte))
+                       set_pte(pte, __pte(0));
+               addr = (addr & page_level_mask(level)) + page_level_size(level);
+       }
+       __flush_tlb_all();
+
        addr = PAGE_OFFSET;
        end  = PAGE_OFFSET + get_max_mapped();
 
@@ -1160,6 +1191,7 @@ void __init tdx_early_init(void)
         */
        x86_platform.guest.enc_status_change_prepare = 
tdx_enc_status_change_prepare;
        x86_platform.guest.enc_status_change_finish  = 
tdx_enc_status_change_finish;
+       x86_init.paging.early_decrypt_page = tdx_early_decrypt_page;
 
        x86_platform.guest.enc_cache_flush_required  = tdx_cache_flush_required;
        x86_platform.guest.enc_tlb_flush_required    = tdx_tlb_flush_required;
-- 
2.53.0


Reply via email to