175 lines
9.2 KiB
Markdown
175 lines
9.2 KiB
Markdown
# Raspberry Pi 4 / 400
|
|
|
|
MontaukOS runs on the Raspberry Pi 4 and 400 through the Pi Firmware Task
|
|
Force's UEFI firmware ([pftf/RPi4](https://github.com/pftf/RPi4), an EDK2
|
|
build) in ACPI mode, booting Limine like any other UEFI machine.
|
|
|
|
## Building and writing the SD card
|
|
|
|
```bash
|
|
./toolchain/build-montauk-toolchain.sh aarch64 # once
|
|
make ARCH=aarch64 rpi4-sd # -> montauk-rpi4.img
|
|
sudo dd if=montauk-rpi4.img of=/dev/sdX bs=4M conv=fsync
|
|
```
|
|
|
|
`scripts/mk-rpi4-sd.sh` downloads the firmware release once into
|
|
`rpi4-firmware/` (`PFTF_VERSION=v1.53` by default) and builds an MBR image:
|
|
|
|
| Partition | Type | Contents |
|
|
|---|---|---|
|
|
| 1 | 0xEF, FAT32 | the firmware files, Limine (`EFI/BOOT/BOOTAA64.EFI`), `boot/kernel` |
|
|
| 2 | 0x83, ext2, label `MONTAUKROOT` | the system tree (the contents of `ramdisk.tar`) |
|
|
|
|
Limine loads only the kernel; the `limine.conf` on the card has no ramdisk
|
|
module. Finding no ramdisk, the kernel mounts the partition labelled
|
|
`MONTAUKROOT` as `0:/` (`Fs/Boot.cpp`), read through the SD driver
|
|
(`Drivers/Storage/Sdhci`), and the ESP after it. Files load as they are
|
|
used instead of the firmware reading the whole 80 MB tree before boot.
|
|
Unlike the ramdisk, the root is persistent: changes to `0:/` survive a
|
|
reboot, so shut down from the desktop rather than pulling power.
|
|
The image is sized to its contents (`ROOT_FREE_MIB`, default 64, sets the
|
|
free space on the root) and grows to fill the card on first boot.
|
|
|
|
### First boot: growing the root
|
|
|
|
Only the SD image ships `0:/os/firstboot.elf` (`programs/src/firstboot`,
|
|
built into `programs/bin-aarch64-rpi/`, which the script lays over the
|
|
system tree), and the script adds its init service, ahead of all others,
|
|
and its capability grant (`storage_admin`, `system_image`) to the image's
|
|
`init.toml` and `capabilities.toml`. On first boot it shows a status card
|
|
in the login screen's style while it calls `SYS_FS_GROW` on drive 0 in a
|
|
loop:
|
|
|
|
1. The kernel grows the MBR entry of the root partition as far as the
|
|
filesystem can use (to the end of the card, or 1 TiB), on a 1 MiB
|
|
boundary (`Gpt::ResizeMbrPartition`).
|
|
2. The ext2 driver grows the mounted volume (`Fs/Ext2.cpp`, "Online
|
|
grow"): each call writes the bitmaps and zeroed inode tables of a few
|
|
new block groups past the old end, then the last call commits the
|
|
bitmaps, descriptor table and superblock. The descriptor table grows
|
|
into the blocks `mke2fs -O resize_inode -E resize=2^28` reserved; the
|
|
leftover reservation is freed and `resize_inode` cleared, so the volume
|
|
grows once.
|
|
|
|
The new groups' inode tables are the bulk of the writes (0.1% of the card,
|
|
one inode per 256 KiB), written as long multi-block runs. Once the volume has grown, `firstboot.elf` deletes
|
|
itself and init skips its (optional) service from then on. If it fails it
|
|
stays and the next boot tries again; an interrupted grow is safe, since the
|
|
new groups are only referenced once the superblock is written, and a
|
|
partition larger than its filesystem is valid.
|
|
|
|
`make ARCH=aarch64 rpi4-sd ROOTFS=ramdisk` builds the old layout instead:
|
|
one FAT32 partition with `boot/ramdisk.tar`, which Limine loads through the
|
|
firmware. It boots slowly but does not depend on the kernel's SD driver.
|
|
|
|
`make ARCH=aarch64 run-rpi4-sd` boots the ext2 image on QEMU with the card
|
|
on `sdhci-pci`, so the root comes through the same driver. QEMU's UEFI has
|
|
no SD driver, so the card's ESP is also attached on its own over virtio for
|
|
the firmware to load Limine from. The card is padded to
|
|
`RPI4_QEMU_CARD_MIB` (default 4096), and every run starts from a fresh
|
|
copy, so every run is a first boot.
|
|
|
|
The firmware defaults are what MontaukOS needs; leave them:
|
|
|
|
- **System Table Mode: ACPI.** The kernel reads ACPI only.
|
|
- **Limit RAM to 3 GB: enabled.** The BCM2711's PCIe can only reach the
|
|
first 3 GB (its tables say so in `_DMA`). The kernel warns at boot when
|
|
RAM extends past a device's `_DMA` range.
|
|
|
|
## Serial console
|
|
|
|
The kernel console (the whole boot log, including what was printed before
|
|
the port was found) is mirrored to the UART the firmware's SPCR table names.
|
|
The firmware's `config.txt` applies the `miniuart-bt` overlay, which puts the
|
|
PL011 on GPIO 14/15: header pins 8 (TX) and 10 (RX), ground on pin 6, 115200
|
|
8N1, 3.3 V levels. On the Pi 400 the header is on the back.
|
|
|
|
## How the hardware is reached
|
|
|
|
| Part | Source | Driver |
|
|
|---|---|---|
|
|
| 4x Cortex-A72 | MADT, PSCI (TF-A, SMC) | `Arch/aarch64` SMP via Limine MP |
|
|
| GIC-400 (GICv2) | MADT | `Arch/aarch64/Interrupts.cpp` |
|
|
| Generic timer | GTDT | `Arch/aarch64/Timer.cpp` |
|
|
| Display | UEFI GOP (VideoCore) | Limine framebuffer |
|
|
| Keyboard (built in), USB ports | VL805 xHCI as ACPI `\_SB.SCB0.XHC0` (`PNP0D10`) | `Drivers/USB/Xhci.cpp` platform probe |
|
|
| USB 2 devices (built-in keyboard too) | VIA hub on the VL805's USB 2 root port | `Drivers/USB/Hub.cpp` |
|
|
| Reset, power-off | PSCI | `Arch::Reboot`, `Arch::PowerOff` |
|
|
| Bluetooth (CYW43455 on the 4B, CYW43456 on the 400) | `BCM2EA6` (`\_SB.GDV0.BTH0`) on the mini UART (`BCM2836`) | `Drivers/Bluetooth/Broadcom.cpp`, `Drivers/Serial/BtUart.cpp` |
|
|
| VideoCore firmware mailbox (radio power, clocks, board MAC) | `BCM2849` (`RPIQ`) | `Drivers/Platform/RpiFirmware.cpp` |
|
|
| GPIO pin functions | `BCM2845` (`GPI0`) | `Drivers/Platform/RpiGpio.cpp` |
|
|
|
|
The VL805 sits behind the BCM2711's PCIe, whose config space is not ECAM
|
|
(the root bridge's `_DSD` says `linux-ecam-quirk-id = "bcm2711"`), so the
|
|
kernel does not enumerate PCI there. The firmware has already brought up the
|
|
link and the VL805 and describes the controller as a platform device: a
|
|
register block at `0x600000000`, GSI 175, and `_CCA = 0`, i.e. its DMA does
|
|
not snoop the CPU caches. `XHC0._INI` (run by the AML interpreter) enables
|
|
the controller's PCI memory decoding and bus mastering. The xHCI driver then
|
|
keeps its rings and contexts in uncached memory, bounces control-transfer
|
|
data through an uncached page, and cleans/invalidates the caller buffers of
|
|
bulk transfers (`Memory/Dma.hpp`). It also watches the event ring from the
|
|
timer tick, so input keeps working if the INTx line never fires.
|
|
|
|
The VL805 has one USB 2 root port (port 1) and four USB 3 ones. An on-board
|
|
VIA hub (`2109:3431`) sits on the USB 2 port, and the USB 2 lines of every
|
|
socket, and the Pi 400's internal keyboard (`04d9:0007`), hang off it: a
|
|
keyboard or mouse is always a low/full-speed device behind a high-speed hub,
|
|
reached through that hub's transaction translator.
|
|
|
|
## Bluetooth
|
|
|
|
The radio's Bluetooth half speaks HCI over a UART (H4) on GPIO 30-33 (CTS,
|
|
RTS, TXD, RXD); its power switch, `BT_ON`, is line 0 of the VideoCore
|
|
firmware's GPIO expander, reached through the mailbox. The firmware tables
|
|
describe it as `BTH0`, whose `UARTSerialBus` names the UART it uses: with
|
|
the `miniuart-bt` overlay the PL011 is the console, so Bluetooth has the mini
|
|
UART. The kernel refuses to take a UART that is the SPCR console.
|
|
|
|
At boot the driver probe routes the pins (`ALT5` for the mini UART, `ALT3`
|
|
for the PL011), starts the UART (interrupt driven, 16 KiB receive and
|
|
transmit rings) and the H4 transport under the shared HCI layer. Once the
|
|
root is mounted the idle loop finishes, as for USB adapters:
|
|
|
|
1. power-cycle the controller through `BT_ON`, HCI Reset at 115200 baud;
|
|
2. Read Local Version: the ROM's LMP subversion picks the patch file,
|
|
`0:/os/firmware/brcm/BCM4345C0.hcd` (0x6119, 4B) or `BCM4345C5.hcd`
|
|
(0x6606, 400), shipped unmodified from Raspberry Pi OS's bluez-firmware;
|
|
3. raise both ends to the operating speed (vendor `0xFC18`);
|
|
4. Download Minidriver (`0xFC2E`), then every record of the `.hcd` file
|
|
(Write RAM, then Launch RAM), as Linux's `btbcm` does;
|
|
5. the patched firmware restarts at 115200: reset, raise the speed again.
|
|
|
|
The mini UART's FIFO is 8 bytes deep. Its hardware RTS is tried first; if
|
|
the controller answers with it on, the link runs at 921600 baud, otherwise
|
|
at 460800 without flow control (what Raspberry Pi OS uses), and at 115200
|
|
if the UART has no interrupt. The baud rate derives from the VPU core
|
|
clock, so `mk-rpi4-sd.sh` pins it (`core_freq=500`, `core_freq_min=500` in
|
|
`config.txt`), as the overlay's documentation requires. A failed fast
|
|
bring-up is retried entirely at 115200.
|
|
|
|
The CYW43456 reports a chip-wide default address (`43:45:C5:00:1F:AC`);
|
|
the driver replaces it with the board's Ethernet MAC from the firmware.
|
|
`0:/config/bluetooth.toml` still overrides both (Broadcom `0xFC01`).
|
|
|
|
## Not supported yet
|
|
|
|
- Ethernet (BCM54213 via GENET v5, `BCM6E4E`).
|
|
- SD high-speed mode: the driver transfers by polled PIO at 25 MHz on a
|
|
4-bit bus (multi-block CMD18/CMD25 with Auto CMD12), at most 12.5 MB/s;
|
|
switching the card to 50 MHz (CMD6) would double that.
|
|
- Wi-Fi (the CYW4345x's SDIO half, on the Arasan controller).
|
|
- Audio (HDMI via VideoCore, the 3.5 mm jack via PWM).
|
|
|
|
## Status
|
|
|
|
Bluetooth has not run yet: QEMU has no model of the radio, so it is
|
|
untested on any machine.
|
|
|
|
Everything else above is verified on QEMU only, including the Pi-specific path:
|
|
the firmware's own XHC0 table, retargeted at QEMU's xHCI and loaded with the
|
|
`bcm2711` quirk on QEMU's root bridge, boots to the desktop with non-coherent
|
|
DMA. Real hardware has not been tried yet. The ext2 root on the SD card boots on QEMU (`run-rpi4-sd`); the
|
|
first-boot grow has been tested only against the kernel's ext2 code in a
|
|
host harness (images grown, then checked with `e2fsck`).
|