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