mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [RFC PATCH 00/17] Switch RISC-V to declarative entry points
@ 2026-10-27  7:24 Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 01/17] asm-generic: add registers.h with a pc_inside() helper Ahmad Fatoum
                   ` (16 more replies)
  0 siblings, 17 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr

Every board currently open-codes its PBL entry point: set up the stack,
relocate, call setup_c(), do the lowlevel init and so on, all in C code
that runs before the C environment is ready.

This is error-prone, because board code authors need to be aware of some
very subtle details: global_variable_offset vs get_runtime_offset,
not accessing global variables before relocation, using noinline
functions as compiler barriers, no spilling to stack when ENTRY_FUNCTION
is used with no stack set up...

For the last point, we already had a solution in the form of
ENTRY_FUNCTION_WITHSTACK, which takes a stack pointer and places it
into a well-known location that a generic assembly entry point can read
and use to set up the stack before branching into C code.

That approach is not generic enough however: It only accepts a single
stack pointer when we really want one per execution level and even on
ARM32, it's still implemented using __naked, which precludes use with
clang as clang doesn't allow arbitrary C code there.

Address all of this by generalizing the idea behind
ENTRY_FUNCTION_WITHSTACK into a new ENTRY_POINT macro:
Instead of a board having one board-specific entry point function and a
single stack, it now has a struct of different callbacks and metadata:

struct pbl_stage {
	unsigned long stacktop;		/* stack top or PBL_STACKTOP_* */
	unsigned long relocaddr;	/* 0: don't relocate to specific addr */

	/*
	 * Memory the stage runs from: barebox proper starts out with
	 * mem_minsz, mem_maxsz (default: mem_minsz) is only used to find
	 * the stage by PC.
	 */
	unsigned long mem_start;
	unsigned long mem_minsz;
	unsigned long mem_maxsz;

	void *board_data;

	pbl_board_fn_t prereloc_cpu_init;
	pbl_board_fn_t soc_init;
	void (*board_init)(unsigned long entry_data);
	void (*soc_chainload)(void *board_data);
};

struct pbl_board_info {
	unsigned int soc_type;		/* see pbl_set_soc_type() */

	/* Called without a stack, so it must not spill */
	pbl_locate_pc_fn_t assembly_locate_pc;

	struct pbl_stage rom_stage;	/* PBL_STAGE_ROM */
	struct pbl_stage first_stage;	/* PBL_STAGE_OCRAM */
	struct pbl_stage second_stage;	/* PBL_STAGE_DRAM */
};

A board can now fill this out, define its own callbacks. Common
functionality like the soc_chainload can be shared between boards of the
same SoC. The hairy parts that can not be robustly done in C can be
defined declaratively as data that will be acted upon by the assembly
code. Code that must be written in assembly or run pre-relocation is
explicitly marked as such.

This can get quite verbose, so the idea is that SoC support will define
a macro with DEFAULTS that fills out the common bits that can be
extended (and overridden if needed) by the board code. See the last few
patches for how this looks like.

To make discussion easier, I am switching over all of RISC-V first in
this series. If we reach consensus and this is merged, I will follow-up
by doing the same on ARM.

This applies on top of next (PR #93 is prerequisite)

Ahmad Fatoum (17):
  asm-generic: add registers.h with a pc_inside() helper
  RISC-V: add call_with_stack() helper
  RISC-V: implement relocate_to_adr_full()
  PBL: add SoC type declaration and handoff support
  PBL: introduce board info based entry point framework
  PBL: describe the console in the board info and report the stage
  RISC-V: make barebox_riscv_entry() non-naked
  RISC-V: add PBL heads for the board info stage dispatch
  RISC-V: board-dt-2nd: migrate to board info entry point
  RISC-V: hifive: migrate to board info entry point
  RISC-V: allow relocating the PBL more than once
  RISC-V: nmon: preserve s1-s3 for the caller
  RISC-V: beaglev: migrate to board info entry point
  RISC-V: allwinner-d1: migrate to board info entry point
  RISC-V: erizo: migrate to board info entry point
  RISC-V: litex-linux: migrate to board info entry point
  Documentation: porting: describe the board info entry point

 Documentation/devel/porting.rst           | 124 +++++++++++-
 Documentation/devel/troubleshooting.rst   |   6 +
 arch/Kconfig                              |  28 +++
 arch/riscv/Kconfig                        |   2 +
 arch/riscv/boards/allwinner-d1/lowlevel.c |  18 +-
 arch/riscv/boards/beaglev/lowlevel.c      |  30 ++-
 arch/riscv/boards/erizo/lowlevel.c        |  32 +--
 arch/riscv/boards/hifive/lowlevel.c       |  30 +--
 arch/riscv/boards/litex-linux/lowlevel.c  |  30 +--
 arch/riscv/boot/Makefile                  |   4 +-
 arch/riscv/boot/board-dt-2nd-head.S       |  36 ++++
 arch/riscv/boot/board-dt-2nd.c            |  89 ++++-----
 arch/riscv/boot/entry.c                   |  33 +---
 arch/riscv/boot/entry_ll.S                |  15 ++
 arch/riscv/boot/head.S                    | 198 +++++++++++++++++++
 arch/riscv/boot/nmon_entry.S              |   7 +
 arch/riscv/include/asm/barebox-riscv.h    |  36 +++-
 arch/riscv/include/asm/reloc.h            |   1 +
 arch/riscv/lib/Makefile                   |   1 +
 arch/riscv/lib/asm-offsets.c              |  27 +++
 arch/riscv/lib/call_with_stack.S          |  12 ++
 arch/riscv/lib/pbl.lds.S                  |  13 +-
 arch/riscv/lib/reloc.c                    |   6 +-
 arch/riscv/lib/setupc.S                   |  16 +-
 include/asm-generic/registers.h           |  33 ++++
 include/linux/compiler-clang.h            |  21 ++
 include/linux/compiler-gcc.h              |   3 +
 include/linux/compiler_types.h            |  10 +
 include/mach/sifive/entry.h               |  32 +++
 include/pbl.h                             |   6 +
 include/pbl/entry.h                       | 175 ++++++++++++++++
 include/pbl/handoff-data.h                |  15 ++
 include/pbl/soc-type.h                    | 170 ++++++++++++++++
 pbl/Makefile                              |   2 +
 pbl/entry.c                               | 230 ++++++++++++++++++++++
 pbl/handoff-data.c                        |   2 +
 pbl/soc-type.c                            |  71 +++++++
 37 files changed, 1418 insertions(+), 146 deletions(-)
 create mode 100644 arch/riscv/boot/board-dt-2nd-head.S
 create mode 100644 arch/riscv/boot/entry_ll.S
 create mode 100644 arch/riscv/boot/head.S
 create mode 100644 arch/riscv/lib/call_with_stack.S
 create mode 100644 include/asm-generic/registers.h
 create mode 100644 include/mach/sifive/entry.h
 create mode 100644 include/pbl/entry.h
 create mode 100644 include/pbl/soc-type.h
 create mode 100644 pbl/entry.c
 create mode 100644 pbl/soc-type.c

-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 01/17] asm-generic: add registers.h with a pc_inside() helper
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 02/17] RISC-V: add call_with_stack() helper Ahmad Fatoum
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

The upcoming board info stage dispatch determines the stage it runs in
by checking which stage's memory region covers the program counter.

Add asm-generic/registers.h with pc_inside(), built on the get_pc() that
ARM, RISC-V and PowerPC provide, as the home for such register level
helpers.

Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 include/asm-generic/registers.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 include/asm-generic/registers.h

diff --git a/include/asm-generic/registers.h b/include/asm-generic/registers.h
new file mode 100644
index 000000000000..8ce28f74e1db
--- /dev/null
+++ b/include/asm-generic/registers.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2026 Ahmad Fatoum */
+
+#ifndef __ASM_GENERIC_REGISTERS_H
+#define __ASM_GENERIC_REGISTERS_H
+
+#include <linux/compiler.h>
+#include <asm/common.h>
+
+/*
+ * pc_inside - check if the program counter is in [start, start + size)
+ *
+ * A single unsigned compare, so it also works for a region ending at the
+ * top of the address space.
+ */
+static __always_inline bool pc_inside(unsigned long start, unsigned long size)
+{
+	unsigned long pc = get_pc();
+
+	return pc - start < size;
+}
+
+#endif
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 02/17] RISC-V: add call_with_stack() helper
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 01/17] asm-generic: add registers.h with a pc_inside() helper Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 03/17] RISC-V: implement relocate_to_adr_full() Ahmad Fatoum
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

Switching stacks in C requires inline assembly inside a naked function
and even then, the compiler is free to keep using the old stack pointer.

Let's add call_with_stack() instead, which takes the new stack top, a
function and two arguments for it. Everything is passed in registers, so
it's safe to call before a stack has been set up, as the upcoming PBL
stage dispatch will do.

The prototype goes into asm-generic/registers.h, as other architectures
will implement the same helper.

Assisted-by: Claude:fable-5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/lib/Makefile          |  1 +
 arch/riscv/lib/call_with_stack.S | 12 ++++++++++++
 include/asm-generic/registers.h  | 10 ++++++++++
 3 files changed, 23 insertions(+)
 create mode 100644 arch/riscv/lib/call_with_stack.S

diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index 693248080070..13c7c34a487c 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -4,6 +4,7 @@ extra-y += barebox.lds
 
 obj-y += dtb.o
 obj-pbl-y += sections.o setupc.o reloc.o sections.o runtime-offset.o
+obj-pbl-y += call_with_stack.o
 obj-$(CONFIG_ARCH_HAS_SJLJ) += setjmp.o longjmp.o
 obj-$(CONFIG_RISCV_OPTIMZED_STRING_FUNCTIONS) += memcpy.o memset.o memmove.o
 obj-$(CONFIG_RISCV_SBI) += sbi.o
diff --git a/arch/riscv/lib/call_with_stack.S b/arch/riscv/lib/call_with_stack.S
new file mode 100644
index 000000000000..f3093d062a59
--- /dev/null
+++ b/arch/riscv/lib/call_with_stack.S
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <linux/linkage.h>
+
+.section .text.call_with_stack
+ENTRY(call_with_stack)
+	mv	t1, a1
+	mv	sp, a0
+	mv	a0, a2
+	mv	a1, a3
+	jr	t1
+ENDPROC(call_with_stack)
diff --git a/include/asm-generic/registers.h b/include/asm-generic/registers.h
index 8ce28f74e1db..ec85476019c7 100644
--- a/include/asm-generic/registers.h
+++ b/include/asm-generic/registers.h
@@ -20,4 +20,14 @@ static __always_inline bool pc_inside(unsigned long start, unsigned long size)
 	return pc - start < size;
 }
 
+/*
+ * call_with_stack - switch to the stack ending at stack_top and call fn
+ *
+ * stack_top must be aligned as the architecture requires and fn must not
+ * return. No memory is accessed, so there needn't be a stack yet.
+ */
+void __noreturn call_with_stack(unsigned long stack_top,
+				void (*fn)(unsigned long, unsigned long),
+				unsigned long arg0, unsigned long arg1);
+
 #endif
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 03/17] RISC-V: implement relocate_to_adr_full()
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 01/17] asm-generic: add registers.h with a pc_inside() helper Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 02/17] RISC-V: add call_with_stack() helper Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 04/17] PBL: add SoC type declaration and handoff support Ahmad Fatoum
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

In preparation for the new common PBL entry code that will make
use of relocate_to_adr_full(), implement it for RISC-V as on ARM.

Unlike relocate_to_adr(), which stops copying at __bss_start, the _full
variant copies up to __image_end, so barebox proper can still be
decompressed after the move.

Assisted-by: Claude:opus-5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/include/asm/reloc.h |  1 +
 arch/riscv/lib/setupc.S        | 16 ++++++++++++----
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/arch/riscv/include/asm/reloc.h b/arch/riscv/include/asm/reloc.h
index 9a59326cad27..6b921ff702d7 100644
--- a/arch/riscv/include/asm/reloc.h
+++ b/arch/riscv/include/asm/reloc.h
@@ -7,6 +7,7 @@ unsigned long get_runtime_offset(void);
 
 void relocate_to_current_adr(void);
 void relocate_to_adr(unsigned long target);
+void relocate_to_adr_full(unsigned long target);
 
 void setup_c(void);
 
diff --git a/arch/riscv/lib/setupc.S b/arch/riscv/lib/setupc.S
index 938c78350c7c..68924f2f94df 100644
--- a/arch/riscv/lib/setupc.S
+++ b/arch/riscv/lib/setupc.S
@@ -39,13 +39,22 @@ bss_cleared:
 
 /*
  * void relocate_to_adr(unsigned long targetadr)
+ * void relocate_to_adr_full(unsigned long targetadr)
  *
  * Copy binary to targetadr, relocate code and continue
- * executing at new address.
+ * executing at new address. The _full variant takes the piggy data
+ * along, so barebox proper can still be decompressed afterwards.
  */
 .section .text.relocate_to_adr
-ENTRY(relocate_to_adr)
 					/* a0: target address */
+ENTRY(relocate_to_adr_full)
+	lla	a3, __image_end		/* include piggy data */
+	j	1f
+ENDPROC(relocate_to_adr_full)
+
+ENTRY(relocate_to_adr)
+	lla	a3, __bss_start		/* skip piggy data */
+1:
 	addi	sp, sp, -SZREG * 2
 	lla	a1, _text		/* a1: source address */
 
@@ -61,8 +70,7 @@ ENTRY(relocate_to_adr)
 	add	a2, a2, a0
 	REG_S	a2, (sp)
 
-	lla	a2, __bss_start
-	sub	a2, a2, a1		 /* a2: size */
+	sub	a2, a3, a1		 /* a2: size */
 
 	jal	__memcpy
 
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 04/17] PBL: add SoC type declaration and handoff support
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 03/17] RISC-V: implement relocate_to_adr_full() Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 05/17] PBL: introduce board info based entry point framework Ahmad Fatoum
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

A PBL often supports multiple SoC variants, but barebox proper has no
cheap way to find out which one it's running on before the device tree
is available.

Let's add an enum soc_type, which the upcoming entry point framework
will declare per entry point and hand off to barebox proper, where
barebox_soc_type() returns it. Images not built for a particular SoC,
like barebox-dt-2nd.img, use the SOC_TYPE_GENERIC_DT/EFI pseudo types.

A PBL that only detects the SoC at runtime can still update the type
with pbl_set_soc_type() and barebox proper complains if an entry point
forgot to set it.

Assisted-by: Claude:fable-5.1
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 include/pbl/handoff-data.h |  15 ++++
 include/pbl/soc-type.h     | 170 +++++++++++++++++++++++++++++++++++++
 pbl/Makefile               |   1 +
 pbl/handoff-data.c         |   2 +
 pbl/soc-type.c             |  71 ++++++++++++++++
 5 files changed, 259 insertions(+)
 create mode 100644 include/pbl/soc-type.h
 create mode 100644 pbl/soc-type.c

diff --git a/include/pbl/handoff-data.h b/include/pbl/handoff-data.h
index 81cb0880ad4a..4b9ed31ed94b 100644
--- a/include/pbl/handoff-data.h
+++ b/include/pbl/handoff-data.h
@@ -15,6 +15,7 @@ struct handoff_data {
 #define HANDOFF_DATA_ARM_MACHINE	HANDOFF_DATA_BAREBOX(3)
 #define HANDOFF_DATA_EFI		HANDOFF_DATA_BAREBOX(4)
 #define HANDOFF_DATA_TEE_DT_OVL		HANDOFF_DATA_BAREBOX(5)
+#define HANDOFF_DATA_SOC_TYPE		HANDOFF_DATA_BAREBOX(6)
 
 #define HANDOFF_DATA_BOARD(n)		(0x951726fb + (n))
 
@@ -66,4 +67,18 @@ static inline void *handoff_data_get_entry(unsigned int cookie, size_t *size)
 #endif
 void handoff_data_add_dt(void *fdt);
 
+#ifdef __PBL__
+/*
+ * The stage dispatch hands off the type declared in the board info. An
+ * image running on more than one SoC may still override it later.
+ */
+void pbl_set_soc_type(unsigned int type);
+unsigned int pbl_get_soc_type(void);
+void pbl_handoff_soc_type(void);
+#elif defined(CONFIG_PBL_IMAGE)
+unsigned int barebox_soc_type(void);
+#else
+static inline unsigned int barebox_soc_type(void) { return 0; }
+#endif
+
 #endif /* __PBL_HANDOFF_DATA_H */
diff --git a/include/pbl/soc-type.h b/include/pbl/soc-type.h
new file mode 100644
index 000000000000..be71cdd4338a
--- /dev/null
+++ b/include/pbl/soc-type.h
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#if !defined(__PBL_SOC_TYPE_H) || defined(SOC_TYPE)
+#define __PBL_SOC_TYPE_H
+
+#ifndef SOC_TYPE
+#define __PBL_SOC_TYPE_ENUM
+enum soc_type {
+#define SOC_TYPE(t)	SOC_TYPE_##t,
+#endif
+
+/*
+ * List of SoCs an image can be built for. Images not built for
+ * a particular SoC declare one of the generic pseudo types instead.
+ */
+
+SOC_TYPE(UNKNOWN)
+
+/* pseudo SoC types of the generic images */
+SOC_TYPE(GENERIC_DT)
+SOC_TYPE(GENERIC_EFI)
+
+/* Allwinner */
+SOC_TYPE(SUN20I_D1)
+SOC_TYPE(SUN50I_A64)
+
+/* Altera/Intel SoCFPGA */
+SOC_TYPE(CYCLONE5)
+SOC_TYPE(ARRIA10)
+SOC_TYPE(AGILEX5)
+
+/* Arm development platforms */
+SOC_TYPE(VERSATILEPB)
+SOC_TYPE(VEXPRESS_CA9)
+SOC_TYPE(VEXPRESS_CA15)
+
+/* Broadcom */
+SOC_TYPE(BCM2835)
+SOC_TYPE(BCM2836)
+SOC_TYPE(BCM2837)
+SOC_TYPE(BCM2711)
+SOC_TYPE(BCM2712)
+SOC_TYPE(BCM47XX)
+
+/* Cirrus Logic */
+SOC_TYPE(CLPS711X)
+
+/* Ingenic */
+SOC_TYPE(JZ4755)
+SOC_TYPE(JZ4780)
+
+/* Loongson */
+SOC_TYPE(LS1B)
+
+/* Marvell */
+SOC_TYPE(ARMADA_370)
+SOC_TYPE(ARMADA_38X)
+SOC_TYPE(ARMADA_XP)
+SOC_TYPE(DOVE)
+SOC_TYPE(KIRKWOOD)
+SOC_TYPE(PXA300)
+SOC_TYPE(PXA310)
+
+/* Microchip/Atmel */
+SOC_TYPE(AT91RM9200)
+SOC_TYPE(AT91SAM9260)
+SOC_TYPE(AT91SAM9261)
+SOC_TYPE(AT91SAM9263)
+SOC_TYPE(AT91SAM9G10)
+SOC_TYPE(AT91SAM9G20)
+SOC_TYPE(AT91SAM9G45)
+SOC_TYPE(AT91SAM9N12)
+SOC_TYPE(AT91SAM9X5)
+SOC_TYPE(SAM9X60)
+SOC_TYPE(SAMA5D2)
+SOC_TYPE(SAMA5D3)
+SOC_TYPE(SAMA5D4)
+SOC_TYPE(SAMA7G5)
+
+/* MIPS Technologies */
+SOC_TYPE(MALTA)
+
+/* NVIDIA */
+SOC_TYPE(TEGRA20)
+SOC_TYPE(TEGRA30)
+SOC_TYPE(TEGRA124)
+
+/* NXP i.MX */
+SOC_TYPE(IMX1)
+SOC_TYPE(IMX21)
+SOC_TYPE(IMX23)
+SOC_TYPE(IMX25)
+SOC_TYPE(IMX27)
+SOC_TYPE(IMX28)
+SOC_TYPE(IMX31)
+SOC_TYPE(IMX35)
+SOC_TYPE(IMX50)
+SOC_TYPE(IMX51)
+SOC_TYPE(IMX53)
+SOC_TYPE(IMX6QD)
+SOC_TYPE(IMX6SDL)
+SOC_TYPE(IMX6SL)
+SOC_TYPE(IMX6SX)
+SOC_TYPE(IMX6UL)
+SOC_TYPE(IMX6ULL)
+SOC_TYPE(IMX7)
+SOC_TYPE(IMX8MM)
+SOC_TYPE(IMX8MN)
+SOC_TYPE(IMX8MP)
+SOC_TYPE(IMX8MQ)
+SOC_TYPE(IMX93)
+SOC_TYPE(VF610)
+
+/* NXP Layerscape */
+SOC_TYPE(LS1021A)
+SOC_TYPE(LS1028A)
+SOC_TYPE(LS1046A)
+
+/* Qualcomm Atheros */
+SOC_TYPE(AR231X)
+SOC_TYPE(AR9331)
+SOC_TYPE(AR9344)
+SOC_TYPE(QCA4531)
+
+/* Rockchip */
+SOC_TYPE(RK3188)
+SOC_TYPE(RK3288)
+SOC_TYPE(RK3562)
+SOC_TYPE(RK3568)
+SOC_TYPE(RK3576)
+SOC_TYPE(RK3588)
+
+/* Samsung */
+SOC_TYPE(EXYNOS8895)
+SOC_TYPE(EXYNOS990)
+
+/* SiFive */
+SOC_TYPE(FU540)
+SOC_TYPE(FU740)
+
+/* StarFive */
+SOC_TYPE(JH7100)
+
+/* STMicroelectronics */
+SOC_TYPE(STM32MP13)
+SOC_TYPE(STM32MP15)
+
+/* Texas Instruments */
+SOC_TYPE(AM33XX)
+SOC_TYPE(AM35XX)
+SOC_TYPE(AM62X)
+SOC_TYPE(AM62LX)
+SOC_TYPE(OMAP3)
+SOC_TYPE(OMAP4)
+
+/* Xilinx/AMD */
+SOC_TYPE(ZYNQ)
+SOC_TYPE(ZYNQMP)
+
+/* FPGA soft SoCs */
+SOC_TYPE(ERIZO)
+SOC_TYPE(LITEX)
+
+
+#ifdef __PBL_SOC_TYPE_ENUM
+#undef SOC_TYPE
+};
+#undef __PBL_SOC_TYPE_ENUM
+#endif
+
+#endif /* __PBL_SOC_TYPE_H */
diff --git a/pbl/Makefile b/pbl/Makefile
index 140781bfd906..5d7c9b06015d 100644
--- a/pbl/Makefile
+++ b/pbl/Makefile
@@ -13,5 +13,6 @@ pbl-$(CONFIG_LIBFDT) += fdt.o
 pbl-$(CONFIG_PBL_CONSOLE) += console.o
 obj-pbl-y += handoff-data.o
 obj-pbl-$(CONFIG_PBL_IMAGE_ELF) += layout.o
+obj-pbl-y += soc-type.o
 obj-pbl-$(CONFIG_MMU) += mmu.o
 obj-$(CONFIG_PBL_IMAGE_ELF) += barebox-proper.o
diff --git a/pbl/handoff-data.c b/pbl/handoff-data.c
index f45f4d7a78ce..3bdd36716b4e 100644
--- a/pbl/handoff-data.c
+++ b/pbl/handoff-data.c
@@ -205,6 +205,8 @@ static const char *handoff_data_entry_name(struct handoff_data_entry *hde)
 		return "ARM machine number";
 	case HANDOFF_DATA_TEE_DT_OVL:
 		return "handoff TEE FDTO";
+	case HANDOFF_DATA_SOC_TYPE:
+		return "handoff SoC type";
 	default:
 		sprintf(name, "handoff %08x", hde->cookie);
 		return name;
diff --git a/pbl/soc-type.c b/pbl/soc-type.c
new file mode 100644
index 000000000000..4b035a3cecbd
--- /dev/null
+++ b/pbl/soc-type.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <init.h>
+#include <linux/printk.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <pbl/soc-type.h>
+#include <pbl/handoff-data.h>
+
+#ifdef __PBL__
+
+static unsigned int __pbl_soc_type __section(.data);
+
+void pbl_set_soc_type(unsigned int type)
+{
+	__pbl_soc_type = type;
+}
+
+unsigned int pbl_get_soc_type(void)
+{
+	return __pbl_soc_type;
+}
+
+/*
+ * The handoff entry references the variable itself, so pbl_set_soc_type()
+ * still takes effect until the handoff data is moved out of the PBL.
+ */
+void pbl_handoff_soc_type(void)
+{
+	size_t size;
+
+	/* Firmware returning into the PBL could add the entry twice */
+	if (handoff_data_get_entry(HANDOFF_DATA_SOC_TYPE, &size))
+		return;
+
+	handoff_data_add(HANDOFF_DATA_SOC_TYPE,
+			 &__pbl_soc_type, sizeof(__pbl_soc_type));
+}
+
+#else /* barebox proper */
+
+unsigned int barebox_soc_type(void)
+{
+	unsigned int *type;
+	size_t size;
+
+	type = handoff_data_get_entry(HANDOFF_DATA_SOC_TYPE, &size);
+	if (!type)
+		return 0;
+
+	return *type;
+}
+
+/*
+ * A missing entry means an ENTRY_FUNCTION() entry point, which declares
+ * no SoC; an entry left at SOC_TYPE_UNKNOWN is a board info that forgot.
+ */
+static int soc_type_check(void)
+{
+	unsigned int *type;
+	size_t size;
+
+	type = handoff_data_get_entry(HANDOFF_DATA_SOC_TYPE, &size);
+	if (type && *type == SOC_TYPE_UNKNOWN)
+		pr_err("PBL entry point must declare SoC type\n");
+
+	return 0;
+}
+postcore_initcall(soc_type_check);
+
+#endif
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 05/17] PBL: introduce board info based entry point framework
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (3 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 04/17] PBL: add SoC type declaration and handoff support Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 06/17] PBL: describe the console in the board info and report the stage Ahmad Fatoum
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

Every board open-codes its PBL entry point: set up the stack, relocate,
call setup_c(), do the lowlevel init and so on, all in code that runs
before the C environment is ready. This is easy to get wrong and leaves
common code with no idea of what the boot flow looks like.

Let's have boards describe it instead: struct pbl_board_info has up to
three stages (ROM, OCRAM, DRAM), each with a memory region, a stack, an
optional relocation address and callbacks for CPU, SoC and board init
and for chainloading. ENTRY_POINT() defines it with the image head and
defaults of the given SoC, which the board may override, hence the
__diag_ignore_all() imported from Linux.

The image head branches to pbl_head_start_v1(), which determines the
stage from the PC, sets up its stack and runs the CPU init callback,
before relocating, clearing BSS and running the remaining callbacks.

As this runs before there is a stack, which C can't guarantee,
architectures are meant to implement pbl_head_start_v1() in assembly
and select ARCH_HAS_PBL_HEAD_START_ASM. The C implementation added here
is weak and serves as reference. With COMPILE_TEST, PBL_HEAD_START_ASM
can be disabled to test it. It's naked where the compiler allows C in
naked functions, which GCC does for ARM32 and RISC-V, but clang doesn't.

Everything is versioned, so we can add a v2 alongside should the scheme
ever need to change incompatibly.

Assisted-by: Claude:fable-5.1
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/Kconfig                   |  28 ++++++
 include/linux/compiler-clang.h |  21 +++++
 include/linux/compiler-gcc.h   |   3 +
 include/linux/compiler_types.h |  10 ++
 include/pbl/entry.h            | 158 +++++++++++++++++++++++++++++++
 pbl/Makefile                   |   1 +
 pbl/entry.c                    | 165 +++++++++++++++++++++++++++++++++
 7 files changed, 386 insertions(+)
 create mode 100644 include/pbl/entry.h
 create mode 100644 pbl/entry.c

diff --git a/arch/Kconfig b/arch/Kconfig
index d24cf525b029..f36eab86d891 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -95,6 +95,34 @@ config ARCH_HAS_DATA_ABORT_MASK_PBL
 config ARCH_HAS_ZERO_PAGE
 	bool
 
+config ARCH_HAS_NAKED
+	def_bool y
+	depends on (ARM && CPU_32) || RISCV
+	# clang allows only basic asm statements in naked functions
+	depends on !CC_IS_CLANG
+	help
+	  The compiler supports C in naked functions: it emits neither
+	  prologue nor epilogue, but may still spill to the stack.
+
+config ARCH_HAS_PBL_ENTRY
+	bool
+	help
+	  The architecture has PBL image heads for ENTRY_POINT() boards.
+
+config ARCH_HAS_PBL_HEAD_START_ASM
+	bool
+	help
+	  The architecture implements pbl_head_start_v1() in assembly.
+
+config PBL_HEAD_START_ASM
+	bool "Use the assembly PBL stage dispatch" if COMPILE_TEST
+	depends on ARCH_HAS_PBL_HEAD_START_ASM
+	default y
+	help
+	  Say N to use the C pbl_head_start_v1() in pbl/entry.c instead,
+	  which may spill to the stack before it is set up. Only useful
+	  for testing.
+
 config ARCH_HAS_MALLOC_SIZE
 	bool
 	help
diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h
index 681e347283b2..4b83885a9255 100644
--- a/include/linux/compiler-clang.h
+++ b/include/linux/compiler-clang.h
@@ -55,3 +55,24 @@
 #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
 #define __assume_aligned(a, ...)	\
 	__attribute__((__assume_aligned__(a, ## __VA_ARGS__)))
+
+/*
+ * Turn individual warnings and errors on and off locally, depending
+ * on version.
+ */
+#define __diag_str1(s)		#s
+#define __diag_str(s)		__diag_str1(s)
+#define __diag(s)		_Pragma(__diag_str(clang diagnostic s))
+
+#define __diag_clang(version, severity, s) \
+	__diag_clang_ ## version(__diag_clang_ ## severity s)
+
+/* Severity used in pragma directives */
+#define __diag_clang_ignore	ignored
+#define __diag_clang_warn	warning
+#define __diag_clang_error	error
+
+#define __diag_clang_13(s)	__diag(s)
+
+#define __diag_ignore_all(option, comment) \
+	__diag_clang(13, ignore, option)
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 2534386d040f..79d23ba62072 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -235,6 +235,9 @@
 #define __diag_GCC_8(s)
 #endif
 
+#define __diag_ignore_all(option, comment) \
+	__diag(__diag_GCC_ignore option)
+
 /*
  * Prior to 9.1, -Wno-alloc-size-larger-than (and therefore the "alloc_size"
  * attribute) do not work, and must be disabled.
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index 0e49c6795409..7837f45efca2 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -313,6 +313,10 @@ struct ftrace_likely_data {
 #define __diag_GCC(version, severity, string)
 #endif
 
+#ifndef __diag_ignore_all
+#define __diag_ignore_all(option, comment)
+#endif
+
 #define __diag_push()	__diag(push)
 #define __diag_pop()	__diag(pop)
 
@@ -452,4 +456,10 @@ struct ftrace_likely_data {
 #define __prereloc \
 	notrace __no_sanitize_address __no_stack_protector
 
+/*
+ * Marks functions that may run before the stack is set up and thus must
+ * not spill. Documentation only, nothing enforces it.
+ */
+#define __unsafe_prestack
+
 #endif /* __LINUX_COMPILER_TYPES_H */
diff --git a/include/pbl/entry.h b/include/pbl/entry.h
new file mode 100644
index 000000000000..17056a3ab1ca
--- /dev/null
+++ b/include/pbl/entry.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2026 Ahmad Fatoum */
+
+#ifndef __PBL_ENTRY_H_
+#define __PBL_ENTRY_H_
+
+#include <linux/bits.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <asm-generic/memory_layout.h>
+#include <pbl/soc-type.h>
+
+/* Special stacktop values, a real stack top is 16-byte aligned */
+#define PBL_STACKTOP_KEEP		0	/* leave SP as it is */
+#define PBL_STACKTOP_BEFORE_BAREBOX	1	/* grows down from the image start */
+#define PBL_STACKTOP_AFTER_BAREBOX	2	/* CONFIG_STACK_SIZE behind the image end */
+
+enum pbl_stage_idx {
+	PBL_STAGE_ROM,		/* pre-sram */
+	PBL_STAGE_OCRAM,	/* pre-ram */
+	PBL_STAGE_DRAM,		/* some ram */
+	PBL_STAGE_FINAL,	/* all ram */
+	PBL_STAGE_COUNT,	/* sentinel */
+};
+
+typedef void (*pbl_board_fn_t)(void);
+typedef enum pbl_stage_idx (*pbl_locate_pc_fn_t)(void);
+
+/*
+ * Only the callbacks of the stage the PBL was entered in are run.
+ *
+ * prereloc_cpu_init runs on the stage's stack, but unrelocated and before
+ * BSS is cleared, so it must be __prereloc and may not use globals.
+ * The other callbacks run relocated with BSS cleared.
+ */
+struct pbl_stage {
+	unsigned long stacktop;		/* stack top or PBL_STACKTOP_* */
+	unsigned long relocaddr;	/* 0: don't relocate to specific addr */
+
+	/*
+	 * Memory the stage runs from: barebox proper starts out with
+	 * mem_minsz, mem_maxsz (default: mem_minsz) is only used to find
+	 * the stage by PC.
+	 */
+	unsigned long mem_start;
+	unsigned long mem_minsz;
+	unsigned long mem_maxsz;
+
+	void *board_data;
+
+	pbl_board_fn_t prereloc_cpu_init;
+	pbl_board_fn_t soc_init;
+	void (*board_init)(unsigned long entry_data);
+	void (*soc_chainload)(void *board_data);
+};
+
+struct pbl_board_info {
+	unsigned int soc_type;		/* see pbl_set_soc_type() */
+
+	/* Called without a stack, so it must not spill */
+	pbl_locate_pc_fn_t assembly_locate_pc;
+
+	struct pbl_stage rom_stage;	/* PBL_STAGE_ROM */
+	struct pbl_stage first_stage;	/* PBL_STAGE_OCRAM */
+	struct pbl_stage second_stage;	/* PBL_STAGE_DRAM */
+};
+
+extern struct pbl_board_info __pbl_board_info;
+
+/* Always inlined, because it's called before relocation */
+static __always_inline struct pbl_stage *
+pbl_stage(struct pbl_board_info *pbi, int idx)
+{
+	switch (idx) {
+	case PBL_STAGE_ROM:
+		return &pbi->rom_stage;
+	case PBL_STAGE_OCRAM:
+		return &pbi->first_stage;
+	case PBL_STAGE_DRAM:
+		return &pbi->second_stage;
+	default:
+		return NULL;
+	}
+}
+
+struct pbl_stage *pbl_get_stage(void);
+
+void setup_c(void);
+
+#ifdef CONFIG_ARCH_HAS_NAKED
+#define __pbl_naked __naked
+#else
+#define __pbl_naked
+#endif
+
+/*
+ * pbl_head_start_v1 - stage dispatch the image head branches to
+ *
+ * Runs unrelocated and without a stack: determines the stage, switches
+ * to its stack, runs prereloc_cpu_init and continues in
+ * pbl_head_reloc_v1(). entry_data is a boot register the head passes on
+ * to board_init.
+ *
+ * The C implementation in pbl/entry.c is weak, so the architecture can
+ * override it with one in assembly, see CONFIG_PBL_HEAD_START_ASM.
+ */
+void __noreturn pbl_head_start_v1(unsigned long entry_data);
+
+/* Relocates, clears BSS and runs the remaining callbacks. Needs a stack */
+void __noreturn pbl_head_reloc_v1(unsigned long stage,
+				  unsigned long entry_data);
+
+/*
+ * ENTRY_POINT_HEAD - define entry point @name with image head @head
+ *
+ * Opens the board info, ENTRY_POINT_END closes it. The function only
+ * keeps head and board info from being garbage collected, but ARM's
+ * ENTRY_FUNCTION() will branch to it, so it may not use the stack.
+ */
+#define ENTRY_POINT_HEAD(name, head)					\
+	void name(ulong r0, ulong r1, ulong r2);			\
+	void __pbl_naked __unsafe_prestack				\
+		__section(.text_head_entry_##name) name			\
+			(ulong r0, ulong r1, ulong r2)			\
+	{								\
+		__keep_symbolref(head);					\
+	__diag_push();							\
+	__diag_ignore_all("-Woverride-init",				\
+			  "Allow board overrides for SoC defaults");	\
+		static __section(.pbl_board_info_##name)		\
+			struct pbl_board_info __board_info = {		\
+				/* unset fields are zero (static) */
+
+/* Closes the board info, but not the function, for ARM's ENTRY_FUNCTION() */
+#define ENTRY_POINT_CONTINUE						\
+			};						\
+		__diag_pop();						\
+		__keep_symbolref(__board_info);
+
+#define ENTRY_POINT_END							\
+	ENTRY_POINT_CONTINUE						\
+	}
+
+#define PBL_SOC_PASTE2(soc, field)	PBL_SOC_##soc##_##field
+#define PBL_SOC_PASTE(soc, field)	PBL_SOC_PASTE2(soc, field)
+
+/*
+ * ENTRY_POINT - define entry point @name for a board with SoC @soc
+ *
+ * Head and board info defaults come from PBL_SOC_<soc>_HEAD and
+ * PBL_SOC_<soc>_DEFAULTS. The board info fields that follow override
+ * the defaults.
+ */
+#define ENTRY_POINT(soc, name)				\
+	ENTRY_POINT_HEAD(name, PBL_SOC_PASTE(soc, HEAD))\
+	PBL_SOC_PASTE(soc, DEFAULTS),
+
+#endif
diff --git a/pbl/Makefile b/pbl/Makefile
index 5d7c9b06015d..3255feb9d645 100644
--- a/pbl/Makefile
+++ b/pbl/Makefile
@@ -4,6 +4,7 @@
 # only unsed by the pbl
 #
 pbl-$(CONFIG_PBL_CONSTRUCTORS) += ctors.o
+pbl-$(CONFIG_ARCH_HAS_PBL_ENTRY) += entry.o
 pbl-y += misc.o
 pbl-y += string.o
 pbl-y += malloc.o
diff --git a/pbl/entry.c b/pbl/entry.c
new file mode 100644
index 000000000000..1349e7c56a93
--- /dev/null
+++ b/pbl/entry.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2026 Ahmad Fatoum
+/*
+ * Stage dispatch for ENTRY_POINT() boards. pbl_head_start_v1() runs
+ * before there is a stack, but even naked, the compiler may spill, so
+ * the C version here is weak for architectures to override in assembly.
+ */
+
+#include <linux/types.h>
+#include <linux/align.h>
+#include <asm/reloc.h>
+#include <asm/sections.h>
+#include <asm-generic/registers.h>
+#include <barebox.h>
+#include <pbl/entry.h>
+#include <pbl/soc-type.h>
+#include <pbl/handoff-data.h>
+
+/*
+ * runtime_address() without the volatile, which would cost a stack slot.
+ * It's only needed across relocation, which doesn't happen this early.
+ */
+#define prestack_runtime_address(sym) \
+	((void *)RELOC_HIDE((char *)(sym), global_variable_offset()))
+
+static struct pbl_stage *current_stage;
+
+struct pbl_stage *pbl_get_stage(void)
+{
+	return current_stage;
+}
+
+static __always_inline __prereloc __unsafe_prestack bool
+stage_has_entry(const struct pbl_stage *stage)
+{
+	return stage->soc_init || stage->board_init || stage->soc_chainload;
+}
+
+static __always_inline __prereloc __unsafe_prestack int
+pbl_determine_stage_idx(struct pbl_board_info *pbi)
+{
+	int earliest_stage_idx = -1;
+	int stage_idx;
+
+	if (pbi->assembly_locate_pc) {
+		pbl_locate_pc_fn_t locate_pc;
+
+		locate_pc = (void *)((ulong)pbi->assembly_locate_pc +
+				     get_runtime_offset());
+		return locate_pc();
+	}
+
+	for (stage_idx = 0; stage_idx < PBL_STAGE_COUNT; stage_idx++) {
+		struct pbl_stage *stage = pbl_stage(pbi, stage_idx);
+
+		if (!stage)
+			continue;
+
+		if (stage_has_entry(stage) && earliest_stage_idx < 0)
+			earliest_stage_idx = stage_idx;
+
+		if (pc_inside(stage->mem_start,
+			      stage->mem_maxsz ?: stage->mem_minsz))
+			return stage_idx;
+	}
+
+	return earliest_stage_idx;
+}
+
+static void __noreturn pbl_head_cpu_init_v1(ulong stage_ul, ulong entry_data);
+static void __noreturn pbl_head_entry_v1(ulong entry_data);
+
+void __weak __pbl_naked __prereloc __unsafe_prestack
+pbl_head_start_v1(unsigned long entry_data)
+{
+	struct pbl_board_info *pbi =
+		prestack_runtime_address(&__pbl_board_info);
+	struct pbl_stage *stage;
+	ulong stage_ul;
+	ulong stacktop;
+
+	stage = pbl_stage(pbi, pbl_determine_stage_idx(pbi));
+	if (!stage)
+		__hang();
+
+	stage_ul = (ulong)stage;
+	stacktop = stage->stacktop;
+
+	if (stacktop == PBL_STACKTOP_BEFORE_BAREBOX)
+		stacktop = (ulong)prestack_runtime_address(__image_start);
+	else if (stacktop == PBL_STACKTOP_AFTER_BAREBOX)
+		stacktop = (ulong)prestack_runtime_address(__image_end) +
+			CONFIG_STACK_SIZE;
+
+	stacktop = ALIGN_DOWN(stacktop, 16);
+
+	if (stacktop)
+		call_with_stack(stacktop, pbl_head_cpu_init_v1,
+				stage_ul, entry_data);
+	else
+		pbl_head_cpu_init_v1(stage_ul, entry_data);
+}
+
+/* Runs on the stage's stack, but still unrelocated */
+static void __noreturn __prereloc
+pbl_head_cpu_init_v1(ulong stage_ul, ulong entry_data)
+{
+	struct pbl_stage *stage = (void *)stage_ul;
+
+	if (stage && stage->prereloc_cpu_init) {
+		pbl_board_fn_t cpu_init;
+
+		cpu_init = (void *)((ulong)stage->prereloc_cpu_init +
+				    get_runtime_offset());
+		cpu_init();
+	}
+
+	pbl_head_reloc_v1(stage_ul, entry_data);
+}
+
+void __prereloc noinline
+pbl_head_reloc_v1(ulong stage_ul, ulong entry_data)
+{
+	struct pbl_stage *stage = (void *)stage_ul;
+
+	if (!stage)
+		__hang();
+
+	if (stage->relocaddr)
+		relocate_to_adr_full(stage->relocaddr);
+	else
+		relocate_to_current_adr();
+
+	setup_c();
+
+	pbl_head_entry_v1(entry_data);
+}
+
+/* noinline, so &__pbl_board_info isn't computed before relocation */
+static void noinline pbl_head_entry_v1(ulong entry_data)
+{
+	struct pbl_board_info *pbi = &__pbl_board_info;
+	struct pbl_stage *stage;
+	int stage_idx;
+
+	stage_idx = pbl_determine_stage_idx(pbi);
+	stage = pbl_stage(pbi, stage_idx);
+	if (!stage)
+		hang();
+
+	/* A multi-SoC image may still override this from its board init */
+	pbl_set_soc_type(pbi->soc_type);
+	pbl_handoff_soc_type();
+
+	current_stage = stage;
+
+	if (stage->soc_init)
+		stage->soc_init();
+	if (stage->board_init)
+		stage->board_init(entry_data);
+	if (stage->soc_chainload)
+		stage->soc_chainload(stage->board_data);
+
+	hang();
+}
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 06/17] PBL: describe the console in the board info and report the stage
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (4 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 05/17] PBL: introduce board info based entry point framework Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 07/17] RISC-V: make barebox_riscv_entry() non-naked Ahmad Fatoum
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

Board entry points usually set up the console UART first thing and
print a '>' with putc_ll() to show that they have been reached.

Let's make the console part of the board info instead and have the stage
dispatch print the stage number followed by '>', e.g. "2>", once it's
up. As the UART may need clocks enabled by SoC init or a description
only filled in by board init, the console is tried before SoC init and
again after each of the two.

Without PBL console support or a console description, the stage number
goes to DEBUG_LL. With DEBUG, each callback run is reported as well.

Assisted-by: Claude:opus-5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 Documentation/devel/troubleshooting.rst |  6 +++
 include/pbl.h                           |  6 +++
 include/pbl/entry.h                     | 19 ++++++-
 pbl/entry.c                             | 67 ++++++++++++++++++++++++-
 4 files changed, 96 insertions(+), 2 deletions(-)

diff --git a/Documentation/devel/troubleshooting.rst b/Documentation/devel/troubleshooting.rst
index 2aef57c3c1e2..39cc88a9a7dd 100644
--- a/Documentation/devel/troubleshooting.rst
+++ b/Documentation/devel/troubleshooting.rst
@@ -62,6 +62,12 @@ options:
   the serial console needs to be done in software in some situations before
   the UART is accessible from the outside.
 
+  Boards using the board info entry point (``ENTRY_POINT()``) will
+  also indicate the boot stage before the ``>`` character, e.g. ``1>2>``
+  means the prebootloader entered its 1st stage followed by entering the
+  2nd stage. ``CONFIG_DEBUG_PBL`` would provide more information on what
+  callback is being executed.
+
   .. note::
      Make sure the correct UART index or address is selected under
      **Kernel low-level debugging port** in ``menuconfig``.
diff --git a/include/pbl.h b/include/pbl.h
index 45640c26e32e..9efc520d6a69 100644
--- a/include/pbl.h
+++ b/include/pbl.h
@@ -57,4 +57,10 @@ void __noreturn barebox_pbl_entry(ulong, ulong, void *);
 #define pbl_puts	puts_ll
 #endif
 
+#ifdef DEBUG
+#define pbl_debug	pbl_puts
+#else
+#define pbl_debug	(void)
+#endif
+
 #endif /* __PBL_H__ */
diff --git a/include/pbl/entry.h b/include/pbl/entry.h
index 17056a3ab1ca..acf0ea1e1824 100644
--- a/include/pbl/entry.h
+++ b/include/pbl/entry.h
@@ -26,12 +26,27 @@ enum pbl_stage_idx {
 typedef void (*pbl_board_fn_t)(void);
 typedef enum pbl_stage_idx (*pbl_locate_pc_fn_t)(void);
 
+struct pbl_console_driver {
+	void (*putc)(void *arg, int c);	/* as accepted by pbl_set_putc() */
+};
+
+/*
+ * arg is usually the UART base address. A console without setup callback
+ * is registered before soc_init, else after. Without a console, the PBL
+ * prints through DEBUG_LL.
+ */
+struct pbl_console {
+	void (*setup)(void *arg);
+	void *arg;
+	const struct pbl_console_driver *driver;
+};
+
 /*
  * Only the callbacks of the stage the PBL was entered in are run.
  *
  * prereloc_cpu_init runs on the stage's stack, but unrelocated and before
  * BSS is cleared, so it must be __prereloc and may not use globals.
- * The other callbacks run relocated with BSS cleared.
+ * The console setup and the other callbacks run relocated with BSS cleared.
  */
 struct pbl_stage {
 	unsigned long stacktop;		/* stack top or PBL_STACKTOP_* */
@@ -60,6 +75,8 @@ struct pbl_board_info {
 	/* Called without a stack, so it must not spill */
 	pbl_locate_pc_fn_t assembly_locate_pc;
 
+	struct pbl_console console;
+
 	struct pbl_stage rom_stage;	/* PBL_STAGE_ROM */
 	struct pbl_stage first_stage;	/* PBL_STAGE_OCRAM */
 	struct pbl_stage second_stage;	/* PBL_STAGE_DRAM */
diff --git a/pbl/entry.c b/pbl/entry.c
index 1349e7c56a93..827af905964b 100644
--- a/pbl/entry.c
+++ b/pbl/entry.c
@@ -11,7 +11,11 @@
 #include <asm/reloc.h>
 #include <asm/sections.h>
 #include <asm-generic/registers.h>
+#include <console.h>
+#include <debug_ll.h>
+#include <stdio.h>
 #include <barebox.h>
+#include <pbl.h>
 #include <pbl/entry.h>
 #include <pbl/soc-type.h>
 #include <pbl/handoff-data.h>
@@ -136,6 +140,44 @@ pbl_head_reloc_v1(ulong stage_ul, ulong entry_data)
 	pbl_head_entry_v1(entry_data);
 }
 
+static void pbl_console_report_stage(int stage_idx)
+{
+	static bool stage_reported;
+	char stage_str[2] = "0";
+
+	if (stage_reported)
+		return;
+
+	stage_str[0] += stage_idx;
+
+	pbl_puts(stage_str);
+	pbl_puts(">");
+
+	stage_reported = true;
+}
+
+/* Called until there is a console, board_init may still describe it */
+static void pbl_console_init(const struct pbl_console *console,
+			     int stage_idx)
+{
+	static bool console_initialized;
+
+	if (console_initialized)
+		return;
+
+	if (console->setup)
+		console->setup(console->arg);
+
+	if (console->driver && console->driver->putc) {
+		pbl_set_putc(console->driver->putc, console->arg);
+		console_initialized = true;
+	}
+
+	/* Without PBL_CONSOLE, pbl_puts() is DEBUG_LL's puts_ll() */
+	if (console_initialized || !IS_ENABLED(CONFIG_PBL_CONSOLE))
+		pbl_console_report_stage(stage_idx);
+}
+
 /* noinline, so &__pbl_board_info isn't computed before relocation */
 static void noinline pbl_head_entry_v1(ulong entry_data)
 {
@@ -154,12 +196,35 @@ static void noinline pbl_head_entry_v1(ulong entry_data)
 
 	current_stage = stage;
 
+	/* A console needing no setup is readily usable in soc_init */
+	if (!pbi->console.setup)
+		pbl_console_init(&pbi->console, stage_idx);
+
 	if (stage->soc_init)
 		stage->soc_init();
+
+	/* Initialize console if possible */
+	pbl_console_init(&pbi->console, stage_idx);
+
+	if (stage->soc_init)
+		pbl_debug("soc_init done\n");
+
 	if (stage->board_init)
 		stage->board_init(entry_data);
-	if (stage->soc_chainload)
+
+	/* Initialize console if possible */
+	pbl_console_init(&pbi->console, stage_idx);
+
+	/* Without a console, the stage goes to the DEBUG_LL fallback */
+	pbl_console_report_stage(stage_idx);
+
+	if (stage->board_init)
+		pbl_debug("board_init done\n");
+
+	if (stage->soc_chainload) {
+		pbl_debug("Chainloading..\n");
 		stage->soc_chainload(stage->board_data);
+	}
 
 	hang();
 }
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 07/17] RISC-V: make barebox_riscv_entry() non-naked
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (5 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 06/17] PBL: describe the console in the board info and report the stage Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 08/17] RISC-V: add PBL heads for the board info stage dispatch Ahmad Fatoum
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

barebox_riscv_entry() is a naked function that switches the stack with
inline assembly and then continues in C. GCC only supports basic asm in
naked functions and clang rejects anything else outright.

Let's move the stack switch into an assembly __barebox_riscv_entry() like
on ARM. barebox_riscv_entry() becomes an always inlined function, so the
naked ENTRY_FUNCTION()s, which may run without a stack, can still call it.

Assisted-by: Claude:opus-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/boot/Makefile               |  2 +-
 arch/riscv/boot/entry.c                | 31 --------------------------
 arch/riscv/boot/entry_ll.S             | 15 +++++++++++++
 arch/riscv/include/asm/barebox-riscv.h | 30 ++++++++++++++++++++++---
 4 files changed, 43 insertions(+), 35 deletions(-)
 delete mode 100644 arch/riscv/boot/entry.c
 create mode 100644 arch/riscv/boot/entry_ll.S

diff --git a/arch/riscv/boot/Makefile b/arch/riscv/boot/Makefile
index 936f6e2e0391..2cd6f098d5ef 100644
--- a/arch/riscv/boot/Makefile
+++ b/arch/riscv/boot/Makefile
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
-pbl-y += entry.o uncompress.o
+pbl-y += entry_ll.o uncompress.o
 pbl-y += nmon_entry.o
 pbl-$(CONFIG_BOARD_GENERIC_DT) += board-dt-2nd.o
diff --git a/arch/riscv/boot/entry.c b/arch/riscv/boot/entry.c
deleted file mode 100644
index f5a536fc78a9..000000000000
--- a/arch/riscv/boot/entry.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <types.h>
-
-#include <asm/barebox-riscv.h>
-
-#include "entry.h"
-#include <debug_ll.h>
-
-/*
- * Main RISC-V entry point. Call this with the memory region you can
- * spare for barebox. This doesn't necessarily have to be the full
- * SDRAM. The currently running binary can be inside or outside of
- * this region. The PBL can be running inside or outside of this
- * region.
- *
- * -> membase + memsize
- *   STACK_SIZE              - stack
- *   128KiB                  - early memory space
- * -> maximum end of barebox binary
- */
-
-void __noreturn __naked barebox_riscv_entry(unsigned long membase,
-					    unsigned long memsize, void *boarddata,
-					    unsigned flags)
-{
-	unsigned long stack_top = riscv_mem_stack_top(membase, membase + memsize);
-	asm volatile ("move sp, %0" : : "r"(stack_top));
-	riscv_set_flags(flags);
-	barebox_pbl_start(membase, memsize, boarddata);
-}
-
diff --git a/arch/riscv/boot/entry_ll.S b/arch/riscv/boot/entry_ll.S
new file mode 100644
index 000000000000..3611d72a3a78
--- /dev/null
+++ b/arch/riscv/boot/entry_ll.S
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <linux/linkage.h>
+
+/*
+ * a0: memory base
+ * a1: memory size
+ * a2: board data
+ * a3: new value for SP
+ */
+.section .text.__barebox_riscv_entry
+ENTRY(__barebox_riscv_entry)
+	mv	sp, a3
+	tail	barebox_pbl_start
+ENDPROC(__barebox_riscv_entry)
diff --git a/arch/riscv/include/asm/barebox-riscv.h b/arch/riscv/include/asm/barebox-riscv.h
index 9fdffb213e8c..4a7726ddc508 100644
--- a/arch/riscv/include/asm/barebox-riscv.h
+++ b/arch/riscv/include/asm/barebox-riscv.h
@@ -25,9 +25,6 @@
 #include <asm/reloc.h>
 #include <pbl/handoff-data.h>
 
-void __noreturn __naked barebox_riscv_entry(unsigned long membase, unsigned long memsize,
-					    void *boarddata, unsigned int flags);
-
 #define barebox_riscv_machine_entry(membase, memsize, boarddata) \
 	barebox_riscv_entry(membase, memsize, boarddata, RISCV_M_MODE)
 
@@ -82,6 +79,33 @@ static inline unsigned long riscv_mem_barebox_image(unsigned long membase,
 	return ALIGN_DOWN(endmem - size, SZ_1M);
 }
 
+void __noreturn __barebox_riscv_entry(unsigned long membase,
+				      unsigned long memsize, void *boarddata,
+				      unsigned long sp);
+
+/*
+ * Main RISC-V entry point. Call this with the memory region you can
+ * spare for barebox. This doesn't necessarily have to be the full
+ * SDRAM. The currently running binary can be inside or outside of
+ * this region. The PBL can be running inside or outside of this
+ * region.
+ *
+ * -> membase + memsize
+ *   STACK_SIZE              - stack
+ *   128KiB                  - early memory space
+ * -> maximum end of barebox binary
+ *
+ * Inline, so naked ENTRY_FUNCTION()s can call it without a stack.
+ */
+static __always_inline void __noreturn
+barebox_riscv_entry(unsigned long membase, unsigned long memsize,
+		    void *boarddata, unsigned int flags)
+{
+	riscv_set_flags(flags);
+	__barebox_riscv_entry(membase, memsize, boarddata,
+			      riscv_mem_stack_top(membase, membase + memsize));
+}
+
 #define ENTRY_FUNCTION(name, arg0, arg1, arg2)                          \
 	void name (ulong r0, ulong r1, ulong r2);                       \
 	static void __##name(ulong, ulong, ulong);                      \
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 08/17] RISC-V: add PBL heads for the board info stage dispatch
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (6 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 07/17] RISC-V: make barebox_riscv_entry() non-naked Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 09/17] RISC-V: board-dt-2nd: migrate to board info entry point Ahmad Fatoum
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

Unlike on ARM, the privilege mode is part of the RISC-V boot protocol and
even the cache maintenance during relocation depends on it, so the head
needs to know which mode it was started in.

Let's add two heads: one for images that firmware enters in supervisor
mode like a Linux kernel, with the hart ID in a0 and the device tree in
a1, and one for images entered in machine mode without a boot protocol,
like Erizo and LiteX. Both lay out the same header as
__barebox_riscv_head(), so the image tools keep working, and branch to
pbl_head_start_v1(), which RISC-V implements in assembly.

Boards whose stage describes the memory for barebox proper can use the
new pbl_stage_barebox_entry() as chainload callback.

Assisted-by: Claude:fable-5.1
Assisted-by: Claude:opus-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/Kconfig                     |   2 +
 arch/riscv/boot/Makefile               |   2 +-
 arch/riscv/boot/entry.c                |  16 ++
 arch/riscv/boot/head.S                 | 198 +++++++++++++++++++++++++
 arch/riscv/include/asm/barebox-riscv.h |   6 +
 arch/riscv/lib/asm-offsets.c           |  27 ++++
 arch/riscv/lib/pbl.lds.S               |  13 +-
 7 files changed, 262 insertions(+), 2 deletions(-)
 create mode 100644 arch/riscv/boot/entry.c
 create mode 100644 arch/riscv/boot/head.S

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 40a79b6834ff..0041269123e3 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -20,6 +20,8 @@ config RISCV
 	select RISCV_TIMER if RISCV_SBI
 	select HW_HAS_PCI
 	select PBL_CONSTRUCTORS
+	select ARCH_HAS_PBL_ENTRY
+	select ARCH_HAS_PBL_HEAD_START_ASM
 	select PBL_IMAGE_ELF
 	select HAVE_ARCH_BOARD_GENERIC_DT
 	select HAVE_ARCH_BOOTM_OFTREE
diff --git a/arch/riscv/boot/Makefile b/arch/riscv/boot/Makefile
index 2cd6f098d5ef..d98adf7f724b 100644
--- a/arch/riscv/boot/Makefile
+++ b/arch/riscv/boot/Makefile
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
-pbl-y += entry_ll.o uncompress.o
+pbl-y += head.o entry.o entry_ll.o uncompress.o
 pbl-y += nmon_entry.o
 pbl-$(CONFIG_BOARD_GENERIC_DT) += board-dt-2nd.o
diff --git a/arch/riscv/boot/entry.c b/arch/riscv/boot/entry.c
new file mode 100644
index 000000000000..11b66ab48b63
--- /dev/null
+++ b/arch/riscv/boot/entry.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <pbl/entry.h>
+#include <asm/barebox-riscv.h>
+
+/*
+ * soc_chainload callback for boards whose stage describes the memory for
+ * barebox proper. The head has already recorded the mode in the flags.
+ */
+void __noreturn pbl_stage_barebox_entry(void *boarddata)
+{
+	struct pbl_stage *stage = pbl_get_stage();
+
+	barebox_riscv_entry(stage->mem_start, stage->mem_minsz, boarddata,
+			    riscv_get_flags());
+}
diff --git a/arch/riscv/boot/head.S b/arch/riscv/boot/head.S
new file mode 100644
index 000000000000..a205965e33ad
--- /dev/null
+++ b/arch/riscv/boot/head.S
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+
+/*
+ * PBL image heads for ENTRY_POINT() boards. The privilege mode is part of
+ * the RISC-V boot protocol and needed as early as the cache maintenance
+ * during relocation, so there is a head per mode, which records it like
+ * riscv_set_flags() would.
+ */
+
+/* Linker will point this at the selected board's struct pbl_board_info */
+.globl __pbl_board_info
+
+/*
+ * Same header as __barebox_riscv_head(), so the image tools keep working.
+ * Execution continues at \setup.
+ */
+.macro	pbl_image_header setup
+	/* Relaxation and compressed instructions would shift the fields */
+	.option push
+	.option norelax
+	.option norvc
+	nop				/* code0 */
+	j	\setup			/* code1 */
+	.balign	8
+	.dword	0			/* Image load offset from start of RAM */
+	.dword	_barebox_image_size	/* Effective Image size */
+	.dword	0			/* Kernel flags */
+	.word	0			/* version */
+	.word	0			/* reserved */
+	.dword	0			/* reserved */
+	.asciz	"barebox"		/* magic 1 */
+	.balign	8
+	.ascii	"RSCV"			/* magic 2 */
+	.word	0			/* reserved (PE-COFF offset) */
+	.option pop
+.endm
+
+/*
+ * Head for images entered like a Linux kernel: supervisor mode, hart ID
+ * in a0 for tp, where riscv_hartid() expects it, and the device tree in
+ * a1, which becomes entry_data.
+ */
+.section .text_head_prologue_v1_supervisor, "ax"
+ENTRY(__barebox_pbl_head_v1_supervisor)
+	pbl_image_header .Lsupervisor_setup
+
+.Lsupervisor_setup:
+	li	fp, 0
+
+	li	t0, RISCV_S_MODE_ASM
+	csrw	sscratch, t0
+	mv	tp, a0
+	mv	a0, a1			/* entry_data = device tree */
+	tail	pbl_head_start_v1
+ENDPROC(__barebox_pbl_head_v1_supervisor)
+
+/*
+ * Head for images entered in machine mode without a boot protocol, e.g.
+ * out of reset. a0 is passed on as entry_data.
+ */
+.section .text_head_prologue_v1_machine, "ax"
+ENTRY(__barebox_pbl_head_v1_machine)
+	pbl_image_header .Lmachine_setup
+
+.Lmachine_setup:
+	li	fp, 0
+
+	li	t0, RISCV_M_MODE_ASM
+	csrw	mscratch, t0
+	tail	pbl_head_start_v1
+ENDPROC(__barebox_pbl_head_v1_machine)
+
+/*
+ * asm_pbl_head_start_v1 - assembly version of pbl_head_start_v1()
+ *
+ *   a0 = entry_data on entry, then struct pbl_board_info *
+ *   a1 = struct pbl_stage * (may be NULL)
+ *   s1 = entry_data
+ *   s2 = runtime offset
+ *   s3 = struct pbl_stage * across the prereloc_cpu_init() call
+ *
+ * s1-s3 are callee-saved, so they survive the callbacks.
+ */
+.section .text.asm_pbl_head_start_v1, "ax"
+ENTRY(asm_pbl_head_start_v1)
+	mv	s1, a0
+
+	/* Board info pointers need the runtime offset until relocation ran */
+	call	get_runtime_offset
+	mv	s2, a0
+
+	lla	a0, __pbl_board_info
+
+	/* Stage determination, mirroring pbl_determine_stage_idx() */
+	REG_L	t0, PBI_LOCATE_PC(a0)
+	beqz	t0, .Lscan
+
+	/* Must be a leaf function: there may be no stack yet */
+	add	t0, t0, s2
+	jalr	t0			/* returns stage index in a0 */
+	mv	t1, a0			/* idx */
+	lla	a0, __pbl_board_info
+	li	a1, 0			/* pbl_stage(): NULL by default */
+
+	/*
+	 * pbl_stage_case - one case of the pbl_stage() switch; the cases
+	 * are mutually exclusive, so each falls through to the next.
+	 */
+	.macro	pbl_stage_case idx, offs
+	li	t2, \idx
+	bne	t1, t2, .Lnext_case\@
+	addi	a1, a0, \offs
+.Lnext_case\@:
+	.endm
+
+	pbl_stage_case	PBL_STAGE_ROM_ASM, PBI_ROM_STAGE
+	pbl_stage_case	PBL_STAGE_OCRAM_ASM, PBI_FIRST_STAGE
+	pbl_stage_case	PBL_STAGE_DRAM_ASM, PBI_SECOND_STAGE
+	j	.Lhave_stage		/* a1 stays NULL for other indices */
+
+.Lscan:
+	li	a1, 0			/* earliest stage with entry */
+	auipc	t3, 0			/* pc, as get_pc() does */
+
+	/*
+	 * pbl_check_stage - one pbl_determine_stage_idx() iteration for the
+	 * stage at byte offset \offs
+	 */
+	.macro	pbl_check_stage offs
+	addi	t0, a0, \offs
+	/* stage_has_entry()? */
+	REG_L	t4, PBS_SOC_INIT(t0)
+	REG_L	t5, PBS_BOARD_INIT(t0)
+	or	t4, t4, t5
+	REG_L	t5, PBS_SOC_CHAINLOAD(t0)
+	or	t4, t4, t5
+	beqz	t4, .Lcheck_pc\@
+	bnez	a1, .Lcheck_pc\@
+	mv	a1, t0			/* earliest ?: stage */
+.Lcheck_pc\@:
+	/* pc_inside(mem_start, mem_maxsz ?: mem_minsz)? */
+	REG_L	t4, PBS_MEM_START(t0)
+	REG_L	t5, PBS_MEM_MAXSZ(t0)
+	bnez	t5, .Lhave_memsz\@
+	REG_L	t5, PBS_MEM_MINSZ(t0)
+.Lhave_memsz\@:
+	sub	t4, t3, t4
+	bltu	t4, t5, .Lfound
+	.endm
+
+	pbl_check_stage	PBI_ROM_STAGE
+	pbl_check_stage	PBI_FIRST_STAGE
+	pbl_check_stage	PBI_SECOND_STAGE
+	j	.Lhave_stage		/* stage = earliest (a1) */
+.Lfound:
+	mv	a1, t0
+
+.Lhave_stage:
+	/* Misconfigured board info; there may be no stack to call with */
+.Lno_stage:
+	beqz	a1, .Lno_stage
+
+	/* stage->stacktop is a stack top or a PBL_STACKTOP_* value */
+	REG_L	t0, PBS_STACKTOP(a1)
+	li	t1, PBS_STACKTOP_BEFORE_BAREBOX
+	bne	t0, t1, .Lnot_before_barebox
+	lla	t0, __image_start
+	j	.Lhave_stacktop
+.Lnot_before_barebox:
+	li	t1, PBS_STACKTOP_AFTER_BAREBOX
+	bne	t0, t1, .Lhave_stacktop
+	lla	t0, __image_end
+	li	t1, CONFIG_STACK_SIZE
+	add	t0, t0, t1
+.Lhave_stacktop:
+	andi	t0, t0, -16		/* ALIGN_DOWN(stacktop, 16) */
+	beqz	t0, .Lstack_set
+	mv	sp, t0
+.Lstack_set:
+	/* CPU init callback, unrelocated like assembly_locate_pc() above */
+	REG_L	t0, PBS_CPU_INIT(a1)
+	beqz	t0, .Lcpu_init_done
+	add	t0, t0, s2
+	mv	s3, a1
+	jalr	t0
+	mv	a1, s3
+.Lcpu_init_done:
+	mv	a0, a1
+	mv	a1, s1
+	tail	pbl_head_reloc_v1	/* (a0 = stage, a1 = entry_data) */
+ENDPROC(asm_pbl_head_start_v1)
+
+#ifdef CONFIG_PBL_HEAD_START_ASM
+SYM_FUNC_ALIAS(pbl_head_start_v1, asm_pbl_head_start_v1)
+#endif
diff --git a/arch/riscv/include/asm/barebox-riscv.h b/arch/riscv/include/asm/barebox-riscv.h
index 4a7726ddc508..c7ca339607c4 100644
--- a/arch/riscv/include/asm/barebox-riscv.h
+++ b/arch/riscv/include/asm/barebox-riscv.h
@@ -33,6 +33,12 @@
 	barebox_riscv_entry(membase, memsize, boarddata, RISCV_S_MODE); \
 } while (0)
 
+/* PBL image heads for ENTRY_POINT_HEAD(), see arch/riscv/boot/head.S */
+extern char __barebox_pbl_head_v1_supervisor[];
+extern char __barebox_pbl_head_v1_machine[];
+
+void __noreturn pbl_stage_barebox_entry(void *boarddata);
+
 static inline unsigned long riscv_mem_stack_top(unsigned long membase,
 						unsigned long endmem)
 {
diff --git a/arch/riscv/lib/asm-offsets.c b/arch/riscv/lib/asm-offsets.c
index 48960e58a6bc..a7a092cb5d72 100644
--- a/arch/riscv/lib/asm-offsets.c
+++ b/arch/riscv/lib/asm-offsets.c
@@ -9,6 +9,8 @@
 #include <linux/kbuild.h>
 #include <linux/kernel.h>
 #include <asm/ptrace.h>
+#include <asm/system.h>
+#include <pbl/entry.h>
 
 #define STACK_ALIGN		16
 
@@ -56,5 +58,30 @@ int main(void)
 	 * We allocate a pt_regs on the stack. This ensures the alignment is sane.
 	 */
 	DEFINE(PT_SIZE_ON_STACK, ALIGN(sizeof(struct pt_regs), STACK_ALIGN));
+	BLANK();
+	COMMENT("struct pbl_board_info offsets");
+	OFFSET(PBI_LOCATE_PC,		pbl_board_info, assembly_locate_pc);
+	OFFSET(PBI_ROM_STAGE,		pbl_board_info, rom_stage);
+	OFFSET(PBI_FIRST_STAGE,		pbl_board_info, first_stage);
+	OFFSET(PBI_SECOND_STAGE,	pbl_board_info, second_stage);
+	DEFINE(PBI_SIZEOF,		sizeof(struct pbl_board_info));
+	BLANK();
+	COMMENT("struct pbl_stage offsets");
+	OFFSET(PBS_STACKTOP,		pbl_stage, stacktop);
+	OFFSET(PBS_MEM_START,		pbl_stage, mem_start);
+	OFFSET(PBS_MEM_MINSZ,		pbl_stage, mem_minsz);
+	OFFSET(PBS_MEM_MAXSZ,		pbl_stage, mem_maxsz);
+	OFFSET(PBS_CPU_INIT,		pbl_stage, prereloc_cpu_init);
+	OFFSET(PBS_SOC_INIT,		pbl_stage, soc_init);
+	OFFSET(PBS_BOARD_INIT,		pbl_stage, board_init);
+	OFFSET(PBS_SOC_CHAINLOAD,	pbl_stage, soc_chainload);
+	BLANK();
+	DEFINE(PBL_STAGE_ROM_ASM,	PBL_STAGE_ROM);
+	DEFINE(PBL_STAGE_OCRAM_ASM,	PBL_STAGE_OCRAM);
+	DEFINE(PBL_STAGE_DRAM_ASM,	PBL_STAGE_DRAM);
+	DEFINE(PBS_STACKTOP_BEFORE_BAREBOX,	PBL_STACKTOP_BEFORE_BAREBOX);
+	DEFINE(PBS_STACKTOP_AFTER_BAREBOX,	PBL_STACKTOP_AFTER_BAREBOX);
+	DEFINE(RISCV_S_MODE_ASM,	RISCV_S_MODE);
+	DEFINE(RISCV_M_MODE_ASM,	RISCV_M_MODE);
 	return 0;
 }
diff --git a/arch/riscv/lib/pbl.lds.S b/arch/riscv/lib/pbl.lds.S
index e580ec06f32c..84c4917e67ad 100644
--- a/arch/riscv/lib/pbl.lds.S
+++ b/arch/riscv/lib/pbl.lds.S
@@ -5,6 +5,7 @@
 #include <asm/barebox.lds.h>
 #include <asm-generic/memory_layout.h>
 #include <asm-generic/pointer.h>
+#include <asm/asm-offsets.h>
 
 OUTPUT_ARCH(BAREBOX_OUTPUT_ARCH)
 OUTPUT_FORMAT(BAREBOX_OUTPUT_FORMAT)
@@ -18,6 +19,9 @@ SECTIONS
 	.text      :
 	{
 		_stext = .;
+		/* Custom image headers go before the common heads */
+		*(.text_image_header*)
+		*(.text_head_prologue*)
 		*(.text_head_entry*)
 		__bare_init_start = .;
 		*(.text_bare_init*)
@@ -28,8 +32,15 @@ SECTIONS
 	BAREBOX_BARE_INIT_SIZE
 	BAREBOX_PBL_SIZE
 
-	. = ALIGN(4);
+	. = ALIGN(ASM_SZPTR);
 	__start_rodata = .;
+	__pbl_board_info = .;
+	.rodata.pbl_board_info : {
+		*(.pbl_board_info_*)
+	}
+	ASSERT(. - __pbl_board_info <= PBI_SIZEOF, "Only one PBL per image allowed")
+
+	. = ALIGN(4);
 	.rodata : { *(.rodata*) }
 
 	. = ALIGN(ASM_SZPTR);
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 09/17] RISC-V: board-dt-2nd: migrate to board info entry point
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (7 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 08/17] RISC-V: add PBL heads for the board info stage dispatch Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 10/17] RISC-V: hifive: " Ahmad Fatoum
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

The Linux image header moves into a head of its own, which branches into
the supervisor mode PBL head. That head records the mode and stashes the
hart ID already, so the entry point no longer needs to.

The generic image can't know its UART in advance, so the board init
callback still looks it up by root compatible. The chainload callback
keeps barebox proper clear of QEMU's FDT as before.

Assisted-by: Claude:fable-5.1
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/boot/Makefile            |  2 +-
 arch/riscv/boot/board-dt-2nd-head.S | 36 ++++++++++++
 arch/riscv/boot/board-dt-2nd.c      | 89 +++++++++++++----------------
 3 files changed, 78 insertions(+), 49 deletions(-)
 create mode 100644 arch/riscv/boot/board-dt-2nd-head.S

diff --git a/arch/riscv/boot/Makefile b/arch/riscv/boot/Makefile
index d98adf7f724b..7878d85b3e64 100644
--- a/arch/riscv/boot/Makefile
+++ b/arch/riscv/boot/Makefile
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 pbl-y += head.o entry.o entry_ll.o uncompress.o
 pbl-y += nmon_entry.o
-pbl-$(CONFIG_BOARD_GENERIC_DT) += board-dt-2nd.o
+pbl-$(CONFIG_BOARD_GENERIC_DT) += board-dt-2nd.o board-dt-2nd-head.o
diff --git a/arch/riscv/boot/board-dt-2nd-head.S b/arch/riscv/boot/board-dt-2nd-head.S
new file mode 100644
index 000000000000..32f84412dcfe
--- /dev/null
+++ b/arch/riscv/boot/board-dt-2nd-head.S
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <linux/linkage.h>
+#include <asm/image.h>
+
+#if __riscv_xlen == 64
+#define IMAGE_LOAD_OFFSET 0x200000 /* Image load offset (2MB) from start of RAM */
+#else
+#define IMAGE_LOAD_OFFSET 0x400000 /* Image load offset (4MB) from start of RAM */
+#endif
+
+/*
+ * Linux RISC-V image header, so barebox-dt-2nd.img boots like a kernel.
+ * The stack grows down from the image start into the load offset.
+ */
+.section .text_image_header_dt_2nd, "ax"
+ENTRY(__dt_2nd_head)
+	/* Relaxation and compressed instructions would shift the fields */
+	.option push
+	.option norelax
+	.option norvc
+	nop				/* code0 */
+	j	1f			/* code1 */
+	.balign	8
+	.dword	IMAGE_LOAD_OFFSET	/* Image load offset from start of RAM */
+	.dword	_barebox_image_size	/* Effective Image size */
+	.dword	__HEAD_FLAGS		/* Kernel flags */
+	.word	RISCV_HEADER_VERSION	/* version */
+	.word	0			/* reserved */
+	.dword	0			/* reserved */
+	.ascii	RISCV_IMAGE_MAGIC	/* magic 1 */
+	.ascii	RISCV_IMAGE_MAGIC2	/* magic 2 */
+	.word	0			/* reserved (PE-COFF offset) */
+	.option pop
+1:
+	j	__barebox_pbl_head_v1_supervisor
+ENDPROC(__dt_2nd_head)
diff --git a/arch/riscv/boot/board-dt-2nd.c b/arch/riscv/boot/board-dt-2nd.c
index a5e82257e269..36c0d5faa984 100644
--- a/arch/riscv/boot/board-dt-2nd.c
+++ b/arch/riscv/boot/board-dt-2nd.c
@@ -4,70 +4,57 @@
 #include <asm/sections.h>
 #include <linux/sizes.h>
 #include <asm/ns16550.h>
-#include <pbl.h>
-#include <fdt.h>
-
-#if __riscv_xlen == 64
-#define IMAGE_LOAD_OFFSET 0x200000 /* Image load offset(2MB) from start of RAM */
-#else
-#define IMAGE_LOAD_OFFSET 0x400000 /* Image load offset(4MB) from start of RAM */
-#endif
-
-/* because we can depend on being loaded at an offset, we can just use
- * our load address as stack top
- */
-#define __barebox_riscv_head() \
-	__barebox_riscv_header("auipc sp, 0", IMAGE_LOAD_OFFSET, \
-			       RISCV_HEADER_VERSION, "RISCV", RISCV_IMAGE_MAGIC2)
-
 #include <asm/barebox-riscv.h>
+#include <pbl.h>
+#include <pbl/entry.h>
+#include <fdt.h>
 
 static void virt_ns16550_putc(void *base, int ch)
 {
-	early_ns16550_putc(ch, base, 0, readb, writeb);
+	early_ns16550_putc(ch, IOMEM(base), 0, readb, writeb);
 }
 
-static void virt_ns16550_init(void)
+static void virt_ns16550_setup(void *base)
 {
-	void __iomem *base = IOMEM(0x10000000);
-
-	early_ns16550_init(base, 3686400 / CONFIG_BAUDRATE, 0, writeb);
-	pbl_set_putc(virt_ns16550_putc, base);
+	early_ns16550_init(IOMEM(base), 3686400 / CONFIG_BAUDRATE, 0, writeb);
 }
 
+static struct pbl_console riscv_virtio_console = {
+	.setup = virt_ns16550_setup,
+	.arg = (void *)0x10000000,
+	.driver = &(struct pbl_console_driver) { virt_ns16550_putc }
+};
+
 static const struct fdt_device_id console_ids[] = {
-	{ .compatible = "riscv-virtio", .data = virt_ns16550_init },
+	{ .compatible = "riscv-virtio", .data = &riscv_virtio_console },
 	{ /* sentinel */ }
 };
 
-static void noinline __noreturn start_dt_2nd_nonnaked(unsigned long hartid,
-						      unsigned long _fdt)
+/* entry_data is the device tree the image was started with, if any */
+static void dt_2nd_bdinit(unsigned long entry_data)
 {
-	unsigned long membase, memsize, endmem, endfdt, barebox_size;
-	struct fdt_header *fdt = (void *)_fdt;
-	void (*pbl_uart_init)(void);
+	struct fdt_header *fdt = (void *)entry_data;
+	const struct pbl_console *console;
 
 	if (!fdt)
 		hang();
 
-	/*
-	 * We need to call this here, as a multiplatform build
-	 * depends on querying mode for riscv_vendor_id()
-	 */
-	riscv_set_flags(RISCV_S_MODE);
+	console = fdt_device_get_match_data(fdt, "/", console_ids);
+	if (console)
+		__pbl_board_info.console = *console;
 
-	relocate_to_current_adr();
-	setup_c();
+	pbl_get_stage()->board_data = fdt;
+}
 
-	pbl_uart_init = fdt_device_get_match_data(fdt, "/", console_ids);
-	if (pbl_uart_init) {
-		pbl_uart_init();
-		putchar('>');
-	}
+static void __noreturn dt_2nd_chainload(void *boarddata)
+{
+	unsigned long membase, memsize, endmem, endfdt, barebox_size;
+	unsigned long boarddata_ul = (unsigned long)boarddata;
+	struct fdt_header *fdt = boarddata;
 
 	fdt_find_mem(fdt, &membase, &memsize);
 	endmem = membase + memsize;
-	endfdt = _fdt + be32_to_cpu(fdt->totalsize);
+	endfdt = boarddata_ul + be32_to_cpu(fdt->totalsize);
 
 	/*
 	 * QEMU likes to place the FDT at the end of RAM, where barebox
@@ -80,13 +67,19 @@ static void noinline __noreturn start_dt_2nd_nonnaked(unsigned long hartid,
 	handoff_data_add_dt(fdt);
 
 	if (riscv_mem_barebox_image(membase, endmem, barebox_size, NULL) < endfdt &&
-	    _fdt < riscv_mem_stack_top(membase, endmem))
-		memsize = ALIGN_DOWN(_fdt - membase, SZ_1M);
+	    boarddata_ul < riscv_mem_stack_top(membase, endmem))
+		memsize = ALIGN_DOWN(boarddata_ul - membase, SZ_1M);
 
-	barebox_riscv_supervisor_entry(membase, memsize, hartid, NULL);
+	/* The head has set the mode and the hart ID in tp already */
+	barebox_riscv_entry(membase, memsize, NULL, RISCV_S_MODE);
 }
 
-ENTRY_FUNCTION(start_dt_2nd, hartid, _fdt, a2)
-{
-	start_dt_2nd_nonnaked(hartid, _fdt);
-}
+/* The console depends on the machine, so dt_2nd_bdinit() looks it up */
+extern char __dt_2nd_head[];
+
+ENTRY_POINT_HEAD(start_dt_2nd, __dt_2nd_head)
+	.soc_type = SOC_TYPE_GENERIC_DT,
+	.second_stage.stacktop = PBL_STACKTOP_BEFORE_BAREBOX,
+	.second_stage.board_init = dt_2nd_bdinit,
+	.second_stage.soc_chainload = dt_2nd_chainload,
+ENTRY_POINT_END
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 10/17] RISC-V: hifive: migrate to board info entry point
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (8 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 09/17] RISC-V: board-dt-2nd: migrate to board info entry point Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 11/17] RISC-V: allow relocating the PBL more than once Ahmad Fatoum
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

OpenSBI starts the HiFive images like a kernel, so they use the
supervisor mode head. Both boards hand the first 128M of DRAM to barebox
proper, so that goes into the new SiFive SoC defaults and the boards
only add their built-in device tree.

The '>' is now printed by the stage dispatch.

Assisted-by: Claude:fable-5.1
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/boards/hifive/lowlevel.c | 30 ++++++++-------------------
 include/mach/sifive/entry.h         | 32 +++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 21 deletions(-)
 create mode 100644 include/mach/sifive/entry.h

diff --git a/arch/riscv/boards/hifive/lowlevel.c b/arch/riscv/boards/hifive/lowlevel.c
index 5e8969bef1da..ab60c7e96a78 100644
--- a/arch/riscv/boards/hifive/lowlevel.c
+++ b/arch/riscv/boards/hifive/lowlevel.c
@@ -1,26 +1,14 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
-#include <common.h>
-#include <asm/barebox-riscv.h>
-#include <debug_ll.h>
+#include <mach/sifive/entry.h>
 
-static __always_inline void start_hifive(unsigned long hartid, void *fdt)
-{
-	putc_ll('>');
+extern char __dtb_z_hifive_unmatched_a00_start[];
+extern char __dtb_z_hifive_unleashed_a00_start[];
 
-	barebox_riscv_supervisor_entry(0x80000000, SZ_128M, hartid, fdt);
-}
+ENTRY_POINT(FU740, start_hifive_unmatched)
+	.second_stage.board_data = __dtb_z_hifive_unmatched_a00_start,
+ENTRY_POINT_END
 
-ENTRY_FUNCTION(start_hifive_unmatched, a0, a1, a2)
-{
-	extern char __dtb_z_hifive_unmatched_a00_start[];
-
-	start_hifive(a0, __dtb_z_hifive_unmatched_a00_start + get_runtime_offset());
-}
-
-ENTRY_FUNCTION(start_hifive_unleashed, a0, a1, a2)
-{
-	extern char __dtb_z_hifive_unleashed_a00_start[];
-
-	start_hifive(a0, __dtb_z_hifive_unleashed_a00_start + get_runtime_offset());
-}
+ENTRY_POINT(FU540, start_hifive_unleashed)
+	.second_stage.board_data = __dtb_z_hifive_unleashed_a00_start,
+ENTRY_POINT_END
diff --git a/include/mach/sifive/entry.h b/include/mach/sifive/entry.h
new file mode 100644
index 000000000000..04529099dcf4
--- /dev/null
+++ b/include/mach/sifive/entry.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __MACH_SIFIVE_ENTRY_H
+#define __MACH_SIFIVE_ENTRY_H
+
+#include <linux/sizes.h>
+#include <pbl/entry.h>
+#include <asm/barebox-riscv.h>
+
+#define SIFIVE_DRAM_BASE	0x80000000
+
+/*
+ * OpenSBI starts the image in supervisor mode in DRAM. Every board has
+ * at least 128M, boards with more can raise mem_minsz. OpenSBI leaves
+ * the UART set up, so there's no console and the PBL uses DEBUG_LL.
+ */
+#define PBL_SOC_SIFIVE_DEFAULTS(soc)				\
+	.soc_type = SOC_TYPE_##soc,				\
+	.second_stage = {					\
+		.stacktop = SIFIVE_DRAM_BASE + SZ_128M,		\
+		.mem_start = SIFIVE_DRAM_BASE,			\
+		.mem_minsz = SZ_128M,				\
+		.soc_chainload = pbl_stage_barebox_entry,	\
+	}
+
+#define PBL_SOC_FU540_HEAD	__barebox_pbl_head_v1_supervisor
+#define PBL_SOC_FU540_DEFAULTS	PBL_SOC_SIFIVE_DEFAULTS(FU540)
+
+#define PBL_SOC_FU740_HEAD	__barebox_pbl_head_v1_supervisor
+#define PBL_SOC_FU740_DEFAULTS	PBL_SOC_SIFIVE_DEFAULTS(FU740)
+
+#endif
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 11/17] RISC-V: allow relocating the PBL more than once
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (9 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 10/17] RISC-V: hifive: " Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 12/17] RISC-V: nmon: preserve s1-s3 for the caller Ahmad Fatoum
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

relocate_image() doesn't update the relocation entries, so a second
relocation applies an offset relative to the first runtime address to
the link-time addresses and writes wrong values to wrong places.

This happens when the PBL is first relocated in place and
barebox_pbl_start() then copies it into the memory it was passed. ARM64
has handled this since fab26e665163 ("ARM: aarch64: Fixup relocation
table for the second relocation"), so let's rebase each entry after
applying it the same way.

Like on ARM, relocate_to_adr() now also copies the BSS, as it's already
in use and setup_c() won't clear it again at the destination.

Tested on QEMU virt with an image whose stage memory starts above the
address OpenSBI starts it at, which used to fault right after relocation.

Assisted-by: Claude:opus-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/lib/reloc.c  | 6 +++++-
 arch/riscv/lib/setupc.S | 2 +-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/arch/riscv/lib/reloc.c b/arch/riscv/lib/reloc.c
index 99dde7bfffe2..9eede11b3528 100644
--- a/arch/riscv/lib/reloc.c
+++ b/arch/riscv/lib/reloc.c
@@ -46,7 +46,7 @@ static void relocate_image(unsigned long offset,
 
 		switch (RISC_R_TYPE(rela->r_info)) {
 		case R_RISCV_NONE:
-			break;
+			continue;
 		case R_RISCV_RELATIVE:
 			*fixup = rela->r_addend + offset;
 			break;
@@ -63,6 +63,10 @@ static void relocate_image(unsigned long offset,
 			putc_ll('\n');
 			__hang();
 		}
+
+		/* Rebase the entry, so the image can be relocated again */
+		rela->r_addend += offset;
+		rela->r_offset += offset;
 	}
 
 }
diff --git a/arch/riscv/lib/setupc.S b/arch/riscv/lib/setupc.S
index 68924f2f94df..ba9193288a81 100644
--- a/arch/riscv/lib/setupc.S
+++ b/arch/riscv/lib/setupc.S
@@ -53,7 +53,7 @@ ENTRY(relocate_to_adr_full)
 ENDPROC(relocate_to_adr_full)
 
 ENTRY(relocate_to_adr)
-	lla	a3, __bss_start		/* skip piggy data */
+	lla	a3, __bss_stop		/* skip piggy data */
 1:
 	addi	sp, sp, -SZREG * 2
 	lla	a1, _text		/* a1: source address */
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 12/17] RISC-V: nmon: preserve s1-s3 for the caller
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (10 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 11/17] RISC-V: allow relocating the PBL more than once Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 13/17] RISC-V: beaglev: migrate to board info entry point Ahmad Fatoum
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

nmon clobbers s1-s3, but barebox_nmon_entry() is called from C, where
these registers are callee-saved. With NMON enabled, BeagleV thus starts
barebox proper with the 'q' that left nmon as hart ID. The upcoming
stage dispatch also keeps its state in these registers.

There may be no stack yet, so let's stash them in a4-a6, which neither
nmon nor the DEBUG_LL macros touch.

Fixes: 1e29b4d8024a ("RISC-V: make it possible to run nmon from PBL C code")
Assisted-by: Claude:opus-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/boot/nmon_entry.S | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/riscv/boot/nmon_entry.S b/arch/riscv/boot/nmon_entry.S
index ae017d2a1cf5..54ff3d48d299 100644
--- a/arch/riscv/boot/nmon_entry.S
+++ b/arch/riscv/boot/nmon_entry.S
@@ -8,6 +8,13 @@
 .section .text_bare_init
 
 ENTRY(__barebox_nmon_entry)
+	/* nmon uses s1-s3, keep them in registers it leaves alone */
+	mv	a4, s1
+	mv	a5, s2
+	mv	a6, s3
 	riscv_nmon
+	mv	s1, a4
+	mv	s2, a5
+	mv	s3, a6
 	ret
 ENDPROC(__barebox_nmon_entry)
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 13/17] RISC-V: beaglev: migrate to board info entry point
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (11 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 12/17] RISC-V: nmon: preserve s1-s3 for the caller Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 14/17] RISC-V: allwinner-d1: " Ahmad Fatoum
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

OpenSBI starts the BeagleV image below the 512M at 0x84000000 that
barebox gets. Instead of relocating in place and having
barebox_pbl_start() copy the PBL again, the stage names the region as
relocaddr, so the PBL is relocated once, directly into it.

DEBUG_LL init and nmon go into the CPU init callback, which runs before
relocation like the old entry point did. The '>' is now printed by the
stage dispatch.

Assisted-by: Claude:opus-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/boards/beaglev/lowlevel.c | 30 +++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/arch/riscv/boards/beaglev/lowlevel.c b/arch/riscv/boards/beaglev/lowlevel.c
index db884dc762ce..b2eab26c3ca5 100644
--- a/arch/riscv/boards/beaglev/lowlevel.c
+++ b/arch/riscv/boards/beaglev/lowlevel.c
@@ -2,19 +2,31 @@
 
 #include <common.h>
 #include <debug_ll.h>
+#include <pbl/entry.h>
 #include <asm/barebox-riscv.h>
 #include <asm/riscv_nmon.h>
 
-ENTRY_FUNCTION(start_beaglev_starlight, a0, a1, a2)
-{
-	extern char __dtb_z_jh7100_beaglev_starlight_start[];
-	void *fdt;
+#define BEAGLEV_MEM_BASE	0x84000000
 
+extern char __dtb_z_jh7100_beaglev_starlight_start[];
+
+static void __prereloc beaglev_cpu_init(void)
+{
 	debug_ll_init();
 	barebox_nmon_entry();
-	putc_ll('>');
-
-	fdt = __dtb_z_jh7100_beaglev_starlight_start + get_runtime_offset();
-
-	barebox_riscv_supervisor_entry(0x84000000, SZ_512M, a0, fdt);
 }
+
+/*
+ * OpenSBI starts its payload below the memory handed to barebox, so the
+ * PBL relocates there first.
+ */
+ENTRY_POINT_HEAD(start_beaglev_starlight, __barebox_pbl_head_v1_supervisor)
+	.soc_type = SOC_TYPE_JH7100,
+	.second_stage.stacktop = BEAGLEV_MEM_BASE + SZ_512M,
+	.second_stage.relocaddr = BEAGLEV_MEM_BASE,
+	.second_stage.mem_start = BEAGLEV_MEM_BASE,
+	.second_stage.mem_minsz = SZ_512M,
+	.second_stage.board_data = __dtb_z_jh7100_beaglev_starlight_start,
+	.second_stage.prereloc_cpu_init = beaglev_cpu_init,
+	.second_stage.soc_chainload = pbl_stage_barebox_entry,
+ENTRY_POINT_END
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 14/17] RISC-V: allwinner-d1: migrate to board info entry point
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (12 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 13/17] RISC-V: beaglev: migrate to board info entry point Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 15/17] RISC-V: erizo: " Ahmad Fatoum
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

OpenSBI enters the D1 image like a kernel, so it uses the supervisor mode
head, which stashes the hart ID in tp and records the device tree in a1
as entry data. The board init callback makes that the stage's board_data
for pbl_stage_barebox_entry() to pass on. Memory region and stack top are
unchanged.

Assisted-by: Claude:opus-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/boards/allwinner-d1/lowlevel.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/arch/riscv/boards/allwinner-d1/lowlevel.c b/arch/riscv/boards/allwinner-d1/lowlevel.c
index 2b07a81edbfa..eb21662f5ea2 100644
--- a/arch/riscv/boards/allwinner-d1/lowlevel.c
+++ b/arch/riscv/boards/allwinner-d1/lowlevel.c
@@ -1,12 +1,22 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
 #include <common.h>
-#include <debug_ll.h>
+#include <pbl/entry.h>
 #include <asm/barebox-riscv.h>
 
-#define DRAM_BASE	0x40000000
+#define DRAM_BASE	0x40000000UL
 
-ENTRY_FUNCTION(start_allwinner_d1, a0, a1, a2)
+/* OpenSBI passes on the device tree the SPL loaded */
+static void allwinner_d1_bdinit(unsigned long entry_data)
 {
-	barebox_riscv_supervisor_entry(DRAM_BASE, SZ_1G, a0, (void *)a1);
+	pbl_get_stage()->board_data = (void *)entry_data;
 }
+
+ENTRY_POINT_HEAD(start_allwinner_d1, __barebox_pbl_head_v1_supervisor)
+	.soc_type = SOC_TYPE_SUN20I_D1,
+	.second_stage.stacktop = DRAM_BASE + SZ_1G,
+	.second_stage.mem_start = DRAM_BASE,
+	.second_stage.mem_minsz = SZ_1G,
+	.second_stage.board_init = allwinner_d1_bdinit,
+	.second_stage.soc_chainload = pbl_stage_barebox_entry,
+ENTRY_POINT_END
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 15/17] RISC-V: erizo: migrate to board info entry point
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (13 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 14/17] RISC-V: allwinner-d1: " Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 16/17] RISC-V: litex-linux: " Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 17/17] Documentation: porting: describe the " Ahmad Fatoum
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

Erizo starts barebox from read-only memory on POR, which can be the
boot ROM, the SPI flash or QEMU's BIOS ROM, so the old code copied the
PBL to the start of SDRAM when it wasn't running from there. The stage
names that as relocaddr instead, which is skipped when the image was
loaded there.

DEBUG_LL initialization and nmon go into the CPU init callback, which
runs before relocation, so nmon still runs from where the image was
started and can load a new one into SDRAM. The '>' now comes from the
stage dispatch.

Assisted-by: Claude:opus-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/boards/erizo/lowlevel.c | 32 ++++++++++++++++++++----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/arch/riscv/boards/erizo/lowlevel.c b/arch/riscv/boards/erizo/lowlevel.c
index ab06634bdb8d..ff588b56ee4b 100644
--- a/arch/riscv/boards/erizo/lowlevel.c
+++ b/arch/riscv/boards/erizo/lowlevel.c
@@ -1,22 +1,32 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
 #include <common.h>
+#include <pbl/entry.h>
 #include <asm/barebox-riscv.h>
 #include <debug_ll.h>
 #include <asm/riscv_nmon.h>
 
-ENTRY_FUNCTION(start_erizo_generic, a0, a1, a2)
-{
-	extern char __dtb_z_erizo_generic_start[];
-	void *fdt;
+#define ERIZO_SDRAM_BASE	0x80000000
 
+extern char __dtb_z_erizo_generic_start[];
+
+static void __prereloc erizo_cpu_init(void)
+{
 	debug_ll_init();
 	barebox_nmon_entry();
-	putc_ll('>');
-
-	/* On POR, we are running from read-only memory here. */
-
-	fdt = __dtb_z_erizo_generic_start + get_runtime_offset();
-
-	barebox_riscv_machine_entry(0x80000000, SZ_8M, fdt);
 }
+
+/*
+ * On POR, we are running from read-only memory, so the PBL relocates to
+ * the start of SDRAM. It stays in place if it was loaded there.
+ */
+ENTRY_POINT_HEAD(start_erizo_generic, __barebox_pbl_head_v1_machine)
+	.soc_type = SOC_TYPE_ERIZO,
+	.second_stage.stacktop = ERIZO_SDRAM_BASE + SZ_8M,
+	.second_stage.relocaddr = ERIZO_SDRAM_BASE,
+	.second_stage.mem_start = ERIZO_SDRAM_BASE,
+	.second_stage.mem_minsz = SZ_8M,
+	.second_stage.board_data = __dtb_z_erizo_generic_start,
+	.second_stage.prereloc_cpu_init = erizo_cpu_init,
+	.second_stage.soc_chainload = pbl_stage_barebox_entry,
+ENTRY_POINT_END
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 16/17] RISC-V: litex-linux: migrate to board info entry point
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (14 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 15/17] RISC-V: erizo: " Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  2026-10-27  7:24 ` [RFC PATCH 17/17] Documentation: porting: describe the " Ahmad Fatoum
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

The LiteX BIOS loads barebox into RAM and starts it in machine mode, so
the image uses the machine mode head and the stage describes the RAM
the old entry point passed to barebox_riscv_machine_entry().

nmon goes into the CPU init callback, which runs before relocation like
the old entry point did. The '>' now comes from the stage dispatch.

Assisted-by: Claude:opus-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/riscv/boards/litex-linux/lowlevel.c | 30 ++++++++++++++----------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/arch/riscv/boards/litex-linux/lowlevel.c b/arch/riscv/boards/litex-linux/lowlevel.c
index 6be3d62e64fb..2d7713cd48fc 100644
--- a/arch/riscv/boards/litex-linux/lowlevel.c
+++ b/arch/riscv/boards/litex-linux/lowlevel.c
@@ -1,22 +1,26 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
 #include <common.h>
+#include <pbl/entry.h>
 #include <asm/barebox-riscv.h>
-#include <debug_ll.h>
 #include <asm/riscv_nmon.h>
 
-ENTRY_FUNCTION(start_litex_linux, a0, a1, a2)
+#define LITEX_RAM_BASE	0x40000000
+
+extern char __dtb_z_litex_linux_start[];
+
+static void __prereloc litex_cpu_init(void)
 {
-	extern char __dtb_z_litex_linux_start[];
-	void *fdt;
-
-	/* BIOS will have loaded us into RAM */
-
 	barebox_nmon_entry();
-
-	putc_ll('>');
-
-	fdt = __dtb_z_litex_linux_start + get_runtime_offset();
-
-	barebox_riscv_machine_entry(0x40000000, SZ_256M, fdt);
 }
+
+/* BIOS will have loaded us into RAM */
+ENTRY_POINT_HEAD(start_litex_linux, __barebox_pbl_head_v1_machine)
+	.soc_type = SOC_TYPE_LITEX,
+	.second_stage.stacktop = LITEX_RAM_BASE + SZ_256M,
+	.second_stage.mem_start = LITEX_RAM_BASE,
+	.second_stage.mem_minsz = SZ_256M,
+	.second_stage.board_data = __dtb_z_litex_linux_start,
+	.second_stage.prereloc_cpu_init = litex_cpu_init,
+	.second_stage.soc_chainload = pbl_stage_barebox_entry,
+ENTRY_POINT_END
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 17/17] Documentation: porting: describe the board info entry point
  2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
                   ` (15 preceding siblings ...)
  2026-10-27  7:24 ` [RFC PATCH 16/17] RISC-V: litex-linux: " Ahmad Fatoum
@ 2026-10-27  7:24 ` Ahmad Fatoum
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2026-10-27  7:24 UTC (permalink / raw)
  To: barebox; +Cc: mfe, mtr, Ahmad Fatoum

Describe ENTRY_POINT() and the stage dispatch as the way to write a
board's PBL entry point. The description of the entry point written by
hand moves into a section of its own below it and the section on image
heads gains the stage dispatch a head branches to.

Assisted-by: Claude:fable-5.1
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 Documentation/devel/porting.rst | 124 ++++++++++++++++++++++++++++++--
 1 file changed, 119 insertions(+), 5 deletions(-)

diff --git a/Documentation/devel/porting.rst b/Documentation/devel/porting.rst
index 3676e85d181c..d5f0f955085a 100644
--- a/Documentation/devel/porting.rst
+++ b/Documentation/devel/porting.rst
@@ -40,9 +40,91 @@ Entry point
 ===========
 
 The PBL's entry point is the first of your code that's run. What happens
-there depends on the previously running code. If a previous stage has already
-initialized the DRAM, the only thing you need to do is to set up a stack and
-call the common PBL code with a memory region and your device tree blob::
+there depends on the previously running code. Boards describe it with
+``ENTRY_POINT()`` and leave setting up stack, relocation and BSS to common
+code. If a previous stage has already initialized the DRAM, the only thing
+you need to do is to describe the memory region, your console and your
+device tree blob::
+
+  #include <pbl/entry.h>
+  #include <mach/mysoc/entry.h>
+
+  extern char __dtb_z_my_board_start[];
+
+  ENTRY_POINT(MYSOC, start_my_board)
+  	.console.arg = IOMEM(MY_UART_BASE),
+  	.second_stage.mem_start = 0x80000000,
+  	.second_stage.mem_minsz = SZ_256M,
+  	.second_stage.board_data = __dtb_z_my_board_start,
+  ENTRY_POINT_END
+
+Let's look at this line by line:
+
+``ENTRY_POINT(MYSOC, start_my_board)``
+   Defines the entry point ``start_my_board`` and a board info, which
+   starts out with the defaults from ``<mach/mysoc/entry.h>``, see
+   :ref:`entry_point_defaults`. Fields set by the board override them.
+   For a SoC without defaults, use ``ENTRY_POINT_HEAD(name, head)`` and
+   set ``.soc_type`` yourself.
+
+``.console.arg = IOMEM(MY_UART_BASE)``
+   The SoC defaults usually provide the console driver and, if the UART
+   isn't usable on entry, a setup callback, so the board only needs to
+   name its UART. Without a console, the PBL prints through ``DEBUG_LL``.
+
+``.second_stage``
+   A board info has up to three stages: ``rom_stage`` for executing in
+   place from ROM, ``first_stage`` for SRAM and ``second_stage`` for DRAM.
+   On entry, the stage whose memory contains the program counter is
+   selected, or the earliest stage with callbacks if there is none.
+
+``.second_stage.mem_start = 0x80000000``
+   ``mem_start`` and ``mem_minsz`` describe the memory the stage runs from.
+   barebox proper starts out with this memory and may add more later on.
+   If the memory can be larger than ``mem_minsz``, set ``mem_maxsz``, so
+   the stage is still found by program counter.
+
+``.second_stage.board_data = __dtb_z_my_board_start``
+   The device tree passed to barebox proper, see ``__dtb_z_*`` below.
+   Boards that select their device tree at runtime call
+   ``handoff_data_add_dt()`` from their board init callback instead.
+
+A stage can also have a stack top (``stacktop``), an address to relocate
+to (``relocaddr``) and callbacks, which run in this order:
+
+``prereloc_cpu_init``
+   Runs before relocation and before BSS is cleared, so it must be
+   ``__prereloc`` and may not access global variables.
+
+``soc_init``
+   SoC lowlevel initialization.
+
+``board_init``
+   Board setup like DRAM initialization. Its ``entry_data`` argument is
+   a register the image head saved, e.g. the device tree on RISC-V.
+
+``soc_chainload``
+   Doesn't return. Usually enters barebox proper, e.g. with
+   ``pbl_stage_barebox_entry()``, which passes it the stage's memory and
+   ``board_data``.
+
+The SoC defaults usually provide the SoC callbacks, so a board typically
+only sets ``board_init``. If the PBL runs from SRAM first, because the
+whole image doesn't fit, ``first_stage.board_init`` sets up DRAM and
+``first_stage.soc_chainload`` loads the full image, which is then entered
+through ``second_stage``.
+
+The console is brought up before ``soc_init`` if it needs no setup, else
+after it and failing that after ``board_init``. Then the number of the
+stage is printed followed by ``>``, e.g. ``2>``, see :ref:`troubleshooting`.
+
+Unrelocated ENTRY_FUNCTION
+--------------------------
+
+Boards not using ``ENTRY_POINT()`` write the entry point as a C function
+that does the above by hand. If a previous stage has already initialized
+the DRAM, the only thing you need to do is to set up a stack and call the
+common PBL code with a memory region and your device tree blob::
 
   #include <asm/barebox-arm.h>
   #include <console.h>
@@ -127,7 +209,8 @@ Looking at other boards you might see some different patterns:
    stack beforehand. If either a barebox assembly entry point,
    ``ENTRY_FUNCTION_WITHSTACK`` or earlier firmware has set up the stack, there is
    no reason to use ``__naked``, just use ``ENTRY_FUNCTION_WITHSTACK`` with a zero
-   stack top.
+   stack top. Functions that run before the stack is set up are additionally
+   marked ``__unsafe_prestack`` for documentation.
 
 ``noinline``
    Compiler code inlining is oblivious to stack manipulation in
@@ -337,17 +420,46 @@ and the entry point should be written in a dedicated assembly file.
 This is the case with ARM64, see for example ``__barebox_arm64_head`` and the
 ``ENTRY_PROC`` macro.
 
+For ``ENTRY_POINT()``, the image head does what the boot protocol
+requires, like recording the privilege mode on RISC-V, and branches to
+the stage dispatch ``pbl_head_start_v1()`` with a boot argument register
+as ``entry_data``, see ``arch/riscv/boot/head.S``. ``pbl_head_start_v1()``
+runs before there is a stack, so architectures should implement it in
+assembly and select ``ARCH_HAS_PBL_HEAD_START_ASM``. The C version in
+``pbl/entry.c`` is only a fallback, because even in a naked function,
+the compiler may spill to the stack.
+
 Another way, which is often used for non-executable headers with extra
 meta-information like a checksum, is adding a new tool to ``scripts/``
 and have it run as part of the image build process. ``images/`` contains
 various examples.
 
+.. _entry_point_defaults:
+
+Entry point defaults
+====================
+
+Boards of the same SoC share its boot flow, so it's described once in
+``include/mach/$platform/entry.h``, e.g. ``include/mach/sifive/entry.h``:
+
+``PBL_SOC_<SOC>_HEAD``
+   The image head. Usually one the architecture provides for the boot
+   protocol, unless the BootROM requires a special header.
+
+``PBL_SOC_<SOC>_DEFAULTS``
+   The board info fields ``ENTRY_POINT(<SOC>, ...)`` starts out with,
+   at least ``.soc_type``, the SoC's entry in ``include/pbl/soc-type.h``.
+   barebox proper can query it with ``barebox_soc_type()``.
+
+Functions written for the defaults carry the ``PBL_SOC_<SOC>_`` prefix,
+so they aren't mistaken for the lowlevel API older boards call.
+
 Memory controller setup
 =======================
 
 If you've an external DRAM controller, you will need to configure it.
 This may involve enabling clocks and PLLs. This should all happen
-in the PBL entry point.
+in the PBL entry point, i.e. the first stage's board init callback.
 
 Chainloading
 ============
@@ -360,6 +472,8 @@ One good way to go about it is to check whether the program counter
 is in DRAM or SRAM. If in DRAM, we can assume that the image was loaded
 in full and we can just go into the common PBL entry and extract barebox
 proper. If in SRAM, we'll need to load the remainder from the boot medium.
+This is the distinction between the first and second stage of a board
+info entry point.
 
 This loading requires the PBL to have a driver for the boot medium as
 well as its prerequisites like clocks, resets or pin multiplexers.
-- 
2.47.3




^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2026-10-27  8:59 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-27  7:24 [RFC PATCH 00/17] Switch RISC-V to declarative entry points Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 01/17] asm-generic: add registers.h with a pc_inside() helper Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 02/17] RISC-V: add call_with_stack() helper Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 03/17] RISC-V: implement relocate_to_adr_full() Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 04/17] PBL: add SoC type declaration and handoff support Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 05/17] PBL: introduce board info based entry point framework Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 06/17] PBL: describe the console in the board info and report the stage Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 07/17] RISC-V: make barebox_riscv_entry() non-naked Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 08/17] RISC-V: add PBL heads for the board info stage dispatch Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 09/17] RISC-V: board-dt-2nd: migrate to board info entry point Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 10/17] RISC-V: hifive: " Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 11/17] RISC-V: allow relocating the PBL more than once Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 12/17] RISC-V: nmon: preserve s1-s3 for the caller Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 13/17] RISC-V: beaglev: migrate to board info entry point Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 14/17] RISC-V: allwinner-d1: " Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 15/17] RISC-V: erizo: " Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 16/17] RISC-V: litex-linux: " Ahmad Fatoum
2026-10-27  7:24 ` [RFC PATCH 17/17] Documentation: porting: describe the " Ahmad Fatoum

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox