[PATCH 00/12] x86: Add single 64-bit U-Boot without SPL for QEMU
From: Simon Glass <simon.glass@canonical.com> This series adds support for building a single 64-bit U-Boot binary with integrated 16-bit and 32-bit startup code, eliminating the need for SPL on platforms where RAM is available immediately (e.g. QEMU). The new start_from_32.S enters in 32-bit protected mode (from start16.S), builds identity-mapped page tables with 2MB pages, enables SSE and PAE, then transitions to 64-bit long mode before calling board_init_f(). Since the 64-bit binary is linked as PIE, the 16-bit startup objects cannot be linked into it directly. Instead they are linked into a separate 32-bit ELF and their sections are extracted to binary files for binman. Along the way this moves the x86 16-bit binary rules from the top-level Makefile into arch/x86/Makefile, and switches x86_64 to use MSR_FS_BASE for the global data pointer instead of a writable global variable. Tested on QEMU x86_64 - boots to the command prompt. The existing SPL-based qemu-x86_64 and 32-bit qemu-x86 builds are unaffected. Simon Glass (12): x86: Allow disabling the regparm calling-convention x86: Use MSR_FS_BASE for gd pointer on x86_64 efi: Cache gd->relocaddr for EFI runtime services x86: Move 16-bit binary rules to arch/x86/Makefile x86: Exclude X86_32BIT_INIT for 64-bit builds x86: Build 16-bit startup objects as 32-bit for x86_64 x86: Link 16-bit startup into separate 32-bit ELF x86: Add 32-to-64-bit startup code x86: Add qemu-x86_64_nospl defconfig x86: qemu: Enable MTRR setup for x86_64 no-SPL scripts: build-qemu: Add --no-spl option for x86_64 CI: Add test coverage for qemu-x86_64_nospl .azure-pipelines.yml | 3 + .gitlab-ci.yml | 6 + Makefile | 10 - arch/x86/Kconfig | 24 +- arch/x86/Makefile | 39 +++ arch/x86/config.mk | 2 + arch/x86/cpu/Makefile | 15 ++ arch/x86/cpu/i386/interrupt.c | 10 +- arch/x86/cpu/qemu/qemu.c | 4 +- arch/x86/cpu/start.S | 23 +- arch/x86/cpu/start_from_32.S | 248 ++++++++++++++++++ arch/x86/cpu/start_from_spl.S | 21 +- arch/x86/cpu/start_from_tpl.S | 19 +- arch/x86/cpu/u-boot-16bit.lds | 30 +++ arch/x86/cpu/x86_64/cpu.c | 2 + arch/x86/cpu/x86_64/misc.c | 12 +- arch/x86/dts/emulation-u-boot.dtsi | 2 +- arch/x86/include/asm/global_data.h | 26 +- board/emulation/qemu-x86/MAINTAINERS | 1 + configs/qemu-x86_64_nospl_defconfig | 82 ++++++ lib/efi_loader/efi_runtime.c | 19 +- scripts/build-qemu | 9 +- .../bin/travis-ci/conf.qemu-x86_64_nospl_na | 30 +++ .../u_boot_boardenv_qemu_x86_64_nospl_na.py | 14 + 24 files changed, 613 insertions(+), 38 deletions(-) create mode 100644 arch/x86/cpu/start_from_32.S create mode 100644 arch/x86/cpu/u-boot-16bit.lds create mode 100644 configs/qemu-x86_64_nospl_defconfig create mode 100644 test/hooks/bin/travis-ci/conf.qemu-x86_64_nospl_na create mode 100644 test/hooks/py/travis-ci/u_boot_boardenv_qemu_x86_64_nospl_na.py -- 2.43.0 base-commit: 06331d87ef4298638e64dab5a4d7f4ba2c19fdcb branch: qemv
From: Simon Glass <simon.glass@canonical.com> By default, U-Boot on 32-bit x86 uses -mregparm=3 to pass the first three function arguments in registers (EAX, EDX, ECX) rather than on the stack. This is more efficient but makes it harder to link U-Boot against libraries which expect the standard calling convention, e.g. using ulib from Rust. Add a Kconfig option X86_NO_REGPARM to disable regparm and use the standard i386 calling convention where all arguments are passed on the stack. Adjust the assembly-based startup code to support both options. For qemu-x86 this increases code size by about 42K, i.e. 4.1% growth. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/Kconfig | 14 ++++++++++++++ arch/x86/config.mk | 2 ++ arch/x86/cpu/i386/interrupt.c | 10 ++++++++-- arch/x86/cpu/start.S | 23 +++++++++++++++++++++-- arch/x86/cpu/start_from_spl.S | 21 ++++++++++++++++++++- arch/x86/cpu/start_from_tpl.S | 19 ++++++++++++++++++- 6 files changed, 83 insertions(+), 6 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 835cf2d8025..a7d616a401d 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -750,6 +750,20 @@ config X86_HARDFP start-up code for 64-bit mode and changes the compiler options for 64-bit to enable SSE. +config X86_NO_REGPARM + bool "Disable register parameters (regparm=3)" + depends on !X86_64 + help + By default, U-Boot on 32-bit x86 uses -mregparm=3 to pass the first + three function arguments in registers (EAX, EDX, ECX) rather than on + the stack. This is more efficient but can cause issues with debugging + tools or when interfacing with code that expects the standard calling + convention. + + Select this option to disable regparm and use the standard i386 + calling convention where all arguments are passed on the stack. This + may be useful for debugging or for running in certain emulators. + config HAVE_ITSS bool "Enable ITSS" help diff --git a/arch/x86/config.mk b/arch/x86/config.mk index 0deea05c15f..51f5026fb2c 100644 --- a/arch/x86/config.mk +++ b/arch/x86/config.mk @@ -83,8 +83,10 @@ LDSCRIPT := $(LDSCRIPT_EFI) else ifeq ($(IS_32BIT),y) +ifndef CONFIG_X86_NO_REGPARM PLATFORM_CPPFLAGS += -mregparm=3 endif +endif KBUILD_LDFLAGS += --emit-relocs LDFLAGS_FINAL += --gc-sections $(if $(CONFIG_XPL_BUILD),,-pie) diff --git a/arch/x86/cpu/i386/interrupt.c b/arch/x86/cpu/i386/interrupt.c index 6f78b072cde..22241005948 100644 --- a/arch/x86/cpu/i386/interrupt.c +++ b/arch/x86/cpu/i386/interrupt.c @@ -354,9 +354,15 @@ asm(".globl irq_common_entry\n" \ "pushl %esi\n" \ "pushl %edx\n" \ "pushl %ecx\n" \ - "pushl %ebx\n" \ - "mov %esp, %eax\n" \ + "pushl %ebx\n" +#ifdef CONFIG_X86_NO_REGPARM + "pushl %esp\n" \ "call irq_llsr\n" \ + "addl $4, %esp\n" +#else + "mov %esp, %eax\n" \ + "call irq_llsr\n" +#endif "popl %ebx\n" \ "popl %ecx\n" \ "popl %edx\n" \ diff --git a/arch/x86/cpu/start.S b/arch/x86/cpu/start.S index 385a691265e..a9f034d8c7e 100644 --- a/arch/x86/cpu/start.S +++ b/arch/x86/cpu/start.S @@ -134,10 +134,20 @@ car_init_ret: */ #endif /* Set up global data */ +#ifdef CONFIG_X86_NO_REGPARM + push %esp + call board_init_f_alloc_reserve + add $4, %esp + mov %eax, %esp + push %eax + call board_init_f_init_reserve + add $4, %esp +#else mov %esp, %eax call board_init_f_alloc_reserve mov %eax, %esp call board_init_f_init_reserve +#endif #ifdef CONFIG_DEBUG_UART call debug_uart_init @@ -171,10 +181,16 @@ skip_hob: /* Set parameter to board_init_f() to boot flags */ post_code(POST_START_DONE) - xorl %eax, %eax /* Enter, U-Boot! */ +#ifdef CONFIG_X86_NO_REGPARM + push $0 call board_init_f + add $4, %esp +#else + xorl %eax, %eax + call board_init_f +#endif /* indicate (lack of) progress */ movw $0x85, %ax @@ -188,10 +204,13 @@ board_init_f_r_trampoline: * RAM, BSS has been cleared and relocation adjustments have been * made. It is now time to jump into the in-RAM copy of U-Boot * - * %eax = Address of top of new stack + * %eax = Address of top of new stack (or 4(%esp) without regparm) */ /* Stack grows down from top of SDRAM */ +#ifdef CONFIG_X86_NO_REGPARM + movl 4(%esp), %eax +#endif movl %eax, %esp /* See if we need to disable CAR */ diff --git a/arch/x86/cpu/start_from_spl.S b/arch/x86/cpu/start_from_spl.S index abfd4abb623..1d6777c2160 100644 --- a/arch/x86/cpu/start_from_spl.S +++ b/arch/x86/cpu/start_from_spl.S @@ -38,18 +38,34 @@ _start: use_existing_stack: mov %esp, %eax 2: +#ifdef CONFIG_X86_NO_REGPARM + push %eax + call board_init_f_alloc_reserve + add $4, %esp + mov %eax, %esp + push %eax + call board_init_f_init_reserve + add $4, %esp +#else call board_init_f_alloc_reserve mov %eax, %esp call board_init_f_init_reserve +#endif #ifdef CONFIG_DEBUG_UART call debug_uart_init #endif call x86_cpu_reinit_f +#ifdef CONFIG_X86_NO_REGPARM + push $0 + call board_init_f + add $4, %esp +#else xorl %eax, %eax call board_init_f +#endif call board_init_f_r /* Should not return here */ @@ -64,10 +80,13 @@ board_init_f_r_trampoline: * adjustments have been made. It is now time to jump into the in-RAM * copy of U-Boot * - * %eax = Address of top of new stack + * %eax = Address of top of new stack (or 4(%esp) without regparm) */ /* Stack grows down from top of SDRAM */ +#ifdef CONFIG_X86_NO_REGPARM + movl 4(%esp), %eax +#endif movl %eax, %esp /* Re-enter U-Boot by calling board_init_f_r() */ diff --git a/arch/x86/cpu/start_from_tpl.S b/arch/x86/cpu/start_from_tpl.S index 9a4974a5f1b..ca6613217d2 100644 --- a/arch/x86/cpu/start_from_tpl.S +++ b/arch/x86/cpu/start_from_tpl.S @@ -15,6 +15,19 @@ .type _start, @function _start: /* Set up memory using the existing stack */ +#ifdef CONFIG_X86_NO_REGPARM + push %esp + call board_init_f_alloc_reserve + add $4, %esp + mov %eax, %esp + push %eax + call board_init_f_init_reserve + add $4, %esp + + push $0 + call board_init_f + add $4, %esp +#else mov %esp, %eax call board_init_f_alloc_reserve mov %eax, %esp @@ -23,6 +36,7 @@ _start: xorl %eax, %eax call board_init_f +#endif call board_init_f_r /* Should not return here */ @@ -35,10 +49,13 @@ board_init_f_r_trampoline: * TPL has been executed: SDRAM has been initialised, BSS has been * cleared. * - * %eax = Address of top of new stack + * %eax = Address of top of new stack (or 4(%esp) without regparm) */ /* Stack grows down from top of SDRAM */ +#ifdef CONFIG_X86_NO_REGPARM + movl 4(%esp), %eax +#endif movl %eax, %esp /* Re-enter SPL by calling board_init_f_r() */ -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The x86_64 global data pointer currently relies on a writable global_data_ptr variable in the .data section, which needs RAM to be available before relocation. Use MSR_FS_BASE to hold the address of gd->arch.gd_addr instead, mirroring how 32-bit x86 uses the FS segment descriptor base. Remove the global_data_ptr variable from misc.c and update arch_setup_gd() to call set_gd(). Separate the EFI_APP and X86_64 cases in global_data.h so each has its own set_gd() implementation. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/cpu/x86_64/misc.c | 12 ++---------- arch/x86/include/asm/global_data.h | 26 ++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/arch/x86/cpu/x86_64/misc.c b/arch/x86/cpu/x86_64/misc.c index fc449ca4ed6..cb953a92eee 100644 --- a/arch/x86/cpu/x86_64/misc.c +++ b/arch/x86/cpu/x86_64/misc.c @@ -5,21 +5,13 @@ */ #include <init.h> +#include <asm/global_data.h> DECLARE_GLOBAL_DATA_PTR; -/* - * Global declaration of gd. - * - * As we write to it before relocation we have to make sure it is not put into - * a .bss section which may overlap a .rela section. Initialization forces it - * into a .data section which cannot overlap any .rela section. - */ -struct global_data *global_data_ptr = (struct global_data *)~0; - void arch_setup_gd(gd_t *new_gd) { - global_data_ptr = new_gd; + set_gd(new_gd); } int misc_init_r(void) diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h index 0d9fa823121..1b614067f76 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -10,6 +10,7 @@ #ifndef __ASSEMBLY__ #include <linux/types.h> +#include <asm/msr-index.h> #include <asm/processor.h> #include <asm/mrccache.h> #include <asm/u-boot.h> @@ -137,9 +138,8 @@ struct arch_global_data { #include <asm-generic/global_data.h> #ifndef __ASSEMBLY__ -# if defined(CONFIG_EFI_APP) || CONFIG_IS_ENABLED(X86_64) +# if defined(CONFIG_EFI_APP) -/* TODO(sjg@chromium.org): Consider using a fixed register for gd on x86_64 */ #define gd global_data_ptr static inline void set_gd(volatile gd_t *gd_ptr) @@ -166,6 +166,28 @@ static inline notrace gd_t *get_fs_gd_ptr(void) #define gd get_fs_gd_ptr() +#if CONFIG_IS_ENABLED(X86_64) +/* + * On x86_64, use MSR_FS_BASE to hold the address of gd->arch.gd_addr, mirroring + * how 32-bit x86 uses the FS segment descriptor base. This avoids the need for + * a writable global_data_ptr variable, which would require the .data section to + * be in RAM before relocation. + */ +static inline void set_gd(volatile gd_t *gd_ptr) +{ + gd_t *p = (gd_t *)gd_ptr; + unsigned long addr; + + p->arch.gd_addr = p; + addr = (unsigned long)&p->arch.gd_addr; + asm volatile("wrmsr" : : + "c" (MSR_FS_BASE), + "a" ((unsigned int)addr), + "d" ((unsigned int)(addr >> 32)) + : "memory"); +} +#endif + #define DECLARE_GLOBAL_DATA_PTR # endif -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The EFI runtime service SetVirtualAddressMap() and the functions it calls access gd->relocaddr. On architectures where 'gd' is accessed through a CPU register (ARM via r9/x18, RISC-V via tp, x86_64 via MSR_FS_BASE), the register is repurposed by the OS after ExitBootServices(). When the OS later calls SetVirtualAddressMap(), the gd pointer is invalid, causing a page fault. On x86_64, the recent switch to MSR_FS_BASE for the gd pointer means that %fs:0 now reads from the Linux kernel's per-CPU data area instead of U-Boot's global_data, resulting in a bogus relocaddr value and a crash during the EFI virtual-address relocation. Cache gd->relocaddr in an __efi_runtime_data variable during the initial relocation call from board_r.c (when gd is still valid) and use this cached value in all three runtime-time references. This is correct for all architectures since EFI runtime code should not depend on U-Boot's gd after ExitBootServices(). Fix a checkpatch warning while here. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- lib/efi_loader/efi_runtime.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 0a6004f404d..bd09d78d047 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -97,6 +97,7 @@ struct elf_rela { static __efi_runtime_data struct efi_mem_desc *efi_virtmap; static __efi_runtime_data efi_uintn_t efi_descriptor_count; static __efi_runtime_data efi_uintn_t efi_descriptor_size; +static __efi_runtime_data ulong efi_relocaddr; /* * EFI runtime code lives in two stages. In the first stage, U-Boot and an EFI @@ -684,7 +685,7 @@ static __efi_runtime void efi_relocate_runtime_table(ulong offset) void **pos; /* Relocate the runtime services pointers */ - patchoff = offset - gd->relocaddr; + patchoff = offset - efi_relocaddr; for (pos = (void **)&efi_runtime_services.get_time; pos <= (void **)&efi_runtime_services.query_variable_info; ++pos) { if (*pos) @@ -721,6 +722,18 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) if (gd_ulib()) return; + /* + * Cache gd->relocaddr for use by the EFI runtime services after + * the OS has taken over. On architectures where 'gd' is accessed + * through a register (ARM, RISC-V, x86_64), it becomes invalid + * once the OS overwrites that register. + * + * The first call (map == NULL) comes from board_r.c during + * U-Boot init, when gd is still valid. + */ + if (!map) + efi_relocaddr = gd->relocaddr; + #ifdef IS_RELA struct elf_rela *rel = (void *)__efi_runtime_rel_start; #else @@ -734,7 +747,7 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) ulong *p; ulong newaddr; - p = (void*)((ulong)rel->offset - base) + gd->relocaddr; + p = (void *)((ulong)rel->offset - base) + efi_relocaddr; /* * The runtime services table is updated in @@ -910,7 +923,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map( map = (void*)virtmap + (descriptor_size * i); if (map->type == EFI_RUNTIME_SERVICES_CODE) { ulong new_offset = map->virtual_start - - map->physical_start + gd->relocaddr; + map->physical_start + efi_relocaddr; efi_relocate_runtime_table(new_offset); efi_runtime_relocate(new_offset, map); -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The rules for extracting u-boot-x86-start16.bin and u-boot-x86-reset16.bin are x86-specific but live in the top-level Makefile. Move them to arch/x86/Makefile where they belong, dropping the outer ifdef CONFIG_X86 guard since that file is only included for x86 builds. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- Makefile | 10 ---------- arch/x86/Makefile | 8 ++++++++ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index f9a24cf21a0..ee5b54c4c51 100644 --- a/Makefile +++ b/Makefile @@ -1694,16 +1694,6 @@ quiet_cmd_ldr = LD $@ cmd_ldr = $(LD) $(LDFLAGS_$(@F)) \ $(filter-out FORCE,$^) -o $@ -ifdef CONFIG_X86 -OBJCOPYFLAGS_u-boot-x86-start16.bin := -O binary -j .start16 -u-boot-x86-start16.bin: u-boot FORCE - $(call if_changed,objcopy) - -OBJCOPYFLAGS_u-boot-x86-reset16.bin := -O binary -j .resetvec -u-boot-x86-reset16.bin: u-boot FORCE - $(call if_changed,objcopy) - -endif # CONFIG_X86 OBJCOPYFLAGS_u-boot-app.efi := $(OBJCOPYFLAGS_EFI) u-boot-app.efi: u-boot dts/dt.dtb FORCE diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 7dc3171cebf..07e8df15afb 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -25,3 +25,11 @@ head-$(CONFIG_$(PHASE_)X86_16BIT_INIT) += arch/x86/cpu/resetvec.o libs-y += arch/x86/cpu/ libs-y += arch/x86/lib/ + +OBJCOPYFLAGS_u-boot-x86-start16.bin := -O binary -j .start16 +u-boot-x86-start16.bin: u-boot FORCE + $(call if_changed,objcopy) + +OBJCOPYFLAGS_u-boot-x86-reset16.bin := -O binary -j .resetvec +u-boot-x86-reset16.bin: u-boot FORCE + $(call if_changed,objcopy) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> X86_32BIT_INIT is currently selected whenever X86_RESET_VECTOR is set and SPL is not used. This is wrong for 64-bit builds without SPL, which handle the 32-bit-to-64-bit transition in their own startup code rather than using the 32-bit init path. Add !X86_64 to the default condition so that 64-bit builds are excluded. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index a7d616a401d..db3f16a7222 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -208,7 +208,7 @@ config TPL_X86_16BIT_INIT config X86_32BIT_INIT bool depends on X86_RESET_VECTOR - default y if X86_RESET_VECTOR && !SPL + default y if X86_RESET_VECTOR && !SPL && !X86_64 help This is enabled when 32-bit init is in U-Boot proper -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The 64-bit PIE linker cannot handle 16-bit relocations, so start16.o and resetvec.o cannot be linked into the 64-bit u-boot binary. Override the assembler flags to build these objects as 32-bit (elf_i386) when targeting x86_64, and exclude them from head-y since they are handled separately by binman. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/Makefile | 2 ++ arch/x86/cpu/Makefile | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 07e8df15afb..8abedd6e5e9 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -20,8 +20,10 @@ endif endif endif # EFI +ifndef CONFIG_$(PHASE_)X86_64 head-$(CONFIG_$(PHASE_)X86_16BIT_INIT) += arch/x86/cpu/start16.o head-$(CONFIG_$(PHASE_)X86_16BIT_INIT) += arch/x86/cpu/resetvec.o +endif libs-y += arch/x86/cpu/ libs-y += arch/x86/lib/ diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile index 82ae65f141a..71feca3bf29 100644 --- a/arch/x86/cpu/Makefile +++ b/arch/x86/cpu/Makefile @@ -26,6 +26,17 @@ endif extra-$(CONFIG_$(PHASE_)X86_16BIT_INIT) += resetvec.o start16.o +ifdef CONFIG_$(PHASE_)X86_64 +# The 16-bit startup code must be assembled as 32-bit since the 64-bit +# linker cannot handle 16-bit relocations in a PIE binary. These +# objects are not linked into the 64-bit u-boot; instead their sections +# are extracted to binary files for binman to place. +AFLAGS_REMOVE_start16.o := -march=core2 -m64 +AFLAGS_start16.o := -march=i386 -m32 +AFLAGS_REMOVE_resetvec.o := -march=core2 -m64 +AFLAGS_resetvec.o := -march=i386 -m32 +endif + obj-y += cpu.o ifndef CONFIG_TPL_BUILD obj-y += cpu_x86.o -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> On x86_64 with 16-bit init, start16.o and resetvec.o are built as 32-bit objects and cannot be linked into the 64-bit PIE binary. Add a linker script (u-boot-16bit.lds) and Makefile rules to link them into a small 32-bit ELF (u-boot-x86-16bit.elf) with the correct section addresses. The existing objcopy rules then extract the .start16 and .resetvec sections from this ELF instead of from the main u-boot binary. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/Makefile | 27 ++++++++++++++++++++++++++- arch/x86/cpu/u-boot-16bit.lds | 30 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 arch/x86/cpu/u-boot-16bit.lds diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 8abedd6e5e9..955a728e361 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -29,9 +29,34 @@ libs-y += arch/x86/cpu/ libs-y += arch/x86/lib/ OBJCOPYFLAGS_u-boot-x86-start16.bin := -O binary -j .start16 +OBJCOPYFLAGS_u-boot-x86-reset16.bin := -O binary -j .resetvec + +ifneq ($(CONFIG_X86_64)$(CONFIG_X86_16BIT_INIT),yy) +# Normal case: extract 16-bit sections from the u-boot ELF u-boot-x86-start16.bin: u-boot FORCE $(call if_changed,objcopy) -OBJCOPYFLAGS_u-boot-x86-reset16.bin := -O binary -j .resetvec u-boot-x86-reset16.bin: u-boot FORCE $(call if_changed,objcopy) +else +# 64-bit with 16-bit init: the 16-bit objects are built as 32-bit and +# not linked into the 64-bit u-boot. Link them into a small 32-bit +# ELF with the correct addresses so that cross-references (e.g., +# resetvec -> start16 -> _start) are resolved. +u-boot-x86-16bit.lds: $(srctree)/arch/x86/cpu/u-boot-16bit.lds prepare FORCE + $(call if_changed_dep,cpp_lds) + +LDFLAGS_u-boot-x86-16bit.elf := -m elf_i386 -static -nostdlib \ + --defsym=_start=$(CONFIG_TEXT_BASE) + +u-boot-x86-16bit.elf: arch/x86/cpu/start16.o arch/x86/cpu/resetvec.o \ + u-boot-x86-16bit.lds FORCE + $(LD) $(LDFLAGS_u-boot-x86-16bit.elf) -T u-boot-x86-16bit.lds \ + arch/x86/cpu/start16.o arch/x86/cpu/resetvec.o -o $@ + +u-boot-x86-start16.bin: u-boot-x86-16bit.elf FORCE + $(call if_changed,objcopy) + +u-boot-x86-reset16.bin: u-boot-x86-16bit.elf FORCE + $(call if_changed,objcopy) +endif diff --git a/arch/x86/cpu/u-boot-16bit.lds b/arch/x86/cpu/u-boot-16bit.lds new file mode 100644 index 00000000000..048d0f17b4f --- /dev/null +++ b/arch/x86/cpu/u-boot-16bit.lds @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Linker script for 16-bit x86 startup code (start16 + resetvec). + * + * Written by Simon Glass <simon.glass@canonical.com> + * + * Used when building a 64-bit U-Boot with integrated 16-bit init. + * The 16-bit objects are built as 32-bit and linked separately so that + * the 64-bit PIE binary does not see 16-bit relocations. + */ + +#include <config.h> + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS +{ + . = START_16 - RESET_SEG_START; + .start16 : AT (START_16) { + KEEP(*(.start16)); + } + + . = RESET_VEC_LOC - RESET_SEG_START; + .resetvec : AT (RESET_VEC_LOC) { + KEEP(*(.resetvec)); + } + + /DISCARD/ : { *(.note.gnu.property) } +} -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add start_from_32.S which provides a complete startup path from the 16-bit reset vector through to 64-bit board_init_f(). The entry point is .code32, called from start16.S after the 16-to-32-bit transition. It builds identity-mapped page tables with 2MB pages, enables PAE and SSE, then transitions to long mode. The 32-bit section uses position-independent code (call/pop for the instruction pointer) because the 64-bit binary is linked as PIE. The far jump to 64-bit mode uses lret rather than ljmp to avoid PIE-incompatible relocations. The boot GDT is copied from ROM to RAM before use because KVM cannot perform the implicit GDT data read from the ROM region during the far jump that loads the 64-bit CS descriptor. Update the Makefile and Kconfig to select start_from_32.o when X86_64 and X86_16BIT_INIT are both enabled, and skip the ucode-ptr binman entry for x86_64 non-SPL builds. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/Kconfig | 8 +- arch/x86/Makefile | 4 + arch/x86/cpu/Makefile | 4 + arch/x86/cpu/start_from_32.S | 248 +++++++++++++++++++++++++++++ arch/x86/dts/emulation-u-boot.dtsi | 2 +- 5 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 arch/x86/cpu/start_from_32.S diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index db3f16a7222..4fdfe5b90fe 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -44,9 +44,11 @@ config X86_RUN_64BIT_NO_SPL bool "64-bit" select X86_64 help - Build U-Boot as a 64-bit binary without SPL. As U-Boot enters - in 64-bit mode, the assumption is that the silicon is fully - initialized (MP, page tables, etc.). + Build U-Boot as a single 64-bit binary without SPL. If the + board selects X86_RESET_VECTOR, the binary includes 16-bit + and 32-bit startup code that transitions to 64-bit mode + before running any C code. Otherwise U-Boot enters directly + in 64-bit mode (e.g. when launched from coreboot). endchoice diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 955a728e361..e8813aa7e28 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -2,7 +2,11 @@ ifeq ($(CONFIG_EFI_APP),) ifdef CONFIG_$(PHASE_)X86_64 +ifeq ($(CONFIG_$(PHASE_)X86_16BIT_INIT),y) +head-y := arch/x86/cpu/start_from_32.o +else head-y := arch/x86/cpu/start64.o +endif else ifeq ($(CONFIG_$(PHASE_)X86_16BIT_INIT),y) head-y := arch/x86/cpu/start.o diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile index 71feca3bf29..210290364fb 100644 --- a/arch/x86/cpu/Makefile +++ b/arch/x86/cpu/Makefile @@ -7,7 +7,11 @@ # Daniel Engström, Omicron Ceti AB, daniel@omicron.se. ifeq ($(CONFIG_$(PHASE_)X86_64),y) +ifeq ($(CONFIG_$(PHASE_)X86_16BIT_INIT),y) +extra-y = start_from_32.o +else extra-y = start64.o +endif else ifeq ($(CONFIG_$(PHASE_)X86_16BIT_INIT),y) extra-y = start.o diff --git a/arch/x86/cpu/start_from_32.S b/arch/x86/cpu/start_from_32.S new file mode 100644 index 00000000000..197e4398cdb --- /dev/null +++ b/arch/x86/cpu/start_from_32.S @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * 64-bit x86 Startup Code with integrated 32-bit init + * + * Entry point _start is .code32, called from start16.S after the + * 16-to-32-bit transition. This sets up an identity-mapped page table + * and transitions to 64-bit mode before calling into the normal + * board_init_f() flow. + * + * The 32-bit section uses position-independent code (call/pop for the + * instruction pointer) because the 64-bit binary is linked as PIE. + * + * Copyright 2026 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#include <config.h> +#include <asm/msr-index.h> +#include <asm/processor.h> +#include <asm/processor-flags.h> + +/* + * Page-table base address - must be 4KB aligned and below 4GB. + * Uses 24KB total: PML4 (4KB) + PDPT (4KB) + 4 PD tables (4KB each) + */ +#define PT_BASE 0x80000 + +/* ------------------------------------------------------------------ */ + +.section .text.start +.code32 +.globl _start +.type _start, @function +_start: + /* Load the segment registers to match the GDT loaded in start16.S */ + movl $(X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE), %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %gs + movl %eax, %ss + + /* Set up the stack in the CAR/SRAM region */ + movl $(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - 4), %esp + + /* Clear IDT */ + subl $8, %esp + movl $0, 4(%esp) /* base = 0 */ + movw $0, 2(%esp) /* padding */ + movw $0, (%esp) /* limit = 0 */ + lidt (%esp) + addl $8, %esp + + /* + * Get our runtime address into %ebx so we can reference data + * position-independently (the 64-bit binary is linked as PIE) + */ + call 2f +2: popl %ebx + + /* + * Copy the boot GDT from ROM to RAM and load it from there. + * KVM's EPT may not allow data reads from the ROM region, so + * the GDT must be in RAM for the far jump to read the 64-bit + * CS descriptor. + */ +#define GDT_RAM 0x2000 + leal (boot_gdt - 2b)(%ebx), %esi + movl $GDT_RAM, %edi + movl $((boot_gdt_end - boot_gdt) / 4), %ecx + cld + rep movsl + + subl $8, %esp + movl $GDT_RAM, 2(%esp) /* base in RAM */ + movw $(boot_gdt_end - boot_gdt - 1), (%esp) /* limit */ + lgdt (%esp) + addl $8, %esp + + /* + * Build identity-mapped page tables at PT_BASE (maps 4GB with + * 2MB pages). This is similar to build_pagetable() in + * arch/x86/cpu/i386/cpu.c (which also sets the US/A/DT bits) + * but must be done in assembly because page tables are needed + * to enter 64-bit mode and all C code in this build is compiled + * for 64-bit. + * + * Layout (24KB total): + * PT_BASE + 0x0000 PML4 (512 entries, only [0] used) + * PT_BASE + 0x1000 PDPT (512 entries, [0]..[3] used) + * PT_BASE + 0x2000 PD for 0-1GB (512 * 2MB entries) + * PT_BASE + 0x3000 PD for 1-2GB + * PT_BASE + 0x4000 PD for 2-3GB + * PT_BASE + 0x5000 PD for 3-4GB + */ + + /* Zero 24KB */ + movl $PT_BASE, %edi + xorl %eax, %eax + movl $(6 * 4096 / 4), %ecx + rep stosl + + /* PML4[0] -> PDPT */ + movl $(PT_BASE + 0x1000 + 0x03), %eax /* Present + RW */ + movl %eax, PT_BASE + + /* PDPT[0..3] -> four PD tables */ + movl $(PT_BASE + 0x2000 + 0x03), %eax + movl %eax, (PT_BASE + 0x1000 + 0 * 8) + addl $0x1000, %eax + movl %eax, (PT_BASE + 0x1000 + 1 * 8) + addl $0x1000, %eax + movl %eax, (PT_BASE + 0x1000 + 2 * 8) + addl $0x1000, %eax + movl %eax, (PT_BASE + 0x1000 + 3 * 8) + + /* + * Fill the four PD tables (2048 entries total). + * Each entry maps a 2MB page: address | PS(bit7) | RW | P + */ + movl $(PT_BASE + 0x2000), %edi + movl $0x00000083, %eax /* 0MB, PS + RW + P */ + movl $2048, %ecx +1: + movl %eax, (%edi) + movl $0, 4(%edi) /* high 32 bits = 0 */ + addl $0x200000, %eax /* next 2MB page */ + addl $8, %edi + decl %ecx + jnz 1b + + /* + * Transition to 64-bit long mode. This is similar to + * cpu_call64() in arch/x86/cpu/i386/call64.S but uses lret + * instead of ljmp (which would emit a PIE-incompatible + * relocation). It also enables SSE which call64.S does not + * need to do. + */ + + /* Disable paging (should already be off after reset) */ + movl %cr0, %eax + andl $~X86_CR0_PG, %eax + movl %eax, %cr0 + + /* Enable PAE and SSE (x86_64 gcc assumes SSE2 is available) */ + movl %cr4, %eax + orl $(X86_CR4_PAE | X86_CR4_OSFXSR), %eax + movl %eax, %cr4 + + /* Clear CR0.EM so SSE instructions do not fault */ + movl %cr0, %eax + andl $~X86_CR0_EM, %eax + movl %eax, %cr0 + + /* Point CR3 at PML4 */ + movl $PT_BASE, %eax + movl %eax, %cr3 + + /* Enable Long Mode in EFER */ + movl $MSR_EFER, %ecx + rdmsr + btsl $_EFER_LME, %eax + wrmsr + + /* Enable paging -> activates long mode */ + movl %cr0, %eax + orl $X86_CR0_PG, %eax + movl %eax, %cr0 + + /* + * Jump to 64-bit code segment. Use lret to avoid the + * PIE-incompatible relocation that a direct ljmp would emit. + */ + leal (start64 - 2b)(%ebx), %eax + pushl $(X86_GDT_ENTRY_64BIT_CS * X86_GDT_ENTRY_SIZE) + pushl %eax + lret + +/* ------------------------------------------------------------------ */ +.code64 +start64: + /* Set up memory using the existing stack */ + mov %rsp, %rdi + call board_init_f_alloc_reserve + mov %rax, %rsp + + call board_init_f_init_reserve + + xor %rdi, %rdi + call board_init_f + call board_init_f_r + + /* Should not return here */ + jmp . + +.globl board_init_f_r_trampoline64 +.type board_init_f_r_trampoline64, @function +board_init_f_r_trampoline64: + /* + * SDRAM has been initialised, U-Boot code has been copied into + * RAM, BSS has been cleared and relocation adjustments have been + * made. It is now time to jump into the in-RAM copy of U-Boot + * + * %rsi = Address of top of new stack + * %rdi = New gd + */ + + /* Stack grows down from top of SDRAM */ + movq %rsi, %rsp + + /* Re-enter U-Boot by calling board_init_f_r() */ + call board_init_f_r + +/* ------------------------------------------------------------------ */ +/* Data */ +/* ------------------------------------------------------------------ */ + +/* + * Boot GDT - includes valid 32-bit CS/DS entries (matching start16.S's + * selectors 0x10 and 0x18) plus the 64-bit CS at entry 9 (selector + * 0x48, matching U-Boot's standard GDT numbering). + * + * This is copied to RAM before use because KVM cannot perform the + * implicit GDT data read from the ROM region during the far jump + * to 64-bit mode. + * + * When arch_setup_gd() later loads the real GDT the CS selector (0x48) + * remains valid. + */ +.align 16 +boot_gdt: + /* Entry 0: NULL */ + .quad 0 + /* Entry 1: unused (matches start16.S layout) */ + .quad 0 + /* Entry 2: 32-bit code segment (selector 0x10) */ + .quad 0x00cf9b000000ffff + /* Entry 3: 32-bit data segment (selector 0x18) */ + .quad 0x00cf93000000ffff + /* Entries 4-8: unused */ + .fill 5, 8, 0 + + /* Entry 9: 64-bit code segment (selector 0x48) */ + .quad 0x00af9a000000ffff + + /* Entry 10-11: unused (keep GDT same size as real one) */ + .quad 0 + .quad 0 +boot_gdt_end: diff --git a/arch/x86/dts/emulation-u-boot.dtsi b/arch/x86/dts/emulation-u-boot.dtsi index 7245fe51b3b..64d61b207da 100644 --- a/arch/x86/dts/emulation-u-boot.dtsi +++ b/arch/x86/dts/emulation-u-boot.dtsi @@ -12,7 +12,7 @@ u-boot-spl-with-ucode-ptr { optional-ucode; }; -#else +#elif !defined(CONFIG_X86_64) u-boot-with-ucode-ptr { optional-ucode; }; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add a defconfig for QEMU x86_64 that builds a single 64-bit binary with integrated 16-bit and 32-bit startup code, removing the need for SPL. The existing SPL-based qemu-x86_64 build is unaffected. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- board/emulation/qemu-x86/MAINTAINERS | 1 + configs/qemu-x86_64_nospl_defconfig | 82 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 configs/qemu-x86_64_nospl_defconfig diff --git a/board/emulation/qemu-x86/MAINTAINERS b/board/emulation/qemu-x86/MAINTAINERS index efb8b46daaf..77482bc1e57 100644 --- a/board/emulation/qemu-x86/MAINTAINERS +++ b/board/emulation/qemu-x86/MAINTAINERS @@ -11,3 +11,4 @@ S: Maintained F: board/emulation/qemu-x86/ F: board/emulation/common/ F: configs/qemu-x86_64_defconfig +F: configs/qemu-x86_64_nospl_defconfig diff --git a/configs/qemu-x86_64_nospl_defconfig b/configs/qemu-x86_64_nospl_defconfig new file mode 100644 index 00000000000..4452b8c6e0a --- /dev/null +++ b/configs/qemu-x86_64_nospl_defconfig @@ -0,0 +1,82 @@ +CONFIG_X86=y +CONFIG_TEXT_BASE=0xffc00000 +CONFIG_SYS_MALLOC_F_LEN=0x2000 +CONFIG_BLOBLIST_SIZE_RELOC=0x40000 +CONFIG_NR_DRAM_BANKS=8 +CONFIG_ENV_SIZE=0x40000 +CONFIG_MAX_CPUS=2 +CONFIG_DEFAULT_DEVICE_TREE="qemu-x86_q35" +CONFIG_DEBUG_UART_BASE=0x3f8 +CONFIG_DEBUG_UART_CLOCK=1843200 +CONFIG_X86_RUN_64BIT_NO_SPL=y +CONFIG_DEBUG_UART=y +CONFIG_UBOOT_ROMSIZE_KB_4096=y +# CONFIG_HAVE_MICROCODE is not set +CONFIG_SMP=y +CONFIG_GENERATE_PIRQ_TABLE=y +CONFIG_GENERATE_MP_TABLE=y +CONFIG_TARGET_QEMU_X86_64=y +CONFIG_FIT=y +CONFIG_BOOTSTD_FULL=y +CONFIG_BOOTSTAGE=y +CONFIG_BOOTSTAGE_REPORT=y +CONFIG_SHOW_BOOT_PROGRESS=y +CONFIG_USE_BOOTARGS=y +CONFIG_BOOTARGS="root=/dev/sdb3 init=/sbin/init rootwait ro" +CONFIG_BOOTCOMMAND="bootfl scan -lb" +CONFIG_CONSOLE_RECORD=y +# CONFIG_CONSOLE_RECORD_INIT_F is not set +CONFIG_SYS_CONSOLE_INFO_QUIET=y +CONFIG_LOG=y +CONFIG_LOGF_FUNC=y +CONFIG_DISPLAY_BOARDINFO_LATE=y +CONFIG_PCI_INIT_R=y +CONFIG_BLOBLIST_FIXED=y +CONFIG_BLOBLIST_ADDR=0x10000 +CONFIG_CMD_CPU=y +CONFIG_CMD_BOOTEFI_SELFTEST=y +CONFIG_CMD_NVEDIT_EFI=y +CONFIG_CMD_MEM_SEARCH=y +CONFIG_CMD_IDE=y +CONFIG_CMD_SPI=y +CONFIG_CMD_USB=y +CONFIG_CMD_CAT=y +# CONFIG_CMD_SETEXPR is not set +CONFIG_BOOTP_BOOTFILESIZE=y +CONFIG_CMD_EFIDEBUG=y +CONFIG_CMD_TIME=y +CONFIG_CMD_BOOTSTAGE=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_ENV_OVERWRITE=y +CONFIG_ENV_IS_IN_FAT=y +CONFIG_ENV_FAT_INTERFACE="virtio" +CONFIG_ENV_FAT_DEVICE_AND_PART="0:1" +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_USE_BOOTFILE=y +CONFIG_BOOTFILE="bzImage" +CONFIG_TFTP_TSIZE=y +CONFIG_USE_ROOTPATH=y +CONFIG_REGMAP=y +CONFIG_SYSCON=y +CONFIG_SYS_IDE_MAXDEVICE=4 +CONFIG_SYS_ATA_DATA_OFFSET=0 +CONFIG_SYS_ATA_REG_OFFSET=0 +CONFIG_SYS_ATA_ALT_OFFSET=0 +CONFIG_ATAPI=y +CONFIG_LBA48=y +CONFIG_SYS_64BIT_LBA=y +CONFIG_CPU=y +CONFIG_NVME_PCI=y +CONFIG_DM_RNG=y +CONFIG_SYS_NS16550_PORT_MAPPED=y +CONFIG_SPI=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_KEYBOARD=y +CONFIG_CONSOLE_TRUETYPE=y +CONFIG_VIDEO_BOCHS=y +# CONFIG_VIDEO_VESA is not set +CONFIG_CONSOLE_SCROLL_LINES=5 +CONFIG_GENERATE_ACPI_TABLE=y +CONFIG_CMD_DHRYSTONE=y +# CONFIG_GZIP is not set +CONFIG_UNIT_TEST=y -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The x86_64 no-SPL build does not programme MTRRs because gd->arch.has_mtrr is never set to true. In the SPL-based flow, SPL sets up the MTRRs during its init and they persist into U-Boot proper. Without SPL, nothing sets them up. Set has_mtrr in x86_cpu_init_f() for x86_64, and extend the arch_cpu_init() guard in the QEMU board code to also cover X86_16BIT_INIT so that it is called in the no-SPL path. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/cpu/qemu/qemu.c | 4 +++- arch/x86/cpu/x86_64/cpu.c | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index b393205acb3..87e61558e8d 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -108,14 +108,16 @@ void qemu_chipset_init(void) } } -#if CONFIG_IS_ENABLED(X86_32BIT_INIT) +#if CONFIG_IS_ENABLED(X86_32BIT_INIT) || CONFIG_IS_ENABLED(X86_16BIT_INIT) int arch_cpu_init(void) { post_code(POST_CPU_INIT); return x86_cpu_init_f(); } +#endif +#if CONFIG_IS_ENABLED(X86_32BIT_INIT) int checkcpu(void) { return 0; diff --git a/arch/x86/cpu/x86_64/cpu.c b/arch/x86/cpu/x86_64/cpu.c index 25ae92c702f..610155d893c 100644 --- a/arch/x86/cpu/x86_64/cpu.c +++ b/arch/x86/cpu/x86_64/cpu.c @@ -61,6 +61,8 @@ int x86_cpu_reinit_f(void) int x86_cpu_init_f(void) { + gd->arch.has_mtrr = true; + return 0; } -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add a -X/--no-spl flag that selects the qemu-x86_64_nospl board when running with the x86 64-bit configuration. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- scripts/build-qemu | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/build-qemu b/scripts/build-qemu index 46fb14cf8a6..4f77eb88167 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -49,6 +49,8 @@ def parse_args(): help='Run qboot instead of U-Boot') parser.add_argument('-x', '--xpl', action='store_true', help='Use xPL image rather than U-Boot proper') + parser.add_argument('-X', '--no-spl', action='store_true', + help='Use no-SPL build (x86_64 only)') parser.add_argument('-T', '--tkey', action='store_true', help='Enable TKey USB passthrough for testing') parser.add_argument( @@ -162,8 +164,13 @@ class BuildQemu: if args.tkey: # Pass through TKey USB device to QEMU self.qemu_extra.extend(['-device', 'usb-host,vendorid=0x1207,productid=0x8887']) + if args.no_spl and self.helper.bitness != 64: + raise ValueError('-X/--no-spl requires 64-bit mode (cannot be used with -w)') if self.helper.bitness == 64: - self.board = 'qemu-x86_64' + if args.no_spl: + self.board = 'qemu-x86_64_nospl' + else: + self.board = 'qemu-x86_64' self.helper.qemu = 'qemu-system-x86_64' else: raise ValueError(f"Invalid arch '{args.arch}'") -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add CI entries for the new qemu-x86_64_nospl board in both GitLab and Azure pipelines, along with the corresponding test hook configuration files. The test configuration is identical to qemu-x86_64 since both use the same QEMU binary and u-boot.rom BIOS file. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- .azure-pipelines.yml | 3 ++ .gitlab-ci.yml | 6 ++++ .../bin/travis-ci/conf.qemu-x86_64_nospl_na | 30 +++++++++++++++++++ .../u_boot_boardenv_qemu_x86_64_nospl_na.py | 14 +++++++++ 4 files changed, 53 insertions(+) create mode 100644 test/hooks/bin/travis-ci/conf.qemu-x86_64_nospl_na create mode 100644 test/hooks/py/travis-ci/u_boot_boardenv_qemu_x86_64_nospl_na.py diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 2b04cf205c9..5759a78a14b 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -486,6 +486,9 @@ stages: qemu_x86_64: TEST_PY_BD: "qemu-x86_64" TEST_PY_TEST_SPEC: "not sleep" + qemu_x86_64_nospl: + TEST_PY_BD: "qemu-x86_64_nospl" + TEST_PY_TEST_SPEC: "not sleep" qemu_xtensa_dc233c: TEST_PY_BD: "qemu-xtensa-dc233c" TEST_PY_TEST_SPEC: "not sleep and not efi" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 509a2956ecb..0b6ca99a8ba 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -664,6 +664,12 @@ qemu-x86_64 test.py: TEST_PY_TEST_SPEC: "not sleep" <<: *buildman_and_testpy_dfn +qemu-x86_64_nospl test.py: + variables: + TEST_PY_BD: "qemu-x86_64_nospl" + TEST_PY_TEST_SPEC: "not sleep" + <<: *buildman_and_testpy_dfn + qemu-xtensa-dc233c test.py: variables: TEST_PY_BD: "qemu-xtensa-dc233c" diff --git a/test/hooks/bin/travis-ci/conf.qemu-x86_64_nospl_na b/test/hooks/bin/travis-ci/conf.qemu-x86_64_nospl_na new file mode 100644 index 00000000000..f6a1b5e65a7 --- /dev/null +++ b/test/hooks/bin/travis-ci/conf.qemu-x86_64_nospl_na @@ -0,0 +1,30 @@ +# Copyright (c) 2016 Konsulko Group. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +console_impl=qemu +qemu_machine="pc" +qemu_binary="qemu-system-x86_64" +qemu_extra_args="-nographic -cpu qemu64 -netdev user,id=net0,tftp=${UBOOT_TRAVIS_BUILD_DIR} -device e1000,netdev=net0 " +qemu_extra_args+=" -object rng-random,filename=/dev/random,id=rng0" +qemu_extra_args+=" -device virtio-rng-pci,rng=rng0,max-bytes=1024,period=1000" + +qemu_kernel_args="-bios ${U_BOOT_BUILD_DIR}/u-boot.rom" +reset_impl=none +flash_impl=none diff --git a/test/hooks/py/travis-ci/u_boot_boardenv_qemu_x86_64_nospl_na.py b/test/hooks/py/travis-ci/u_boot_boardenv_qemu_x86_64_nospl_na.py new file mode 100644 index 00000000000..b1541596630 --- /dev/null +++ b/test/hooks/py/travis-ci/u_boot_boardenv_qemu_x86_64_nospl_na.py @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0+ + +import os +import travis_tftp + +env__net_uses_pci = True +env__net_dhcp_server = True +env__net_tftp_readable_file = travis_tftp.file2env('u-boot') +env__efi_loader_helloworld_file = travis_tftp.file2env('lib/efi_loader/helloworld.efi') +env__efi_loader_check_smbios = True +env__efi_loader_grub_file = travis_tftp.file2env('grub_x64.efi') +env__efi_fit_tftp_file = { + "dn" : os.environ['UBOOT_TRAVIS_BUILD_DIR'], +} -- 2.43.0
participants (1)
-
Simon Glass