Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/boot: Create a confined code area for startup code

In order to be able to have tight control over which code may execute
from the early 1:1 mapping of memory, but still link vmlinux as a single
executable, prefix all symbol references in startup code with __pi_, and
invoke it from outside using the __pi_ prefix.

Use objtool to check that no absolute symbol references are present in
the startup code, as these cannot be used from code running from the 1:1
mapping.

Note that this also requires disabling the latent-entropy GCC plugin, as
the global symbol references that it injects would require explicit
exports, and given that the startup code rarely executes more than once,
it is not a useful source of entropy anyway.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/20250828102202.1849035-43-ardb+git@google.com

authored by

Ard Biesheuvel and committed by
Borislav Petkov (AMD)
7b38dec3 749627c3

+27 -12
+14
arch/x86/boot/startup/Makefile
··· 4 4 KBUILD_CFLAGS += -D__DISABLE_EXPORTS -mcmodel=small -fPIC \ 5 5 -Os -DDISABLE_BRANCH_PROFILING \ 6 6 $(DISABLE_STACKLEAK_PLUGIN) \ 7 + $(DISABLE_LATENT_ENTROPY_PLUGIN) \ 7 8 -fno-stack-protector -D__NO_FORTIFY \ 8 9 -fno-jump-tables \ 9 10 -include $(srctree)/include/linux/hidden.h ··· 37 36 # 38 37 $(pi-objs): objtool-enabled = 1 39 38 $(pi-objs): objtool-args = $(if $(delay-objtool),,$(objtool-args-y)) --noabs 39 + 40 + # 41 + # Confine the startup code by prefixing all symbols with __pi_ (for position 42 + # independent). This ensures that startup code can only call other startup 43 + # code, or code that has explicitly been made accessible to it via a symbol 44 + # alias. 45 + # 46 + $(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ 47 + $(obj)/%.pi.o: $(obj)/%.o FORCE 48 + $(call if_changed,objcopy) 49 + 50 + targets += $(obj-y) 51 + obj-y := $(patsubst %.o,%.pi.o,$(obj-y))
-1
arch/x86/boot/startup/sev-shared.c
··· 12 12 #include <asm/setup_data.h> 13 13 14 14 #ifndef __BOOT_COMPRESSED 15 - #define error(v) pr_err(v) 16 15 #define has_cpuflag(f) boot_cpu_has(f) 17 16 #else 18 17 #undef WARN
-1
arch/x86/boot/startup/sme.c
··· 568 568 569 569 #ifdef CONFIG_MITIGATION_PAGE_TABLE_ISOLATION 570 570 /* Local version for startup code, which never operates on user page tables */ 571 - __weak 572 571 pgd_t __pti_set_user_pgtbl(pgd_t *pgdp, pgd_t pgd) 573 572 { 574 573 return pgd;
+1 -1
arch/x86/coco/sev/core.c
··· 272 272 273 273 do { 274 274 ret = ghcb ? svsm_perform_ghcb_protocol(ghcb, call) 275 - : svsm_perform_msr_protocol(call); 275 + : __pi_svsm_perform_msr_protocol(call); 276 276 } while (ret == -EAGAIN); 277 277 278 278 if (sev_cfg.ghcbs_initialized)
+1
arch/x86/include/asm/setup.h
··· 53 53 extern unsigned long __startup_64(unsigned long p2v_offset, struct boot_params *bp); 54 54 extern void startup_64_setup_gdt_idt(void); 55 55 extern void startup_64_load_idt(void *vc_handler); 56 + extern void __pi_startup_64_load_idt(void *vc_handler); 56 57 extern void early_setup_idt(void); 57 58 extern void __init do_early_exception(struct pt_regs *regs, int trapnr); 58 59
+1
arch/x86/include/asm/sev.h
··· 551 551 }; 552 552 553 553 int svsm_perform_msr_protocol(struct svsm_call *call); 554 + int __pi_svsm_perform_msr_protocol(struct svsm_call *call); 554 555 int snp_cpuid(void (*cpuid_fn)(void *ctx, struct cpuid_leaf *leaf), 555 556 void *ctx, struct cpuid_leaf *leaf); 556 557
+1 -1
arch/x86/kernel/head64.c
··· 319 319 handler = vc_boot_ghcb; 320 320 } 321 321 322 - startup_64_load_idt(handler); 322 + __pi_startup_64_load_idt(handler); 323 323 }
+4 -4
arch/x86/kernel/head_64.S
··· 71 71 xorl %edx, %edx 72 72 wrmsr 73 73 74 - call startup_64_setup_gdt_idt 74 + call __pi_startup_64_setup_gdt_idt 75 75 76 76 /* Now switch to __KERNEL_CS so IRET works reliably */ 77 77 pushq $__KERNEL_CS ··· 91 91 * subsequent code. Pass the boot_params pointer as the first argument. 92 92 */ 93 93 movq %r15, %rdi 94 - call sme_enable 94 + call __pi_sme_enable 95 95 #endif 96 96 97 97 /* Sanitize CPU configuration */ ··· 111 111 * programmed into CR3. 112 112 */ 113 113 movq %r15, %rsi 114 - call __startup_64 114 + call __pi___startup_64 115 115 116 116 /* Form the CR3 value being sure to include the CR3 modifier */ 117 117 leaq early_top_pgt(%rip), %rcx ··· 562 562 /* Call C handler */ 563 563 movq %rsp, %rdi 564 564 movq ORIG_RAX(%rsp), %rsi 565 - call do_vc_no_ghcb 565 + call __pi_do_vc_no_ghcb 566 566 567 567 /* Unwind pt_regs */ 568 568 POP_REGS
+3 -3
arch/x86/mm/mem_encrypt_boot.S
··· 16 16 17 17 .text 18 18 .code64 19 - SYM_FUNC_START(sme_encrypt_execute) 19 + SYM_FUNC_START(__pi_sme_encrypt_execute) 20 20 21 21 /* 22 22 * Entry parameters: ··· 69 69 ANNOTATE_UNRET_SAFE 70 70 ret 71 71 int3 72 - SYM_FUNC_END(sme_encrypt_execute) 72 + SYM_FUNC_END(__pi_sme_encrypt_execute) 73 73 74 - SYM_FUNC_START(__enc_copy) 74 + SYM_FUNC_START_LOCAL(__enc_copy) 75 75 ANNOTATE_NOENDBR 76 76 /* 77 77 * Routine used to encrypt memory in place.
+2 -1
tools/objtool/check.c
··· 3564 3564 if (func && insn_func(insn) && func != insn_func(insn)->pfunc) { 3565 3565 /* Ignore KCFI type preambles, which always fall through */ 3566 3566 if (!strncmp(func->name, "__cfi_", 6) || 3567 - !strncmp(func->name, "__pfx_", 6)) 3567 + !strncmp(func->name, "__pfx_", 6) || 3568 + !strncmp(func->name, "__pi___pfx_", 11)) 3568 3569 return 0; 3569 3570 3570 3571 if (file->ignore_unreachables)