Skip to content

Releases: joeferner/rpi-hal

rpi-loader 0.7.0

Choose a tag to compare

@github-actions github-actions released this 02 Nov 22:32

Added

  • --escape picks the key that leaves terminal mode, as ^X caret
    notation or a single character, and RPI_LOADER_ESCAPE sets 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::init before 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-loader

The host CLI is a separate install: cargo install rpi-loader.

rpi-kickstart 0.5.0

Choose a tag to compare

@github-actions github-actions released this 02 Nov 22:31

Added

  • A bcm2712 feature, and a heap over all of a Pi 5's RAM. The
    feature names the chip, forwarding to rpi-hal's like bcm2711 does,
    and with it heap::init gives 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-hal 0.10 → 0.11 and rpi-hal-embassy 0.10 → 0.11. A
    consumer's own rpi-hal and rpi-hal-embassy must move with them,
    since their types cross this crate's API. The bcm2712 heap above
    needs 0.11's mem::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, same heap::init
    and heap::usage; embedded-alloc is no longer a dependency.

Fixed

  • storage::mount printed 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

Choose a tag to compare

@github-actions github-actions released this 02 Nov 22:30

Added

  • PCIe and NVMe on the Pi 5's external connector. New pcie module:
    Pcie::init(PCIE1, LinkSpeed, &timer) resets the BCM2712's pcie1
    root complex, programs its windows, trains the link and numbers the bus
    behind it (the firmware leaves this controller in reset, so no
    config.txt line is needed); functions() lists what answered and
    enable() assigns a function's memory BARs and turns on decoding and
    bus mastering. New nvme module: Nvme::new(&function, &mut Memory, &timer) brings up a controller with one polled I/O queue pair, then
    read_blocks/write_blocks/flush/shutdown on namespace 1, any
    4-byte-aligned buffer anywhere in RAM moved directly, others through a
    bounce page. The MMU now maps the first gigabyte of pcie1's outbound
    window (0x1b_0000_0000), new pac::PCIE1 token, and the nvme
    example reads, times and round-trips a block on the SSD. With the
    resident-fat feature, NvmeBlockDevice wraps an Nvme as that
    crate's BlockDevice, 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 move read_blocks' and write_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 in sd_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. New Sd::adma. Internally, the cache-maintenance
    helpers now take any address, including above 4 GiB.

  • multicore on the Pi 5. Core1::spawn and its siblings start
    cores 1-3 on the BCM2712 through the firmware's PSCI CPU_ON call
    (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. spawn panics
    with the PSCI error code if the firmware refuses. The four multicore
    examples build for the Pi 5 unchanged, and multicore_id runs there.

  • pwm on 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 a Channel
    implementing embedded_hal::pwm::SetDutyCycle, with set_inverted /
    clr_inverted. Pins are the header's GPIO12/13/18/19 (Pwm0Pin) and
    the fan connector's GPIO45 (Pwm1Pin). Pwm::<PWM1>::fan_rpm reads the
    fan's speed. New pac::PWM0/PWM1 tokens, and the pwm_fan example,
    which steps the fan from off to full and prints its RPM.

  • fault-report on 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. The fault_report example reads 8 GiB there
    to fault (the BCM2712 maps its whole low 4 GiB as RAM, so the other
    boards' 0xf000_0000 is an ordinary read).

  • mem on the Pi 5. mem::heap_region and mem::image_end are
    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's heap needs them, so a Pi 5 board can have a heap
    through it. heap_alloc builds for the Pi 5.

  • High-speed SD on the Pi 5. After the 4-bit bus, Sd::init asks
    the card for high speed with CMD6 and, 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, and Sd::init_with to cap the mode, and
    the sd_speed example, 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::init now 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. New BusMode::Sdr50 and BusMode::Sdr104; init asks
    for Sdr104. Taking the clock down fully and rewriting SD_PIN_SEL on
    every clock change, and selecting the delay-line PHY, follow Linux's
    sdhci-brcmstb for 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 all heap_region
    covers. New mem::ram_bytes gives the board's whole RAM from its
    revision code, and mem::high_ram_region the 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_ram example writes all of it with values made from each word's
    address and reads them back. mem::Error gains UnknownRam.

Fixed

  • cpu::core_id returned 0 on every Pi 5 core. The Cortex-A76 puts
    the core number in MPIDR'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 CPU 0x1f_0000_0000 to 0x1c_0000_0000,
    so the first access through pac::RP1_BASE (the console UART, GPIO,
    I2C, PWM) stalled the core with no fault. New rp1::init moves the
    window back to RP1_BASE, keeping the PCIe address the bootloader
    assigned RP1, and rt's boot sequence calls it before kmain, through
    a new per-chip rpi_hal_soc_init hook in boot64.s. A program with its
    own boot sequence calls rp1::init itself, before its first RP1 access.

rpi-hal-embassy 0.11.0

Choose a tag to compare

@github-actions github-actions released this 02 Nov 22:31

Changed

  • rpi-hal 0.10 → 0.11. Breaking only in that a consumer's own
    rpi-hal must move to 0.11 as well, as with every rpi-hal minor.
    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 and rpi-hal has no interrupt controller
    for that chip yet.

rpi-loader 0.6.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 22:56

Added

  • Preliminary Pi 5 (BCM2712) firmware, behind a new bcm2712
    feature, built with make build64-bcm2712 into kernel8.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 needs os_check=0, kernel_address=0x80000
    and pciex4_reset=0 in config.txt, and no firmware files.

Changed

  • CMD_SET_BAUD checks the rate with rpi-hal's
    Uart::supports_baud
    instead 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 with rpi-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-hal 0.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-loader

The host CLI is a separate install: cargo install rpi-loader.

rpi-kickstart 0.4.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 22:54

Changed

  • rpi-hal 0.9 → 0.10 and rpi-hal-embassy 0.9 → 0.10. Breaking only
    in that a consumer's own rpi-hal and rpi-hal-embassy must move with
    them, since their types cross this crate's API. Nothing in this crate
    changed. rpi-hal 0.10's preliminary Pi 5 support has no bcm2712
    feature here yet; the chip features this crate forwards are unchanged.

rpi-hal 0.10.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 22:54

Added

  • I2C constructors that take a frequency:
    I2c::<BSC1>::init_with_frequency and I2c::<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 with divider_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: whether set_baud would 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, as rpi-loader acknowledges a baud change at the old rate.
  • mailbox::Mailbox::measured_clock_rate_hz (tag 0x0003_0047,
    "Get Clock Rate Measured"): the rate a clock is actually running at, as
    the firmware measures it. clock_rate_hz reports 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.
    The mailbox_props example 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 bcm2712 chip
    feature, AArch64 only. There is no generated PAC for the chip, so
    pac is 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 same Pin API as the other chips, so the
    blink example builds unchanged), the SoC's always-on GPIO block
    (gio_aon), which carries the ACT LED (new example blink_act), and
    uart on RP1's UART0, the same GPIO14/15 pins and Uart::init call as
    the other chips, so uart_hello and uart_echo build unchanged. On
    this chip init sets RP1's UART clock itself, to its 50 MHz crystal,
    rather than relying on the firmware's choice; init_bluetooth and the
    async half are left out. Uart::set_baud now 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 the 0xC000_0000 alias the
    older chips need, so the translation is the identity there. sd
    builds too, over the Pi 5's sdio1 SDHCI controller (pac::SDIO1)
    with the same API; sd_read, sd_resident_fat_read and sd_fat_write
    run unchanged. The card's power and I/O voltage are always-on GPIO
    lines on this board, so Sd::init power-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, so init
    takes but doesn't use mailbox there. No DMA or async variants, and
    sd::Error::Dma doesn't exist under bcm2712. i2c builds too, as a
    new driver for RP1's I2C controllers (Synopsys DesignWare), with
    pac::BSC1 naming I2C1 on GPIO2/3 and pac::BSC0 I2C0 on GPIO0/1 so
    header-bus code ports across; it does real repeated starts between a
    transaction's operations, and i2c_sh1106_oled runs unchanged. The
    AArch64 MMU maps the chip's peripherals above 4 GiB. The rest of the
    crate is cfg'd out on this chip until each driver is checked against
    it. multicore, fault-report, vchiq/mmal and v3d are rejected
    with a compile_error!. The card needs pciex4_reset=0 in
    config.txt to reach RP1.

Deprecated

  • I2c::<BSC1>::init, I2c::<BSC0>::init and I2c::<BSC0>::init_id,
    which take a raw DIV.CDIV, in favour of the init*_with_frequency
    constructors. A raw divider only means what its caller thinks at one
    core clock: the reset default 0x05dc, 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_for stays, for working
    out the rate a divider produces.

Fixed

  • sd_fat_read, sd_fat_write and sd_resident_fat_read didn't build
    for the Pi 4.
    They took the controller as peripherals.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_write seeds 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_frame are emitted for
    aarch64-unknown-none-softfloat, and linker64.ld didn't place them,
    so the linker put them at the load address ahead of .text. _start
    moved to 0x80800, and the firmware, which jumps to the load address,
    started executing .eh_frame_hdr data. linker64.ld now discards both
    sections; every profile is panic = "abort", so nothing reads them.

  • A nested critical section hung with the multicore feature. The
    critical-section crate allows nesting, and code nests without meaning
    to — a log call inside a critical_section::with is one. With
    multicore on, acquire also took a cross-core spinlock that was a
    plain flag, which couldn't tell the core holding it from another core
    asking, so an inner acquire spun 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
    release frees it. New example multicore_critical_section checks
    nesting and that two cores still exclude each other. Builds without
    multicore are unchanged.

rpi-hal-embassy 0.10.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 22:54

Changed

  • rpi-hal 0.9 → 0.10. Breaking only in that a consumer's own
    rpi-hal must move to 0.10 as well, as with every rpi-hal minor.
    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 and rpi-hal has no interrupt controller for that
    chip yet. It also deprecates the raw-divider I2C constructors, so the
    embassy_i2c example 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

Choose a tag to compare

@github-actions github-actions released this 16 Oct 15:34

Changed

  • Moved into joeferner/rpi-hal;
    the old rpi-loader repository 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 points repository there and its new homepage at
    https://joeferner.github.io/rpi-hal/crates/loader/.
  • The firmware builds against rpi-hal 0.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-loader

The host CLI is a separate install: cargo install rpi-loader.

rpi-loader-ota 0.3.1

Choose a tag to compare

@github-actions github-actions released this 16 Oct 15:31

Changed