diff options
author | Ahmad Fatoum <a.fatoum@pengutronix.de> | 2022-01-14 09:42:25 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2022-01-17 08:10:10 +0100 |
commit | 880c9803b95a08520359c49285f57af874ee109f (patch) | |
tree | 26dfeb600a37a4be14fe0150870e153d4809dabf /arch/arm | |
parent | daafe431e65e65d98f388f77673810bed93b58f8 (diff) | |
download | barebox-880c9803b95a08520359c49285f57af874ee109f.tar.gz barebox-880c9803b95a08520359c49285f57af874ee109f.tar.xz |
ARM: implement ENTRY_FUNCTION_WITHSTACK
The point of ENTRY_FUNCTION is to write the entry point in C. Due to
lack of __naked on ARM64, the start of the entry point will have prologue
using stack and it's not possible to set up the stack safely without
branching into non-inline assembly[0]. On ARM32, where we got __naked, we
have the potential for a different problem: If BootROM sets up stack for
us and we branch to a naked function, which doesn't set up its own
stack, compiler may decide to spill local variables overwriting
instructions it had already run[1].
For code reuse between ARM and ARM64, it would be nice to use the same
entry point structure for both. Currently, the only way is to write it
in non-inline assembly using the ENTRY_PROC macro.
This introduces another way: the ARM64 barebox header has enough space
for 8 instructions of which 5 are unused (2 instructions compiler prologue
+ 1 instruction to jump after the header), we could place a stack setup
routine there to avoid having to write a separate assembly file.
For ARM32, we just call arm_setup_stack and branch out directly after,
freeing board porters of the burden of getting it right.
Add a new ENTRY_FUNCTION_WITHSTACK to realize this.
[0]: 76bced6fe146 ("ARM: document arm_setup_stack() pitfalls"),
[1]: b51b15ba1738 ("RISC-V: board-dt-2nd: move low level init into nonnaked function")
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Link: https://lore.barebox.org/20220114084227.308804-1-a.fatoum@pengutronix.de
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/include/asm/barebox-arm-head.h | 6 | ||||
-rw-r--r-- | arch/arm/include/asm/barebox-arm.h | 47 | ||||
-rw-r--r-- | arch/arm/lib/pbl.lds.S | 1 |
3 files changed, 49 insertions, 5 deletions
diff --git a/arch/arm/include/asm/barebox-arm-head.h b/arch/arm/include/asm/barebox-arm-head.h index 187d12c9fc..3e8da0da7c 100644 --- a/arch/arm/include/asm/barebox-arm-head.h +++ b/arch/arm/include/asm/barebox-arm-head.h @@ -44,14 +44,10 @@ static inline void __barebox_arm_head(void) "1: b 1b\n" #endif #else + /* 5 instructions added by ENTRY_FUNCTION */ /* two instruction long function prologue */ /* only use if stack is initialized! */ "b 2f\n" - "nop\n" - "nop\n" - "nop\n" - "nop\n" - "nop\n" #endif ".asciz \"barebox\"\n" #ifdef CONFIG_CPU_32 diff --git a/arch/arm/include/asm/barebox-arm.h b/arch/arm/include/asm/barebox-arm.h index cfb5943f33..2fbdaaa356 100644 --- a/arch/arm/include/asm/barebox-arm.h +++ b/arch/arm/include/asm/barebox-arm.h @@ -18,6 +18,7 @@ #include <linux/types.h> #include <linux/compiler.h> #include <asm/barebox-arm-head.h> +#include <asm/common.h> #include <asm/sections.h> /* @@ -161,6 +162,51 @@ static inline unsigned long arm_mem_barebox_image(unsigned long membase, } } +#ifdef CONFIG_CPU_64 + +#define ____emit_entry_prologue(instr, ...) do { \ + static __attribute__ ((unused,section(".text_head_prologue"))) \ + const u32 __entry_prologue[] = {(instr), ##__VA_ARGS__}; \ + barrier_data(__entry_prologue); \ +} while(0) + +#define __emit_entry_prologue(instr1, instr2, instr3, instr4, instr5) \ + ____emit_entry_prologue(instr1, instr2, instr3, instr4, instr5) + +#define __ARM_SETUP_STACK(stack_top) \ + __emit_entry_prologue(0x14000002 /* b pc+0x8 */, \ + stack_top /* 32-bit literal */, \ + 0x18ffffe9 /* ldr w9, top */, \ + 0xb4000049 /* cbz x9, pc+0x8 */, \ + 0x9100013f /* mov sp, x9 */) +#else +#define __ARM_SETUP_STACK(stack_top) if (stack_top) arm_setup_stack(stack_top) +#endif + +/* + * Unlike ENTRY_FUNCTION, this can be used to setup stack for a C entry + * point on both ARM32 and ARM64. ENTRY_FUNCTION on ARM64 can only be used + * if preceding boot stage has initialized the stack pointer. + * + * Stack top of 0 means stack is already set up. In that case, the follow-up + * code block will not be inlined and may spill to stack right away. + */ +#define ENTRY_FUNCTION_WITHSTACK(name, stack_top, arg0, arg1, arg2) \ + void name(ulong r0, ulong r1, ulong r2); \ + \ + static void __##name(ulong, ulong, ulong); \ + \ + void NAKED __section(.text_head_entry_##name) name \ + (ulong r0, ulong r1, ulong r2) \ + { \ + __barebox_arm_head(); \ + __ARM_SETUP_STACK(stack_top); \ + __##name(r0, r1, r2); \ + } \ + static void noinline __##name \ + (ulong arg0, ulong arg1, ulong arg2) + + #define ENTRY_FUNCTION(name, arg0, arg1, arg2) \ void name(ulong r0, ulong r1, ulong r2); \ \ @@ -170,6 +216,7 @@ static inline unsigned long arm_mem_barebox_image(unsigned long membase, (ulong r0, ulong r1, ulong r2) \ { \ __barebox_arm_head(); \ + __ARM_SETUP_STACK(0); \ __##name(r0, r1, r2); \ } \ static void NAKED noinline __##name \ diff --git a/arch/arm/lib/pbl.lds.S b/arch/arm/lib/pbl.lds.S index 0a0fb8b5ac..e77b3220fc 100644 --- a/arch/arm/lib/pbl.lds.S +++ b/arch/arm/lib/pbl.lds.S @@ -31,6 +31,7 @@ SECTIONS .text : { _stext = .; + *(.text_head_prologue*) *(.text_head_entry*) __bare_init_start = .; *(.text_bare_init*) |