[Concept,01/12] x86: Allow disabling the regparm calling-convention

Message ID 20260211143309.1183113-2-sjg@u-boot.org
State New
Headers
Series x86: Add single 64-bit U-Boot without SPL for QEMU |

Commit Message

Simon Glass Feb. 11, 2026, 2:32 p.m. UTC
  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(-)
  

Patch

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() */