From: Ahmad Fatoum <a.fatoum@barebox.org>
To: barebox@lists.infradead.org
Cc: mfe@pengutronix.de, mtr@pengutronix.de,
Ahmad Fatoum <a.fatoum@barebox.org>
Subject: [RFC PATCH 17/17] Documentation: porting: describe the board info entry point
Date: Tue, 27 Oct 2026 08:24:50 +0100 [thread overview]
Message-ID: <20261027074014.3280396-18-a.fatoum@barebox.org> (raw)
In-Reply-To: <20261027074014.3280396-1-a.fatoum@barebox.org>
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
prev parent reply other threads:[~2026-10-27 8:07 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` Ahmad Fatoum [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261027074014.3280396-18-a.fatoum@barebox.org \
--to=a.fatoum@barebox.org \
--cc=barebox@lists.infradead.org \
--cc=mfe@pengutronix.de \
--cc=mtr@pengutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox