rpi-kickstart
The bring-up a bare-metal Raspberry Pi application does before it is an
application, on top of the rpi-hal crate.
rpi-hal stops at the peripheral: it hands out a Uart, a Mailbox, an
Sd. What every board then does with them turns out to be the same
program written again — a console the whole image can print to, a heap
sized from what the firmware reports, a card mounted and a settings file
read off it, a site served out of RAM, a clock that something has to set.
This crate is that program, written once.
Status
Early. The repository, the build and the release path are in place, and the modules are being moved in one at a time, each behind the feature named for it.
| Feature | What it is |
|---|---|
console |
One sink for the whole image, and logln! |
entropy |
The SoC's hardware RNG, and the getrandom backend over it |
heap |
A TLSF #[global_allocator], sized from what the firmware reports |
config |
A TOML settings file into the board's own schema, with file:line:column errors and the checks TOML cannot make |
storage |
The card's FAT volume, mounted with resident-fat; with config, config::load reads the settings file off it |
site |
Everything under /WWW on the card, read into RAM at boot and looked up by request path |
clock |
The wall clock: one sink every time source sets and everything else reads, None until set; with storage, files are stamped with it |
tz |
Local time over a tz-rs zone |
sntp |
Sets clock from an NTP server, and keeps it set |
tls |
TLS 1.3 client connections over an embassy-net TCP socket, with real certificate verification — over rustls-rustcrypto, pre-release and unaudited |
https |
A minimal HTTPS client over tls: one request per connection, chunked bodies reassembled |
notify |
Outbound notifications over https: a Channel trait, with Ntfy (push) and Resend (email) shipped |
webpki-roots |
Mozilla's trust anchors for tls, compiled in |
net |
Gets the board onto the network whichever way it can — USB Ethernet first, the radio as a fallback — as an embassy-net stack |
ethernet |
USB Ethernet as a net interface: the LAN9514 on a Pi 2B/3B and the LAN7800 on a 3B+, told apart on the bus |
wifi |
The on-board radio as a net interface, through the bring-up the board supplies |
mdns |
An mDNS responder for one name, re-read on every pass so a rename takes effect without a reboot |
web |
The picoserve socket loop a pool of the board's tasks runs, and the pieces its router is built from; with site, SiteFiles serves the card's /WWW |
auth |
Password login for the web interface: a PBKDF2 verifier, a session table, the session cookie, and RequireLogin, a layer over every route that asks the board's whitelist |
metrics |
A writer for Prometheus's text exposition format: escaped labels, and decimals without a float formatter |
ota |
The over-the-air update route — read, install, answer in one shared JSON shape — and the reboot handshake after it |
splash |
A boot logo baked into the kernel as run-length-encoded RGB565, decoded with no allocation |
splash-build |
The encoder for splash, for a board's build.rs: a PNG in, the bytes splash::Image reads. Host only — it needs std |
The examples read their settings from kickstart.toml at the root of the
card; copy kickstart.toml.example and fill it in.
entropy and heap each install something the whole binary resolves
against — a getrandom backend and a #[global_allocator]. There is no
way for a library to make such a choice overridable, so both are off by
default and enabling one is how a board says it wants this crate to take
it.
Chips
bcm2835 (Pi 1, Zero), bcm2837 (Pi 2, 3) and bcm2711 (Pi 4) forward
to rpi-hal's features of the same name. None is a default.
Most boards never name one here: an application's own rpi-hal line
already selects a chip and cargo unifies it across the graph. They exist
for a build that has no such line — this repository's own, and a consumer
that depends on rpi-kickstart without depending on rpi-hal directly.
They are not mutually exclusive, because cargo features cannot be. Two at
once resolves by rpi-hal's precedence — bcm2711 > bcm2837 >
bcm2835 — rather than failing, which is why nothing here builds with
--all-features; the make recipes name an explicit set instead.
entropy and heap need a chip; console deliberately reaches nothing
in rpi-hal, so a board taking only that needs no selection at all.
examples/hello.rs takes no features at all, which is what makes it
useful: it proves the parts that have nothing to do with the crate's
contents — toolchain, targets, linker script, both load addresses — so if
it boots, none of those is what broke.
Design
Everything is opt-in. Each capability is its own feature and its own module, and none are on by default. A board states what it is and pays for nothing else — a board with no network takes no TLS, and a board with a DS3231 takes no SNTP.
Everything is replaceable. Where a piece has a plausible second implementation — the time source above all — what this crate owns is the sink the rest of the image reads through, and filling it is the application's choice. Nothing here is a fork to escape from.
The tasks stay with the application. An
#[embassy_executor::task] cannot be generic, so this crate never
declares one: it exposes async fns and the application wraps them in
tasks with its own concrete types. Routing, the #[global_allocator] and
the #[panic_handler] stay there too, for the same reason — they are
single, program-wide choices that a library taking them away cannot give
back.
Relationship to the other crates
| Crate | What it is |
|---|---|
rpi-hal |
The HAL: peripherals, boot sequence, MMU, drivers |
rpi-hal-embassy |
Embassy's platform pieces: time driver, executor, network adapters |
rpi-kickstart |
This crate: the application layer above both |
rpi-loader-ota |
The OTA bundle format, shared with the loader |
Building
AArch32 is the default target only because it is the one a Pi 2 can run;
make build covers both. The example images link against rpi-hal's
published rpi-link.x, which is already at the load address for the
target being built — 0x8000 for a kernel7.img, 0x80000 for a
kernel8.img — so an image direct-boots where the firmware puts it.
make pre-commit runs the whole check set: formatting, clippy, the
library and example builds for both architectures, and a doc build with
warnings denied.
ARMv6 (Pi 1, Pi Zero) has no make recipes here, unlike rpi-hal and
rpi-hal-embassy. That target is tier 3 — no precompiled core, so
nightly and -Z build-std — and nothing in this crate is
architecture-specific, so adding it is a make change rather than a
source one whenever a board wants it.
Examples
See examples/. Each one's header comment says what it
demonstrates and what its output is evidence for.
scripts/build-example.sh <name> produces target/kernel7.img;
scripts/build-example64.sh <name> produces target/kernel8.img. Copy
either to an SD card's boot partition, or send it over UART with
rpi-loader:
License
MIT OR Apache-2.0