2026-08-17·10 min read

RISC-V bootloader lifecycle, walked through one boot log

RISC-VU-BootOpenSBIboot logStarFiveVisionFiveembedded

A single StarFive VisionFive V2 boot log walks through every stage the RISC-V boot chain has. U-Boot SPL loads OpenSBI. OpenSBI runs in M-mode, drops to S-mode, and hands off to full U-Boot. U-Boot boots Linux. If you're coming from ARM or x86, some of the terms: M-mode / S-mode, HARTs, PMP, SBI: don't have direct equivalents; the log below is the shortest route to what they mean in practice.

The full capture is at /samples/bootintel-6.txt. Paste it into the Fingerprinter and the detectors will call out the stage transitions inline. This post walks them in order.

The four stages

StageVersion line as capturedPrivilege
1. U-Boot SPLU-Boot SPL 2021.10 (Nov 10 2022 - 13:29:36 +0800)M-mode
Runs from the SoC boot ROM. Trains DRAM, loads OpenSBI + full U-Boot from SPI flash. Small enough to fit in on-chip SRAM.
2. OpenSBIOpenSBI v1.0 · Platform Name : StarFive VisionFive V2M-mode
RISC-V's M-mode firmware. Owns machine-level state; exposes the Supervisor Binary Interface (SBI) to whatever comes next. Then drops privilege and jumps.
3. U-Boot properU-Boot 2021.10 (Nov 10 2022 - 13:29:36 +0800), Build: jenkins-VF2_515_Branch_SD…S-mode
The bootloader most people mean when they say "U-Boot." Runs in S-mode, reads env from SPI, enumerates MMC/PCIe, loads the kernel + DTB from the boot partition.
4. Linux kernelLinux version 5.15.0-starfive (sw_buildbot@mdcsw02) (riscv64-unknown-…S-mode
The RISC-V kernel port. Takes over S-mode from U-Boot; keeps using OpenSBI (still resident in M-mode) for the operations it can't do itself (IPIs, timers, HART management).

Stage 1: U-Boot SPL loads the world

The first line of the capture:

U-Boot SPL 2021.10 (Nov 10 2022 - 13:29:36 +0800)
DDR version: dc2e84f0.
Trying to boot from SPI

SPL stands for Secondary Program Loader. It's a stripped- down U-Boot that runs from on-chip SRAM (a few tens of KB) with exactly one job: train DRAM, then load the real bootloader from somewhere: SPI flash in this case. Because SRAM is precious, SPL can't contain networking, USB, or half the drivers full U-Boot ships with. Trade-off is deliberate: get DRAM up, get out of the way.

On this board, SPL then hands off to OpenSBI (whose image was loaded alongside U-Boot on SPI). The transition is silent in the log: SPL prints "Trying to boot from SPI" and the next thing the console sees is the OpenSBI banner.

Stage 2: OpenSBI: the piece with no ARM/x86 equivalent

RISC-V has three privilege levels: M (machine, most privileged), S (supervisor, where a Unix kernel runs), and U (user). M-mode owns the hardware; it's where a modern OS never runs.

On ARM you'd call this EL3 / TrustZone Secure Monitor. On x86 you'd think System Management Mode. On RISC-V, the M- mode firmware is typically a standardized open-source implementation called OpenSBI, hosted under the RISC-V Software organization on GitHub and used across most RISC-V Linux platforms. (The SBI specification itself is governed by RISC-V International; OpenSBI is the reference implementation, not the standards body.) It exposes an ABI called the Supervisor Binary Interface (SBI) that the S-mode kernel calls into whenever it needs something only M-mode can do: sending an inter-processor interrupt, reading the machine timer, powering off a HART, and so on. Think Linux syscalls, but running one level deeper in the privilege stack.

The log tells you exactly which OpenSBI is running and what it thinks about the hardware:

OpenSBI v1.0
Platform Name             : StarFive VisionFive V2
Platform Features         : medeleg
Platform HART Count       : 5
Platform IPI Device       : aclint-mswi
Platform Timer Device     : aclint-mtimer @ 4000000Hz
Firmware Base             : 0x40000000
Firmware Size             : 360 KB
Runtime SBI Version       : 0.3

Five HARTs. One boots. The other four wait in a park loop until S-mode brings them online via SBI HSM (HART State Management) calls. This is exactly the kind of thing OpenSBI is for: the kernel doesn't have direct access to inter-HART wakeup registers: it asks M-mode.

HART, not core

RISC-V calls its execution contexts HARTs : Hardware Threads. In practice that's usually one per core, but the term is deliberately looser than "core" because a design can put multiple HARTs on one physical core (analogous to SMT / Hyper-Threading). The JH7110 on this VisionFive V2 exposes five HARTs to OpenSBI: four SiFive U74 application cores plus a smaller SiFive S7 monitor-class core. Elsewhere in the log the kernel prints CPU with hartid=0 is not available and brings up only four CPUs: the monitor HART is masked off in the device tree.

What the Boot HART report tells you

Right after the platform table, OpenSBI prints a per-boot-HART description:

Boot HART ID              : 1
Boot HART Domain          : root
Boot HART Priv Version    : v1.11
Boot HART Base ISA        : rv64imafdcbx
Boot HART PMP Count       : 8
Boot HART PMP Granularity : 4096
Boot HART MIDELEG         : 0x0000000000000222
Boot HART MEDELEG         : 0x000000000000b109

Reading it top to bottom:

  • Boot HART ID = 1. The one selected to boot the OS; the other four HARTs are 0, 2, 3, 4 and will be started by S-mode later.
  • Priv Version = v1.11. The RISC-V privileged spec version this HART implements. Determines which CSRs exist and how mode transitions work.
  • Base ISA = rv64imafdcbx. This is what OpenSBI prints as the base ISA string: 64-bit (rv64), integer base (i), integer multiply/divide (m), atomics (a), single-precision float (f), double-precision float (d), compressed 16-bit encoding (c), plus two trailing letters (b, x). Take those with a grain of salt: on U74-class silicon the ratified bit-manipulation set is reported as separate Zba/Zbb/… extensions, and x conventionally denotes non-standard vendor extensions. SiFive documents the U74 as RV64GC; the extra letters here are a display artifact (Boot HART ISA Extensions : none in the log confirms nothing beyond the base string is being enumerated). Notice that once U-Boot proper takes over (stage 3 below) it reports the CPU as rv64imacu: a narrower feature set that U-Boot itself was compiled for.
  • PMP Count = 8, PMP Granularity = 4096. PMP is Physical Memory Protection: the M-mode-owned mechanism that carves the physical address space into regions with independent R/W/X permissions per HART. Functionally it sits between ARM's per-core MPU (same idea, per-HART) and TZASC (bus-level partitioning); neither is a perfect analog. Eight regions is the low end : the RISC-V Priv spec allows up to 16 entries in v1.11 and up to 64 in v1.12.
  • MIDELEG = 0x222, MEDELEG = 0xb109. These are the interrupt- and exception-delegation masks : which M-mode traps get delegated down to S-mode instead of being handled by OpenSBI itself. 0x222 delegates the three standard S-mode interrupts (SSI, STI, SEI: bits 1, 5, 9), which is the expected shape for a Linux-capable config. 0xb109 delegates instruction-address-misaligned, breakpoint, user-ecall, and the three page-fault exceptions: again standard for handing normal traps off to the kernel while keeping M-mode-only causes (like M-mode ecall or illegal instructions) at OpenSBI. If you ever debug a mysterious OpenSBI hang, these two masks are where you check that S-mode is actually getting the traps it expects.

Stage 3: U-Boot proper takes over

After OpenSBI drops to S-mode, the log shifts to the familiar U-Boot banner:

U-Boot 2021.10 (Nov 10 2022 - 13:29:36 +0800), Build: jenkins-VF2_515_Branch_SD…

CPU:   rv64imacu
Model: StarFive VisionFive V2
DRAM:  8 GiB
MMC:   sdio0@16010000: 0, sdio1@16020000: 1
Loading Environment from SPIFlash... SF: Detected gd25lq128 with page size 256 B
*** Warning - bad CRC, using default environment

Same version string as SPL: not a coincidence; they were built from the same source tree and packed into the SPI image together. From this point the flow looks like any other U-Boot: read env, enumerate MMC, pick a boot script, load kernel + DTB. The one RISC-V-specific detail is that when U-Boot calls into the kernel with bootm, the transition happens entirely within S-mode; the M-mode OpenSBI stays resident at physical address 0x40000000 for the entire life of the running system.

Aside

That Warning - bad CRC, using default environment line is a common first-boot artifact when SPI flash was never initialized with a signed U-Boot environment. Harmless the first time; if it persists across reboots the env-save path is broken or the SPI region has drifted.

Stage 4: Linux

Starting kernel ...
[    0.000000] Linux version 5.15.0-starfive (sw_buildbot@mdcsw02) (riscv64-unknown-…

The kernel takes over S-mode; OpenSBI keeps M-mode. Every subsequent kernel operation that used to be handled by BIOS callbacks (x86) or SMC calls (ARM) becomes an ecall into OpenSBI: that's the SBI call convention. From userspace you never see it; from the kernel's point of view it's a well-defined function table it can rely on regardless of which vendor built the SoC.

How this compares to ARM and x86

ConceptRISC-VARMx86
Highest privilegeM-modeEL3 (Secure Monitor)Ring 0 + SMM
Kernel privilegeS-modeEL1Ring 0
Firmware→kernel ABISBI (open, versioned)PSCI + vendor SMCACPI + BIOS/UEFI
Per-core namingHARTCPU / coreCPU / logical processor
Physical memory protectionPMP (per-HART, 8-64 regions)TZASC / MPUVT-d/IOMMU + SMRR

The single biggest departure: on RISC-V the M-mode firmware is standardized, open source, and shared across every vendor. There isn't an equivalent of "the vendor's proprietary TrustZone blob" sitting between your kernel and the hardware. OpenSBI is the whole thing and you can read the source. This is a security posture win that RISC-V deployments generally take for granted; every other architecture has spent a decade litigating equivalent transparency.

One caveat: M-mode is open, but the pre-M-mode pieces on any given SoC generally are not. This VF2 log opens with DDR version: dc2e84f0: a StarFive-signed DRAM-init blob that runs before U-Boot SPL and is not part of the OpenSBI source tree. The JH7110 boot ROM is likewise closed. So the open-M-mode story is real, but the "every byte of the boot chain is auditable" version of that story isn't.

What to check on your own RISC-V device

Capture a boot log per the UART adapter guide and look for four things:

  1. A U-Boot SPL line early, followed by an OpenSBI banner. If SPL is absent, the SoC boot ROM may be jumping straight to a monolithic build.
  2. The OpenSBI version and Runtime SBI Version. Older OpenSBI (pre-1.0) shipped SBI 0.2; upstream OpenSBI v1.4 and later implement most of SBI 2.0 (ratified in 2024). Vendor BSP forks frequently lag. A vendor stuck on OpenSBI 0.9 in 2026 is running a fork with the same "never rebased" failure mode we covered in the U-Boot 1.1.x post.
  3. The Boot HART ISA string: mismatch between what OpenSBI reports and what U-Boot proper reports is often the difference between the silicon's real feature set and what the software stack was configured for.
  4. Whether there's an interruptable autoboot prompt in U-Boot proper. RISC-V boards are not immune; the JH7110 reference builds ship with a countdown by default.

Related

  • Interruptable autoboot: U-Boot's countdown prompt shows up in RISC-V boot logs too, and the mitigation is the same CONFIG_BOOTDELAY=-2 config flip.
  • U-Boot 1.1.x still shipping: the "forked in 2005, never rebased" pattern applies to OpenSBI + SPL forks as much as it applies to U-Boot itself.
  • UART adapters worth owning: the JH7110 UART on VisionFive V2 is 3.3 V and 115200 8N1 by default; a Tigard or the $3 sacrifice CH340 both work.
  • Device Fingerprinter : paste the boot log, get the bootloader chain called out inline. Client-side, no upload.
  • Reading a lot of RISC-V boot logs and want them tracked together with version diffs? BootIntel's paid tiers hold per-device history so "OpenSBI version drift across our fleet" is a query, not a spreadsheet.