[PATCH 1/3] firmware: fw_base.S: add a formatted OpenSBI firmware header
Anup Patel
anup at brainfault.org
Tue Sep 15 22:08:49 PDT 2026
On Tue, Sep 1, 2026 at 7:52 AM Zong Li <zong.li at sifive.com> wrote:
>
> Add a fixed 128-byte header at the very beginning of every OpenSBI
> firmware image, regardless of the firmware type. The header lets the
> previous booting stage identify an OpenSBI image and, more importantly,
> gives it a well-known place inside the image to hand over information
> to OpenSBI by patching a few words instead of setting up registers.
>
> This first patch only introduces the layout:
>
> - a 4-byte jump over the header to _start_real, so that _start stays
> the entry point of the image. The jump is assembled with
> '.option norvc' so that the fields behind it are always at a fixed
> offset, even for a build with compressed instructions enabled,
>
> - the 'OSBI' magic, a header version, the XLEN the firmware was built
> for and the size of the firmware image, so that the previous booting
> stage can validate the image and figure out how much memory it
> occupies,
>
> - a flags word and three 8-byte override values, which are unused
> (and zero) for now and are wired up by the next patch.
>
> The layout is deliberately identical for RV32 and RV64 so that the
> previous booting stage can parse the header without knowing the XLEN of
> the firmware upfront. Each override value is followed by a reserved
> 8-byte slot so that the same layout can be extended to RV128 later.
>
> Suggested-by: Anup Patel <anup at brainfault.org>
> Signed-off-by: Zong Li <zong.li at sifive.com>
> ---
> firmware/fw_base.S | 68 ++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 68 insertions(+)
>
> diff --git a/firmware/fw_base.S b/firmware/fw_base.S
> index 0c5c65c1..dd2adb8a 100644
> --- a/firmware/fw_base.S
> +++ b/firmware/fw_base.S
> @@ -17,6 +17,15 @@
> #define BOOT_LOTTERY_ACQUIRED 1
> #define BOOT_STATUS_BOOT_HART_DONE 1
>
> +/* OpenSBI firmware header */
> +#define FW_HEADER_MAGIC_VALUE 0x4942534f /* ASCII string "OSBI" */
> +#define FW_HEADER_VERSION 0x1
> +#define FW_HEADER_SIZE 128
> +#define FW_HEADER_RESERVED_OFFSET 0x48
> +#define FW_HEADER_FLAGS_OVERRIDE_A0 (1 << 0)
> +#define FW_HEADER_FLAGS_OVERRIDE_A1 (1 << 1)
> +#define FW_HEADER_FLAGS_OVERRIDE_A2 (1 << 2)
> +
> .macro MOV_3R __d0, __s0, __d1, __s1, __d2, __s2
> add \__d0, \__s0, zero
> add \__d1, \__s1, zero
> @@ -44,8 +53,67 @@
> .section .entry, "ax", %progbits
> .align 3
> .globl _start
> + .globl _start_real
> .globl _start_warm
> _start:
> + /*
> + * OpenSBI firmware header
> + *
> + * Every OpenSBI firmware image starts with this fixed 128-byte
> + * header. The layout is identical for RV32 and RV64 (and can be
> + * extended for RV128 by using the reserved half of each override
> + * field), so the previous booting stage can parse the header without
> + * knowing the XLEN of the firmware upfront.
> + *
> + * Offset Size Field
> + * 0x00 4 jump to _start_real
> + * 0x04 4 magic ('OSBI')
> + * 0x08 4 header version
> + * 0x0c 4 firmware XLEN
> + * 0x10 4 firmware size (_fw_end - _fw_start)
> + * 0x14 4 flags (FW_HEADER_FLAGS_*)
> + * 0x18 8 override value for a0
> + * 0x20 8 reserved (upper half of the a0 value on RV128)
Thinking about this more, we don't need "override value of a0" in the
header instead whenever FW_HEADER_FLAGS_OVERRIDE_A0 is
set we can use mhartid CSR value as the value of a0. This will work
better for all CPUs entering at _start.
> + * 0x28 8 override value for a1
> + * 0x30 8 reserved (upper half of the a1 value on RV128)
> + * 0x38 8 override value for a2
> + * 0x40 8 reserved (upper half of the a2 value on RV128)
> + * 0x48 56 reserved
> + */
> +_fw_header_jump:
> + /*
> + * Must stay a 4-byte instruction so that the fields below are always
> + * at a fixed offset. 'norvc' keeps the assembler from picking c.j and
> + * 'norelax' keeps the linker from compressing it later on, which it
> + * would otherwise happily do for a jump this short.
> + */
> + .option push
> + .option norvc
> + .option norelax
> + j _start_real
> + .option pop
> +_fw_header_magic:
> + .word FW_HEADER_MAGIC_VALUE
> +_fw_header_version:
> + .word FW_HEADER_VERSION
> +_fw_header_xlen:
> + .word __riscv_xlen
> +_fw_header_size:
> + .word (_fw_end - _fw_start)
> +_fw_header_flags:
> + .word 0
> +_fw_header_override_a0:
> + .dword 0
> + .dword 0 /* reserved */
> +_fw_header_override_a1:
> + .dword 0
> + .dword 0 /* reserved */
> +_fw_header_override_a2:
> + .dword 0
> + .dword 0 /* reserved */
> + /* Reserved, pads the header up to FW_HEADER_SIZE bytes */
> + .fill (FW_HEADER_SIZE - FW_HEADER_RESERVED_OFFSET), 1, 0
> +_start_real:
> /* Find preferred boot HART id */
> MOV_3R s0, a0, s1, a1, s2, a2
> call fw_boot_hart
> --
> 2.43.7
>
Regards,
Anup
More information about the opensbi
mailing list