Repository navigation
Releases: joeferner/rpi-hal
Release list
rpi-loader 0.7.0
Added
--escapepicks the key that leaves terminal mode, as^Xcaret
notation or a single character, andRPI_LOADER_ESCAPEsets it for
every invocation. Ctrl-] stays the default, but a keyboard layout with
no]key (German, Nordic) has no reliable way to type it. The
terminal-mode banner names the key in use, calls the default the
telnet escape, and says how to change it.
Fixed
- The Pi 5 firmware never answered with a bootloader from 2024-10-10
or later. Those bootloaders put RP1, and so the loader's console, at
a different CPU address than rpi-hal expects, and the first console
access stalled the core. The loader now calls rpi-hal's new
rp1::initbefore it opens the console, which moves RP1 back. - Ctrl-C did not stop a handshake the device talked over. The CLI
checked for Ctrl-C only between HELLO attempts, and each attempt waited
up to 2 s per byte for the ACK, so a device that kept printing (a
kernel still running instead of the loader) restarted that wait with
every byte and Ctrl-C was never noticed. Every read on the link now
blocks for at most 100 ms and checks for Ctrl-C in between, and the
connect line says Ctrl-C aborts.
Images
Copy the image matching your board to the SD card's boot
partition, renamed to the filename in its own name:
kernel7.img (32-bit), kernel8.img (64-bit) or kernel.img
(ARMv6). The firmware loads one of those names and no others,
chosen by the CPU it finds, and a board handed the wrong one
stops without printing anything. Use kernel8.img only with
arm_64bit=1 in config.txt.
bcm2837 covers the Pi 2 and Pi 3 (a Pi 2 v1.1 takes the
kernel7.img alone: its cores are 32-bit only); bcm2711 covers
the Pi 4; bcm2835 covers the Pi 1 and Pi Zero, and is ARMv6, so
it has no 64-bit counterpart. bcm2712 covers the Pi 5,
preliminarily: 64-bit only, and its card needs no firmware files
but a config.txt with os_check=0, kernel_address=0x80000
and pciex4_reset=0 (see the loader's README). Verify with
sha256sum -c SHA256SUMS.
rpi-loader-<version>.bash is the bash completion for the host
CLI, for installing without building it first:
sudo cp rpi-loader-<version>.bash \
/usr/share/bash-completion/completions/rpi-loaderThe host CLI is a separate install: cargo install rpi-loader.
rpi-kickstart 0.5.0
Added
- A
bcm2712feature, and a heap over all of a Pi 5's RAM. The
feature names the chip, forwarding torpi-hal's likebcm2711does,
and with itheap::initgives the allocator the board's RAM above the
first gigabyte (rpi_hal::mem::high_ram_region) as well as the
firmware's share below it: 8182 MiB on an 8 GB board, where it was 1014.
Changed
rpi-hal0.10 → 0.11 andrpi-hal-embassy0.10 → 0.11. A
consumer's ownrpi-halandrpi-hal-embassymust move with them,
since their types cross this crate's API. Thebcm2712heap above
needs 0.11'smem::high_ram_region.- The heap is
rlsf's TLSF driven directly, in place of
embedded-alloc's wrapper of the same allocator, which takes one
region where a Pi 5's heap is two. Same allocator, sameheap::init
andheap::usage;embedded-allocis no longer a dependency.
Fixed
storage::mountprinted 512-byte clusters as "0 KiB". The mount
line gave the cluster size in whole KiB, and 512 bytes, which the small
FAT32 boot partition of a Raspberry Pi OS card uses, rounded down to 0.
Sizes under 1 KiB are now printed in bytes.
rpi-hal 0.11.0
Added
-
PCIe and NVMe on the Pi 5's external connector. New
pciemodule:
Pcie::init(PCIE1, LinkSpeed, &timer)resets the BCM2712'spcie1
root complex, programs its windows, trains the link and numbers the bus
behind it (the firmware leaves this controller in reset, so no
config.txtline is needed);functions()lists what answered and
enable()assigns a function's memory BARs and turns on decoding and
bus mastering. Newnvmemodule:Nvme::new(&function, &mut Memory, &timer)brings up a controller with one polled I/O queue pair, then
read_blocks/write_blocks/flush/shutdownon namespace 1, any
4-byte-aligned buffer anywhere in RAM moved directly, others through a
bounce page. The MMU now maps the first gigabyte ofpcie1's outbound
window (0x1b_0000_0000), newpac::PCIE1token, and thenvme
example reads, times and round-trips a block on the SSD. With the
resident-fatfeature,NvmeBlockDevicewraps anNvmeas that
crate'sBlockDevice, so a FAT32 volume on the SSD mounts like the SD
card's; drives formatted with 512-byte blocks only, anything else is
nvme::Error::BlockSize. -
ADMA2 for the Pi 5's SD card, off by default.
Sd::set_adma(true)
has the controller moveread_blocks' andwrite_blocks' data over its
own DMA, from a table of 64-bit descriptors, for any 4-byte-aligned
buffer anywhere in RAM (the controller sees RAM at its physical
addresses). Same data both ways insd_speed, reads in every mode and
a 1 MiB write; no faster, since the FIFO already keeps up with the bus,
and about 1% slower at 1 MiB a command for the cache maintenance, hence
off. What it frees is the CPU, for a transfer that doesn't block, which
isn't here yet. NewSd::adma. Internally, the cache-maintenance
helpers now take any address, including above 4 GiB. -
multicoreon the Pi 5.Core1::spawnand its siblings start
cores 1-3 on the BCM2712 through the firmware's PSCICPU_ONcall
(smc), which is how the Pi 5's ARM Trusted Firmware holds them; the
spin-table the other AArch64 boards use does not exist there, and its
address is inside the firmware's own reserved memory.spawnpanics
with the PSCI error code if the firmware refuses. The four multicore
examples build for the Pi 5 unchanged, andmulticore_idruns there. -
pwmon the Pi 5, over RP1's two PWM blocks.Pwm::<PWM0 | PWM1>::init(block, clock_divisor)runs the block from the 50MHz
crystal;channel(&gpio, pin, range)hands back aChannel
implementingembedded_hal::pwm::SetDutyCycle, withset_inverted/
clr_inverted. Pins are the header's GPIO12/13/18/19 (Pwm0Pin) and
the fan connector's GPIO45 (Pwm1Pin).Pwm::<PWM1>::fan_rpmreads the
fan's speed. Newpac::PWM0/PWM1tokens, and thepwm_fanexample,
which steps the fan from off to full and prints its RPM. -
fault-reporton the Pi 5. The handler needed no changes; the
compile_error!is gone, and the report goes out on RP1's UART0 like
any other console write. Thefault_reportexample reads 8 GiB there
to fault (the BCM2712 maps its whole low 4 GiB as RAM, so the other
boards'0xf000_0000is an ordinary read). -
memon the Pi 5.mem::heap_regionandmem::image_endare
built for the BCM2712, where they had been compiled out with the rest
of the not-yet-ported modules though nothing in them is chip-specific.
rpi-kickstart'sheapneeds them, so a Pi 5 board can have a heap
through it.heap_allocbuilds for the Pi 5. -
High-speed SD on the Pi 5. After the 4-bit bus,
Sd::initasks
the card for high speed withCMD6and, if it has it, switches the
card, the host's high-speed bit and the clock from 25 to 50MHz, so a
multi-block read goes from 12.0 to 23.9 MB/s. Best-effort like the
4-bit bus: a card without it, or a failed switch, stays at default
speed. The other chips stay at default speed for now. New
sd::BusMode,Sd::bus_mode, andSd::init_withto cap the mode, and
thesd_speedexample, which reads the same data in every mode and
reports each one's throughput and the cost of a command. -
UHS-I SD on the Pi 5: SDR50 and SDR104.
Sd::initnow asks a
card for 1.8V signalling, switches it (CMD11, Host Control 2's 1.8V
bit and the I/O rail's always-on GPIO line), then switches it to
SDR104 at 200MHz or SDR50 at 100MHz (CMD6) and tunes the sampling
point (CMD19). A multi-block read goes from 23.9 MB/s at high speed to
47.4 (SDR50) and 72.8 (SDR104), every mode reading the same data. A
failed step powers the card off and starts again one mode lower, down
to high speed. NewBusMode::Sdr50andBusMode::Sdr104;initasks
forSdr104. Taking the clock down fully and rewritingSD_PIN_SELon
every clock change, and selecting the delay-line PHY, follow Linux's
sdhci-brcmstbfor this chip. -
All of a Pi 5's RAM. The firmware reports only the first gigabyte
to the ARM, whatever the board's size, and that is allheap_region
covers. Newmem::ram_bytesgives the board's whole RAM from its
revision code, andmem::high_ram_regionthe rest above 1 GiB, with
what lies past 4 GiB mapped first by the new
mmu::map_ram_above_4g(1 GiB blocks, after boot, since the boot-time
map can't know how far the RAM goes). On an 8 GB board that is
0x4000_0000..0x2_0000_0000, 7 GiB on top of the 1014 MiB heap. The
high_ramexample writes all of it with values made from each word's
address and reads them back.mem::ErrorgainsUnknownRam.
Fixed
-
cpu::core_idreturned 0 on every Pi 5 core. The Cortex-A76 puts
the core number inMPIDR's Aff1 field, not Aff0.core_id, the boot
stub's core-0 check and the secondary-core trampoline now read Aff1 on
the BCM2712. -
Nothing on RP1 worked with a Pi 5 bootloader from 2024-10-10 or
later. Those bootloaders start PCIe memory at 2 GiB, which moves
pcie2's window onto RP1 from CPU0x1f_0000_0000to0x1c_0000_0000,
so the first access throughpac::RP1_BASE(the console UART, GPIO,
I2C, PWM) stalled the core with no fault. Newrp1::initmoves the
window back toRP1_BASE, keeping the PCIe address the bootloader
assigned RP1, andrt's boot sequence calls it beforekmain, through
a new per-chiprpi_hal_soc_inithook inboot64.s. A program with its
own boot sequence callsrp1::inititself, before its first RP1 access.
rpi-hal-embassy 0.11.0
Changed
rpi-hal0.10 → 0.11. Breaking only in that a consumer's own
rpi-halmust move to 0.11 as well, as with everyrpi-halminor.
Nothing in this crate's API changed. 0.11's additions are Pi 5
(bcm2712) drivers, which this crate still doesn't offer: its time
driver is interrupt-driven andrpi-halhas no interrupt controller
for that chip yet.
rpi-loader 0.6.0
Added
- Preliminary Pi 5 (BCM2712) firmware, behind a new
bcm2712
feature, built withmake build64-bcm2712intokernel8.img(AArch64
only), with every command,eeprom-*included (through RP1's I2C0 on
the same GPIO0/1). Releases carry it as a sixth image,
rpi-loader-<version>-bcm2712-kernel8.img. Nothing in
the boot stub changed: the loader runs with the MMU off, which reaches
the Pi 5's peripherals above 4 GiB directly, and hands off at EL2 as on
the other boards. The card needsos_check=0,kernel_address=0x80000
andpciex4_reset=0inconfig.txt, and no firmware files.
Changed
CMD_SET_BAUDchecks the rate withrpi-hal's
Uart::supports_baudinstead of the firmware's own copy of the
divisor arithmetic, which assumed the 48 MHz UART clock of every board
before the Pi 5 (50 MHz there).- The EEPROM bus runs at 100 kHz on every board. It was the BSC's
reset-default divider, documented as 100 kHz for a 150 MHz core clock,
which is 166 kHz on a board running its core at 250 MHz (a Pi 3, for
one). The firmware now sets it up withrpi-hal's
init_id_with_frequency, which asks for the real core clock first; a
mailbox failure there is reported as an I2C error before the bus is
touched. - The firmware builds against
rpi-hal0.10. The wire protocol, the
CLI's arguments and the load addresses are unchanged, so a 0.5 CLI and
a 0.6 loader work together either way round.
Images
Copy the image matching your board to the SD card's boot
partition, renamed to the filename in its own name:
kernel7.img (32-bit), kernel8.img (64-bit) or kernel.img
(ARMv6). The firmware loads one of those names and no others,
chosen by the CPU it finds, and a board handed the wrong one
stops without printing anything. Use kernel8.img only with
arm_64bit=1 in config.txt.
bcm2837 covers the Pi 2 and Pi 3 (a Pi 2 v1.1 takes the
kernel7.img alone: its cores are 32-bit only); bcm2711 covers
the Pi 4; bcm2835 covers the Pi 1 and Pi Zero, and is ARMv6, so
it has no 64-bit counterpart. bcm2712 covers the Pi 5,
preliminarily: 64-bit only, and its card needs no firmware files
but a config.txt with os_check=0, kernel_address=0x80000
and pciex4_reset=0 (see the loader's README). Verify with
sha256sum -c SHA256SUMS.
rpi-loader-<version>.bash is the bash completion for the host
CLI, for installing without building it first:
sudo cp rpi-loader-<version>.bash \
/usr/share/bash-completion/completions/rpi-loaderThe host CLI is a separate install: cargo install rpi-loader.
rpi-kickstart 0.4.0
Changed
rpi-hal0.9 → 0.10 andrpi-hal-embassy0.9 → 0.10. Breaking only
in that a consumer's ownrpi-halandrpi-hal-embassymust move with
them, since their types cross this crate's API. Nothing in this crate
changed.rpi-hal0.10's preliminary Pi 5 support has nobcm2712
feature here yet; the chip features this crate forwards are unchanged.
rpi-hal 0.10.0
Added
- I2C constructors that take a frequency:
I2c::<BSC1>::init_with_frequencyandI2c::<BSC0>::init_with_frequency
/init_id_with_frequency, each(gpio, bsc, frequency_hz, &mut mailbox, timer) -> Result<Self, mailbox::Error>. They ask the firmware
for the core clock and compute the divider withdivider_for, so SCL
is the rate asked for (or the nearest below it) on any board and any
config.txt. They exist on the Pi 5 too (all but the camera-bus one),
so I2C code written against them builds for every chip. Every I2C
example uses them now. uart::Uart::supports_baud: whetherset_baudwould accept a
rate, without touching the hardware, computed against the chip's own
UART clock. For a protocol that agrees to a rate before switching to
it, asrpi-loaderacknowledges a baud change at the old rate.mailbox::Mailbox::measured_clock_rate_hz(tag0x0003_0047,
"Get Clock Rate Measured"): the rate a clock is actually running at, as
the firmware measures it.clock_rate_hzreports the rate the firmware
has set, which can differ when the firmware moves a clock on its own,
so its documentation no longer calls that the "real, current" rate.
Themailbox_propsexample prints both for the ARM clock. On a Pi 2B
they disagree from boot, 600 MHz set and 900 measured, and
set_clock_rate_hz's documentation now says so: it claimed an unasked
ARM clock stays at its floor, when it runs at full speed for a while
and drops to the floor later.- Preliminary BCM2712 (Pi 5) support, behind a new
bcm2712chip
feature, AArch64 only. There is no generated PAC for the chip, so
pacis a small hand-written module of the same shape. What builds:
the System Timer (timer), the 40-pin header's GPIO0-27 on the RP1
south bridge (gpio, the samePinAPI as the other chips, so the
blinkexample builds unchanged), the SoC's always-on GPIO block
(gio_aon), which carries the ACT LED (new exampleblink_act), and
uarton RP1's UART0, the same GPIO14/15 pins andUart::initcall as
the other chips, souart_helloanduart_echobuild unchanged. On
this chipinitsets RP1's UART clock itself, to its 50 MHz crystal,
rather than relying on the firmware's choice;init_bluetoothand the
asynchalf are left out.Uart::set_baudnow computes its divisor
from the chip's UART clock instead of a hardcoded 48 MHz.mailbox
builds too, at the BCM2712's address. Its firmware takes plain
physical buffer addresses rather than the0xC000_0000alias the
older chips need, so the translation is the identity there.sd
builds too, over the Pi 5'ssdio1SDHCI controller (pac::SDIO1)
with the same API;sd_read,sd_resident_fat_readandsd_fat_write
run unchanged. The card's power and I/O voltage are always-on GPIO
lines on this board, soSd::initpower-cycles the card through them
into 3.3 V signalling rather than inheriting whatever mode the
bootloader left it in. The base clock is a fixed 200 MHz, soinit
takes but doesn't usemailboxthere. No DMA or async variants, and
sd::Error::Dmadoesn't exist underbcm2712.i2cbuilds too, as a
new driver for RP1's I2C controllers (Synopsys DesignWare), with
pac::BSC1naming I2C1 on GPIO2/3 andpac::BSC0I2C0 on GPIO0/1 so
header-bus code ports across; it does real repeated starts between a
transaction's operations, andi2c_sh1106_oledruns unchanged. The
AArch64 MMU maps the chip's peripherals above 4 GiB. The rest of the
crate iscfg'd out on this chip until each driver is checked against
it.multicore,fault-report,vchiq/mmalandv3dare rejected
with acompile_error!. The card needspciex4_reset=0in
config.txtto reach RP1.
Deprecated
I2c::<BSC1>::init,I2c::<BSC0>::initandI2c::<BSC0>::init_id,
which take a rawDIV.CDIV, in favour of theinit*_with_frequency
constructors. A raw divider only means what its caller thinks at one
core clock: the reset default0x05dc, documented as 100 kHz, is
166 kHz on a board whose core runs at 250 MHz, and the Pi 5's
controller has no divider at all.i2c::divider_forstays, for working
out the rate a divider produces.
Fixed
-
sd_fat_read,sd_fat_writeandsd_resident_fat_readdidn't build
for the Pi 4. They took the controller asperipherals.EMMC, which
is the card's controller only on the Pi 2/3. They use
Sd::steal_emmc()now, which names the right one on every chip, and
sd_fat_writeseeds its test value from the System Timer instead of
the Pi 2/3-only hardware RNG. -
AArch64 images started with unwind tables instead of code. With
the current nightly,.eh_frame_hdr/.eh_frameare emitted for
aarch64-unknown-none-softfloat, andlinker64.lddidn't place them,
so the linker put them at the load address ahead of.text._start
moved to0x80800, and the firmware, which jumps to the load address,
started executing.eh_frame_hdrdata.linker64.ldnow discards both
sections; every profile ispanic = "abort", so nothing reads them. -
A nested critical section hung with the
multicorefeature. The
critical-sectioncrate allows nesting, and code nests without meaning
to — a log call inside acritical_section::withis one. With
multicoreon,acquirealso took a cross-core spinlock that was a
plain flag, which couldn't tell the core holding it from another core
asking, so an inneracquirespun forever waiting for its own core.
The lock now records its owner (cpu::core_id) and a depth: the owning
core goes a level deeper, other cores wait, and only the outermost
releasefrees it. New examplemulticore_critical_sectionchecks
nesting and that two cores still exclude each other. Builds without
multicoreare unchanged.
rpi-hal-embassy 0.10.0
Changed
rpi-hal0.9 → 0.10. Breaking only in that a consumer's own
rpi-halmust move to 0.10 as well, as with everyrpi-halminor.
Nothing in this crate's API changed. 0.10 adds preliminary Pi 5
(bcm2712) support, which this crate doesn't offer: its time driver is
interrupt-driven andrpi-halhas no interrupt controller for that
chip yet. It also deprecates the raw-divider I2C constructors, so the
embassy_i2cexample now builds its bus with
I2c::<BSC1>::init_with_frequency; it stops with a message if the core
clock can't be read, instead of assuming 250 MHz.
rpi-loader 0.5.1
Changed
- Moved into joeferner/rpi-hal;
the oldrpi-loaderrepository is archived, its release pages (images
up to 0.5.0) left in place. Releases from here are tagged
loader-v<version>, so images download from
https://github.com/joeferner/rpi-hal/releases/download/loader-v<version>/.
The CLI's manifest pointsrepositorythere and its newhomepageat
https://joeferner.github.io/rpi-hal/crates/loader/. - The firmware builds against
rpi-hal0.9.1. The wire protocol, the
CLI's arguments and the load addresses are unchanged, so a 0.5.0 CLI and
a 0.5.1 loader work together either way round.
Images
Copy the image matching your board to the SD card's boot
partition, renamed to the filename in its own name:
kernel7.img (32-bit), kernel8.img (64-bit) or kernel.img
(ARMv6). The firmware loads one of those names and no others,
chosen by the CPU it finds, and a board handed the wrong one
stops without printing anything. Use kernel8.img only with
arm_64bit=1 in config.txt.
bcm2837 covers the Pi 2 and Pi 3 (a Pi 2 v1.1 takes the
kernel7.img alone: its cores are 32-bit only); bcm2711 covers
the Pi 4; bcm2835 covers the Pi 1 and Pi Zero, and is ARMv6, so
it has no 64-bit counterpart. Verify with
sha256sum -c SHA256SUMS.
rpi-loader-<version>.bash is the bash completion for the host
CLI, for installing without building it first:
sudo cp rpi-loader-<version>.bash \
/usr/share/bash-completion/completions/rpi-loaderThe host CLI is a separate install: cargo install rpi-loader.
rpi-loader-ota 0.3.1
Changed
- Moved into joeferner/rpi-hal,
with the loader; the oldrpi-loaderrepository is archived. The
manifest'srepositorypoints there and its newhomepageat
https://joeferner.github.io/rpi-hal/crates/loader/. Releases from here
are taggedloader-ota-v<version>. No API or bundle-format change.