Clockwork uConsole · Volume 4
Boot, Firmware, SD, and eMMC
Multi-stage boot chains for Pi and Rockchip; EEPROM and U-Boot config; SD/eMMC/USB/NVMe/PXE; recovery and unbricking
Contents
1. About this Volume
This volume is what happens between the moment you press the power button and the moment your shell prompt appears. It is the most procedural-feeling volume of the series — there are checklists, command examples, and “if A then B else C” decision trees — but the procedures rest on a foundation of staged-bootloader theory that is worth the patience it takes to unpack.
Volume 3 stubbed out the boot path for each compute module class — Pi CM4, Pi CM5, and Radxa CM5 — and deferred the chain detail to here. This volume picks up that thread. We walk each stage of the Pi BCM27xx boot chain (with explicit attention to what changed between the CM4 era and the CM5 era), then walk the Rockchip RK3588S2 chain from BootROM through U-Boot, and then dig into the storage-attached side of boot — SD, eMMC, USB, NVMe, network. The closing chapters are the recovery / unbricking story (worth knowing before you brick something) and the cheat-sheet of common config.txt edits.
The reader is assumed to have read Volume 3 and is comfortable with terms like “Boot ROM,” “EEPROM,” “kernel image,” “device tree.” If those are new, skim the first chapter of bootlin.com/training/boot-time/ before continuing — it’s free and excellent.
A note on scope: we describe the Linux boot path. We do not describe Windows-on-ARM, Android, ChromeOS, or any other guest OS that could run on these modules. Those exist (the Pi 5 does run a Windows-on-ARM developer build), but they’re not the uConsole’s center of gravity, and adding them would double the volume without doubling the value.
2. The Boot Problem
Before the chains: what is a boot chain, and why are there always so many stages?
2.1 Why staged loaders exist
A modern ARM SoC powers up with no DRAM initialised, no clocks running at speed, no peripheral drivers loaded, and no filesystem — nothing more than a small block of immutable mask-ROM code at a fixed reset vector. That code’s only job is to decide what to load next from a small subset of supported sources (a serial ROM, an SD card’s first sector, a USB host endpoint), and then jump to the loaded image. The image then has to set up enough of the world (DRAM, clocks, the CPU’s cache hierarchy, an IO controller) to load the next stage. That stage does more setup, then loads the next, and so on. Each stage is responsible for making the next stage’s job easier.
The reasons there are typically four to five stages, not one or two:
- Mask ROM is immutable. It can’t be updated to add new boot sources. It has to remain small (mask ROM is expensive in silicon area) and conservative (it can’t fail on hardware variations).
- The first writable stage runs from on-die SRAM. SRAM is small (~64 KB to ~1 MB on these SoCs). The image that fits in SRAM has to do just enough to bring up DRAM, then load the next stage from somewhere bigger.
- Subsequent stages run from DRAM but still without a kernel — these stages do peripheral init, secure-boot verification, sometimes memory-protection setup (TrustZone), and they hand off to a more general-purpose loader.
- The general-purpose loader (U-Boot, GRUB on x86, the Pi’s
start.elf) understands filesystems, displays a menu, lets the user pick what to boot, and parses configuration files. - The kernel is what users think of as “Linux.” It still has its own staging — uncompressing itself, mounting initramfs, running early init, then transitioning to the rootfs.
The engineering reason staged-loader complexity has won is that no single binary can be both “small enough to fit in mask ROM” and “smart enough to display a menu and parse a filesystem.” The staging spreads complexity across a chain where each stage operates within a budget the previous stage gave it.
2.2 Pi vs Rockchip — same problem, different culture
The Pi Foundation and Rockchip solve the same boot problem with different cultural priorities. The two are worth contrasting because the differences explain a lot of what feels weird about each.
| Concern | Pi (Broadcom) approach | Rockchip approach |
|---|---|---|
| GPU vs CPU as boot host | GPU boots first; CPU is started later by the GPU firmware. | CPU boots first; the GPU is initialised much later. |
| First-stage code | Closed-source binary blob (bootcode.bin, start.elf). | Open-source U-Boot SPL. |
| Boot config syntax | INI-style config.txt with Pi-specific keys. | U-Boot environment variables (bootcmd, bootargs). |
| Bootloader update path | rpi-eeprom-update (fully managed). | Manual flash with rkdeveloptool or dd. |
| Boot device ordering | BOOT_ORDER register in EEPROM (one nibble per source). | U-Boot’s boot_targets list. |
| Documentation | Excellent on Pi side (CC-BY); mostly Pi-Foundation-curated. | Patchy; mix of vendor docs, Armbian wiki, forum threads. |
| Open-sourceness | Most stages closed; start.elf is a binary blob. | Every stage is OSS (TPL/SPL/U-Boot all rebuildable from source). |
| Recovery mode | USB MSD with rpiboot — Pi appears as a mass-storage device. | MaskROM mode with rkdeveloptool — Rockchip appears as a vendor recovery device. |
| Boot-source flexibility | Up to 8 sources in BOOT_ORDER (SD, USB, network, NVMe, RESTART). | Defined per-board in U-Boot device tree; flexible but per-build. |
The pithy summary: Pi optimises for “it just works”; Rockchip optimises for “it’s all open source”. Both work. Both have warts. The uConsole’s CM4/CM5 path is on the Pi side; the Radxa CM5 path is on the Rockchip side.
2.3 What “boot” means on the uConsole
Concretely, on a uConsole, “boot” is the sequence:
- You press the power button on the back of the case.
- The AXP228 PMIC runs its power-on sequence (Vol 2 §3.5) — DCDCs come up, ALDOs come up,
PWROKasserts,RUNgoes high on the CM connector. - The compute module’s SoC reset releases. The mask ROM starts executing.
- The boot chain (Pi or Rockchip) runs through stages 1–4 above.
- Linux loads, mounts the rootfs, runs
systemd, brings up the framebuffer driver for the JD9365DA-H3 display, brings up the keyboard MCU’s USB endpoint, brings up the audio path. - You see the login prompt or desktop.
A uConsole boot from cold takes about 15–25 seconds depending on module + storage + OS. The visible time (from screen-comes-on to login-prompt) is shorter — most of the boot chain runs while the screen is still dark. We come back to “why is my boot so slow?” optimisation in Volume 11 (Performance).
3. The Pi Boot Chain (CM4)
The Pi CM4 runs the classic BCM27xx boot chain. This chapter walks each stage in detail. The stages and the artifacts each consumes/produces:
┌──────────────────┐
│ Power-on reset │
└────────┬─────────┘
│
▼
┌─────────────────────┐ Stage 0
│ Boot ROM (BCM2711) │ (mask ROM, immutable)
│ reads SPI EEPROM │
└────────┬────────────┘
│
▼
┌─────────────────────┐ Stage 1
│ EEPROM bootloader │ (writable; updated via rpi-eeprom-update)
│ decides boot src │
│ enumerates SD/USB/ │
│ NVMe/network │
└────────┬────────────┘
│ (chosen src)
▼
┌─────────────────────┐ Stage 2
│ bootcode.bin │ (on /boot/, signed by Pi)
│ GPU firmware init │
└────────┬────────────┘
│
▼
┌─────────────────────┐ Stage 3
│ start4.elf │ (the GPU firmware proper)
│ loads kernel.img, │
│ cmdline.txt, │
│ config.txt, │
│ device tree (.dtb),│
│ overlays │
└────────┬────────────┘
│ (kernel running)
▼
┌─────────────────────┐ Stage 4
│ Linux kernel boots │
│ initramfs → init │
│ systemd → login │
└─────────────────────┘
3.1 Stage 0 — Boot ROM in the BCM2711
The first instructions executed after reset come from a small mask ROM inside the BCM2711.1 This ROM is unmodifiable — it’s wired into the silicon. Its job is small:
- Initialise basic clocks and the SPI controller.
- Read the first ~4 KB of the SPI EEPROM (CM4 has a 16 Mbit SPI flash on-module dedicated to the EEPROM bootloader).
- Verify the EEPROM image’s signature.
- Load the rest of the EEPROM bootloader into on-die SRAM.
- Jump to the EEPROM bootloader.
If the EEPROM is corrupt or signature-invalid, the Boot ROM falls back to a USB Device Mode — the CM4 enumerates as a USB mass-storage device on its USB-C port, and a host running rpiboot can then push a known-good EEPROM image into the SPI flash. This is the rpiboot recovery path covered in §3.6.
There is an interesting consequence of “Boot ROM lives in the SoC, EEPROM lives on the module”: a CM4’s boot ROM is the same as a Pi 4’s boot ROM, but the EEPROM contents differ between Pi 4 and CM4. The EEPROM image you’d flash to “fix” a brick has to be the CM4 variant.
3.2 Stage 1 — the SPI EEPROM
The EEPROM2 is a Winbond 16 Mbit (2 MB) SPI flash on the CM4 module — separate from the SoC’s mask ROM and separate from the eMMC. It contains:
- The Stage 1 bootloader binary (signed).
- The bootloader’s configuration (the famous
BOOT_ORDERand friends). - A revision/version stamp.
- Reserved space for future use.
Stage 1’s job is to enumerate the configured boot sources in BOOT_ORDER order, decide which has a valid Stage 2 image (bootcode.bin), and load it. The enumeration is non-trivial — Stage 1 is what brings up the SD controller for SD boot, the USB hub for USB MSD boot, the PCIe controller for NVMe boot (Pi 5 / CM5 only), and the Ethernet PHY for network boot.
The EEPROM bootloader is the most updated piece of firmware in the Pi ecosystem. New EEPROM releases ship every few months, fixing boot-source enumeration bugs, adding new boot sources, adjusting timeouts, and improving SD-card compatibility. rpi-eeprom-update is the management tool — see §4.4.
3.3 Stage 2 — bootcode.bin
This stage is Pi-Foundation-only — closed-source, signed, distributed only via Pi’s package repository. bootcode.bin is loaded from the boot partition (FAT32) of whatever boot device Stage 1 picked. Its job:
- Initialise DRAM (memory training, calibration).
- Initialise the GPU.
- Load
start4.elf(the GPU firmware proper, Stage 3) from the boot partition. - Hand off to
start4.elf.
bootcode.bin is small (~50 KB). It exists in this position because start4.elf is too big to fit in SRAM and DRAM has to be brought up before start4.elf can be loaded.
On the CM5 / Pi 5, bootcode.bin is gone — Stage 2’s role is folded into the EEPROM bootloader. See §5.
3.4 Stage 3 — start4.elf (the GPU firmware)
start4.elf is the GPU firmware blob — it runs on the VideoCore VI (CM4) or VII (CM5) GPU, not on the ARM cores. The ARM cores remain in a held state until the GPU brings them up. This is unusual — most ARM systems boot from the CPU side and bring the GPU up later — but it is the Pi family’s distinctive choice and has been since the original Pi.
start4.elf does:
- Reads
config.txt(we cover this in §13). - Loads the device tree blob (
.dtb) corresponding to the detected board. - Applies device-tree overlays as instructed in
config.txt. - Reads
cmdline.txtfor kernel command-line parameters. - Loads the Linux kernel image (
kernel.img, orkernel8.imgfor 64-bit ARM). - Sets up the ARM cores.
- Hands the kernel control of the system.
Variants of the GPU firmware include start.elf (legacy 32-bit), start4.elf (64-bit ARMv8), start_x.elf (extended hardware-codec features), and start_cd.elf (cut-down — minimal features for low-memory configs). The CM4 typically uses start4.elf.
Like bootcode.bin, start4.elf is closed-source and signed by Pi. The community has occasionally produced reverse-engineered alternatives (notably librerpi/rpi-open-firmware); none have reached production-quality on the CM4.
3.5 Stage 4 — Linux kernel and init
The kernel takes over from start4.elf. It:
- Runs early decompression (the kernel image is gzip- or xz-compressed).
- Sets up the per-CPU and per-core state.
- Mounts the initramfs (an in-memory rootfs) if configured.
- Calls
init(typically/sbin/init, which on modern systems issystemd). systemdruns unit files in dependency order — early boot, mount fstab entries, start network, run user services, light up the display manager or login prompt.
The Pi’s kernel.img/kernel8.img is a Pi-built kernel that includes the Clockwork patches for the JD9365DA-H3 display and the GD32 keyboard MCU. The community Bookworm/Trixie images (Vol 5) bundle their own kernel image with the same patches applied to a more modern kernel base.
3.6 The recovery path — USB MSD via rpiboot
When the EEPROM is corrupt, when the eMMC is empty, or when you simply want to flash an image to eMMC without removing the module, you boot the CM4 into USB Device Mode and connect a host running rpiboot.3 The procedure:
- On the host (Linux/macOS):
git clone https://github.com/raspberrypi/usbboot && cd usbboot && make && sudo ./rpiboot. Leave it running; it waits for a USB device. - On the uConsole: Power off. Hold the
nRPIBOOTGPIO low (jumper or short to GND) — on the CM4 carrier this is a labelled jumper on the V3.14 mainboard. - Connect the uConsole’s USB-C port to the host. Apply power to the uConsole.
- The CM4 enumerates as a USB mass-storage device. The host’s
rpibootrecognises it and presents the eMMC as/dev/sdX. - Flash the image:
sudo dd if=raspios.img of=/dev/sdX bs=4M status=progress conv=fsync. Or userpi-imager(the official GUI flasher). - Disconnect, remove the
nRPIBOOTjumper, reboot. The CM4 now boots from the freshly-flashed eMMC.
This is the standard way to get a fresh image onto an eMMC CM4. There is no SD card slot path for eMMC modules — you need rpiboot (or you swap to a Lite + uSD module).
4. EEPROM Configuration
The EEPROM is the single most important piece of firmware on a CM4. This chapter is a hands-on reference for inspecting, editing, and updating it.
4.1 What’s stored in the CM4 EEPROM
A current Pi-EEPROM image (firmware ≥ 2024) contains, roughly:
| Region | Size | Purpose |
|---|---|---|
| Bootloader binary | ~256 KB | The Stage 1 code (signed by Pi) |
| Configuration data | ~16 KB | INI-style key/value pairs (BOOT_ORDER, WAKE_ON_GPIO, etc.) |
| Public key | ~1 KB | Signature verification key for bootcode.bin and start4.elf |
| Reserved / future | rest of 2 MB | Unused |
The configuration is what most users care about. It is editable by a tool (rpi-eeprom-config) without re-flashing the entire image — the tool patches just the configuration region and re-signs.
4.2 rpi-eeprom-config walkthrough
rpi-eeprom-config is shipped on every Pi OS / Bookworm / Trixie image. From a running uConsole (with sudo):
# View the current EEPROM config:
sudo rpi-eeprom-config
# Edit interactively in $EDITOR:
sudo rpi-eeprom-config --edit
# Apply a config from a file:
sudo rpi-eeprom-config --apply /path/to/boot.conf
--edit opens the config in your editor; on save, rpi-eeprom-config validates, writes a new EEPROM image to /var/lib/rpi-eeprom/firmware-default-pieeprom-2024-XX-XX.bin, and queues it for application on next reboot. The actual EEPROM flash happens in early boot before the kernel starts — there’s no live re-flash.
A typical CM4 config looks like:
[all]
BOOT_UART=0
WAKE_ON_GPIO=1
POWER_OFF_ON_HALT=1
BOOT_ORDER=0xf41
PSU_MAX_CURRENT=5000
We covered POWER_OFF_ON_HALT=1 in Volume 2 §14 (the PMIC-latch quirk fix). The other keys:
| Key | Purpose |
|---|---|
BOOT_UART | 1 = enable verbose boot messages on the UART (good for debugging hangs). |
WAKE_ON_GPIO | 1 = wake from halt on GPIO toggle. Default for the uConsole power btn. |
POWER_OFF_ON_HALT | 1 = on halt, drop power-rail (the AXP228 latch fix from Vol 2 §14). |
BOOT_ORDER | The boot-source priority — see §4.3. |
PSU_MAX_CURRENT | Tells firmware the max current supply can deliver (used for thermal mgmt). |
4.3 The BOOT_ORDER register
BOOT_ORDER is a hex value where each nibble (4 bits) is a boot source. The bootloader reads nibbles right-to-left (least-significant first) and tries them in that order. A 0xf nibble means “restart this list” (loop forever).
Nibble values:
| Nibble | Source | Meaning |
|---|---|---|
0x0 | (stop) | Stop trying — drop into recovery. |
0x1 | SD card | Try the SD card slot. |
0x2 | Network boot | TFTP/PXE. |
0x3 | RPIBOOT | USB-MSD recovery mode (forces the device to wait for rpiboot). |
0x4 | USB mass storage | Try connected USB MSD devices. |
0x5 | BCM-USB-MSD | Internal USB-MSD enumeration (advanced — Pi 5 only). |
0x6 | NVMe | NVMe over PCIe (Pi 5 / CM5 only). |
0xe | Stop and timeout | Stop trying after a timeout. Useful for eventually entering recovery. |
0xf | Restart | Repeat the list. |
The default BOOT_ORDER=0xf41 reads as: Try SD (1), then USB (4), then RESTART (f) — i.e., loop forever between SD and USB. This is sensible defaults for most users.
Common variants for the uConsole:
| Loadout | BOOT_ORDER | Reads as |
|---|---|---|
| Default (SD-first, fall back to USB, loop) | 0xf41 | SD → USB → restart |
| eMMC-first (CM4 with eMMC) | 0xf41 | (eMMC enumerates as SD-class to the loader) |
| USB-first (boot from external USB-C SSD) | 0xf14 | USB → SD → restart |
| Network-first (lab provisioning) | 0xf241 | Network → SD → USB → restart |
| NVMe-first (CM5 with NVMe via adapter) | 0xf416 | NVMe → USB → SD → restart |
Force-recovery (always wait for rpiboot) | 0xf3 | RPIBOOT only — useful for “this CM4 is borked, factory reset it” |
4.4 Updating to the latest EEPROM
EEPROM updates are routine and important. The Pi packages tracker shipd a new EEPROM every 1–3 months, fixing boot bugs and adding sources.
Inspect the current version:
$ vcgencmd bootloader_version
2024-12-15 (15)
copyright (c) 2012 Broadcom
version 2024-12-15
$ rpi-eeprom-update
*** UPDATE AVAILABLE ***
BOOTLOADER: update available
CURRENT: 2024-12-15
LATEST: 2025-04-08
RELEASE: latest
FIRMWARE: ...
Apply the update:
sudo rpi-eeprom-update -a
sudo reboot
The kernel writes the new EEPROM image during early boot. If something goes wrong (rare — the update has a built-in rollback), the bootloader falls back to the previous version automatically.
For a CM4 with a stale EEPROM (e.g., from a Lite + uSD that hasn’t been online), the manual flash via rpiboot is also an option:
# On the host:
cd ~/usbboot/recovery
git pull
sudo ./rpiboot
# (uConsole booted in nRPIBOOT mode, EEPROM gets re-flashed.)
4.5 Locking down EEPROM (write-protect)
For a deployed uConsole — say a ham-radio appliance left in a remote shack — you can write-protect the EEPROM:
- Set
EEPROM_WP=1in the EEPROM config. - Pull the
EEPROM_nWPGPIO low (mainboard test pad — see Vol 2 §5.1).
After this, rpi-eeprom-config --apply will refuse to write. To re-enable, raise EEPROM_nWP and clear the config flag.
Most users will never write-protect. The exception is appliance-style deployments where you want to prevent firmware-level tampering.
5. The Pi 5 / CM5 Boot Era
The Pi 5 (and therefore the CM5) brought significant changes to the boot story. Most are improvements; a few are migrations. This chapter is the diff.
5.1 What changed in stages 2 and 3
The Pi 5 and CM5 use the BCM2712. The BCM2712’s boot architecture is consolidated: the GPU does less, and the EEPROM does more.
| Stage | CM4 (BCM2711) | CM5 (BCM2712) |
|---|---|---|
| Stage 0 | ARM mask ROM in the SoC | Same (different ROM image; same role) |
| Stage 1 EEPROM | Stage 1 only — picks boot source | Stages 1+2 — picks source AND inits DRAM |
| Stage 2 | bootcode.bin on the boot partition | Removed. Folded into EEPROM. |
| Stage 3 | start4.elf on boot partition | start.elf (a slimmer GPU firmware) |
| Stage 4 | Linux kernel | Same |
The practical effect is a simpler boot partition: bootcode.bin is no longer needed (its absence on a CM5 image won’t cause failure), and start.elf does less work because DRAM is already initialised. Boot times on the CM5 are typically 1–2 seconds faster than the CM4 for the same OS image, partly from this simplification.
5.2 NVMe boot, USB-3 MSD boot, network boot
The Pi 5 era EEPROM understands new boot sources:
- NVMe boot —
0x6nibble. Requires the device-tree blob to know about the NVMe controller, and thepcie-slotconfig inconfig.txt. On a CM5 in the uConsole, this requires the V3.14_V5 mainboard or the HackerGadgets adapter to wire the second PCIe lane to a Mini PCIe → M.2 adapter or to a CM5 NVMe board. - USB-3 MSD boot —
0x4nibble (same as USB-2, but the bootloader now knows about USB-3). On the V3.14 mainboard, USB is capped at 2.0 even with a CM5 (Vol 7 §7.4.3); the V3.14_V5 + adapter unlocks USB-3. - Network boot improvements — faster TFTP, TLS-secured network boot for managed deployments (less relevant for the uConsole).
5.3 The CM5’s RTC story
The Pi 5 ships with a tiny coin-cell RTC backup on the carrier (not the module) — the Pi 5 Model B has a CR2032 socket. The CM5 does not have an on-module RTC. If you want time-keeping across power-off, you need an external RTC.
For the uConsole, the options are:
- The HackerGadgets AIO V2 — has a PCF85063A + CR1220 RTC integrated (Vol 7 §7.4.3). Linux exposes it as
/dev/rtc1. - A DS3231 or DS1307 RTC module on the I²C bus of the 40-pin header (DIY).
- NTP at boot — if you have network connectivity at boot, set the time via
chronydorsystemd-timesyncd. The default for most desktop loadouts.
If your loadout records timestamped data offline (RF captures with sample-time, packet-capture files, log files for forensics), invest in a hardware RTC. Otherwise NTP is fine.
5.4 /boot/firmware/config.txt semantics
A subtle change between CM4 and CM5: the path of the boot partition mount point. Before the Pi 5 era:
- CM4 / Pi 4 / Pi 3: boot partition mounted at
/boot/.
From the Pi 5 era onward:
- CM5 / Pi 5 (Bookworm and later): boot partition mounted at
/boot/firmware/.
If you’re following an old tutorial that says “edit /boot/config.txt”, on a CM5 / Bookworm-era image it’s /boot/firmware/config.txt. The legacy path may still resolve via a symlink on some images, but the canonical location is /boot/firmware/. We use the modern path in §13.
6. The Rockchip Boot Chain (RK3588S2)
If you’ve installed a Radxa CM5 in your uConsole (Volume 3 §5), this is your boot chain. It’s structurally similar to the Pi chain — staged loaders, each progressively smarter — but every stage is open-source U-Boot or U-Boot-derived code, and the toolchain is rkdeveloptool not rpiboot.
6.1 Stage 0 — BootROM and BOOT_MODE straps
The RK3588S2 has its own immutable mask ROM. It’s separate from the Pi’s, of course — different SoC, different ROM. The Rockchip BootROM4 does:
- Reads the BOOT_MODE strap pins to decide which storage to boot from. (On the Radxa CM5, these are tied to the carrier — for the uConsole, they’re on the mainboard’s CM connector and are pulled to favour eMMC if present, otherwise SD.)
- Reads the TPL (Tertiary Program Loader) image from the chosen boot device.
- Verifies the TPL signature against the OTP-stored public key.
- Loads TPL into the SoC’s on-die SRAM.
- Jumps to TPL.
If TPL fails or signature-invalid, the BootROM enters MaskROM mode — it enumerates as a USB device (vendor ID 0x2207, product depending on chip), and a host running rkdeveloptool can push a known-good image. This is the Rockchip equivalent of rpiboot (§12.3).
6.2 Stage 1 — TPL (DRAM init)
The TPL5 is a small (~16-32 KB) U-Boot derivative whose only job is to bring up DRAM. The DDR controller on the RK3588S2 needs careful sequenced initialisation, including memory training (calibration of timing margins). TPL does this:
- Configures the LPDDR4X PHY.
- Runs memory training (varies by speed bin and DRAM module).
- Tests the resulting margins.
- Transitions execution from SRAM to DRAM.
- Loads SPL into DRAM and jumps to it.
TPL is fully open source — every Rockchip module’s TPL is in the U-Boot tree. You can rebuild it with custom DRAM timings if you’re doing exotic things (LPDDR5 overclocking, low-power SDP). Most users never touch it.
6.3 Stage 2 — SPL (U-Boot SPL)
SPL (Secondary Program Loader) is U-Boot’s “small” build configuration. It runs from DRAM, has access to the storage controllers, and its job is to:
- Initialise basic peripherals (UART for console, the SD/eMMC controller for storage access).
- Read U-Boot proper from the SPL boot partition.
- Verify and load U-Boot.
- Jump to U-Boot.
SPL also handles the TrustZone setup if secure boot is enabled — establishing the secure world, installing TEE (Trusted Execution Environment, typically OP-TEE), then handing off to U-Boot in non-secure world.
6.4 Stage 3 — U-Boot proper
U-Boot proper6 is a much larger binary (~1-4 MB) with full filesystem support, a command-line interface, an environment editor, network stack, USB host stack, and the ability to:
- Interactively select boot images via menu.
- Edit boot variables (
bootcmd,bootargs). - Probe storage devices and present their filesystems.
- Boot Linux from any of: eMMC, SD, USB, NVMe, network (TFTP/NFS).
- Apply device-tree overlays similar to Pi’s
config.txtbut in U-Boot syntax.
The default behaviour on a Radxa CM5 image is fully automatic — U-Boot reads its environment, runs bootcmd, which loads and boots Linux. To intervene, you connect a UART (Vol 11 covers this) and press a key during U-Boot countdown — you get the U-Boot CLI.
6.5 Stage 4 — Linux + initramfs
Same as the Pi side — kernel decompresses, initramfs runs, init (systemd) starts. The difference is the kernel’s device tree: the Rockchip kernel uses rk3588s2-uconsole.dtb (community-built), which describes the JD9365DA-H3 panel, the Mini PCIe slot, the audio path, the keyboard hub, etc. Volume 6 covers the device tree in detail.
6.6 fw_setenv — editing U-Boot environment from Linux
U-Boot’s environment is the equivalent of Pi’s EEPROM config. It lives in a dedicated flash region (defined in U-Boot’s build config — usually a partition called u-boot-env or uboot-env). Linux can read and write it via fw_setenv / fw_printenv:7
# View current environment:
sudo fw_printenv
# View one key:
sudo fw_printenv bootcmd
# Set a key:
sudo fw_setenv bootargs 'root=/dev/mmcblk1p2 rootwait'
# Remove a key:
sudo fw_setenv bootargs
The tool needs a config file at /etc/fw_env.config that tells it where the U-Boot env partition lives. A typical config:
# Device Offset Env. size
/dev/mtd0 0x0000 0x10000
Or for an env partition on the eMMC:
/dev/mmcblk0 0x3F8000 0x8000
Check your image’s config file before editing — getting this wrong wedges U-Boot.
Common edits for the uConsole:
| Variable | Typical value | What it does |
|---|---|---|
bootcmd | run distro_bootcmd | Calls the distro-defined boot script |
bootargs | console=ttyS2,1500000 root=/dev/mmcblk1p2 rw rootwait | Kernel command line |
boot_targets | mmc0 mmc1 nvme0 usb0 pxe dhcp | Order of boot sources |
fdtfile | rockchip/rk3588s2-uconsole.dtb | Which device tree to load |
kernel_addr_r | 0x00280000 | DRAM address to load kernel into |
7. SD Card Boot
Most uConsoles boot from SD. This chapter is the SD boot reference.
7.1 Partition layout
A standard Pi-OS / Bookworm SD layout is two partitions:
| Partition | Size | Filesystem | Mount point | Contents |
|---|---|---|---|---|
mmcblk0p1 | 256-512 MB | FAT32 | /boot (legacy) or /boot/firmware (Pi 5 era) | bootcode.bin, start4.elf, kernel*.img, config.txt, cmdline.txt, *.dtb, overlays/ |
mmcblk0p2 | rest | ext4 | / | Linux rootfs |
The Rockchip layout for a Radxa CM5 image is more partitioned — typically 6-8 partitions for boot loaders, U-Boot env, kernel, dtb, rootfs, and recovery — but the Linux-visible layout when running is similar (mmcblk1p1 / mmcblk1p2).
7.2 SD card classes and what they mean
Modern SD cards advertise their performance with two speed-class systems that overlap confusingly:
| Class label | Min sustained sequential | Min random 4K read | Use case for the uConsole |
|---|---|---|---|
| Class 10 | 10 MB/s sequential | (not specified) | Legacy; barely adequate |
| UHS-I | 10 MB/s | (not specified) | Mainstream |
| U1 | 10 MB/s | (not specified) | Marketing duplicate of UHS-I |
| U3 | 30 MB/s | (not specified) | Video-friendly |
| A1 | 10 MB/s | 1500 IOPS (~6 MB/s) | “Apps” — usable for OS boot |
| A2 | 10 MB/s | 4000 IOPS (~16 MB/s) | “Apps” tier — recommended for OS |
| V30 | 30 MB/s | (video bias) | Video; fine for OS |
| V60 / V90 | 60 / 90 MB/s | (video bias) | Video; overkill for OS |
For the uConsole, A2 is the right choice. A1 is acceptable for one-shot loadouts; A2 makes the day-to-day experience meaningfully better because random 4K reads dominate package management, browser caches, and database queries.
Recommended specific cards (all available 2026):
- Samsung Pro Plus (UHS-I, U3, A2) — well-priced, reliable.
- SanDisk Extreme Pro (UHS-I, U3, A2) — the standard.
- Kingston Canvas Go! Plus (UHS-I, U3, A2) — solid budget option.
- Lexar Professional 1066x — fast, slightly pricier.
Avoid: anything marketed as “Class 10” only without an A1/A2 rating; un-branded cards from marketplaces (counterfeits are rampant); cards labelled “for camera” only — they may be V30 but A0 (no random-IO spec).
7.3 The 256 GB ceiling and the SDXC spec
There is a well-circulated community report that 256 GB is the maximum SD size the uConsole boots from.8 This deserves unpacking.
The SDXC standard supports cards up to 2 TB, and a current (2025+) Pi EEPROM bootloader can boot from any SDXC card the bootloader’s ROM understands. In practice:
- Cards ≤ 256 GB: nearly always work.
- Cards 256 GB to 1 TB: mostly work with a current EEPROM; older EEPROM versions may fail to enumerate.
- Cards > 1 TB: often work but with caveats around format (must be FAT32 boot partition, which has a 32 GB max — needs special re-formatting tools); slow boot.
If your card “isn’t detected” by the bootloader, the most common fix is update the EEPROM (§4.4). The next most common fix is re-format the boot partition as FAT32 — Windows refuses to format >32 GB as FAT32 by default; use mkfs.vfat -F 32 from Linux/macOS or Rufus on Windows.
For the cheatsheet (Vol 12 §15): “SD card not detected” → “Update EEPROM, then verify boot partition is FAT32.”
7.4 Multi-OS / multi-boot strategies
Several approaches:
7.4.1 One-OS-per-card
The simplest. Buy 4-8 microSD cards, label each with the OS+loadout (Pi OS, Kali, Parrot, BlackArch, RetroPie, DragonOS), and swap as needed. The uConsole’s SD slot is accessible without case-opening.
7.4.2 BerryBoot
BerryBoot is a multi-boot manager for Pi systems. It puts a small bootloader on the boot partition, then a list of OS images on the rest of the card. At boot it presents a menu and chains into the chosen OS. Supports Pi OS, Ubuntu, RetroPie, OctoPi, and others. Useful if you want one card with several OSes.
Limits: not aware of CM5; some images don’t work cleanly. Maintained but not as actively as the OSes themselves.
7.4.3 PINN / NOOBS
The original Pi multi-installer. PINN (PINN Is Not NOOBS) is the maintained fork. Same idea as BerryBoot — single card, multiple OSes. Slightly more polished UI; same caveats.
7.4.4 Manual BOOT_ORDER switching
For two-OS setups: install one OS to eMMC and one to SD. Set BOOT_ORDER=0xf41 to prefer SD. Pop the SD in for “Loadout B,” remove for “Loadout A.”
7.4.5 GPT + multi-rootfs
Advanced. One large card with a single boot partition and multiple rootfs partitions. The boot kernel-cmdline picks the rootfs. Switch via cmdline.txt edits or via a tiny menu kernel. Used by some custom distros (Buildroot’s genimage, some Yocto recipes).
7.5 SD-specific gotchas
Some classics:
- Counterfeit cards. A “1 TB” card from a marketplace may report 1 TB to the OS but actually only have 32 GB of flash, with the remainder lying.
f3probe(Linux/macOS) detects this. Run it on every new card before trusting it. - Wear on the boot partition. The boot partition has heavy small-write traffic during normal operation. After a few years, the boot partition can become read-only or corrupted while the rootfs is fine. Keep a spare card.
- Bus-speed mismatch. A UHS-I card in a UHS-I slot runs at full UHS-I speed; in a non-UHS slot it falls back to legacy speeds (much slower). The uConsole’s SD slot supports UHS-I.
- Slow boot from a fresh card. First boot includes filesystem expansion (
raspi-configdoes this on first boot), so the very first boot is slower than steady state. This is normal.
8. eMMC Boot
eMMC boots like SD but faster. This chapter is the eMMC reference.
8.1 eMMC partition layout (same scheme, different IO)
eMMC presents itself as /dev/mmcblk0 with the same FAT32 boot + ext4 root layout. The Linux kernel sees no functional difference; the bootloader sees a faster device.
8.2 USB-MSD flash via rpiboot
For Pi CM4 / CM5 with eMMC, rpiboot is the canonical tool for flashing the eMMC from a host. Procedure already covered in §3.6. Expanded version:
# On the host, install Pi image and tooling:
sudo apt install rpiboot rpi-imager # Debian/Ubuntu
# Or build from source:
git clone https://github.com/raspberrypi/usbboot
cd usbboot
make
# Run rpiboot in a terminal, leave running:
sudo ./rpiboot
# (it now waits for a Pi MSD device.)
On the uConsole:
- Power off, batteries out.
- Locate the
nRPIBOOTjumper on the V3.14 mainboard. It’s a small 2-pin header near the CM connector — the silkscreen is “NRPIBOOT” or “RPIBOOT”. Place a jumper across both pins, or short with a wire. - Connect USB-C to the host.
- Apply power (insert batteries OR plug USB-C-to-host AND a charger into a separate USB-C splitter — the uConsole needs power on the charger port plus the data link to the host).
- The host’s
rpibootconsole message:RPi: Started up CM4, thenRPi: Got message: GET_STORAGE_INFO. The eMMC enumerates as/dev/sdXon the host. - Use
rpi-imager(GUI) ordd(command line) to flash. Verify the device —lsblkto confirm/dev/sdXis the CM4’s eMMC, not your host’s drive. - After flash completes, eject
/dev/sdX, disconnect USB-C, remove thenRPIBOOTjumper, re-power.
8.3 rkdeveloptool flash for Rockchip eMMC
For Radxa CM5 + eMMC, the tool is rkdeveloptool:9
# Install dependencies and build:
sudo apt install libudev-dev libusb-1.0-0-dev autoconf automake pkg-config
git clone https://github.com/rockchip-linux/rkdeveloptool
cd rkdeveloptool
autoreconf -i && ./configure && make
# Put the uConsole in MaskROM mode (see §12.3 for trigger details).
# Verify connection:
sudo ./rkdeveloptool ld
# (should list "DevNo=1 Vid=0x2207, Pid=0x350b (or similar)")
# Download the bootloader:
sudo ./rkdeveloptool db rk3588_loader_v1.13.bin # path to bootloader
# Flash the image:
sudo ./rkdeveloptool wl 0 uconsole-radxa.img
# Reset to boot:
sudo ./rkdeveloptool rd
The bootloader binary (rk3588_loader_v*.bin) needs to be downloaded from Rockchip’s vendor releases or Radxa’s wiki. The image (uconsole-radxa.img) is the same .img file you’d flash to an SD card.
8.4 Wear-leveling and lifetime
eMMC chips have a Flash Translation Layer (FTL) that handles wear-leveling automatically. Modern eMMC 5.1 chips are rated for 3,000-10,000 erase cycles per block with 100-1,000x effective lifetime improvement from wear-leveling. In typical OS use (writing logs, package updates, browser cache), an eMMC 5.1 should last 5-10 years.
What kills eMMC faster:
- Excessive writes — running a database with no
commit_intervaltuning, building Docker images repeatedly, runningfiobenchmarks in a loop. - Sustained 100% disk activity — kernel compiles, big rsync jobs.
- Power-loss during writes — eMMC is not journaled at the FTL level. A power-cut during a metadata write can cause filesystem corruption that wear-levels work around but doesn’t undo.
Mitigations:
- Mount
/var/logastmpfsif logging is heavy. - Use
fstrimweekly to maintain FTL performance:sudo systemctl enable --now fstrim.timer. - Move heavy-write workloads to an SD or USB SSD, leaving eMMC for the OS.
8.5 Backup and restore
Backing up an eMMC requires either rpiboot mode (so the host sees it as a block device) or a running Linux session (so you can dd the image to an external drive).
From a running uConsole with a USB drive mounted at /mnt/backup:
# Backup (block-level):
sudo dd if=/dev/mmcblk0 of=/mnt/backup/uconsole-emmc.img bs=4M status=progress
# Or compress on the fly:
sudo dd if=/dev/mmcblk0 bs=4M status=progress | xz -T0 > /mnt/backup/uconsole-emmc.img.xz
Restoring requires rpiboot (you can’t dd over the running rootfs):
# Boot uConsole into rpiboot mode (§3.6 step-by-step).
# On host with eMMC enumerated as /dev/sdX:
sudo dd if=/mnt/backup/uconsole-emmc.img of=/dev/sdX bs=4M status=progress conv=fsync
# Or for a compressed backup:
xzcat /mnt/backup/uconsole-emmc.img.xz | sudo dd of=/dev/sdX bs=4M status=progress conv=fsync
For Pi-specific incremental backups, rpi-clone is a friendly wrapper that handles partition resizing, checksum verification, and “boot from backup” testing.
9. USB Boot
USB boot is the path for users who want a single fast SSD as the rootfs. It’s simpler than PXE, faster than SD, and on the V3.14_V5 + CM5 combo, uses USB 3.0 for ~400 MB/s sequential reads.
9.1 USB-MSD boot from a USB-C SSD
Procedure on the Pi CM4/CM5 path:
- Prepare the USB SSD. Format with the same FAT32 boot + ext4 root layout as an SD or eMMC.
- Flash an OS image to the USB SSD using
ddfrom a host orrpi-imagerselecting the USB device. - Update the EEPROM to a version supporting USB boot (any 2024+ EEPROM does).
- Set
BOOT_ORDERappropriately (e.g.,0xf14for “USB first, then SD, then loop”). - Plug the USB SSD into the uConsole’s USB-A port (V3.14) or the V3.14_V5’s USB-C port.
- Reboot. The bootloader enumerates USB MSDs after SD; if the SSD has a valid boot partition, it boots from there.
9.2 The V3.14 USB-2 cap and the V3.14_V5 USB-3 path
On the V3.14 mainboard, every USB port goes through the GL850G hub which is USB 2.0 only. Even with a CM5 + USB-3-capable SSD, you get USB 2.0 throughput — about 35 MB/s sequential. This is faster than a typical microSD A1 but slower than a microSD A2.
On the V3.14_V5 mainboard with the HackerGadgets adapter and a CM5: one of the CM5’s USB-3 root hubs is exposed on the AIO V2’s USB-C port. That gives USB 3.0 throughput (~400 MB/s), which is much faster than any SD or eMMC option.
For the decision matrix: USB boot only beats SD/eMMC if you have V3.14_V5 + CM5 + a USB-3 SSD. On stock V3.14, USB boot is a convenience (easy hot-swap, large capacity) more than a speed choice.
9.3 Choosing a USB enclosure that works for boot
Not every USB-SSD enclosure boots reliably. Pi has an official compatibility list but it’s incomplete. Rules of thumb:
- UAS (USB Attached SCSI) support is good for performance but historically buggy with some chipsets. Some chipsets (notably the JMS580) have a UAS-bug that causes the Pi bootloader to hang. The fix is to disable UAS via the
usb-storage.quirks=kernel parameter:
(Where# In cmdline.txt: usb-storage.quirks=152d:0578:u152d:0578is the vendor:product ID of the offending chipset; find vialsusb.) - Cheap “USB-3” enclosures sometimes lie about the chipset. Stick to known-good brands: Samsung T7, SanDisk Extreme Portable, Crucial X9.
- External power: most USB-3 SSDs can run on bus power, but high-load operations (database commits, kernel compiles) draw spikes that can cause brownouts on the Pi’s USB rails. If you see “spurious” filesystem corruption after benchmark runs, try a powered hub or a USB-Y cable for extra power.
10. NVMe Boot Paths
NVMe is the fastest storage option. Here are the three paths on the uConsole, in order from “easiest” to “most exotic.”
10.1 CM5 native NVMe (with the V3.14_V5 + AIO V2)
This is the cleanest path:
- CM5 module installed in the uConsole.
- V3.14_V5 mainboard.
- HackerGadgets AIO V2 expansion card or a Mini PCIe → M.2 NVMe adapter.
- NVMe SSD installed on the adapter.
- EEPROM updated to support NVMe boot.
BOOT_ORDER=0xf416(NVMe-first).
Throughput: ~1 GB/s sequential, ~100 MB/s random 4K (limited by Mini PCIe PCIe Gen 3 ×1, not the NVMe device itself).
10.2 CM4 NVMe via Mini PCIe + M.2 adapter
Same physical setup as §10.1 but on a CM4. The CM4 has only PCIe Gen 2 ×1, so:
- Throughput: ~500 MB/s sequential (Gen 2 bandwidth limit).
- The CM4 EEPROM doesn’t natively support NVMe boot — you boot from SD/eMMC into a kernel with NVMe drivers, then pivot-root to the NVMe.
Because of the pivot-root requirement, this is more advanced. The Pi forum has working configs for this; search “CM4 NVMe boot pivot root.”
10.3 NVMe over USB-C (CM4 fallback)
Cheap and cheerful: USB-C-to-NVMe enclosures exist for $25-50, and the CM4 boots from USB-MSD natively. Throughput is capped at USB 2.0 (~35 MB/s on V3.14) or USB 3.0 (~400 MB/s on V3.14_V5). Less than full Mini-PCIe NVMe bandwidth, but a one-line setup with no PCIe-bridge complications.
11. Network Boot (PXE)
For a managed lab (multiple uConsoles to provision identically), network boot is much faster to deploy than flashing each card individually.
11.1 dnsmasq + tftp server setup
A simple dual-purpose dnsmasq setup serves both DHCP (with PXE options) and TFTP:
# On a Linux server in the lab network (192.168.10.0/24):
sudo apt install dnsmasq
# /etc/dnsmasq.conf:
interface=eth0
bind-interfaces
dhcp-range=192.168.10.100,192.168.10.200,24h
enable-tftp
tftp-root=/srv/tftp
# Pi-specific: tell DHCP clients to PXE-boot from this server
dhcp-option-force=66,192.168.10.1
dhcp-boot=bootcode.bin
# Restart:
sudo systemctl restart dnsmasq
Then in /srv/tftp/ put the Pi boot artefacts: bootcode.bin, start4.elf, the kernel, and the cmdline.txt pointing at an NFS rootfs.
11.2 NFS-root configuration
# On the server:
sudo apt install nfs-kernel-server
# /etc/exports:
/srv/nfs/uconsole-rootfs 192.168.10.0/24(rw,sync,no_root_squash,no_subtree_check)
sudo systemctl restart nfs-kernel-server
In the PXE’d /srv/tftp/cmdline.txt:
root=/dev/nfs nfsroot=192.168.10.1:/srv/nfs/uconsole-rootfs,vers=4,proto=tcp ip=dhcp rw rootwait
The uConsole boots, gets DHCP, fetches bootcode.bin over TFTP, fetches kernel via TFTP, kernel mounts NFS-root, runs init, login prompt appears.
11.3 The lab use-case
Why bother? Because:
- Provisioning is centralised. Update
/srv/nfs/uconsole-rootfsonce; every PXE-booted uConsole reflects the change. - No per-device card management. No microSD cards to lose, no eMMCs to flash.
- Uniform images for a class / cohort. Useful for teaching environments (a hardware-hacking class, a classroom of uConsoles for student CTFs).
- Easy rollback. Snapshot the NFS rootfs; restore from snapshot if a class trashes the OS.
For a single-uConsole user, PXE is overkill. For five or more, it’s a productivity win.
12. Recovery and Unbricking
This chapter is the “I broke it” reference. Things to read before you brick something.
12.1 Symptom → fix matrix
| Symptom | Likely cause | First-line fix |
|---|---|---|
| No power LED, no screen | PMIC latch / cells flat / battery connector loose | Reseat battery board’s mainboard connector firmly. Charge. |
| Power LED on, screen black | EEPROM corrupt OR display driver not loaded | rpiboot recovery (§3.6), then check start4.elf is valid. |
| Pi splash, then black | Kernel panic (corrupt rootfs) | Boot from a different SD; mount the failed card; check logs. |
| ”Card not detected” at boot | EEPROM doesn’t support card size | Update EEPROM (§4.4). Also verify FAT32 boot partition. |
Boots but kernel panic in init | Corrupt initramfs | Re-flash the boot partition. |
| Boots but no screen | DSI driver missing in kernel | Verify config.txt has dtoverlay= for the panel. |
| Boots but no keyboard | USB hub or GD32 firmware issue | lsusb. If GL850G missing — mainboard hardware. If keyboard MCU missing — re-seat. |
| Boots but no audio | ALSA card numbering changed | amixer. Check /usr/share/alsa/cards/. Reinstall ALSA. |
| Boots, then drops to emergency shell | Rootfs unmountable | Boot a recovery card; fsck the original rootfs. |
vcgencmd reports under-voltage detected | PSU not delivering 5V/3A | Charge / use a better USB-C cable. |
| AIO V2 not detected | GPIO power-gating (the AIO V2 gotcha, Vol 7 §7.4.3) | Pull the SDR/LoRa/GPS GPIO high per HackerGadgets setup guide. |
| Won’t boot at all after EEPROM update | Bad EEPROM rollout (rare) | rpiboot recovery, flash a known-good EEPROM image. |
| Radxa CM5 won’t boot (red LED only) | Bad TPL or DRAM training failure | Hold MaskROM trigger, rkdeveloptool flash bootloader (§12.3). |
12.2 Pi recovery — rpiboot USB MSD
Already covered in §3.6 / §8.2. Worth stating again: this is the canonical Pi recovery tool. It works as long as the Boot ROM in the BCM2711 / BCM2712 is intact (and that’s mask ROM — virtually impossible to corrupt). It can recover from corrupted EEPROM, corrupted eMMC, corrupted boot partition, even completely empty eMMC (factory-fresh CM4).
The only failure mode where rpiboot can’t save you is physical damage to the SoC or to the SPI EEPROM chip itself. Both are rare; both are not user-serviceable.
12.3 Rockchip MaskROM mode
For a Radxa CM5, the recovery equivalent is MaskROM mode:
- Power off the uConsole, batteries out.
- Locate the MaskROM trigger. On the Radxa CM5, this is a tiny solder pad labelled
MASKROMorRK_MASKROMon the module itself — you short it to a nearby GND pad with tweezers or a piece of wire. - Connect USB-C from the uConsole to a host running
rkdeveloptool. - Apply power. The SoC enters MaskROM mode (the BootROM enumerates as a USB device with VID
0x2207). - Release the MASKROM short.
- On the host:
sudo rkdeveloptool ldshould show the device. - From here, flash bootloader / image / rootfs as in §8.3.
This is fiddlier than Pi’s rpiboot because the trigger is a tiny solder pad rather than a labeled jumper. Don’t apply power with the MASKROM pad bridged for more than a few seconds — repeated entry into MaskROM mode is fine, but holding it bridged during normal boot can cause flash corruption.
12.4 Re-flashing a corrupt EEPROM
If the EEPROM update process was interrupted (power loss during rpi-eeprom-update):
- Boot into
rpibootmode (§3.6). - On the host:
cd ~/usbboot/recovery && sudo ./rpiboot. - The recovery flow flashes a known-good EEPROM image (the latest released Pi EEPROM bundled with the
rpibootsource tree). - Reboot. Verify with
vcgencmd bootloader_version.
12.5 SD-card resurrection
For a corrupted SD card:
# On a Linux host:
sudo dd if=/dev/sdX of=damaged.img bs=4M status=progress conv=noerror,sync
# (this gets as much data as possible from the card)
# Then either:
# (a) Mount the image read-only and copy data off:
sudo mount -o loop,ro damaged.img /mnt/recovery
# (or use kpartx to enumerate partitions if the card is partitioned)
sudo kpartx -av damaged.img
# (b) Flash the image to a fresh card and boot:
sudo dd if=damaged.img of=/dev/sdY bs=4M status=progress
Use e2fsck for ext4 partitions and fsck.vfat for the FAT32 boot partition. Not every card is recoverable — bad blocks at the wrong locations (boot sector, superblocks) can be terminal. Always have a backup.
13. Common config.txt and cmdline.txt Recipes
The two boot-config files you’ll edit most often. Both live in /boot/firmware/ (Pi 5 / CM5 / Bookworm) or /boot/ (CM4 / older).
13.1 config.txt — the uConsole essentials
A minimal, working config.txt for a Pi CM4 in the uConsole:
[all]
# Disable rainbow splash on boot (optional, faster cosmetic)
disable_splash=1
# JD9365DA-H3 LCD via DSI0 — applied by Clockwork kernel patch
dtoverlay=clockwork-uconsole-lcd
# 720p framebuffer
hdmi_force_hotplug=1
hdmi_group=2
hdmi_mode=87
hdmi_cvt=1280 720 60 6 0 0 0
# Audio over the OCP8178 amps via PWM (Vol 2 §7)
dtparam=audio=on
# Power management
dtoverlay=disable-bt # disable BT if unused (saves ~150 mW)
# Mini PCIe slot — enable the PCIe controller
dtparam=pciex1
# 64-bit kernel (modern OSes default to this)
arm_64bit=1
For a CM5, change dtoverlay=clockwork-uconsole-lcd to dtoverlay=clockwork-uconsole-lcd-cm5 if the community image differentiates. Many community images auto-detect.
13.2 cmdline.txt — the kernel command line
A minimal, working cmdline.txt:
console=serial0,115200 console=tty1 root=PARTUUID=abc12345-02 rootfstype=ext4 rootwait fsck.repair=yes quiet splash plymouth.ignore-serial-consoles
Key parameters:
console=serial0,115200— kernel messages on UART (debugging).console=tty1— kernel messages on the LCD (so you see boot).root=PARTUUID=...— which partition is the rootfs (useblkidto find).rootwait— wait for the rootfs device before mounting.fsck.repair=yes— auto-repair on boot.quiet splash— silence verbose boot.
13.3 Common edits and what they do
| Goal | Edit |
|---|---|
| Increase max CPU frequency | arm_freq=2000 in config.txt (Pi 5/CM5 only; risk of throttle/instability) |
| Force 1080p HDMI output | hdmi_mode=82 (1080p60) |
| Disable WiFi (lower power) | dtoverlay=disable-wifi |
| Disable BT (lower power) | dtoverlay=disable-bt |
| Verbose boot messages | Remove quiet splash from cmdline.txt |
| Boot into single-user mode (recovery) | Add single to cmdline.txt |
| Boot to specific runlevel | systemd.unit=multi-user.target (no GUI) |
| Boot from external drive | BOOT_ORDER=0xf14 in EEPROM |
| Disable serial console (free GPIO 14/15) | Remove console=serial0,115200 from cmdline.txt; enable_uart=0 |
| Enable I²C on GPIO 2/3 for the AIO V2 GPIO trick | dtparam=i2c_arm=on |
| Read-only root for embedded/kiosk use | Add ro to cmdline.txt; configure overlayfs separately |
14. Vol 12 Cheatsheet Updates
The following one-pagers should be added or updated in Vol 12:
- §3 (Boot recipes): full
BOOT_ORDERnibble decode table from §4.3. - §15 (Error → fix matrix): the symptom → fix table from §12.1.
- §17 (config.txt one-liner reference): the table from §13.3.
- §19 (Recovery commands):
sudo rpiboot+nRPIBOOTjumper for Pi recovery.sudo rkdeveloptool ldfor Rockchip detection.sudo rpi-eeprom-update -afor EEPROM update.sudo fw_setenvexamples.
The cheatsheet entries should be one-page-printable, font-size 9 minimum.
15. Resources
| Source | URL |
|---|---|
| Pi EEPROM bootloader documentation | https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-boot-eeprom |
rpi-eeprom releases | https://github.com/raspberrypi/rpi-eeprom/releases |
rpiboot (USB MSD recovery) | https://github.com/raspberrypi/usbboot |
rpi-imager (official flasher) | https://www.raspberrypi.com/software/ |
| Pi 4 boot flow | https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-boot-eeprom |
| Pi 5 boot flow | https://www.raspberrypi.com/documentation/computers/raspberry-pi-5.html |
| Rockchip BootROM / boot-stage wiki | http://opensource.rock-chips.com/wiki_Boot_option |
rkdeveloptool source | https://github.com/rockchip-linux/rkdeveloptool |
| U-Boot upstream | https://github.com/u-boot/u-boot |
libubootenv (modern fw_setenv) | https://github.com/sbabic/libubootenv |
| BerryBoot (multi-OS for Pi) | https://www.berryterminal.com/doku.php/berryboot |
| PINN multi-installer | https://github.com/procount/pinn |
f3probe (counterfeit-card detector) | https://github.com/AltraMayor/f3 |
rpi-clone | https://github.com/billw2/rpi-clone |
| Bootlin boot-time training | https://bootlin.com/training/boot-time/ |
16. Footnotes
(Footnotes are listed inline above; this section is a placeholder anchor for the index.)
17. Index
A — Active boot source — §4.3.
B — bootcmd (U-Boot) — §6.6. bootcode.bin — §3.3, §5.1. Boot ROM (BCM2711) — §3.1. BootROM (RK3588S2) — §6.1. Boot order — §4.3. Boot UART — §4.2. BOOT_MODE straps — §6.1. BOOT_ORDER register — §4.3. Backup (eMMC) — §8.5. BerryBoot — §7.4.2.
C — cmdline.txt — §13.2. config.txt — §13.1, §13.3. Counterfeit cards — §7.5. Cross-references (Vol 2 §14) — §4.2 (POWER_OFF_ON_HALT). console= — §13.2.
D — dnsmasq — §11.1. Device tree — §3.4, §6.5. dd (block-level backup) — §8.5. DRAM training — §6.2.
E — EEPROM (Pi) — §3.2, §4. eMMC — §8 (full chapter). enable_tftp (dnsmasq) — §11.1. e2fsck — §12.5.
F — f3probe — §7.5. fsck — §12.5. fstrim — §8.4. fw_printenv — §6.6. fw_setenv — §6.6. FAT32 boot partition — §7.1.
G — Geekbench (boot time) — §2.3 (cross-ref). GPU firmware (start4.elf) — §3.4.
H — Hex (BOOT_ORDER nibbles) — §4.3.
I — init (systemd) — §3.5. initramfs — §6.5. I²C (AIO V2 GPIO) — §13.3.
J — JMS580 (UAS quirks) — §9.3. JD9365DA-H3 — §13.1.
K — Kernel — §3.5, §6.5. Kernel command line — §13.2.
L — libubootenv — §6.6. Loadout (PXE) — §11.3. LPDDR4X — §6.2.
M — MaskROM mode (Rockchip) — §6.1, §12.3. Mini PCIe (NVMe boot) — §10.1, §10.2. mkfs.vfat — §7.3. Multi-OS — §7.4.
N — nRPIBOOT jumper — §3.6, §8.2. NFS-root — §11.2. NVMe — §10. Network boot — §11.
O — Open-source (Rockchip stages) — §6. OS choice (per card) — §7.4.
P — Partition layout — §7.1, §8.1. PINN — §7.4.3. PMIC latch (POWER_OFF_ON_HALT) — §4.2 (cross-ref Vol 2 §14). PXE — §11.
Q — Quiet boot (quiet splash) — §13.2.
R — Recovery (Pi rpiboot) — §3.6, §12.2. Recovery (Rockchip MaskROM) — §6.1, §12.3. rkdeveloptool — §6, §8.3, §12.3. rpi-eeprom-config — §4.2. rpi-eeprom-update — §4.4. rpi-clone — §8.5. rpiboot — §3.6, §12.2. Rockchip TPL — §6.2. Rockchip SPL — §6.3. Root NFS — §11.2.
S — SD card — §7. SDXC — §7.3. Single-user mode (recovery boot) — §13.3. Speeds (SD class) — §7.2. SPI EEPROM — §3.2. start4.elf — §3.4. Symptom → fix matrix — §12.1. systemd — §3.5.
T — TPL (Rockchip) — §6.2. tftp — §11.1.
U — UAS quirks — §9.3. U-Boot — §6.4. U-Boot SPL — §6.3. UHS-I (SD) — §7.2. USB-3 boot — §5.2, §9.2. USB-MSD recovery — §3.6. USB boot — §9.
V — vcgencmd bootloader_version — §4.4. V3.14_V5 — §9.2 (cross-ref Vol 2 §17). V3.14 USB-2 cap — §9.2.
W — Wear-leveling — §8.4. WiFi disable — §13.3. Write-protect (EEPROM) — §4.5.
X, Y, Z — None.
Footnotes
-
The BCM2711 boot ROM is documented at the Pi developer pages:
https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-boot-eeprom. The ROM is the same across all BCM2711 SKUs (Pi 4 / Pi 400 / CM4). ↩ -
Pi EEPROM bootloader documentation:
https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-boot-eeprom. Source code:https://github.com/raspberrypi/rpi-eeprom. Releases page:https://github.com/raspberrypi/rpi-eeprom/releases. ↩ -
rpibootsource and documentation:https://github.com/raspberrypi/usbboot.rpiboot -dputs the device in boot-from-host mode;rpibootwithout-dflashes the connected eMMC. ↩ -
Rockchip BootROM and boot-flow documentation:
http://opensource.rock-chips.com/wiki_Boot_option. Detailed boot-stage explanation:https://opensource.rock-chips.com/wiki_System_Trust_Stack. The Armbian wiki has additional context:https://docs.armbian.com/Hardware_Allwinner_overview/. ↩ -
U-Boot’s Rockchip TPL/SPL implementation:
https://github.com/u-boot/u-boot/tree/master/arch/arm/mach-rockchip. Each Rockchip platform has a TPL config inarch/arm/mach-rockchip/rk3588/. ↩ -
U-Boot upstream:
https://github.com/u-boot/u-boot. Rockchip-specific board configs:https://github.com/u-boot/u-boot/tree/master/configs(search forrk3588). ↩ -
fw_setenvandfw_printenvare part oflibubootenv(modern, clean) oru-boot-tools(older, still common). Documentation:https://github.com/sbabic/libubootenv. ↩ -
The 256 GB report: Talking Sasquach video at
youtu.be/4vNxTOFThdg~7:00. Cross-checked against the Pi bootloader documentation:https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-boot-eeprom. The Pi 4 / CM4 EEPROM versions ≥ 2022-04-26 add support for cards up to the SDXC 2 TB max; older versions cap at 256 GB or 512 GB depending on SDXC controller initialisation. The uConsole-specific report likely reflects the EEPROM version on the affected unit at time of recording. ↩ -
rkdeveloptoolsource:https://github.com/rockchip-linux/rkdeveloptool. Build for Linux:sudo apt install libudev-dev libusb-1.0-0-dev pkg-config && git clone … && autoreconf -i && ./configure && make. ↩