From 462e84938a2eeffd64802626e04cb7eba9e95136 Mon Sep 17 00:00:00 2001 From: Luca Succi Date: Wed, 22 Apr 2026 15:06:43 +0200 Subject: [PATCH 1/2] Add rpi02w target support and porting notes --- README.md | 2 + Vagrantfile | 1 + docs/README.md | 3 + docs/porting-notes-rpi02w.md | 60 ++ docs/system_rpi02w.md | 45 + system_rpi02w/Config.in | 2 + system_rpi02w/README.md | 50 ++ system_rpi02w/VERSION | 1 + system_rpi02w/autoboot-a.txt | 6 + system_rpi02w/autoboot-b.txt | 6 + system_rpi02w/cmdline-a.txt | 1 + system_rpi02w/cmdline-b.txt | 1 + system_rpi02w/config.txt | 65 ++ system_rpi02w/crucible.sh | 15 + system_rpi02w/defconfig | 120 +++ system_rpi02w/external.desc | 2 + system_rpi02w/external.mk | 1 + system_rpi02w/fwup.conf | 767 ++++++++++++++++++ system_rpi02w/fwup_include/fwup-common.conf | 45 + system_rpi02w/fwup_include/provisioning.conf | 11 + system_rpi02w/linux/linux-6.12.defconfig | 306 +++++++ system_rpi02w/post-build.sh | 29 + .../rootfs_overlay/etc/erlinit.config | 101 +++ .../rootfs_overlay/etc/fw_env.config | 10 + .../rootfs_overlay/sbin/peripherals-init.sh | 64 ++ .../configs/rpi02w_linux_aarch64_defconfig | 22 + .../configs/rpi02w_linux_x86_64_defconfig | 22 + 27 files changed, 1758 insertions(+) create mode 100644 docs/porting-notes-rpi02w.md create mode 100644 docs/system_rpi02w.md create mode 100644 system_rpi02w/Config.in create mode 100644 system_rpi02w/README.md create mode 100644 system_rpi02w/VERSION create mode 100644 system_rpi02w/autoboot-a.txt create mode 100644 system_rpi02w/autoboot-b.txt create mode 100644 system_rpi02w/cmdline-a.txt create mode 100644 system_rpi02w/cmdline-b.txt create mode 100644 system_rpi02w/config.txt create mode 100644 system_rpi02w/crucible.sh create mode 100644 system_rpi02w/defconfig create mode 100644 system_rpi02w/external.desc create mode 100644 system_rpi02w/external.mk create mode 100644 system_rpi02w/fwup.conf create mode 100644 system_rpi02w/fwup_include/fwup-common.conf create mode 100644 system_rpi02w/fwup_include/provisioning.conf create mode 100644 system_rpi02w/linux/linux-6.12.defconfig create mode 100755 system_rpi02w/post-build.sh create mode 100644 system_rpi02w/rootfs_overlay/etc/erlinit.config create mode 100644 system_rpi02w/rootfs_overlay/etc/fw_env.config create mode 100755 system_rpi02w/rootfs_overlay/sbin/peripherals-init.sh create mode 100644 toolchain/configs/rpi02w_linux_aarch64_defconfig create mode 100644 toolchain/configs/rpi02w_linux_x86_64_defconfig diff --git a/README.md b/README.md index 44cd4b6..33db8b6 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ Supported targets in this repository: - `grisp2` - `kontron-albl-imx8mm` +- `rpi0w` +- `rpi02w` ## Getting Started diff --git a/Vagrantfile b/Vagrantfile index 19268d4..0073a68 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -282,6 +282,7 @@ Vagrant.configure('2') do |config| config.vm.provision 'file', source: "system_grisp2", destination: "/home/vagrant/system_grisp2" config.vm.provision 'file', source: "system_kontron-albl-imx8mm", destination: "/home/vagrant/system_kontron-albl-imx8mm" config.vm.provision 'file', source: "system_rpi0w", destination: "/home/vagrant/system_rpi0w" + config.vm.provision 'file', source: "system_rpi02w", destination: "/home/vagrant/system_rpi02w" config.vm.provider :vmware_desktop do |v, override| # On MacOS, NFS sometimes freezes, use vmware GHFS instead. diff --git a/docs/README.md b/docs/README.md index a467c45..ac2f1d0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,6 +13,8 @@ repo-level [`README.md`](../README.md). the Vagrant wrapper fits in on non-Linux hosts. - [`porting-notes.md`](./porting-notes.md): short field notebook of traps encountered during target bring-up. +- [`porting-notes-rpi02w.md`](./porting-notes-rpi02w.md): delta notes + for porting from `system_rpi0w` to `system_rpi02w`. ## Index @@ -21,6 +23,7 @@ repo-level [`README.md`](../README.md). | `system_grisp2` | Supported | (no design doc yet) | | `system_kontron-albl-imx8mm` | Supported | (no design doc yet) | | `system_rpi0w` | In development; first-boot working, peripheral bring-up staged, A/B lifecycle not yet exercised on hardware | [`system_rpi0w.md`](./system_rpi0w.md) | +| `system_rpi02w` | In development; initial target scaffold for Cortex-A53/ARMv8, hardware validation pending | [`system_rpi02w.md`](./system_rpi02w.md) | ## What a target design doc should cover diff --git a/docs/porting-notes-rpi02w.md b/docs/porting-notes-rpi02w.md new file mode 100644 index 0000000..47528bf --- /dev/null +++ b/docs/porting-notes-rpi02w.md @@ -0,0 +1,60 @@ +# `rpi02w` Porting Notes (vs `rpi0w`) + +This note captures what was different when bringing up `system_rpi02w` +from the existing `system_rpi0w` target. + +## CPU / ABI + +- SoC class changes from ARMv6 to ARMv8-A (`cortex-a53`). +- Userspace/toolchain moves from 32-bit ARM to `aarch64-unknown-linux-gnu`. +- New toolchain defconfigs: + - `toolchain/configs/rpi02w_linux_x86_64_defconfig` + - `toolchain/configs/rpi02w_linux_aarch64_defconfig` + +## Kernel / Boot Artifacts + +- Kernel image consumed by firmware is `kernel8.img` (sourced from Buildroot `Image`). +- DTB switches to `bcm2710-rpi-zero-2-w.dtb`. +- `config.txt` requires 64-bit boot settings: + - `arm_64bit=1` + - `kernel=kernel8.img` + - explicit `start_file` / `fixup_file` selection +- `cmdline-a.txt` / `cmdline-b.txt` map to rootfs A/B: + - `root=/dev/mmcblk0p5` and `root=/dev/mmcblk0p6` + +## Firmware Layout / fwup + +- `system_rpi02w/fwup_include/fwup-common.conf` uses Nerves-compatible + partition geometry for Pi Zero 2 W: + - BOOT-A offset/count: `63 / 38630` + - BOOT-B offset/count: `38693 / 38630` + - AUTOBOOT offset/count: `77323 / 32749` + - ROOTFS-A offset/count: `110080 / 272666` + - ROOTFS-B offset/count: `382746 / 272666` + - APP offset/count: `655412 / 1048576 (expand)` +- `fwup.conf` keeps GRiSP A/B state semantics (`upgrade.*`, `validate.*`, + `rollback.*`, `status.*`) while using Pi `tryboot` behavior via + `autoboot.txt`. + +## Console / Device Bring-up + +- USB gadget serial remains the primary Erlang console (`ttyGS0`). +- UART console (`ttyAMA0`) remains enabled for early boot diagnostics. +- Bluetooth on mini-UART requires explicit kernel config enablement: + - `CONFIG_BT_HCIUART`, `CONFIG_BT_HCIUART_BCM`, + `CONFIG_BT_HCIUART_SERDEV`, `CONFIG_BT_BCM` + - `CONFIG_SERIAL_DEV_BUS` and `CONFIG_SERIAL_DEV_CTRL_TTYPORT` + +## Build / Integration Touchpoints + +- Add `system_rpi02w` folder and `VERSION`. +- Add `system_rpi02w` provisioning entry in `Vagrantfile`. +- Add `rpi02w` to supported target list in top-level `README.md`. +- Add `system_rpi02w` docs entry in `docs/README.md`. + +## Observed Bring-up Pitfalls + +- Stale SD boot files can hide source fixes; verify BOOT-A `config.txt` and + `cmdline.txt` match the current repo before concluding boot regressions. +- ACT LED heartbeat in `config.txt` can look like a fault blink pattern; + use UART output to classify real early-boot failures. diff --git a/docs/system_rpi02w.md b/docs/system_rpi02w.md new file mode 100644 index 0000000..3c7f9ce --- /dev/null +++ b/docs/system_rpi02w.md @@ -0,0 +1,45 @@ +# `system_rpi02w`: Architecture Notes + +Design note for the Raspberry Pi Zero 2 W target. + +## Overview + +`system_rpi02w` is the Pi Zero 2 W sibling of `system_rpi0w`. +It keeps the same GRiSP Alloy update model (`fwup` tasks + uboot-env +schema + tryboot flow), but moves to a Cortex-A53/ARMv8 (aarch64) +toolchain and the Zero 2 W DTB. + +## Key Deltas vs `system_rpi0w` + +- Target name: `rpi02w` +- Toolchain tuple: `aarch64-unknown-linux-gnu` +- Crosstool configs: + - `toolchain/configs/rpi02w_linux_x86_64_defconfig` + - `toolchain/configs/rpi02w_linux_aarch64_defconfig` +- Buildroot arch selection in `system_rpi02w/defconfig`: + - `BR2_aarch64=y` + - `BR2_cortex_a53=y` +- Firmware DTB payload in `system_rpi02w/fwup.conf`: + - `bcm2710-rpi-zero-2-w.dtb` +- Kernel file consumed by fwup from SDK images: + - `${GRISP_SYSTEM}/images/Image` (packaged as `kernel8.img` in BOOT-A/B) + +See also: +- [`porting-notes-rpi02w.md`](./porting-notes-rpi02w.md) for a + checklist-style delta from `system_rpi0w`. + +## Build Pipeline Usage + +```sh +./build-toolchain.sh rpi02w +./build-sdk.sh rpi02w +./build-project.sh rpi02w +./build-firmware.sh rpi02w +``` + +## Status + +- Integrated into repo structure and Vagrant provisioning. +- Boot to Erlang shell confirmed on hardware. +- Bluetooth HCI UART support requires rebuilt kernel/SDK after + `linux-6.12.defconfig` updates. diff --git a/system_rpi02w/Config.in b/system_rpi02w/Config.in new file mode 100644 index 0000000..848e21d --- /dev/null +++ b/system_rpi02w/Config.in @@ -0,0 +1,2 @@ +# Target-specific Buildroot options for system_rpi02w go here. +# Sourced by system_common/Config.in via $BR2_EXTERNAL_GRISP_PATH. diff --git a/system_rpi02w/README.md b/system_rpi02w/README.md new file mode 100644 index 0000000..a99d6f8 --- /dev/null +++ b/system_rpi02w/README.md @@ -0,0 +1,50 @@ +# Raspberry Pi Zero 2 W System Notes + +Target-specific, implementation-coupled notes for `system_rpi02w` +(Raspberry Pi Zero 2 W: BCM2710A1 / Cortex-A53 / ARMv8). + +## Hardware + +| Property | Value | +|---|---| +| Board | Raspberry Pi Zero 2 W | +| SoC | Broadcom BCM2710A1 | +| CPU | Quad-core Cortex-A53 (ARMv8-A) | +| RAM | 512 MiB | +| Wi-Fi/BT | BCM43436, 802.11n + BT 4.2 | + +## Current Target Shape + +- Target name: `rpi02w` +- System directory: `system_rpi02w/` +- Toolchain tuple: `aarch64-unknown-linux-gnu` +- Toolchain configs: + - `toolchain/configs/rpi02w_linux_x86_64_defconfig` + - `toolchain/configs/rpi02w_linux_aarch64_defconfig` +- Kernel source pin: `raspberrypi/linux` tag `stable_20250916` +- Kernel DTB staged into firmware: `bcm2710-rpi-zero-2-w.dtb` + +## Boot and Update Model + +`system_rpi02w` follows the same model as `system_rpi0w`: + +- No U-Boot bootloader in the boot chain. +- VideoCore firmware + `tryboot` A/B boot selection. +- Same GRiSP Alloy fwup task vocabulary and uboot-env schema + (`complete`, `upgrade.{a,b}`, `validate.{a,b}`, `rollback.{a,b}`). + +Key files: + +- `defconfig`: Buildroot target configuration. +- `linux/linux-6.12.defconfig`: custom kernel config seed. +- `fwup.conf`: firmware packaging and A/B task logic. +- `config.txt`, `cmdline-*.txt`, `autoboot-*.txt`: staged boot artifacts. +- `rootfs_overlay/etc/erlinit.config`: runtime init and console selection. + +## Status + +This is an initial target scaffold derived from `system_rpi0w` and adapted +for the Zero 2 W architecture and DTB path. + +- Build integration is wired through the four-stage pipeline. +- Hardware validation is still required (boot, Wi-Fi/BT, A/B lifecycle). diff --git a/system_rpi02w/VERSION b/system_rpi02w/VERSION new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/system_rpi02w/VERSION @@ -0,0 +1 @@ +0.1.0 diff --git a/system_rpi02w/autoboot-a.txt b/system_rpi02w/autoboot-a.txt new file mode 100644 index 0000000..ae7bc99 --- /dev/null +++ b/system_rpi02w/autoboot-a.txt @@ -0,0 +1,6 @@ +[all] +tryboot_a_b=1 +boot_partition=2 + +[tryboot] +boot_partition=3 diff --git a/system_rpi02w/autoboot-b.txt b/system_rpi02w/autoboot-b.txt new file mode 100644 index 0000000..98e148f --- /dev/null +++ b/system_rpi02w/autoboot-b.txt @@ -0,0 +1,6 @@ +[all] +tryboot_a_b=1 +boot_partition=3 + +[tryboot] +boot_partition=2 diff --git a/system_rpi02w/cmdline-a.txt b/system_rpi02w/cmdline-a.txt new file mode 100644 index 0000000..d048bf7 --- /dev/null +++ b/system_rpi02w/cmdline-a.txt @@ -0,0 +1 @@ +root=/dev/mmcblk0p5 rootwait rootfstype=squashfs console=ttyAMA0,115200 console=ttyGS0 ignore_loglevel loglevel=8 initcall_debug diff --git a/system_rpi02w/cmdline-b.txt b/system_rpi02w/cmdline-b.txt new file mode 100644 index 0000000..36daedc --- /dev/null +++ b/system_rpi02w/cmdline-b.txt @@ -0,0 +1 @@ +root=/dev/mmcblk0p6 rootwait rootfstype=squashfs console=ttyAMA0,115200 console=ttyGS0 ignore_loglevel loglevel=8 initcall_debug diff --git a/system_rpi02w/config.txt b/system_rpi02w/config.txt new file mode 100644 index 0000000..5e8462e --- /dev/null +++ b/system_rpi02w/config.txt @@ -0,0 +1,65 @@ +# Raspberry Pi Zero 2 W firmware config. +# +# Read by the VideoCore GPU firmware (start.elf) from the active BOOT-{A,B} +# partition. Tells the GPU how to load the Linux kernel. +# +# Keep this file identical on BOOT-A and BOOT-B unless a slot-specific quirk +# demands otherwise: fwup's complete / upgrade.* tasks write the same +# config.txt to both partitions. +# +# Several values and the miniuart-bt routing trick follow +# nerves_system_rpi0/rpi-firmware/config.txt; individual lines are marked +# "matches nerves_system_rpi0" where the specific choice is non-obvious. + +# Keep the same kernel filename convention as Nerves on 64-bit Pi targets. +kernel=kernel8.img +arm_64bit=1 +start_file=start.elf +fixup_file=fixup.dat + +disable_splash=1 + +# Reserve 192 MiB of the 512 MiB SoC for the GPU/framebuffer; leaves ~320 +# MiB for Linux + userland. Tunable; matches nerves_system_rpi0. +gpu_mem=192 + +# On-board peripherals exposed to Linux. +dtparam=i2c_arm=on +dtparam=spi=on +dtparam=audio=on + +# Enable camera auto-detection for CSI. Safe to leave on even when no camera +# is connected; the kernel just skips it. +camera_auto_detect=1 +display_auto_detect=1 + +# USB OTG / gadget mode for the CDC-ACM serial console on ttyGS0, exposed +# over the middle micro-USB port. On the host, `picocom -b 115200 +# /dev/tty.usbmodem*` (macOS) or `/dev/ttyACM0` (Linux) lands on the BEAM. +dtoverlay=dwc2 + +# Route Bluetooth to the mini-UART and keep PL011 (ttyAMA0) free for the +# Linux console. Without this, console characters race BT traffic on the +# same UART. +enable_uart=1 +dtoverlay=miniuart-bt +# Print second-stage GPU/firmware boot diagnostics to UART (ttyAMA0) so +# failures before Linux are visible on a 3.3V serial adapter. +uart_2ndstage=1 +# Enable firmware/device-tree debug traces during boot. +dtdebug=1 +# The VPU clock must be fixed when the mini-UART shares the PLL with BT. +core_freq=250 + +# Heartbeat on the green ACT LED so the board shows signs of life without +# a UART attached. +dtparam=act_led_activelow=on +dtparam=act_led_trigger=heartbeat + +# Ramoops: reserve a small RAM region for pstore so kernel oopses survive +# a warm reboot. The upstream RPi kernel already ships overlays/ramoops.dtbo +# (arch/arm/boot/dts/overlays/ramoops.dtbo); fwup.conf stages it onto +# BOOT-A/B alongside dwc2 and miniuart-bt. peripherals-init.sh (run by +# erlinit as --pre-run-exec) mounts pstore at /sys/fs/pstore so +# `ls /sys/fs/pstore` shows the previous boot's dmesg. +dtoverlay=ramoops diff --git a/system_rpi02w/crucible.sh b/system_rpi02w/crucible.sh new file mode 100644 index 0000000..481d59d --- /dev/null +++ b/system_rpi02w/crucible.sh @@ -0,0 +1,15 @@ +OS_RELEASE_PRETTY_NAME="GRiSP RPi Zero 2 W" + +BOOTSCHEME=NONE + +SQUASHFS_PRIORITIES=( + "sbin/init" 32762 + "etc/erlinit.config" 32761 +) + +FWUP_IMAGE_TARGETS=( + "complete" "" +) + +GSU_KERNEL_PATH="/boot/kernel8.img" +GSU_PARTITIONS="" diff --git a/system_rpi02w/defconfig b/system_rpi02w/defconfig new file mode 100644 index 0000000..e350ed7 --- /dev/null +++ b/system_rpi02w/defconfig @@ -0,0 +1,120 @@ +# Target-specific Buildroot overrides for Raspberry Pi Zero 2 W. +# Appended to system_common/defconfig by build-sdk.sh. Paths use the +# ${GRISP_*_DIR} placeholders expanded during `make menuconfig` merge. + +BR2_ROOTFS_OVERLAY="${GRISP_COMMON_SYSTEM_DIR}/board/grisp-common/rootfs_overlay ${GRISP_TARGET_SYSTEM_DIR}/rootfs_overlay" +BR2_ROOTFS_POST_BUILD_SCRIPT="${GRISP_COMMON_SYSTEM_DIR}/board/grisp-common/post-build.sh ${GRISP_TARGET_SYSTEM_DIR}/post-build.sh" + +# --- Architecture: ARMv8 (Cortex-A53), 64-bit userspace -------------------- +# (Pi Zero 2 W v1.0 — BCM2710A1.) + +BR2_aarch64=y +BR2_cortex_a53=y +BR2_TAR_OPTIONS="--no-same-owner" + +# --- External toolchain produced by build-toolchain.sh rpi02w -------------- + +BR2_TOOLCHAIN_EXTERNAL=y +BR2_TOOLCHAIN_EXTERNAL_CUSTOM=y +BR2_TOOLCHAIN_EXTERNAL_DOWNLOAD=y +BR2_TOOLCHAIN_EXTERNAL_URL="file://${GRISP_TOP_DIR}/artefacts/grisp_toolchain_aarch64_unknown_linux_gnu-0.1.linux-${GRISP_BUILD_HOST_ARCH}.tar.xz" +BR2_TOOLCHAIN_EXTERNAL_CUSTOM_PREFIX="aarch64-unknown-linux-gnu" +BR2_TOOLCHAIN_EXTERNAL_GCC_13=y +BR2_TOOLCHAIN_EXTERNAL_HEADERS_6_1=y +BR2_TOOLCHAIN_EXTERNAL_CUSTOM_GLIBC=y +BR2_TOOLCHAIN_EXTERNAL_CXX=y +# The crosstool-NG toolchain build enables CT_CC_LANG_FORTRAN and +# CT_CC_GCC_LIBGOMP (see toolchain/configs/rpi02w_linux_*_defconfig). Buildroot +# inspects the external toolchain and errors out if any supported language +# advertised by the toolchain is not opted into here -- matches grisp2/kontron. +BR2_TOOLCHAIN_EXTERNAL_FORTRAN=y +BR2_TOOLCHAIN_EXTERNAL_OPENMP=y +# glibc >= 2.32 dropped Sun RPC (tirpc lives in libtirpc now), and +# Buildroot defaults BR2_TOOLCHAIN_EXTERNAL_INET_RPC=y. The toolchain +# probe fails unless we explicitly opt out -- matches system_kontron. +BR2_TOOLCHAIN_EXTERNAL_INET_RPC=n + +# --- Linux kernel: RPi downstream tag stable_20250916 (6.12) --------------- + +BR2_LINUX_KERNEL=y +BR2_LINUX_KERNEL_CUSTOM_TARBALL=y +BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="https://github.com/raspberrypi/linux/archive/refs/tags/stable_20250916.tar.gz" + +# Full custom kernel defconfig, following the same convention as +# system_grisp2 and system_kontron-albl-imx8mm. See +# system_rpi02w/linux/linux-6.12.defconfig for the config itself and +# docs/porting-notes.md for why we moved away from the bcmrpi_defconfig + +# fragment approach (short version: merge_config.sh can't override +# Kconfig `choice` groups like MODULE_COMPRESS_*). +BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y +BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${GRISP_TARGET_SYSTEM_DIR}/linux/linux-6.12.defconfig" + +BR2_LINUX_KERNEL_IMAGE=y +BR2_LINUX_KERNEL_XZ=y +BR2_LINUX_KERNEL_DTS_SUPPORT=y +BR2_LINUX_KERNEL_INTREE_DTS_NAME="broadcom/bcm2710-rpi-zero-2-w" +# Keep BR2_LINUX_KERNEL_INSTALL_TARGET=n from system_common/defconfig:12. +# fwup reads zImage and DTBs from $BINARIES_DIR directly. +BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y + +# No bootloader (no BR2_TARGET_BAREBOX*, no BR2_TARGET_UBOOT*). +# VideoCore GPU firmware loads the kernel directly via config.txt. + +# --- RPi firmware (GPU blobs + DT overlays) -------------------------------- + +BR2_PACKAGE_RPI_FIRMWARE=y +BR2_PACKAGE_RPI_FIRMWARE_CUSTOM_VERSION=y +BR2_PACKAGE_RPI_FIRMWARE_CUSTOM_VERSION_VALUE="1.20250915" +BR2_PACKAGE_RPI_FIRMWARE_BOOTCODE_BIN=y +BR2_PACKAGE_RPI_FIRMWARE_VARIANT_PI=y +BR2_PACKAGE_RPI_FIRMWARE_VARIANT_PI_X=y + +# --- Wi-Fi (BCM43430) + wpa_supplicant ------------------------------------- +# brcmfmac_sdio-firmware-rpi is the LibreELEC-sourced package that ships the +# Broadcom firmware + the Pi-specific NVRAM txt files under /lib/firmware/brcm +# (e.g. brcmfmac43430-sdio.bin, brcmfmac43430-sdio.clm_blob, and +# brcmfmac43430-sdio.raspberrypi,model-zero-w.txt). BR2_PACKAGE_..._WIFI also +# installs the per-board NVRAM for Pi Zero 2 W. _BT pulls in the BlueZ-format +# .hcd needed to load the onboard BCM43438 BT controller over UART. +# Package choice follows nerves_system_rpi0's nerves_defconfig. + +BR2_PACKAGE_BRCMFMAC_SDIO_FIRMWARE_RPI=y +BR2_PACKAGE_BRCMFMAC_SDIO_FIRMWARE_RPI_WIFI=y +BR2_PACKAGE_BRCMFMAC_SDIO_FIRMWARE_RPI_BT=y +BR2_PACKAGE_WIRELESS_REGDB=y +BR2_PACKAGE_WPA_SUPPLICANT=y +BR2_PACKAGE_WPA_SUPPLICANT_AP_SUPPORT=y +BR2_PACKAGE_WPA_SUPPLICANT_WPA3=y +BR2_PACKAGE_WPA_SUPPLICANT_CTRL_IFACE=y +# wpa_cli ships by default with wpa_supplicant in Buildroot, but keep this +# explicit so a later Kconfig change upstream doesn't quietly drop it. +BR2_PACKAGE_WPA_SUPPLICANT_CLI=y +# `iw` provides `iw dev` / `iw scan` for bring-up diagnostics (separate +# tool from the unrelated `iwd` daemon, which we do NOT ship). +BR2_PACKAGE_IW=y + +# --- Bluetooth (BCM43438 over mini-UART) ----------------------------------- +# miniuart-bt overlay (already in config.txt) routes the BT controller to +# ttyAMA1 at 3 Mbps. btattach (modern) / hciattach (deprecated) then attach a +# BCM HCI node. bluez5_utils provides the userspace stack + bluetoothctl. + +BR2_PACKAGE_BLUEZ5_UTILS=y +BR2_PACKAGE_BLUEZ5_UTILS_CLIENT=y +BR2_PACKAGE_BLUEZ5_UTILS_TOOLS=y +BR2_PACKAGE_BLUEZ5_UTILS_DEPRECATED=y + +# --- Userspace filesystem tooling ------------------------------------------ + +BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_DTC=y + +# --- Device management + console ------------------------------------------- + +BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y +BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0" + +# --- Debugging aids (mirror system_grisp2:66-68; optional for factory) ----- + +BR2_PACKAGE_OPENSSH=y +BR2_TOOLCHAIN_EXTERNAL_GDB_SERVER_COPY=y +BR2_PACKAGE_STRACE=y diff --git a/system_rpi02w/external.desc b/system_rpi02w/external.desc new file mode 100644 index 0000000..9bbb14d --- /dev/null +++ b/system_rpi02w/external.desc @@ -0,0 +1,2 @@ +name: RPI02W +desc: Raspberry Pi Zero 2 W buildroot external tree diff --git a/system_rpi02w/external.mk b/system_rpi02w/external.mk new file mode 100644 index 0000000..6ffc035 --- /dev/null +++ b/system_rpi02w/external.mk @@ -0,0 +1 @@ +# Target-specific Buildroot package recipes for system_rpi02w go here. diff --git a/system_rpi02w/fwup.conf b/system_rpi02w/fwup.conf new file mode 100644 index 0000000..acc1858 --- /dev/null +++ b/system_rpi02w/fwup.conf @@ -0,0 +1,767 @@ +# Firmware configuration file for Raspberry Pi Zero 2 W on Linux +# +# Reuses nerves_system_rpi0's BOOT MECHANISM (no bootloader, VideoCore GPU +# firmware loads the kernel directly, `tryboot_a_b` drives A/B) with +# system_kontron-albl-imx8mm/fwup.conf's RUNTIME CONVENTIONS (same +# GRISP_FW_* defines, same uboot-env schema, same task vocabulary). +# +# The rpi02w-specific bridge for the missing U-Boot: validate.{a,b} and +# rollback.{a,b} rewrite autoboot.txt on the AUTOBOOT FAT partition so +# the next NORMAL reboot (not tryboot) boots the chosen slot. Without this +# the GPU firmware's [all] section of autoboot.txt would keep pointing +# at the pre-upgrade slot. + +require-fwup-version="1.4.0" + +define(ALLOY_ENV_VER, "1") + +# +# Firmware metadata +# +# All of these can be overridden using environment variables of the same name. +# +# Run 'fwup -m' to query values in a .fw file. +# Use 'fw_printenv' to query values on the target. +# +define(GRISP_FW_PRODUCT, "GRiSP RPi Zero 2 W Firmware") +define(GRISP_FW_DESCRIPTION, "") +define(GRISP_FW_VERSION, "") +define(GRISP_FW_PLATFORM, "rpi02w") +define(GRISP_FW_ARCHITECTURE, "aarch64-unknown-linux-gnu") +define(GRISP_FW_AUTHOR, "Peer Stritzinger GmbH") +define(GRISP_FW_VCS_IDENTIFIER, "") +define(GRISP_FW_MISC, "") + +# Partition layout and uboot-env offsets (see fwup_include/fwup-common.conf). +include("${GRISP_SYSTEM}/images/fwup_include/fwup-common.conf") + +# Default host paths if not specified on the commandline. All of these live +# in $BINARIES_DIR after build-sdk.sh and post-build.sh have run. +define(ROOTFS, "${GRISP_TARGET_SYSTEM}/images/rootfs.squashfs") +define(BOOTCODE_BIN, "${GRISP_SYSTEM}/images/rpi-firmware/bootcode.bin") +define(START_ELF, "${GRISP_SYSTEM}/images/rpi-firmware/start.elf") +define(FIXUP_DAT, "${GRISP_SYSTEM}/images/rpi-firmware/fixup.dat") +define(CONFIG_TXT, "${GRISP_SYSTEM}/images/config.txt") +define(CMDLINE_A_TXT, "${GRISP_SYSTEM}/images/cmdline-a.txt") +define(CMDLINE_B_TXT, "${GRISP_SYSTEM}/images/cmdline-b.txt") +define(AUTOBOOT_A_TXT,"${GRISP_SYSTEM}/images/autoboot-a.txt") +define(AUTOBOOT_B_TXT,"${GRISP_SYSTEM}/images/autoboot-b.txt") +define(KERNEL8_IMG, "${GRISP_SYSTEM}/images/Image") +define(DTB_PIZERO2W, "${GRISP_SYSTEM}/images/bcm2710-rpi-zero-2-w.dtb") +define(DTBO_DWC2, "${GRISP_SYSTEM}/images/rpi-firmware/overlays/dwc2.dtbo") +define(DTBO_MINIUART, "${GRISP_SYSTEM}/images/rpi-firmware/overlays/miniuart-bt.dtbo") +# ramoops.dtbo is built by upstream RPi kernel overlays (arch/arm/boot/dts/ +# overlays/ramoops.dtbo) and lands in $BINARIES_DIR/rpi-firmware/overlays/ +# alongside the others — no custom DTS needed. Reserves a small RAM region +# for pstore so kernel oopses survive a soft reboot. +define(DTBO_RAMOOPS, "${GRISP_SYSTEM}/images/rpi-firmware/overlays/ramoops.dtbo") + +# Firmware archive metadata (.fw -> `fwup -m`). +meta-product = ${GRISP_FW_PRODUCT} +meta-description = ${GRISP_FW_DESCRIPTION} +meta-version = ${GRISP_FW_VERSION} +meta-platform = ${GRISP_FW_PLATFORM} +meta-architecture = ${GRISP_FW_ARCHITECTURE} +meta-author = ${GRISP_FW_AUTHOR} +meta-vcs-identifier = ${GRISP_FW_VCS_IDENTIFIER} +meta-misc = ${GRISP_FW_MISC} + +# +# File resources +# +# Declaration order is the order fwup processes them during a .fw stream, +# so keep boot-critical small files first and the big squashfs last. +# +file-resource bootcode.bin { + host-path = ${BOOTCODE_BIN} +} +file-resource start.elf { + host-path = ${START_ELF} +} +file-resource fixup.dat { + host-path = ${FIXUP_DAT} +} +file-resource config.txt { + host-path = ${CONFIG_TXT} +} +file-resource autoboot-a.txt { + host-path = ${AUTOBOOT_A_TXT} +} +file-resource autoboot-b.txt { + host-path = ${AUTOBOOT_B_TXT} +} +file-resource cmdline-a.txt { + host-path = ${CMDLINE_A_TXT} +} +file-resource cmdline-b.txt { + host-path = ${CMDLINE_B_TXT} +} +file-resource kernel8.img { + host-path = ${KERNEL8_IMG} +} +file-resource bcm2710-rpi-zero-2-w.dtb { + host-path = ${DTB_PIZERO2W} +} +file-resource overlays/dwc2.dtbo { + host-path = ${DTBO_DWC2} +} +file-resource overlays/miniuart-bt.dtbo { + host-path = ${DTBO_MINIUART} +} +file-resource overlays/ramoops.dtbo { + host-path = ${DTBO_RAMOOPS} +} +file-resource rootfs.img { + host-path = ${ROOTFS} + # Error out if the rootfs exceeds the partition size. + assert-size-lte = ${ROOTFS_A_PART_COUNT} +} + +# +# MBR: 3 FAT primaries (AUTOBOOT + BOOT-A + BOOT-B), one extended container +# (primary 3) holding 3 logical partitions (ROOTFS-A, ROOTFS-B, APP). +# +mbr mbr { + # Primary 0: AUTOBOOT (boot flag) — GPU ROM loads bootcode.bin from here. + partition 0 { + block-offset = ${AUTOBOOT_PART_OFFSET} + block-count = ${AUTOBOOT_PART_COUNT} + type = 0x0c # FAT32 LBA + boot = true + } + # Primary 1: BOOT-A — needs boot=true for tryboot's `boot_partition=2` + # redirect to work. Without it, BCM2835 bootcode.bin refuses to follow + # autoboot.txt into this slot and stalls with 4 green blinks. + # Matches nerves_system_rpi0 (fwup-common.conf partition 1). + partition 1 { + block-offset = ${BOOT_A_PART_OFFSET} + block-count = ${BOOT_A_PART_COUNT} + type = 0x0c # FAT32 LBA + boot = true + } + # Primary 2: BOOT-B — boot=true for the same reason as BOOT-A. + partition 2 { + block-offset = ${BOOT_B_PART_OFFSET} + block-count = ${BOOT_B_PART_COUNT} + type = 0x0c # FAT32 LBA + boot = true + } + # Primary 3: Extended container — holds 3 logical partitions. + partition 3 { + block-offset = ${EXTENDED_PART_OFFSET} + type = 0x0f # Extended LBA + } + # Logical 1 = /dev/mmcblk0p5 + partition 4 { + block-offset = ${ROOTFS_A_PART_OFFSET} + block-count = ${ROOTFS_A_PART_COUNT} + type = 0x83 # Linux + } + # Logical 2 = /dev/mmcblk0p6 + partition 5 { + block-offset = ${ROOTFS_B_PART_OFFSET} + block-count = ${ROOTFS_B_PART_COUNT} + type = 0x83 # Linux + } + # Logical 3 = /dev/mmcblk0p7 + partition 6 { + block-offset = ${APP_PART_OFFSET} + block-count = ${APP_PART_COUNT} + type = 0x83 # Linux + expand = true + } +} + +# Single 8 KiB uboot-env at block 16. No redundant copy: without U-Boot +# nothing atomic-reads this at boot time, so a second copy buys nothing. +uboot-environment uboot-env { + block-offset = ${UBOOT_ENV_OFFSET} + block-count = ${UBOOT_ENV_COUNT} +} + +# +# Factory / full-reflash task. +# +# Writes MBR, formats AUTOBOOT + BOOT-A, stages the boot payload +# onto AUTOBOOT + BOOT-A, writes rootfs to ROOTFS-A, invalidates +# ROOTFS-B and APP so first boot auto-formats APP, and initialises the +# full uboot-env schema to the grisp_alloy factory-default values. +# +task complete { + # Only match if not mounted. + require-unmounted-destination = true + + on-init { + info("Starting GRiSP RPi Zero 2 W firmware setup") + mbr_write(mbr) + + fat_mkfs(${AUTOBOOT_PART_OFFSET}, ${AUTOBOOT_PART_COUNT}) + fat_setlabel(${AUTOBOOT_PART_OFFSET}, "AUTOBOOT") + # BCM2835 bootcode.bin (rpi-firmware 1.20250915) refuses to process + # autoboot.txt if its own partition has no config.txt. Without this an + # empty config.txt the board stalls at 4 green blinks even though every + # other boot artifact is in place. Nerves hits the same trap — see + # nerves_system_rpi0/fwup.conf `complete` task, same fat_touch call. + fat_touch(${AUTOBOOT_PART_OFFSET}, "config.txt") + + fat_mkfs(${BOOT_A_PART_OFFSET}, ${BOOT_A_PART_COUNT}) + fat_setlabel(${BOOT_A_PART_OFFSET}, "BOOT-A") + fat_mkdir(${BOOT_A_PART_OFFSET}, "overlays") + + # Clear out any old data in the rootfs + app partitions. + trim(${ROOTFS_A_PART_OFFSET}, ${ROOTFS_A_PART_COUNT}) + trim(${ROOTFS_B_PART_OFFSET}, ${ROOTFS_B_PART_COUNT}) + trim(${APP_PART_OFFSET}, ${APP_PART_COUNT}) + + # Wipe and re-seed the uboot-env KV store with the canonical + # grisp_alloy factory schema (byte-for-byte equivalent to + # system_kontron-albl-imx8mm/fwup.conf:230-272). + uboot_clearenv(uboot-env) + + uboot_setenv(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + uboot_setenv(uboot-env, "valid_system", "a") + uboot_setenv(uboot-env, "active_system", "a") + uboot_setenv(uboot-env, "upgrade_available", "0") + uboot_setenv(uboot-env, "upgrade_fallback", "0") + uboot_setenv(uboot-env, "rollback_available", "0") + uboot_setenv(uboot-env, "bootcount", "0") + + uboot_setenv(uboot-env, "system_platform", "${GRISP_FW_PLATFORM}") + uboot_setenv(uboot-env, "system_architecture", "${GRISP_FW_ARCHITECTURE}") + + # System A factory metadata (UUID is filled from the .fw itself). + uboot_setenv(uboot-env, "systema_firmware_uuid", "\${FWUP_META_UUID}") + uboot_setenv(uboot-env, "systema_firmware_version", ${GRISP_FW_VERSION}) + uboot_setenv(uboot-env, "systema_firmware_vcs_id", ${GRISP_FW_VCS_IDENTIFIER}) + uboot_setenv(uboot-env, "systema_firmware_author", ${GRISP_FW_AUTHOR}) + + # System B starts empty. + uboot_setenv(uboot-env, "systemb_firmware_uuid", "") + uboot_setenv(uboot-env, "systemb_firmware_version", "") + uboot_setenv(uboot-env, "systemb_firmware_vcs_id", "") + uboot_setenv(uboot-env, "systemb_firmware_author", "") + + # Placeholder for extra kernel command-line additions (RT isolcpus, + # ramoops address, etc.). cmdline-{a,b}.txt is the authoritative + # source for rpi02w's actual kernel cmdline; this key is carried for + # cross-target schema symmetry. + uboot_setenv(uboot-env, "bootargs_extra", "") + } + + # ---- AUTOBOOT partition: bootcode.bin + autoboot*.txt ------------------ + + on-resource bootcode.bin { + info("Staging bootcode.bin on AUTOBOOT") + fat_write(${AUTOBOOT_PART_OFFSET}, "bootcode.bin") + } + + on-resource autoboot-a.txt { + # Keep a named copy for later slot swaps to fat_write onto autoboot.txt. + fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot-a.txt") + # And make it the live autoboot.txt (slot A is the factory-default). + # fwup forbids two fat_writes on the same resource (stream is + # consumed after the first), so duplicate inside the FAT via fat_cp. + fat_cp(${AUTOBOOT_PART_OFFSET}, "autoboot-a.txt", "autoboot.txt") + } + + on-resource autoboot-b.txt { + fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot-b.txt") + } + + # ---- BOOT-A partition: full boot payload ------------------------------- + # + # Factory flash populates slot A only; BOOT-B is left empty until the + # first `upgrade.b` (which writes every BOOT-B asset itself — see the + # upgrade.b task below). fwup's stream model forbids `fat_write`-ing the + # same resource to two different FATs in a single `on-resource`, and + # `fat_cp` only works inside one FAT. Matching Nerves' rpi0 behaviour is + # the simplest correct fix: before `upgrade.b ` there is no + # scenario in which tryboot-to-B would succeed anyway, since both slots + # would then need the same `rootfs.img` and env state. + + on-resource start.elf { fat_write(${BOOT_A_PART_OFFSET}, "start.elf") } + on-resource fixup.dat { fat_write(${BOOT_A_PART_OFFSET}, "fixup.dat") } + on-resource config.txt { fat_write(${BOOT_A_PART_OFFSET}, "config.txt") } + on-resource cmdline-a.txt { fat_write(${BOOT_A_PART_OFFSET}, "cmdline.txt") } + on-resource kernel8.img { fat_write(${BOOT_A_PART_OFFSET}, "kernel8.img") } + on-resource bcm2710-rpi-zero-2-w.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2710-rpi-zero-2-w.dtb") } + on-resource overlays/dwc2.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/dwc2.dtbo") } + on-resource overlays/miniuart-bt.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/miniuart-bt.dtbo") } + on-resource overlays/ramoops.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/ramoops.dtbo") } + + # cmdline-b.txt is in the archive but intentionally unreferenced here — + # the upgrade.b task consumes it. Unreferenced resources are skipped. + + # ---- Rootfs ------------------------------------------------------------ + + on-resource rootfs.img { + info("Writing root filesystem to partition A") + raw_write(${ROOTFS_A_PART_OFFSET}) + } + + on-finish { + info("Finalising firmware") + + # Invalidate ROOTFS-B so the old squashfs (if any) isn't accidentally + # boot-matched by cmdline-b.txt before upgrade.b writes a real one. + raw_memset(${ROOTFS_B_PART_OFFSET}, 256, 0xff) + raw_memset(${BOOT_B_PART_OFFSET}, 256, 0xff) + + # Invalidate APP so first-boot corrupt-detect formats it cleanly. + raw_memset(${APP_PART_OFFSET}, 256, 0xff) + + info("Firmware setup done") + } +} + +# +# A/B upgrade tasks (kontron vocabulary; rpi02w-specific post-write action: +# reboot_param("0 tryboot")). +# + +# Upgrade system A while running from system B. +task upgrade.a { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_B_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "b") + require-uboot-variable(uboot-env, "valid_system", "b") + require-uboot-variable(uboot-env, "system_platform", "${GRISP_FW_PLATFORM}") + require-uboot-variable(uboot-env, "system_architecture", "${GRISP_FW_ARCHITECTURE}") + + on-init { + info("Upgrading system A (currently running from B)") + + # Any previously-staged upgrade will be overwritten. + uboot_setenv(uboot-env, "upgrade_available", "0") + uboot_setenv(uboot-env, "rollback_available", "0") + + # Reset system A metadata. + uboot_setenv(uboot-env, "systema_firmware_uuid", "") + uboot_setenv(uboot-env, "systema_firmware_version", "") + uboot_setenv(uboot-env, "systema_firmware_vcs_id", "") + uboot_setenv(uboot-env, "systema_firmware_author", "") + + # Reset and format BOOT-A before writing new payload. + fat_mkfs(${BOOT_A_PART_OFFSET}, ${BOOT_A_PART_COUNT}) + fat_setlabel(${BOOT_A_PART_OFFSET}, "BOOT-A") + fat_mkdir(${BOOT_A_PART_OFFSET}, "overlays") + + # Discard the old rootfs. + trim(${ROOTFS_A_PART_OFFSET}, ${ROOTFS_A_PART_COUNT}) + } + + # BOOT-A payload. + on-resource start.elf { fat_write(${BOOT_A_PART_OFFSET}, "start.elf") } + on-resource fixup.dat { fat_write(${BOOT_A_PART_OFFSET}, "fixup.dat") } + on-resource config.txt { fat_write(${BOOT_A_PART_OFFSET}, "config.txt") } + on-resource cmdline-a.txt { fat_write(${BOOT_A_PART_OFFSET}, "cmdline.txt") } + on-resource kernel8.img { fat_write(${BOOT_A_PART_OFFSET}, "kernel8.img") } + on-resource bcm2710-rpi-zero-2-w.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2710-rpi-zero-2-w.dtb") } + on-resource overlays/dwc2.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/dwc2.dtbo") } + on-resource overlays/miniuart-bt.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/miniuart-bt.dtbo") } + on-resource overlays/ramoops.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/ramoops.dtbo") } + + on-resource rootfs.img { + raw_write(${ROOTFS_A_PART_OFFSET}) + } + + on-finish { + uboot_setenv(uboot-env, "systema_firmware_uuid", "\${FWUP_META_UUID}") + uboot_setenv(uboot-env, "systema_firmware_version", ${GRISP_FW_VERSION}) + uboot_setenv(uboot-env, "systema_firmware_vcs_id", ${GRISP_FW_VCS_IDENTIFIER}) + uboot_setenv(uboot-env, "systema_firmware_author", ${GRISP_FW_AUTHOR}) + uboot_setenv(uboot-env, "bootcount", "0") + uboot_setenv(uboot-env, "upgrade_available", "1") + uboot_setenv(uboot-env, "upgrade_fallback", "0") + + # Predict the tryboot outcome: next boot targets A. Without a + # bootloader nothing would otherwise update active_system, so + # status.a.upgraded queries during the validation window + # depend on this being set here. + uboot_setenv(uboot-env, "active_system", "a") + + info("System A upgraded; rebooting into tryboot slot A") + # GPU firmware honours [tryboot] in autoboot.txt for exactly one + # boot, then falls back to [all] unless sticky-switched by + # validate.a (which rewrites autoboot.txt). + reboot_param("0 tryboot") + } + + on-error { } +} + +# Upgrade system B while running from system A. +task upgrade.b { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "a") + require-uboot-variable(uboot-env, "valid_system", "a") + require-uboot-variable(uboot-env, "system_platform", "${GRISP_FW_PLATFORM}") + require-uboot-variable(uboot-env, "system_architecture", "${GRISP_FW_ARCHITECTURE}") + + on-init { + info("Upgrading system B (currently running from A)") + + uboot_setenv(uboot-env, "upgrade_available", "0") + uboot_setenv(uboot-env, "rollback_available", "0") + + uboot_setenv(uboot-env, "systemb_firmware_uuid", "") + uboot_setenv(uboot-env, "systemb_firmware_version", "") + uboot_setenv(uboot-env, "systemb_firmware_vcs_id", "") + uboot_setenv(uboot-env, "systemb_firmware_author", "") + + # Reset and format BOOT-B before writing new payload. + fat_mkfs(${BOOT_B_PART_OFFSET}, ${BOOT_B_PART_COUNT}) + fat_setlabel(${BOOT_B_PART_OFFSET}, "BOOT-B") + fat_mkdir(${BOOT_B_PART_OFFSET}, "overlays") + + trim(${ROOTFS_B_PART_OFFSET}, ${ROOTFS_B_PART_COUNT}) + } + + on-resource start.elf { fat_write(${BOOT_B_PART_OFFSET}, "start.elf") } + on-resource fixup.dat { fat_write(${BOOT_B_PART_OFFSET}, "fixup.dat") } + on-resource config.txt { fat_write(${BOOT_B_PART_OFFSET}, "config.txt") } + on-resource cmdline-b.txt { fat_write(${BOOT_B_PART_OFFSET}, "cmdline.txt") } + on-resource kernel8.img { fat_write(${BOOT_B_PART_OFFSET}, "kernel8.img") } + on-resource bcm2710-rpi-zero-2-w.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2710-rpi-zero-2-w.dtb") } + on-resource overlays/dwc2.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/dwc2.dtbo") } + on-resource overlays/miniuart-bt.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/miniuart-bt.dtbo") } + on-resource overlays/ramoops.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/ramoops.dtbo") } + + on-resource rootfs.img { + raw_write(${ROOTFS_B_PART_OFFSET}) + } + + on-finish { + uboot_setenv(uboot-env, "systemb_firmware_uuid", "\${FWUP_META_UUID}") + uboot_setenv(uboot-env, "systemb_firmware_version", ${GRISP_FW_VERSION}) + uboot_setenv(uboot-env, "systemb_firmware_vcs_id", ${GRISP_FW_VCS_IDENTIFIER}) + uboot_setenv(uboot-env, "systemb_firmware_author", ${GRISP_FW_AUTHOR}) + uboot_setenv(uboot-env, "bootcount", "0") + uboot_setenv(uboot-env, "upgrade_available", "1") + uboot_setenv(uboot-env, "upgrade_fallback", "0") + uboot_setenv(uboot-env, "active_system", "b") + + info("System B upgraded; rebooting into tryboot slot B") + reboot_param("0 tryboot") + } + + on-error { } +} + +# Unexpected running-state (e.g. mounted rootfs offset matches neither A nor B). +task upgrade.unexpected { + require-uboot-variable(uboot-env, "alloy_env_ver", ${ALLOY_ENV_VER}) + require-uboot-variable(uboot-env, "system_platform", ${GRISP_FW_PLATFORM}) + require-uboot-variable(uboot-env, "system_architecture", ${GRISP_FW_ARCHITECTURE}) + on-init { + error("Current system is not validated, or neither system A or B is active.") + } +} + +# env_ver/platform/architecture mismatch (wrong image for this device). +task upgrade.wrongplatform { + on-init { + error("Expecting alloy_env_ver=${ALLOY_ENV_VER}, platform=${GRISP_FW_PLATFORM} and architecture=${GRISP_FW_ARCHITECTURE}") + } +} + +# +# Validation tasks. +# +# Called by userland after a successful tryboot to make the new slot sticky. +# rpi02w-specific: also rewrite autoboot.txt on AUTOBOOT so the next normal +# (non-tryboot) reboot picks the same slot. +# + +task validate.a { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-uboot-variable(uboot-env, "system_platform", "${GRISP_FW_PLATFORM}") + require-uboot-variable(uboot-env, "system_architecture", "${GRISP_FW_ARCHITECTURE}") + require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "a") + require-uboot-variable(uboot-env, "upgrade_available", "1") + + on-init { + info("Validating system A") + uboot_setenv(uboot-env, "upgrade_available", "0") + uboot_setenv(uboot-env, "upgrade_fallback", "0") + uboot_setenv(uboot-env, "valid_system", "a") + uboot_setenv(uboot-env, "bootcount", "0") + uboot_setenv(uboot-env, "rollback_available", "1") + } + + # Make the slot switch sticky: next NORMAL reboot (no tryboot) boots A. + on-resource autoboot-a.txt { + fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt") + } + + on-finish { + info("System A validated; autoboot.txt now points at A") + } +} + +task validate.b { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-uboot-variable(uboot-env, "system_platform", "${GRISP_FW_PLATFORM}") + require-uboot-variable(uboot-env, "system_architecture", "${GRISP_FW_ARCHITECTURE}") + require-path-at-offset("/", ${ROOTFS_B_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "b") + require-uboot-variable(uboot-env, "upgrade_available", "1") + + on-init { + info("Validating system B") + uboot_setenv(uboot-env, "upgrade_available", "0") + uboot_setenv(uboot-env, "upgrade_fallback", "0") + uboot_setenv(uboot-env, "valid_system", "b") + uboot_setenv(uboot-env, "bootcount", "0") + uboot_setenv(uboot-env, "rollback_available", "1") + } + + on-resource autoboot-b.txt { + fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt") + } + + on-finish { + info("System B validated; autoboot.txt now points at B") + } +} + +task validate.unavailable { + require-uboot-variable(uboot-env, "alloy_env_ver", ${ALLOY_ENV_VER}) + require-uboot-variable(uboot-env, "system_platform", ${GRISP_FW_PLATFORM}) + require-uboot-variable(uboot-env, "system_architecture", ${GRISP_FW_ARCHITECTURE}) + on-init { + error("No upgrade pending: nothing to validate.") + } +} + +task validate.wrongplatform { + on-init { + error("Expecting alloy_env_ver=${ALLOY_ENV_VER}, platform=${GRISP_FW_PLATFORM} and architecture=${GRISP_FW_ARCHITECTURE}") + } +} + +# +# Rollback tasks. +# +# Explicit operator action: while on the currently-active slot, switch the +# live autoboot.txt to the OTHER slot so the next reboot lands there. No +# tryboot involved — rollback is immediate and non-one-shot. +# + +task rollback.a { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-uboot-variable(uboot-env, "system_platform", "${GRISP_FW_PLATFORM}") + require-uboot-variable(uboot-env, "system_architecture", "${GRISP_FW_ARCHITECTURE}") + require-path-at-offset("/", ${ROOTFS_B_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "b") + require-uboot-variable(uboot-env, "rollback_available", "1") + require-uboot-variable(uboot-env, "upgrade_available", "0") + + on-init { + info("Rolling back to system A (currently running from B)") + + uboot_setenv(uboot-env, "valid_system", "a") + uboot_setenv(uboot-env, "bootcount", "0") + uboot_setenv(uboot-env, "upgrade_available", "0") + uboot_setenv(uboot-env, "upgrade_fallback", "0") + + uboot_setenv(uboot-env, "systemb_firmware_uuid", "") + uboot_setenv(uboot-env, "systemb_firmware_version", "") + uboot_setenv(uboot-env, "systemb_firmware_vcs_id", "") + uboot_setenv(uboot-env, "systemb_firmware_author", "") + + # Rollback is one-shot until the next successful validation. + uboot_setenv(uboot-env, "rollback_available", "0") + } + + # Point the GPU at A on the next normal reboot. + on-resource autoboot-a.txt { + fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt") + } + + on-finish { + info("Reboot to complete rollback to A") + } +} + +task rollback.b { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-uboot-variable(uboot-env, "system_platform", "${GRISP_FW_PLATFORM}") + require-uboot-variable(uboot-env, "system_architecture", "${GRISP_FW_ARCHITECTURE}") + require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "a") + require-uboot-variable(uboot-env, "rollback_available", "1") + require-uboot-variable(uboot-env, "upgrade_available", "0") + + on-init { + info("Rolling back to system B (currently running from A)") + + uboot_setenv(uboot-env, "valid_system", "b") + uboot_setenv(uboot-env, "bootcount", "0") + uboot_setenv(uboot-env, "upgrade_available", "0") + uboot_setenv(uboot-env, "upgrade_fallback", "0") + + uboot_setenv(uboot-env, "systema_firmware_uuid", "") + uboot_setenv(uboot-env, "systema_firmware_version", "") + uboot_setenv(uboot-env, "systema_firmware_vcs_id", "") + uboot_setenv(uboot-env, "systema_firmware_author", "") + + uboot_setenv(uboot-env, "rollback_available", "0") + } + + on-resource autoboot-b.txt { + fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt") + } + + on-finish { + info("Reboot to complete rollback to B") + } +} + +task rollback.unavailable { + require-uboot-variable(uboot-env, "alloy_env_ver", ${ALLOY_ENV_VER}) + require-uboot-variable(uboot-env, "system_platform", ${GRISP_FW_PLATFORM}) + require-uboot-variable(uboot-env, "system_architecture", ${GRISP_FW_ARCHITECTURE}) + on-init { + error("Rollback is not available or safe in the current state.") + } +} + +task rollback.wrongplatform { + on-init { + error("Expecting alloy_env_ver=${ALLOY_ENV_VER}, platform=${GRISP_FW_PLATFORM} and architecture=${GRISP_FW_ARCHITECTURE}") + } +} + +# +# Status tasks — read-only diagnostics keyed on (active_system, valid_system, +# upgrade_available, rollback_available). Copied verbatim from kontron; the +# rpi02w-specific caveat is that there is a narrow window between a tryboot +# reboot and `fwup -t validate.` during which none of these tasks +# match (the upgrade.* on-finish predictively set active_system, but +# upgrade_available is still 1 and valid_system still points at the old +# slot — which *is* status.a.upgraded / status.b.upgraded). +# + +task status.a.validated_no_rollback { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "a") + require-uboot-variable(uboot-env, "valid_system", "a") + require-uboot-variable(uboot-env, "upgrade_available", "0") + require-uboot-variable(uboot-env, "rollback_available", "0") + on-init { + info("Status: Active=A, state=validated, reboot_needed=no, validation_needed=no, rollback_available=no") + } +} + +task status.b.validated_no_rollback { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_B_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "b") + require-uboot-variable(uboot-env, "valid_system", "b") + require-uboot-variable(uboot-env, "upgrade_available", "0") + require-uboot-variable(uboot-env, "rollback_available", "0") + on-init { + info("Status: Active=B, state=validated, reboot_needed=no, validation_needed=no, rollback_available=no") + } +} + +task status.a.validated_with_rollback { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "a") + require-uboot-variable(uboot-env, "upgrade_available", "0") + require-uboot-variable(uboot-env, "rollback_available", "1") + on-init { + info("Status: Active=A, state=validated, reboot_needed=no, validation_needed=no, rollback_available=yes") + } +} + +task status.b.validated_with_rollback { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_B_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "b") + require-uboot-variable(uboot-env, "upgrade_available", "0") + require-uboot-variable(uboot-env, "rollback_available", "1") + on-init { + info("Status: Active=B, state=validated, reboot_needed=no, validation_needed=no, rollback_available=yes") + } +} + +# Upgrade staged but not yet tryboot'd. +task status.a.upgrading { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "a") + require-uboot-variable(uboot-env, "upgrade_available", "1") + require-uboot-variable(uboot-env, "valid_system", "a") + on-init { + info("Status: Active=A, state=pending-upgrade, reboot_needed=yes, validation_needed=pending-reboot, rollback_available=no") + } +} + +task status.b.upgrading { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_B_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "b") + require-uboot-variable(uboot-env, "upgrade_available", "1") + require-uboot-variable(uboot-env, "valid_system", "b") + on-init { + info("Status: Active=B, state=pending-upgrade, reboot_needed=yes, validation_needed=pending-reboot, rollback_available=no") + } +} + +# Tryboot succeeded, awaiting validate.. +task status.a.upgraded { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "a") + require-uboot-variable(uboot-env, "upgrade_available", "1") + require-uboot-variable(uboot-env, "valid_system", "b") + on-init { + info("Status: Active=A, state=pending-validation, reboot_needed=no, validation_needed=yes, rollback_available=no") + } +} + +task status.b.upgraded { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_B_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "b") + require-uboot-variable(uboot-env, "upgrade_available", "1") + require-uboot-variable(uboot-env, "valid_system", "a") + on-init { + info("Status: Active=B, state=pending-validation, reboot_needed=no, validation_needed=yes, rollback_available=no") + } +} + +# Rollback staged, reboot to take effect. +task status.a.rollback_pending_reboot { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "a") + require-uboot-variable(uboot-env, "upgrade_available", "0") + require-uboot-variable(uboot-env, "valid_system", "b") + on-init { + info("Status: Active=A, state=pending-rollback, reboot_needed=yes, validation_needed=no, rollback_available=no") + } +} + +task status.b.rollback_pending_reboot { + require-uboot-variable(uboot-env, "alloy_env_ver", "${ALLOY_ENV_VER}") + require-path-at-offset("/", ${ROOTFS_B_PART_OFFSET}) + require-uboot-variable(uboot-env, "active_system", "b") + require-uboot-variable(uboot-env, "upgrade_available", "0") + require-uboot-variable(uboot-env, "valid_system", "a") + on-init { + info("Status: Active=B, state=pending-rollback, reboot_needed=yes, validation_needed=no, rollback_available=no") + } +} + +task status.wrongenv { + on-init { + error("Expecting alloy_env_ver=${ALLOY_ENV_VER}") + } +} diff --git a/system_rpi02w/fwup_include/fwup-common.conf b/system_rpi02w/fwup_include/fwup-common.conf new file mode 100644 index 0000000..b9bff87 --- /dev/null +++ b/system_rpi02w/fwup_include/fwup-common.conf @@ -0,0 +1,45 @@ +# fwup common definitions for system_rpi02w. +# +# Keep the SD geometry aligned with nerves_system_rpi0_2. The Raspberry Pi +# boot ROM/firmware stack is sensitive to partition placement, and these +# offsets/counts are known-good on Pi Zero 2 W. +# +# Values are 512-byte blocks. + +# --- uboot-env KV store (8 KiB, single copy) ------------------------------- + +define(UBOOT_ENV_OFFSET, 16) +define(UBOOT_ENV_COUNT, 16) + +# --- Partition layout ------------------------------------------------------- +# Primary partitions: +# p1 (entry 0): AUTOBOOT +# p2 (entry 1): BOOT-A +# p3 (entry 2): BOOT-B +# p4 (entry 3): Extended container +# Logical partitions inside extended: +# p5: ROOTFS-A +# p6: ROOTFS-B +# p7: APP (expand) +# +# This mirrors nerves_system_rpi0_2/fwup_include/fwup-common.conf. + +define(BOOT_A_PART_OFFSET, 63) +define(BOOT_A_PART_COUNT, 38630) + +define-eval(BOOT_B_PART_OFFSET, "${BOOT_A_PART_OFFSET} + ${BOOT_A_PART_COUNT}") +define(BOOT_B_PART_COUNT, ${BOOT_A_PART_COUNT}) + +define-eval(AUTOBOOT_PART_OFFSET, "${BOOT_B_PART_OFFSET} + ${BOOT_B_PART_COUNT}") +define-eval(AUTOBOOT_PART_COUNT, 32749) + +define-eval(EXTENDED_PART_OFFSET, "${AUTOBOOT_PART_OFFSET} + ${AUTOBOOT_PART_COUNT}") + +define(ROOTFS_A_PART_OFFSET, 110080) +define(ROOTFS_A_PART_COUNT, 272666) + +define-eval(ROOTFS_B_PART_OFFSET, "${ROOTFS_A_PART_OFFSET} + ${ROOTFS_A_PART_COUNT}") +define(ROOTFS_B_PART_COUNT, ${ROOTFS_A_PART_COUNT}) + +define-eval(APP_PART_OFFSET, "${ROOTFS_B_PART_OFFSET} + ${ROOTFS_B_PART_COUNT}") +define(APP_PART_COUNT, 1048576) diff --git a/system_rpi02w/fwup_include/provisioning.conf b/system_rpi02w/fwup_include/provisioning.conf new file mode 100644 index 0000000..34487f8 --- /dev/null +++ b/system_rpi02w/fwup_include/provisioning.conf @@ -0,0 +1,11 @@ +# fwup provisioning include for system_rpi02w. +# +# Intentionally empty today. Populate here with per-device first-boot +# provisioning logic (serial numbers, Wi-Fi regulatory domain, crypto +# chip factory keys, etc.) when that pipeline lands. Mirrors the way +# nerves_system_rpi0's fwup_include/provisioning.conf is used as a +# per-device hook point without making the main fwup.conf grow every +# time a factory step is added. +# +# Tasks defined here are merged into the top-level fwup.conf graph at +# include-time, so reference variables like ${UBOOT_ENV_OFFSET} freely. diff --git a/system_rpi02w/linux/linux-6.12.defconfig b/system_rpi02w/linux/linux-6.12.defconfig new file mode 100644 index 0000000..8802737 --- /dev/null +++ b/system_rpi02w/linux/linux-6.12.defconfig @@ -0,0 +1,306 @@ +CONFIG_LOCALVERSION="-v8" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT_RT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_EXPERT=y +# CONFIG_PERF_EVENTS is not set +CONFIG_PROFILING=y +CONFIG_ARCH_BCM=y +CONFIG_ARCH_BCM2835=y +# CONFIG_CAVIUM_ERRATUM_22375 is not set +# CONFIG_CAVIUM_ERRATUM_23154 is not set +# CONFIG_CAVIUM_ERRATUM_27456 is not set +CONFIG_NR_CPUS=4 +CONFIG_HZ_1000=y +CONFIG_COMPAT=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y +CONFIG_RANDOMIZE_BASE=y +CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" +# CONFIG_EFI is not set +# CONFIG_SUSPEND is not set +CONFIG_PM=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_RASPBERRYPI_CPUFREQ=y +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_PARTITION_ADVANCED=y +# CONFIG_EFI_PARTITION is not set +# CONFIG_MQ_IOSCHED_DEADLINE is not set +# CONFIG_MQ_IOSCHED_KYBER is not set +CONFIG_SLAB_FREELIST_RANDOM=y +# CONFIG_COMPAT_BRK is not set +CONFIG_CMA=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_INET_DIAG is not set +# CONFIG_IPV6_SIT is not set +CONFIG_MPTCP=y +CONFIG_CFG80211=m +CONFIG_MAC80211=m +CONFIG_MAC80211_MESH=y +CONFIG_RFKILL=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_RASPBERRYPI_FIRMWARE=y +CONFIG_OF_CONFIGFS=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_EEPROM_AT24=m +CONFIG_NETDEVICES=y +CONFIG_WIREGUARD=m +CONFIG_TUN=m +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SYNOPSYS is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_WLAN_VENDOR_ADMTEK is not set +# CONFIG_WLAN_VENDOR_ATH is not set +# CONFIG_WLAN_VENDOR_ATMEL is not set +CONFIG_BRCMFMAC=m +# CONFIG_WLAN_VENDOR_INTEL is not set +# CONFIG_WLAN_VENDOR_INTERSIL is not set +# CONFIG_WLAN_VENDOR_MARVELL is not set +# CONFIG_WLAN_VENDOR_MEDIATEK is not set +# CONFIG_WLAN_VENDOR_MICROCHIP is not set +# CONFIG_WLAN_VENDOR_PURELIFI is not set +# CONFIG_WLAN_VENDOR_RALINK is not set +# CONFIG_WLAN_VENDOR_REALTEK is not set +# CONFIG_WLAN_VENDOR_RSI is not set +# CONFIG_WLAN_VENDOR_SILABS is not set +# CONFIG_WLAN_VENDOR_ST is not set +# CONFIG_WLAN_VENDOR_TI is not set +# CONFIG_WLAN_VENDOR_ZYDAS is not set +# CONFIG_WLAN_VENDOR_QUANTENNA is not set +CONFIG_BT=m +CONFIG_BT_BREDR=y +CONFIG_BT_LE=y +CONFIG_BT_RFCOMM=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCM=y +CONFIG_BT_HCIUART_SERDEV=y +CONFIG_BT_BCM=m +# CONFIG_INPUT_LEDS is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_RASPBERRYPI_FW=m +# CONFIG_SERIO is not set +CONFIG_SERIAL_DEV_BUS=y +CONFIG_SERIAL_DEV_CTRL_TTYPORT=y +CONFIG_BRCM_CHAR_DRIVERS=y +CONFIG_BCM_VCIO=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=1 +CONFIG_SERIAL_8250_RUNTIME_UARTS=0 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_BCM2835AUX=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_RASPBERRYPI_GPIOMEM=m +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX_PINCTRL=m +CONFIG_I2C_BCM2708=y +CONFIG_I2C_BCM2835=y +CONFIG_SPI=y +CONFIG_SPI_BCM2835=y +CONFIG_SPI_BCM2835AUX=y +CONFIG_SPI_SPIDEV=y +CONFIG_GPIO_BCM_VIRT=y +CONFIG_W1=m +CONFIG_W1_MASTER_GPIO=m +CONFIG_W1_SLAVE_THERM=m +CONFIG_POWER_RESET_GPIO=y +CONFIG_RPI_POE_POWER=m +CONFIG_SENSORS_RASPBERRYPI_HWMON=m +CONFIG_THERMAL=y +CONFIG_BCM2711_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y +CONFIG_BCM2835_WDT=y +CONFIG_BCMA=m +CONFIG_MFD_SYSCON=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_AD5398=m +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY=m +CONFIG_MEDIA_SUPPORT=m +CONFIG_MEDIA_SUPPORT_FILTER=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_PLATFORM_SUPPORT=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_BCM2835_UNICAM_LEGACY=m +CONFIG_VIDEO_RASPBERRYPI_PISP_BE=m +CONFIG_VIDEO_IMX219=m +CONFIG_VIDEO_IMX296=m +CONFIG_VIDEO_IMX477=m +CONFIG_VIDEO_IMX708=m +CONFIG_VIDEO_OV5647=m +CONFIG_VIDEO_AD5398=m +CONFIG_VIDEO_DW9807_VCM=m +CONFIG_DRM=m +CONFIG_DRM_LOAD_EDID_FIRMWARE=y +CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m +CONFIG_DRM_VC4=m +CONFIG_TINYDRM_REPAPER=m +CONFIG_FB=y +CONFIG_FB_BCM2708=y +CONFIG_FB_RPISENSE=m +CONFIG_FB_MODE_HELPERS=y +CONFIG_BACKLIGHT_CLASS_DEVICE=m +CONFIG_BACKLIGHT_RPI=m +CONFIG_BACKLIGHT_GPIO=m +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=m +# CONFIG_SND_SUPPORT_OLD_API is not set +CONFIG_SND_SEQUENCER=m +CONFIG_SND_ALOOP=m +CONFIG_SND_SOC=m +CONFIG_SND_BCM2835_SOC_I2S=m +CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m +CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m +CONFIG_SND_SIMPLE_CARD=m +# CONFIG_HID_GENERIC is not set +CONFIG_USB_DWC2=y +CONFIG_USB_DWC2_PERIPHERAL=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_GADGET=y +CONFIG_USB_LIBCOMPOSITE=y +CONFIG_USB_U_SERIAL=y +CONFIG_USB_F_ACM=y +CONFIG_USB_G_SERIAL=y +# CONFIG_USB_ETH is not set +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BCM2835_MMC=y +CONFIG_MMC_BCM2835_DMA=y +CONFIG_MMC_BCM2835_SDHOST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_TRANSIENT=y +CONFIG_LEDS_TRIGGER_INPUT=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_LEDS_TRIGGER_PATTERN=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_DMADEVICES=y +CONFIG_DMA_BCM2835=y +CONFIG_DMA_BCM2708=y +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_SYSTEM=y +CONFIG_DMABUF_HEAPS_CMA=y +# CONFIG_VHOST_MENU is not set +CONFIG_STAGING=y +CONFIG_STAGING_MEDIA=y +CONFIG_BCM2835_VCHIQ=y +CONFIG_SND_BCM2835=m +CONFIG_VIDEO_BCM2835=m +CONFIG_VIDEO_CODEC_BCM2835=m +CONFIG_VIDEO_ISP_BCM2835=m +CONFIG_CLK_RASPBERRYPI=y +CONFIG_MAILBOX=y +CONFIG_BCM2835_MBOX=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_RASPBERRYPI_POWER=y +CONFIG_EXT4_FS=y +CONFIG_FANOTIFY=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_FILE_DIRECT=y +CONFIG_SQUASHFS_COMPILE_DECOMP_MULTI_PERCPU=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_PMSG=y +CONFIG_PSTORE_RAM=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_850=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_LSM="" +CONFIG_CRYPTO_AES_ARM64_BS=m +# CONFIG_CRYPTO_HW is not set +CONFIG_CRC_ITU_T=m +CONFIG_CRC7=m +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=5 +CONFIG_PRINTK_TIME=y +CONFIG_PANIC_TIMEOUT=10 diff --git a/system_rpi02w/post-build.sh b/system_rpi02w/post-build.sh new file mode 100755 index 0000000..a1cf5ad --- /dev/null +++ b/system_rpi02w/post-build.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +set -e + +source "$( dirname "$0" )/../scripts/common.sh" "$GRISP_TARGET_NAME" + +# Stage target fwup includes so that system_rpi02w/fwup.conf's +# include("${GRISP_SYSTEM}/images/fwup_include/fwup-common.conf") +# resolves at firmware build time. Mirrors system_grisp2/post-build.sh:13. +cp -rf "${GLB_TARGET_SYSTEM_DIR}/fwup_include" "${BINARIES_DIR}" + +# Stage the per-slot boot text files that fwup.conf's file-resources point +# at directly via ${GRISP_SYSTEM}/images/. Unlike zImage / DTBs / +# rpi-firmware blobs these don't come from Buildroot, so post-build has to +# drop them in $BINARIES_DIR itself. +cp -f "${GLB_TARGET_SYSTEM_DIR}/config.txt" "${BINARIES_DIR}/" +cp -f "${GLB_TARGET_SYSTEM_DIR}/cmdline-a.txt" "${BINARIES_DIR}/" +cp -f "${GLB_TARGET_SYSTEM_DIR}/cmdline-b.txt" "${BINARIES_DIR}/" +cp -f "${GLB_TARGET_SYSTEM_DIR}/autoboot-a.txt" "${BINARIES_DIR}/" +cp -f "${GLB_TARGET_SYSTEM_DIR}/autoboot-b.txt" "${BINARIES_DIR}/" + +# Ensure /boot exists in the squashfs as an empty mount point; erlinit +# later mounts /dev/mmcblk0p2 (BOOT-A) here read-only (see erlinit.config). +mkdir -p "${TARGET_DIR}/boot" + +# No depmod loop: with the full custom kernel defconfig there is no +# MODULE_COMPRESS_XZ, so Buildroot's own modules_install pass produces a +# working /lib/modules//modules.dep. system_grisp2 and +# system_kontron-albl-imx8mm rely on the same Buildroot behaviour. diff --git a/system_rpi02w/rootfs_overlay/etc/erlinit.config b/system_rpi02w/rootfs_overlay/etc/erlinit.config new file mode 100644 index 0000000..8e18fd0 --- /dev/null +++ b/system_rpi02w/rootfs_overlay/etc/erlinit.config @@ -0,0 +1,101 @@ +# Additional configuration for erlinit + +# Turn on the debug prints +#-v + +# Specify where erlinit should send the Erlang prompt. +# +# Primary: ttyGS0 -- USB-OTG CDC-ACM serial gadget, exposed by the kernel +# on the Pi Zero 2 W's middle micro-USB port ("USB"). A single micro-USB +# cable from a host Mac or Linux box is enough: it powers the board AND +# carries the BEAM serial console. On the host, `picocom -b 115200 +# /dev/tty.usbmodem*` (macOS) or `picocom -b 115200 /dev/ttyACM0` (Linux) +# lands on this prompt. +# +# erlinit only accepts a single `-c` tty, so there is NO automatic +# fallback to UART if the USB gadget fails to enumerate. However, +# `cmdline-*.txt` lists both tty devices on `console=`, so kernel boot +# messages (and oopses) also come out on ttyAMA0 -- the PL011 UART on +# GPIO14/15 (pins 8/10), 115200 8N1. For early-boot debugging, attach a +# 3.3 V USB-UART adapter to pins 8/10 (GND on 6/9/14/20/25/30/34/39) and +# you'll see the kernel log even when the BEAM prompt on ttyGS0 is not +# yet available. The miniuart-bt overlay routes Bluetooth to the +# mini-UART (ttyAMA1) so ttyAMA0 stays exclusively the Linux console. +-c ttyGS0 + +# If more than one tty is available, warn the user if the wrong one is open. +--warn-unused-tty + +# Use nbtty to improve terminal handling on serial ports. +-s "/usr/bin/nbtty" + +# There's a call to getrandom(2) when loading the crypto NIF that's before +# the runtime can start rngd. This syscall can block the BEAM indefinitely +# if there's not enough entropy in the kernel. Start rngd early to be safe. +--pre-run-exec /usr/sbin/rngd + +# Bring up rpi02w-specific peripherals that don't belong to the BEAM: +# - mount /sys/fs/pstore so the previous boot's kernel oops is visible +# - btattach /dev/ttyAMA1 so the BCM43438 HCI node appears as hci0 +# Script tolerates missing firmware / disabled overlays; never fails boot. +--pre-run-exec /sbin/peripherals-init.sh + +# Specify the user and group IDs for the Erlang VM +#--uid 100 +#--gid 200 + +# Uncomment to ensure that the system clock is set to at least the build +# date/time. Still requires NTP or similar for an actual date, but won't +# be decades off. +#--update-clock + +# Uncomment to hang the board rather than rebooting when Erlang exits. +# NOTE: Do not enable on production boards. +#--hang-on-exit + +# Change the graceful shutdown time. If 10 seconds isn't long enough +# between "poweroff" / "reboot" / "halt" and :init.stop/0 stopping all OTP +# applications, enable this option with a new timeout in milliseconds. +#--graceful-shutdown-timeout 15000 + +# Optionally run a program if the Erlang VM exits +#--run-on-exit /bin/sh + +# Enable UTF-8 filename handling in Erlang and custom inet configuration +-e LANG=en_US.UTF-8;LANGUAGE=en;ERL_INETRC=/etc/erl_inetrc + +# Enable crash dumps (set ERL_CRASH_DUMP_SECONDS=0 to disable) +-e ERL_CRASH_DUMP=/data/crash/erl_crash.dump;ERL_CRASH_DUMP_SECONDS=5 + +# Mount the application partition (run "man fstab" for field names). +# NOTE: these paths must match the corresponding partition offsets in +# system_rpi02w/fwup.conf + fwup_include/fwup-common.conf. If it doesn't, +# the system will probably still work fine, but you won't get shell history +# since grisp_runtime can't mount the application filesystem before the +# history is loaded. If this mount fails due to corruption, grisp_runtime +# will auto-format it. Applications should handle initialising expected +# files and folders. +# +# rpi02w layout: +# /dev/mmcblk0p2 = BOOT-A FAT (active boot-time payload) +# /dev/mmcblk0p7 = APP ext4 (application data) +# +# We intentionally hardcode `/dev/mmcblk0pN` instead of introducing a +# grisp-config-style udev alias layer (/dev/rootdisk0pN): Nerves-style +# static naming is simpler, matches the fwup.conf offsets directly, and +# the SD card path is stable on this SoC. If a second block device ever +# becomes bootable on rpi02w, revisit this decision then. +-m /dev/mmcblk0p2:/boot:vfat:ro,nodev,noexec,nosuid: +-m tmpfs:/root:tmpfs::mode=0700,size=1M +-m tmpfs:/var/log:tmpfs:nodev,noexec,nosuid:mode=0755,size=16M + +# Erlang release search path +-r /srv/erlang + +# Boot the application +--boot start + +# Assign a hostname of the form "rpi02w-". +# See /etc/boardid.config for locating the serial number. +#-d /usr/bin/boardid +-n rpi02w-%s diff --git a/system_rpi02w/rootfs_overlay/etc/fw_env.config b/system_rpi02w/rootfs_overlay/etc/fw_env.config new file mode 100644 index 0000000..c4883aa --- /dev/null +++ b/system_rpi02w/rootfs_overlay/etc/fw_env.config @@ -0,0 +1,10 @@ +# fw_printenv / fw_setenv configuration. +# +# Must match the uboot-env block in system_rpi02w/fwup_include/fwup-common.conf: +# UBOOT_ENV_OFFSET = 16 blocks -> 0x2000 bytes +# UBOOT_ENV_COUNT = 16 blocks -> 0x2000 bytes (8 KiB) +# +# Columns: device, offset, env-size, flash-sector-size, num-sectors + +# Device name Device offset Env size Flash sector size Number of sectors +/dev/mmcblk0 0x2000 0x2000 0x200 16 diff --git a/system_rpi02w/rootfs_overlay/sbin/peripherals-init.sh b/system_rpi02w/rootfs_overlay/sbin/peripherals-init.sh new file mode 100755 index 0000000..bd5ab3d --- /dev/null +++ b/system_rpi02w/rootfs_overlay/sbin/peripherals-init.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# Bring up rpi02w-specific peripherals before erlinit starts the BEAM. +# +# Invoked by erlinit as a --pre-run-exec (see rootfs_overlay/etc/erlinit.config). +# Failures are logged but never fatal: the BEAM must always come up, so +# missing firmware or an unresponsive BT controller should degrade, not brick. + +set -u + +log() { echo "[peripherals-init] $*"; } + +# -------- kernel modules --------------------------------------------------- +# Both brcmfmac (Wi-Fi, SDIO) and hci_uart (Bluetooth, UART) are =m in the +# kernel config. In theory: +# - brcmfmac autoloads via the kernel's usermode helper when the SDIO +# subsystem enumerates the BCM43430 modalias at ~1.9s into boot. +# - hci_uart autoloads via the serdev binding for the `miniuart-bt` DT +# overlay, and indeed does so at ~2.4s (we see it in dmesg before this +# script runs). +# +# In practice brcmfmac loses the autoload race on RPi Zero 2 W: the SDIO +# coldplug uevent fires before /sbin/modprobe has modules.dep paged in, +# the single kernel request_module() call fails silently, and we end up +# at the prompt with no wlan0. Explicit modprobe here makes it +# deterministic. Bluetooth's hci_uart we re-modprobe defensively (no-op +# if serdev already loaded it). Non-fatal in both cases: the BEAM must +# come up regardless, and missing Wi-Fi/BT should degrade the system, +# not brick it. +if ! modprobe brcmfmac 2>/dev/null; then + log "modprobe brcmfmac failed (module missing or depmod incomplete)" +fi +if ! modprobe hci_uart 2>/dev/null; then + log "modprobe hci_uart failed (module missing or depmod incomplete)" +fi + +# -------- pstore / ramoops -------------------------------------------------- +# The ramoops overlay (config.txt: `dtoverlay=ramoops`) reserves a small +# RAM region whose contents survive a soft reboot. Mounting pstore here +# exposes each previous boot's kernel log as /sys/fs/pstore/dmesg-ramoops-*. +# No-op (mount fails harmlessly) if the overlay isn't active or if +# CONFIG_PSTORE_RAM is missing. +if [ -d /sys/fs/pstore ]; then + if ! mount -t pstore pstore /sys/fs/pstore 2>/dev/null; then + log "pstore mount failed (ramoops overlay not applied?); continuing" + fi +fi + +# -------- Bluetooth: attach BCM43438 over mini-UART ------------------------ +# config.txt carries `dtoverlay=miniuart-bt`, which routes the onboard BT +# controller to ttyAMA1 at 3 Mbps. `btattach` from bluez5-utils hands the +# line discipline to the kernel's hciuart driver (CONFIG_BT_HCIUART_BCM=y +# in linux/linux-6.12.defconfig). Run in the background: we don't want the +# BEAM waiting on the BT handshake. `hciconfig hci0 up` is left to +# userspace / grisp OTP. +# +# The firmware blob (BCM43430A1.hcd) is supplied by the brcmfmac_sdio-firmware-rpi +# package (_BT sub-option); without it, btattach will attach the UART but +# leave the controller un-initialised. +if [ -c /dev/ttyAMA1 ] && command -v btattach >/dev/null 2>&1; then + log "attaching Bluetooth on /dev/ttyAMA1 (bcm, 3 Mbps)" + btattach -B /dev/ttyAMA1 -P bcm -S 3000000 >/dev/null 2>&1 & +fi + +exit 0 diff --git a/toolchain/configs/rpi02w_linux_aarch64_defconfig b/toolchain/configs/rpi02w_linux_aarch64_defconfig new file mode 100644 index 0000000..7c95a56 --- /dev/null +++ b/toolchain/configs/rpi02w_linux_aarch64_defconfig @@ -0,0 +1,22 @@ +GLB_TOOLCHAIN_VERSION="0.1" +GLB_TOOLCHAIN_TYPE=crosstool-ng +GLB_CTNG_USE_GIT=true +GLB_CTNG_TAG=5595edc370d8146ca3bbb3052dde48aceaff4970 + +CT_GCC_V_13=y + +CT_ARCH_ARM=y +CT_ARCH_CPU="cortex-a53" +CT_ARCH_64=y +CT_ARCH_FLOAT_HW=y +CT_KERNEL_LINUX=y +CT_LINUX_V_6_1=y +CT_DEBUG_GDB=y + +CT_CONFIG_VERSION="4" +CT_GDB_CROSS_STATIC=y +CT_CC_LANG_CXX=y +CT_TARBALLS_BUILDROOT_LAYOUT=y +CT_CC_GCC_LIBGOMP=y +CT_CC_GCC_BUILD_ID=y +CT_CC_LANG_FORTRAN=y diff --git a/toolchain/configs/rpi02w_linux_x86_64_defconfig b/toolchain/configs/rpi02w_linux_x86_64_defconfig new file mode 100644 index 0000000..7c95a56 --- /dev/null +++ b/toolchain/configs/rpi02w_linux_x86_64_defconfig @@ -0,0 +1,22 @@ +GLB_TOOLCHAIN_VERSION="0.1" +GLB_TOOLCHAIN_TYPE=crosstool-ng +GLB_CTNG_USE_GIT=true +GLB_CTNG_TAG=5595edc370d8146ca3bbb3052dde48aceaff4970 + +CT_GCC_V_13=y + +CT_ARCH_ARM=y +CT_ARCH_CPU="cortex-a53" +CT_ARCH_64=y +CT_ARCH_FLOAT_HW=y +CT_KERNEL_LINUX=y +CT_LINUX_V_6_1=y +CT_DEBUG_GDB=y + +CT_CONFIG_VERSION="4" +CT_GDB_CROSS_STATIC=y +CT_CC_LANG_CXX=y +CT_TARBALLS_BUILDROOT_LAYOUT=y +CT_CC_GCC_LIBGOMP=y +CT_CC_GCC_BUILD_ID=y +CT_CC_LANG_FORTRAN=y From 8200cd70cb1601f5fe020e87996f139e58275f5c Mon Sep 17 00:00:00 2001 From: Luca Succi Date: Wed, 22 Apr 2026 15:36:48 +0200 Subject: [PATCH 2/2] Align partition scheme to rpi0w style --- docs/porting-notes-rpi02w.md | 16 +-- system_rpi02w/cmdline-a.txt | 2 +- system_rpi02w/cmdline-b.txt | 2 +- system_rpi02w/config.txt | 5 - system_rpi02w/fwup.conf | 22 ++-- system_rpi02w/fwup_include/fwup-common.conf | 108 ++++++++++++++------ 6 files changed, 92 insertions(+), 63 deletions(-) diff --git a/docs/porting-notes-rpi02w.md b/docs/porting-notes-rpi02w.md index 47528bf..fe369a3 100644 --- a/docs/porting-notes-rpi02w.md +++ b/docs/porting-notes-rpi02w.md @@ -24,14 +24,14 @@ from the existing `system_rpi0w` target. ## Firmware Layout / fwup -- `system_rpi02w/fwup_include/fwup-common.conf` uses Nerves-compatible - partition geometry for Pi Zero 2 W: - - BOOT-A offset/count: `63 / 38630` - - BOOT-B offset/count: `38693 / 38630` - - AUTOBOOT offset/count: `77323 / 32749` - - ROOTFS-A offset/count: `110080 / 272666` - - ROOTFS-B offset/count: `382746 / 272666` - - APP offset/count: `655412 / 1048576 (expand)` +- `system_rpi02w/fwup_include/fwup-common.conf` intentionally mirrors + `system_rpi0w` partition geometry to preserve the same A/B update style: + - AUTOBOOT offset/count: `2048 / 32768` + - BOOT-A offset/count: `34816 / 65536` + - BOOT-B offset/count: `100352 / 65536` + - ROOTFS-A offset/count: `167936 / 286720` + - ROOTFS-B offset/count: `456704 / 286720` + - APP offset/count: `745472 / 1048576 (expand)` - `fwup.conf` keeps GRiSP A/B state semantics (`upgrade.*`, `validate.*`, `rollback.*`, `status.*`) while using Pi `tryboot` behavior via `autoboot.txt`. diff --git a/system_rpi02w/cmdline-a.txt b/system_rpi02w/cmdline-a.txt index d048bf7..823a1d1 100644 --- a/system_rpi02w/cmdline-a.txt +++ b/system_rpi02w/cmdline-a.txt @@ -1 +1 @@ -root=/dev/mmcblk0p5 rootwait rootfstype=squashfs console=ttyAMA0,115200 console=ttyGS0 ignore_loglevel loglevel=8 initcall_debug +root=/dev/mmcblk0p5 rootwait rootfstype=squashfs console=ttyAMA0,115200 console=ttyGS0 diff --git a/system_rpi02w/cmdline-b.txt b/system_rpi02w/cmdline-b.txt index 36daedc..252f81d 100644 --- a/system_rpi02w/cmdline-b.txt +++ b/system_rpi02w/cmdline-b.txt @@ -1 +1 @@ -root=/dev/mmcblk0p6 rootwait rootfstype=squashfs console=ttyAMA0,115200 console=ttyGS0 ignore_loglevel loglevel=8 initcall_debug +root=/dev/mmcblk0p6 rootwait rootfstype=squashfs console=ttyAMA0,115200 console=ttyGS0 diff --git a/system_rpi02w/config.txt b/system_rpi02w/config.txt index 5e8462e..d933188 100644 --- a/system_rpi02w/config.txt +++ b/system_rpi02w/config.txt @@ -43,11 +43,6 @@ dtoverlay=dwc2 # same UART. enable_uart=1 dtoverlay=miniuart-bt -# Print second-stage GPU/firmware boot diagnostics to UART (ttyAMA0) so -# failures before Linux are visible on a 3.3V serial adapter. -uart_2ndstage=1 -# Enable firmware/device-tree debug traces during boot. -dtdebug=1 # The VPU clock must be fixed when the mini-UART shares the PLL with BT. core_freq=250 diff --git a/system_rpi02w/fwup.conf b/system_rpi02w/fwup.conf index acc1858..07ff335 100644 --- a/system_rpi02w/fwup.conf +++ b/system_rpi02w/fwup.conf @@ -149,7 +149,9 @@ mbr mbr { # Primary 3: Extended container — holds 3 logical partitions. partition 3 { block-offset = ${EXTENDED_PART_OFFSET} + block-count = ${EXTENDED_PART_COUNT} type = 0x0f # Extended LBA + expand = true # grow to fill the SD card; logical APP uses the slack } # Logical 1 = /dev/mmcblk0p5 partition 4 { @@ -168,7 +170,6 @@ mbr mbr { block-offset = ${APP_PART_OFFSET} block-count = ${APP_PART_COUNT} type = 0x83 # Linux - expand = true } } @@ -182,8 +183,8 @@ uboot-environment uboot-env { # # Factory / full-reflash task. # -# Writes MBR, formats AUTOBOOT + BOOT-A, stages the boot payload -# onto AUTOBOOT + BOOT-A, writes rootfs to ROOTFS-A, invalidates +# Writes MBR, formats all three FAT partitions, stages the boot payload +# onto AUTOBOOT + BOOT-A + BOOT-B, writes rootfs to ROOTFS-A, invalidates # ROOTFS-B and APP so first boot auto-formats APP, and initialises the # full uboot-env schema to the grisp_alloy factory-default values. # @@ -208,6 +209,10 @@ task complete { fat_setlabel(${BOOT_A_PART_OFFSET}, "BOOT-A") fat_mkdir(${BOOT_A_PART_OFFSET}, "overlays") + fat_mkfs(${BOOT_B_PART_OFFSET}, ${BOOT_B_PART_COUNT}) + fat_setlabel(${BOOT_B_PART_OFFSET}, "BOOT-B") + fat_mkdir(${BOOT_B_PART_OFFSET}, "overlays") + # Clear out any old data in the rootfs + app partitions. trim(${ROOTFS_A_PART_OFFSET}, ${ROOTFS_A_PART_COUNT}) trim(${ROOTFS_B_PART_OFFSET}, ${ROOTFS_B_PART_COUNT}) @@ -305,7 +310,6 @@ task complete { # Invalidate ROOTFS-B so the old squashfs (if any) isn't accidentally # boot-matched by cmdline-b.txt before upgrade.b writes a real one. raw_memset(${ROOTFS_B_PART_OFFSET}, 256, 0xff) - raw_memset(${BOOT_B_PART_OFFSET}, 256, 0xff) # Invalidate APP so first-boot corrupt-detect formats it cleanly. raw_memset(${APP_PART_OFFSET}, 256, 0xff) @@ -341,11 +345,6 @@ task upgrade.a { uboot_setenv(uboot-env, "systema_firmware_vcs_id", "") uboot_setenv(uboot-env, "systema_firmware_author", "") - # Reset and format BOOT-A before writing new payload. - fat_mkfs(${BOOT_A_PART_OFFSET}, ${BOOT_A_PART_COUNT}) - fat_setlabel(${BOOT_A_PART_OFFSET}, "BOOT-A") - fat_mkdir(${BOOT_A_PART_OFFSET}, "overlays") - # Discard the old rootfs. trim(${ROOTFS_A_PART_OFFSET}, ${ROOTFS_A_PART_COUNT}) } @@ -410,11 +409,6 @@ task upgrade.b { uboot_setenv(uboot-env, "systemb_firmware_vcs_id", "") uboot_setenv(uboot-env, "systemb_firmware_author", "") - # Reset and format BOOT-B before writing new payload. - fat_mkfs(${BOOT_B_PART_OFFSET}, ${BOOT_B_PART_COUNT}) - fat_setlabel(${BOOT_B_PART_OFFSET}, "BOOT-B") - fat_mkdir(${BOOT_B_PART_OFFSET}, "overlays") - trim(${ROOTFS_B_PART_OFFSET}, ${ROOTFS_B_PART_COUNT}) } diff --git a/system_rpi02w/fwup_include/fwup-common.conf b/system_rpi02w/fwup_include/fwup-common.conf index b9bff87..9c430f7 100644 --- a/system_rpi02w/fwup_include/fwup-common.conf +++ b/system_rpi02w/fwup_include/fwup-common.conf @@ -1,45 +1,85 @@ # fwup common definitions for system_rpi02w. # -# Keep the SD geometry aligned with nerves_system_rpi0_2. The Raspberry Pi -# boot ROM/firmware stack is sensitive to partition placement, and these -# offsets/counts are known-good on Pi Zero 2 W. +# Included by system_rpi02w/fwup.conf to share partition offsets, counts and +# the `uboot-env` KV block between tasks. All values are in 512-byte blocks. # -# Values are 512-byte blocks. - -# --- uboot-env KV store (8 KiB, single copy) ------------------------------- - -define(UBOOT_ENV_OFFSET, 16) -define(UBOOT_ENV_COUNT, 16) - -# --- Partition layout ------------------------------------------------------- -# Primary partitions: -# p1 (entry 0): AUTOBOOT -# p2 (entry 1): BOOT-A -# p3 (entry 2): BOOT-B -# p4 (entry 3): Extended container -# Logical partitions inside extended: -# p5: ROOTFS-A -# p6: ROOTFS-B -# p7: APP (expand) +# The 7-partition MBR layout below (AUTOBOOT + BOOT-A/B FAT + extended +# container with ROOTFS-A/B + APP) intentionally matches system_rpi0w. +# ROOTFS and APP sizes are tuned to match +# system_grisp2 / system_kontron-albl-imx8mm for +# cross-target consistency. rootfs_overlay/etc/fw_env.config must stay in +# sync with UBOOT_ENV_OFFSET / UBOOT_ENV_COUNT. # -# This mirrors nerves_system_rpi0_2/fwup_include/fwup-common.conf. +# Partition layout (MBR, 7 entries: 3 primary + extended with 3 logical): +# +# block 0 MBR +# block 16 uboot-env KV store (8 KiB, no redundancy) +# block 2048 AUTOBOOT (FAT, boot flag, 16 MiB) -> /dev/mmcblk0p1 +# block 34816 BOOT-A (FAT, 32 MiB) -> /dev/mmcblk0p2 +# block 100352 BOOT-B (FAT, 32 MiB) -> /dev/mmcblk0p3 +# block 165888 Extended container (primary 3) -> /dev/mmcblk0p4 +# block 167936 ROOTFS-A (squashfs, 140 MiB) -> /dev/mmcblk0p5 +# block 456704 ROOTFS-B (squashfs, 140 MiB) -> /dev/mmcblk0p6 +# block 745472 APP (ext4, expand, 512 MiB) -> /dev/mmcblk0p7 +# +# Rationale: +# - AUTOBOOT is MBR primary 0 with boot=true so the BCM2835 VideoCore +# firmware finds bootcode.bin + autoboot.txt there first. The GPU then +# jumps to BOOT-A (primary 1 = mmcblk0p2) or BOOT-B (primary 2 = p3) +# based on autoboot.txt / tryboot. +# - ROOTFS sizes (140 MiB) match system_grisp2 so a grisp2-sized squashfs +# will fit here too. +# - APP size (512 MiB) matches both grisp2 and kontron for cross-target +# consistency; the `expand = true` flag lets fwup grow it to fill the +# SD card at factory-flash time. +# - EBR chain headers between logical partitions get a 2048-block (1 MiB) +# gap so the logical partition data stays 1 MiB aligned. + +# --- uboot-env KV store (8 KiB, single copy) -------------------------------- + +define(UBOOT_ENV_OFFSET, 16) # 0x2000 bytes = 16 * 512 +define(UBOOT_ENV_COUNT, 16) # 0x2000 bytes (8 KiB), 1 sector = 0x200 + +# --- Partition sizes (all in 512-byte blocks) ------------------------------ + +define(AUTOBOOT_PART_COUNT, 32768) # 16 MiB +define(BOOT_A_PART_COUNT, 65536) # 32 MiB +define(BOOT_B_PART_COUNT, 65536) # 32 MiB +define(ROOTFS_A_PART_COUNT, 286720) # 140 MiB (matches system_grisp2) +define(ROOTFS_B_PART_COUNT, 286720) # 140 MiB +define(APP_PART_COUNT, 1048576) # 512 MiB (expand = true) + +# Offset of the first logical partition inside the extended container leaves +# 2048 blocks (1 MiB) for the primary EBR. Between consecutive logical +# partitions we leave the same gap for chained EBRs. +define(EBR_GAP, 2048) + +# --- Partition offsets ----------------------------------------------------- + +define(AUTOBOOT_PART_OFFSET, 2048) # 1 MiB -define(BOOT_A_PART_OFFSET, 63) -define(BOOT_A_PART_COUNT, 38630) +define-eval(BOOT_A_PART_OFFSET, + "${AUTOBOOT_PART_OFFSET} + ${AUTOBOOT_PART_COUNT}") -define-eval(BOOT_B_PART_OFFSET, "${BOOT_A_PART_OFFSET} + ${BOOT_A_PART_COUNT}") -define(BOOT_B_PART_COUNT, ${BOOT_A_PART_COUNT}) +define-eval(BOOT_B_PART_OFFSET, + "${BOOT_A_PART_OFFSET} + ${BOOT_A_PART_COUNT}") -define-eval(AUTOBOOT_PART_OFFSET, "${BOOT_B_PART_OFFSET} + ${BOOT_B_PART_COUNT}") -define-eval(AUTOBOOT_PART_COUNT, 32749) +# Extended container starts right after BOOT-B. Its size is the sum of the +# three logical partitions plus their EBR gaps. (APP has expand=true so its +# effective size at factory-flash time is whatever is left on the SD card.) +define-eval(EXTENDED_PART_OFFSET, + "${BOOT_B_PART_OFFSET} + ${BOOT_B_PART_COUNT}") -define-eval(EXTENDED_PART_OFFSET, "${AUTOBOOT_PART_OFFSET} + ${AUTOBOOT_PART_COUNT}") +define-eval(EXTENDED_PART_COUNT, + "${EBR_GAP} + ${ROOTFS_A_PART_COUNT} + \ + ${EBR_GAP} + ${ROOTFS_B_PART_COUNT} + \ + ${EBR_GAP} + ${APP_PART_COUNT}") -define(ROOTFS_A_PART_OFFSET, 110080) -define(ROOTFS_A_PART_COUNT, 272666) +define-eval(ROOTFS_A_PART_OFFSET, + "${EXTENDED_PART_OFFSET} + ${EBR_GAP}") -define-eval(ROOTFS_B_PART_OFFSET, "${ROOTFS_A_PART_OFFSET} + ${ROOTFS_A_PART_COUNT}") -define(ROOTFS_B_PART_COUNT, ${ROOTFS_A_PART_COUNT}) +define-eval(ROOTFS_B_PART_OFFSET, + "${ROOTFS_A_PART_OFFSET} + ${ROOTFS_A_PART_COUNT} + ${EBR_GAP}") -define-eval(APP_PART_OFFSET, "${ROOTFS_B_PART_OFFSET} + ${ROOTFS_B_PART_COUNT}") -define(APP_PART_COUNT, 1048576) +define-eval(APP_PART_OFFSET, + "${ROOTFS_B_PART_OFFSET} + ${ROOTFS_B_PART_COUNT} + ${EBR_GAP}")