[PATCH v2 5/6] x86/tdx: Support early sharing of kernel data
From: Zack Rusin
Date: Tue Sep 29 2026 - 00:07:38 EST
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 <kas@xxxxxxxxxx>
Link: https://lore.kernel.org/r/aqqGUAX65s4LdJkr@thinkstation
Signed-off-by: Zack Rusin <zack.rusin@xxxxxxxxxxxx>
---
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