commit f9d514663d8be9e98cd9fe200c09180cbc209818 Author: pstruebi Date: Wed Mar 4 16:36:09 2026 +0100 basic working buildroot - rauc example diff --git a/Config.in b/Config.in new file mode 100644 index 0000000..32cc7e0 --- /dev/null +++ b/Config.in @@ -0,0 +1,2 @@ +# Nothing to see here (yet) +#source "$BR2_EXTERNAL_BEACON_PATH/package/blah/Config.in" diff --git a/agents.md b/agents.md new file mode 100644 index 0000000..33d2c1f --- /dev/null +++ b/agents.md @@ -0,0 +1,99 @@ +# Beacon CM4 Agent Cheat Sheet + +## Automated Flash (jumper bridged, fully scripted) + +**One command** — bridge the EMMC_DISABLE jumper, then run from repo root: +```bash +cd ~/repos/buildroot-beacon +./beacon-buildroot/scripts/flash-cm4.sh +``` +The script: +1. Builds `usbboot/rpiboot` from source if not already compiled +2. Runs `rpiboot -d mass-storage-gadget64` to expose eMMC over USB +3. Auto-detects the CM4 USB block device (~7.3 GiB) +4. Unmounts any auto-mounted partitions +5. Flashes `output/images/sdcard.img.xz` via `bmaptool` (sparse, fast) +6. Prints "Flash complete — remove jumper and power-cycle" + +Override device explicitly if auto-detect picks wrong disk: +```bash +./beacon-buildroot/scripts/flash-cm4.sh /dev/sda +``` + +## UART Console (interactive) + +UART probe on GPIO14 (TX) / GPIO15 (RX), 115200 baud: +```bash +picocom -b 115200 /dev/ttyUSB1 +# or with log capture: +picocom -b 115200 --logfile /tmp/uart-$(date +%s).log /dev/ttyUSB1 +``` +Exit: `Ctrl-A Ctrl-X` + +## UART Log Capture (non-interactive, agent-readable) + +Use `socat` — truly headless, no terminal required, clean line endings: +```bash +# Start BEFORE power-cycling the CM4: +socat -u /dev/ttyUSB1,b115200,rawer,crnl OPEN:/tmp/uart-boot.log,creat,trunc & +# Stop capture after boot is done: +kill %1 +# Read the log: +cat /tmp/uart-boot.log +``` +**Notes**: +- `picocom` backgrounded with `&` gets stopped by job control (SIGTTOU) — do not use it headlessly +- `cat /dev/ttyUSB1` with `stty raw` produces garbled output — do not use it + +## SSH Access + +Login: `user` / `beacon` (root login disabled — use `sudo su -`) +```bash +# Find CM4 IP (DHCP, changes on reboot): +ip neigh show dev enp0s31f6 | grep e4:5f:01:e9:13:96 +# SSH: +sshpass -p beacon ssh user@ +``` +**Note**: Dropbear has no sftp-server — `scp` does NOT work. Transfer files via stdin pipe: +```bash +sshpass -p beacon ssh user@ 'sudo tee /upload/rootfs.raucb > /dev/null' < output/images/rootfs.raucb +``` + +## OTA Update (fully scripted from host) + +```bash +CM4=10.11.0.xx # find via: ip neigh show dev enp0s31f6 + +# 1. Transfer bundle (~51 MB, ~5s on LAN): +sshpass -p beacon ssh user@$CM4 'sudo tee /upload/rootfs.raucb > /dev/null' \ + < output/images/rootfs.raucb + +# 2. Install: +sshpass -p beacon ssh user@$CM4 'rauc install /upload/rootfs.raucb' + +# 3. Reboot into slot B: +sshpass -p beacon ssh user@$CM4 'sudo reboot' + +# 4. After reboot (new IP — find again with ip neigh): +sshpass -p beacon ssh user@$CM4_NEW 'rauc status mark-good && rauc status' +``` +**Note**: ports 8080 and 9090 are taken by other host services — do NOT use HTTP for OTA. + +## RAUC Status + +```bash +sshpass -p beacon ssh user@ 'rauc status' +sudo fw_printenv | grep BOOT_ +``` + +## Rescue Mode + +Short GPIO4 (pin 7) to GND (pin 9) on 40-pin header during power-on. + +## Secure Boot Provision (Milestone 2) + +```bash +update-pieeprom.sh -k private.pem +rpiboot -d secure-boot-recovery +``` +**rpi secure boot private key**: use `/buildroot-beacon/private.pem` — do NOT generate a fresh one. RAUC has its own key; keep both alongside each other. \ No newline at end of file diff --git a/board/beacon-cm4/busybox.fragment b/board/beacon-cm4/busybox.fragment new file mode 100644 index 0000000..daf2b9b --- /dev/null +++ b/board/beacon-cm4/busybox.fragment @@ -0,0 +1,15 @@ +CONFIG_BLKDISCARD=Y +# CONFIG_WATCHDOG is not set +# CONFIG_MOUNT is not set +# CONFIG_KLOGD is not set +# CONFIG_FEATURE_KLOGD_KLOGCTL is not set +# CONFIG_SYSLOGD is not set +# CONFIG_FEATURE_ROTATE_LOGFILE is not set +# CONFIG_FEATURE_REMOTE_LOG is not set +# CONFIG_FEATURE_SYSLOGD_DUP is not set +# CONFIG_FEATURE_SYSLOGD_CFG is not set +# CONFIG_FEATURE_SYSLOGD_PRECISE_TIMESTAMPS is not set +CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 +# CONFIG_FEATURE_IPC_SYSLOG is not set +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 +# CONFIG_FEATURE_KMSG_SYSLOG is not set diff --git a/board/beacon-cm4/cmdline.txt b/board/beacon-cm4/cmdline.txt new file mode 100644 index 0000000..d135414 --- /dev/null +++ b/board/beacon-cm4/cmdline.txt @@ -0,0 +1 @@ +root=/dev/mmcblk0p2 rootwait console=tty1 console=ttyAMA0,115200 fw_dtb net.ifnames=0 diff --git a/board/beacon-cm4/config_cm4.txt b/board/beacon-cm4/config_cm4.txt new file mode 100644 index 0000000..64e5169 --- /dev/null +++ b/board/beacon-cm4/config_cm4.txt @@ -0,0 +1,105 @@ +# For more options and information see +# http://rpf.io/configtxt +# Some settings may impact device functionality. See link above for details + +# uncomment if you get no picture on HDMI for a default "safe" mode +#hdmi_safe=1 + +# uncomment the following to adjust overscan. Use positive numbers if console +# goes off screen, and negative if there is too much border +#overscan_left=16 +#overscan_right=16 +#overscan_top=16 +#overscan_bottom=16 + +# uncomment to force a console size. By default it will be display's size minus +# overscan. +#framebuffer_width=1280 +#framebuffer_height=720 + +# uncomment if hdmi display is not detected and composite is being output +#hdmi_force_hotplug=1 + +# uncomment to force a specific HDMI mode (this will force VGA) +#hdmi_group=1 +#hdmi_mode=1 + +# uncomment to force a HDMI mode rather than DVI. This can make audio work in +# DMT (computer monitor) modes +#hdmi_drive=2 + +# uncomment to increase signal to HDMI, if you have interference, blanking, or +# no display +#config_hdmi_boost=4 + +# uncomment for composite PAL +#sdtv_mode=2 + +#uncomment to overclock the arm. 700 MHz is the default. +#arm_freq=800 + +# Uncomment some or all of these to enable the optional hardware interfaces +#dtparam=i2c_arm=on +#dtparam=i2s=on +#dtparam=spi=on + +# Uncomment this to enable infrared communication. +#dtoverlay=gpio-ir,gpio_pin=17 +#dtoverlay=gpio-ir-tx,gpio_pin=18 + +# Additional overlays and parameters are documented /boot/overlays/README + +# Enable audio (loads snd_bcm2835) +dtparam=audio=on + +# Automatically load overlays for detected cameras +camera_auto_detect=1 + +# Automatically load overlays for detected DSI displays +display_auto_detect=1 + +# Enable DRM VC4 V3D driver +dtoverlay=vc4-kms-v3d +max_framebuffers=2 + +# Disable compensation for displays with overscan +disable_overscan=1 + +[cm4] +# Enable host mode on the 2711 built-in XHCI USB controller. +# This line should be removed if the legacy DWC2 controller is required +# (e.g. for USB device mode) or if USB support is not required. +otg_mode=1 + +[all] + +[pi4] +# Run as fast as firmware / board allows +arm_boost=1 + +[all] + +# End of the default Raspberry Pi config.txt file from: +# https://github.com/RPi-Distro/pi-gen/blob/master/stage1/00-boot-files/files/config.txt + +# Load U-Boot instead of Linux +kernel=u-boot.bin + +# Enable 64-bit support +arm_64bit=1 + +# fixes rpi (3B, 3B+, 3A+, 4B and Zero W) ttyAMA0 serial console +dtoverlay=miniuart-bt + +# Enable watchdog, system will reset if U-Boot and Linux do not boot within 16 seconds +# Requires fairly recent RPi Firmware: +# https://github.com/raspberrypi/firmware/issues/1651 +# Comment this line if you expect to be able to use the U-Boot command prompt! +dtparam=watchdog + +# GPIO 4 has a pull-up enabled at reset, but let's set it explicitly just to be sure +gpio=4=ip,pu + +# Enable early debugging info +uart_2ndstage=1 + diff --git a/board/beacon-cm4/dts/bcm2711-rpi-cm4.dts b/board/beacon-cm4/dts/bcm2711-rpi-cm4.dts new file mode 100644 index 0000000..0474db2 --- /dev/null +++ b/board/beacon-cm4/dts/bcm2711-rpi-cm4.dts @@ -0,0 +1,4 @@ +#include "../../../arm/boot/dts/bcm2711-rpi-cm4.dts" + +#include "custom-cm4.dtsi" + diff --git a/board/beacon-cm4/dts/custom-cm4.dtsi b/board/beacon-cm4/dts/custom-cm4.dtsi new file mode 100644 index 0000000..f8ffdb4 --- /dev/null +++ b/board/beacon-cm4/dts/custom-cm4.dtsi @@ -0,0 +1,65 @@ +/**********************************************************************/ +/* WARNING: */ +/* This file and the resulting dtb installed to the rootfs will be */ +/* IGNORED unless you edit config.txt on the boot partition and */ +/* remove the fw_dtb argument from cmdline.txt! */ +/**********************************************************************/ + +/* miniuart-bt-overlay to fix serial console on CM4 */ + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; + status = "okay"; +}; + +&bt { + status = "disabled"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins &bt_pins &fake_bt_cts>; + status = "okay"; +}; + +&uart0_pins { + brcm,pins; + brcm,function; + brcm,pull; +}; + +&uart1_pins { + brcm,pins = <32 33>; + brcm,function = <2>; /* alt5=UART1 */ + brcm,pull = <0 2>; +}; + +&gpio { + fake_bt_cts: fake_bt_cts { + brcm,pins = <31>; + brcm,function = <1>; /* output */ + }; +}; + +/ { + aliases { + serial0 = "/soc/serial@7e201000"; + serial1 = "/soc/serial@7e215040"; + }; + + __overrides__ { + krnbt = <&minibt>,"status"; + }; +}; + +/* otg_mode=1 */ + +&usb { + status = "disabled"; +}; + +&xhci { + status = "okay"; +}; + diff --git a/board/beacon-cm4/genbootfs.cfg b/board/beacon-cm4/genbootfs.cfg new file mode 100644 index 0000000..fcd659f --- /dev/null +++ b/board/beacon-cm4/genbootfs.cfg @@ -0,0 +1,17 @@ +image boot.vfat { + vfat { + files = { + "bcm2711-rpi-cm4.dtb", + "custom/cmdline.txt", + "rpi-firmware/config.txt", + "rpi-firmware/fixup4.dat", + "rpi-firmware/start4.elf", + "rpi-firmware/overlays", + "u-boot.bin", + "boot.scr" + } + } + + size = 256M +} + diff --git a/board/beacon-cm4/genimage.cfg b/board/beacon-cm4/genimage.cfg new file mode 100644 index 0000000..6fb5718 --- /dev/null +++ b/board/beacon-cm4/genimage.cfg @@ -0,0 +1,85 @@ +image data.ext4 { + name = "Data" + mountpoint = /data + ext4 { + use-mke2fs = true + label = "Data" + features = "^64bit" + } + size = 128M +} + +image upload.ext4 { + name = "Upload" + empty = true + ext4 { + use-mke2fs = true + label = "Upload" + features = "^64bit" + } + size = 900M +} + +image sdcard.img { + hdimage { + partition-table-type = mbr + extended-partition = 4 + } + + partition ubootenv0 { + image = "uboot-env.bin" + in-partition-table = false + offset = 1M + } + + partition ubootenv1 { + image = "uboot-env.bin" + in-partition-table = false + offset = 2M + } + + partition boot0 { + partition-type = 0xC + bootable = true + image = "boot.vfat" + # Leave room for U-Boot environment + offset = 4M + } + + partition boot1 { + image = "boot.vfat" + in-partition-table = false + # 256M + 4M + offset = 260M + } + + partition rescue { + partition-type = 0x83 + image = "rootfs.squashfs" + size = 256M + } + + partition data { + partition-type = 0x83 + image = "data.ext4" + size = 128M + } + + partition rootfs0 { + partition-type = 0x83 + image = "rootfs.ext4" + size = 900M + } + + partition rootfs1 { + partition-type = 0x83 + image = "rootfs.ext4" + size = 900M + } + + partition upload { + partition-type = 0x83 + image = "upload.ext4" + size = 900M + } +} diff --git a/board/beacon-cm4/linux.fragment b/board/beacon-cm4/linux.fragment new file mode 100644 index 0000000..fd03e0b --- /dev/null +++ b/board/beacon-cm4/linux.fragment @@ -0,0 +1,9 @@ +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_DM_VERITY=y +CONFIG_SQUASHFS=y +CONFIG_CRYPTO_SHA256=y +CONFIG_DM_CRYPT=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_XTS=y diff --git a/board/beacon-cm4/post-build.sh b/board/beacon-cm4/post-build.sh new file mode 100755 index 0000000..c27b27e --- /dev/null +++ b/board/beacon-cm4/post-build.sh @@ -0,0 +1,65 @@ +#!/bin/sh + +set -u +set -e + +RAUC_COMPATIBLE="${2:-beacon-cm4}" +BOARD_DIR="$(dirname $0)" +BOARD_NAME="$(basename ${BOARD_DIR})" +# Pass VERSION as an environment variable (eg: export from a top-level Makefile) +# If VERSION is unset, fallback to the Buildroot version +RAUC_VERSION=${VERSION:-${BR2_VERSION_FULL}} + +# Add a console on tty1 +if [ -e ${TARGET_DIR}/etc/inittab ]; then + grep -qE '^tty1::' ${TARGET_DIR}/etc/inittab || \ + sed -i '/GENERIC_SERIAL/a\ +tty1::respawn:/sbin/getty -L tty1 0 vt100 # HDMI console' ${TARGET_DIR}/etc/inittab +# systemd doesn't use /etc/inittab, enable getty.tty1.service instead +elif [ -d ${TARGET_DIR}/etc/systemd ]; then + mkdir -p "${TARGET_DIR}/etc/systemd/system/getty.target.wants" + ln -sf /lib/systemd/system/getty@.service \ + "${TARGET_DIR}/etc/systemd/system/getty.target.wants/getty@tty1.service" +fi + + +# Mount persistent data partitions +if [ -e ${TARGET_DIR}/etc/fstab ]; then + # For configuration data + # WARNING: data=journal is safest, but potentially slow! + grep -qE 'LABEL=Data' ${TARGET_DIR}/etc/fstab || \ + echo "LABEL=Data /data ext4 defaults,data=journal,noatime 0 0" >> ${TARGET_DIR}/etc/fstab + + # For bulk data (eg: firmware updates) + grep -qE 'LABEL=Upload' ${TARGET_DIR}/etc/fstab || \ + echo "LABEL=Upload /upload ext4 defaults,noatime 0 0" >> ${TARGET_DIR}/etc/fstab +fi + +# Copy custom cmdline.txt file +install -D -m 0644 ${BR2_EXTERNAL_BEACON_PATH}/board/beacon-cm4/cmdline.txt ${BINARIES_DIR}/custom/cmdline.txt + +# Copy RAUC certificate +if [ -e ${BR2_EXTERNAL_BEACON_PATH}/openssl-ca/dev/ca.cert.pem ]; then + install -D -m 0644 ${BR2_EXTERNAL_BEACON_PATH}/openssl-ca/dev/ca.cert.pem ${TARGET_DIR}/etc/rauc/keyring.pem +else + echo "RAUC CA certificate not found!" + echo "...did you run the openssl-ca.sh script?" + exit 1 +fi + +# Update RAUC compatible string +sed -i "/compatible/s/=.*\$/=${RAUC_COMPATIBLE}/" ${TARGET_DIR}/etc/rauc/system.conf + +# Create rauc version file +echo "${RAUC_VERSION}" > ${TARGET_DIR}/etc/rauc/version + +# Customize login prompt with login hints +cat <<- EOF >> ${TARGET_DIR}/etc/issue + + Default username:password is [user:beacon] + Root login disabled, use sudo su - + With great power comes great responsibility! + + eth0: \4{eth0} + +EOF diff --git a/board/beacon-cm4/post-image.sh b/board/beacon-cm4/post-image.sh new file mode 100755 index 0000000..1020e78 --- /dev/null +++ b/board/beacon-cm4/post-image.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +set -e + +BOARD_DIR="$(dirname $0)" +BOARD_NAME="$(basename ${BOARD_DIR})" +GENIMAGE_CFG="${BOARD_DIR}/genimage.cfg" +GENIMAGE_TMP="${BUILD_DIR}/genimage.tmp" +GENBOOTFS_CFG="${BOARD_DIR}/genbootfs.cfg" +RAUC_COMPATIBLE="${2:-beacon-cm4}" + +# Pass VERSION as an environment variable (eg: export from a top-level Makefile) +# If VERSION is unset, fallback to the Buildroot version +RAUC_VERSION=${VERSION:-${BR2_VERSION_FULL}} + +# Pass an empty rootpath. genimage makes a full copy of the given rootpath to +# ${GENIMAGE_TMP}/root so passing TARGET_DIR would be a waste of time and disk +# space. We don't rely on genimage to build the rootfs image, just to insert a +# pre-built one in the disk image. + +trap 'rm -rf "${ROOTPATH_TMP}"' EXIT +ROOTPATH_TMP="$(mktemp -d)" + +rm -rf "${GENIMAGE_TMP}" + +# Generate the boot filesystem image + +genimage \ + --rootpath "${ROOTPATH_TMP}" \ + --tmppath "${GENIMAGE_TMP}" \ + --inputpath "${BINARIES_DIR}" \ + --outputpath "${BINARIES_DIR}" \ + --config "${GENBOOTFS_CFG}" + +# Generate a RAUC update bundle for the full system (bootfs + rootfs) +[ -e ${BINARIES_DIR}/update.raucb ] && rm -rf ${BINARIES_DIR}/update.raucb +[ -e ${BINARIES_DIR}/temp-update ] && rm -rf ${BINARIES_DIR}/temp-update +mkdir -p ${BINARIES_DIR}/temp-update + +cat >> ${BINARIES_DIR}/temp-update/manifest.raucm << EOF +[update] +compatible=${RAUC_COMPATIBLE} +version=${RAUC_VERSION} +[bundle] +format=verity +[image.bootloader] +filename=boot.vfat +[image.rootfs] +filename=rootfs.ext4 +EOF + +ln -L ${BINARIES_DIR}/boot.vfat ${BINARIES_DIR}/temp-update/ +ln -L ${BINARIES_DIR}/rootfs.ext4 ${BINARIES_DIR}/temp-update/ + +${HOST_DIR}/bin/rauc bundle \ + --cert ${BR2_EXTERNAL_BEACON_PATH}/openssl-ca/dev/development-1.cert.pem \ + --key ${BR2_EXTERNAL_BEACON_PATH}/openssl-ca/dev/private/development-1.key.pem \ + --keyring ${BR2_EXTERNAL_BEACON_PATH}/openssl-ca/dev/ca.cert.pem \ + ${BINARIES_DIR}/temp-update/ \ + ${BINARIES_DIR}/update.raucb + +# Generate a RAUC update bundle for just the root filesystem +[ -e ${BINARIES_DIR}/rootfs.raucb ] && rm -rf ${BINARIES_DIR}/rootfs.raucb +[ -e ${BINARIES_DIR}/temp-rootfs ] && rm -rf ${BINARIES_DIR}/temp-rootfs +mkdir -p ${BINARIES_DIR}/temp-rootfs + +cat >> ${BINARIES_DIR}/temp-rootfs/manifest.raucm << EOF +[update] +compatible=${RAUC_COMPATIBLE} +version=${RAUC_VERSION} +[bundle] +format=verity +[image.rootfs] +filename=rootfs.ext4 +EOF + +ln -L ${BINARIES_DIR}/rootfs.ext4 ${BINARIES_DIR}/temp-rootfs/ + +${HOST_DIR}/bin/rauc bundle \ + --cert ${BR2_EXTERNAL_BEACON_PATH}/openssl-ca/dev/development-1.cert.pem \ + --key ${BR2_EXTERNAL_BEACON_PATH}/openssl-ca/dev/private/development-1.key.pem \ + --keyring ${BR2_EXTERNAL_BEACON_PATH}/openssl-ca/dev/ca.cert.pem \ + ${BINARIES_DIR}/temp-rootfs/ \ + ${BINARIES_DIR}/rootfs.raucb + +# Parse update.raucb and generate initial rauc.status file +# FIXME: There is probably a MUCH better way to do this, +# suggestions welcome! +eval $(rauc --keyring ${BR2_EXTERNAL_BEACON_PATH}/openssl-ca/dev/ca.cert.pem --output-format=shell info ${BINARIES_DIR}/update.raucb) + +cat > ${BINARIES_DIR}/rauc.status << EOF +[slot.rescue.0] +bundle.compatible=${RAUC_MF_COMPATIBLE} +bundle.version=${RAUC_MF_VERSION} +status=ok + +[slot.${RAUC_IMAGE_CLASS_0}.0] +bundle.compatible=${RAUC_MF_COMPATIBLE} +bundle.version=${RAUC_MF_VERSION} +status=ok +sha256=${RAUC_IMAGE_DIGEST_0} +size=${RAUC_IMAGE_SIZE_0} + +[slot.${RAUC_IMAGE_CLASS_1}.0] +bundle.compatible=${RAUC_MF_COMPATIBLE} +bundle.version=${RAUC_MF_VERSION} +status=ok +sha256=${RAUC_IMAGE_DIGEST_1} +size=${RAUC_IMAGE_SIZE_1} + +[slot.${RAUC_IMAGE_CLASS_1}.1] +bundle.compatible=${RAUC_MF_COMPATIBLE} +bundle.version=${RAUC_MF_VERSION} +status=ok +sha256=${RAUC_IMAGE_DIGEST_1} +size=${RAUC_IMAGE_SIZE_1} +EOF + +# Install rauc.status to genimage rootpath +install -D -m 0644 ${BINARIES_DIR}/rauc.status ${ROOTPATH_TMP}/data/rauc.status + + +# Generate the sdcard image + +rm -rf "${GENIMAGE_TMP}" + +genimage \ + --rootpath "${ROOTPATH_TMP}" \ + --tmppath "${GENIMAGE_TMP}" \ + --inputpath "${BINARIES_DIR}" \ + --outputpath "${BINARIES_DIR}" \ + --config "${GENIMAGE_CFG}" + +# Create a bmap file for the sdcard image +bmaptool create "${BINARIES_DIR}/sdcard.img" -o "${BINARIES_DIR}/sdcard.img.bmap" + +# Compress the sdcard image +[ -e "${BINARIES_DIR}/sdcard.img.xz" ] && rm "${BINARIES_DIR}/sdcard.img.xz" +xz -v -T 0 "${BINARIES_DIR}/sdcard.img" + diff --git a/board/beacon-cm4/rootfs-overlay/boot/uEnv.txt b/board/beacon-cm4/rootfs-overlay/boot/uEnv.txt new file mode 100644 index 0000000..bd2a81a --- /dev/null +++ b/board/beacon-cm4/rootfs-overlay/boot/uEnv.txt @@ -0,0 +1,3 @@ +bootargs_force= +bootargs_extra= + diff --git a/board/beacon-cm4/rootfs-overlay/data/.keep b/board/beacon-cm4/rootfs-overlay/data/.keep new file mode 100644 index 0000000..e69de29 diff --git a/board/beacon-cm4/rootfs-overlay/etc/fw_env.config b/board/beacon-cm4/rootfs-overlay/etc/fw_env.config new file mode 100644 index 0000000..24369e7 --- /dev/null +++ b/board/beacon-cm4/rootfs-overlay/etc/fw_env.config @@ -0,0 +1,2 @@ +/dev/mmcblk0 0x100000 0x8000 +/dev/mmcblk0 0x200000 0x8000 diff --git a/board/beacon-cm4/rootfs-overlay/etc/rauc/system.conf b/board/beacon-cm4/rootfs-overlay/etc/rauc/system.conf new file mode 100644 index 0000000..ea1625a --- /dev/null +++ b/board/beacon-cm4/rootfs-overlay/etc/rauc/system.conf @@ -0,0 +1,31 @@ +[system] +compatible=beacon-cm4 +mountprefix=/run/rauc +statusfile=/data/rauc.status +bootloader=uboot +bundle-formats=-plain + +[keyring] +path=/etc/rauc/keyring.pem +use-bundle-signing-time=true + +[slot.bootloader.0] +device=/dev/mmcblk0 +type=boot-mbr-switch +region-start=4M +region-size=512M + +[slot.rescue.0] +device=/dev/mmcblk0p2 +type=raw + +[slot.rootfs.0] +device=/dev/mmcblk0p5 +type=ext4 +bootname=A + +[slot.rootfs.1] +device=/dev/mmcblk0p6 +type=ext4 +bootname=B + diff --git a/board/beacon-cm4/rootfs-overlay/etc/sudoers.d/user b/board/beacon-cm4/rootfs-overlay/etc/sudoers.d/user new file mode 100644 index 0000000..d9f5d70 --- /dev/null +++ b/board/beacon-cm4/rootfs-overlay/etc/sudoers.d/user @@ -0,0 +1 @@ +user ALL=(ALL) NOPASSWD: ALL diff --git a/board/beacon-cm4/rootfs-overlay/etc/systemd/system.conf.d/watchdog.conf b/board/beacon-cm4/rootfs-overlay/etc/systemd/system.conf.d/watchdog.conf new file mode 100644 index 0000000..6da1615 --- /dev/null +++ b/board/beacon-cm4/rootfs-overlay/etc/systemd/system.conf.d/watchdog.conf @@ -0,0 +1,6 @@ +[Manager] +RuntimeWatchdogSec=10 +#RebootWatchdogSec=10min +#KExecWatchdogSec=off +#WatchdogDevice= + diff --git a/board/beacon-cm4/rootfs-overlay/upload/.keep b/board/beacon-cm4/rootfs-overlay/upload/.keep new file mode 100644 index 0000000..e69de29 diff --git a/board/beacon-cm4/u-boot.fragment b/board/beacon-cm4/u-boot.fragment new file mode 100644 index 0000000..d81a1e7 --- /dev/null +++ b/board/beacon-cm4/u-boot.fragment @@ -0,0 +1,8 @@ +CONFIG_ENV_OFFSET=0x100000 +CONFIG_ENV_OFFSET_REDUND=0x200000 +CONFIG_ENV_SIZE=0x8000 +# CONFIG_ENV_IS_IN_FAT is not set +CONFIG_ENV_IS_IN_MMC=y +CONFIG_SYS_REDUNDAND_ENVIRONMENT=y +CONFIG_CMD_SQUASHFS=y +CONFIG_USB_XHCI_BRCM=y diff --git a/board/beacon-cm4/u-boot_beacon.ush b/board/beacon-cm4/u-boot_beacon.ush new file mode 100644 index 0000000..4cf4afe --- /dev/null +++ b/board/beacon-cm4/u-boot_beacon.ush @@ -0,0 +1,114 @@ +test -n "${BOOT_ORDER}" || setenv BOOT_ORDER "A B" +test -n "${BOOT_A_LEFT}" || setenv BOOT_A_LEFT 3 +test -n "${BOOT_B_LEFT}" || setenv BOOT_B_LEFT 3 +test -n "${bootargs_default}" || setenv bootargs_default coherent_pool=1M vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000 rootwait console=tty1 console=ttyAMA0,115200 +test -n "${DTB_FILE}" || setenv DTB_FILE bcm2711-rpi-cm4.dtb + +# RPi firmware uses a dynamic fdt_addr, but U-Boot does not use the fw +# provided address if fdt_addr is already defined in the environment! +# Copy fdt_addr to a local variable and delete the environment variable +# so it never gets accidentally saved: +fdt_addr=${fdt_addr} +env delete fdt_addr + +# To boot from the rescue partition, tie GPIO4 (pin 7) to GND (pin 9) +# The gpio input command will return an exit status of 0 (true) +# If the pin is high (pulled up by default) the exit status is 1 (false) +if gpio input gpio4 ; then + # GPIO4 is shorted to ground so boot in rescue mode + echo "Booting from rescue partition" + setenv load_uenv "load mmc 0:2 ${kernel_addr_r} /boot/uEnv.txt" + setenv load_fdt "load mmc 0:2 ${fdt_addr_r} /boot/${DTB_FILE}" + setenv load_kernel "load mmc 0:2 ${kernel_addr_r} /boot/Image" + raucargs="root=/dev/mmcblk0p2" + rescue=true +else + raucargs=unset + for BOOT_SLOT in "${BOOT_ORDER}"; do + if test "x${raucargs}" != "xunset"; then + # skip remaining slots + elif test "x${BOOT_SLOT}" = "xA"; then + if test ${BOOT_A_LEFT} -gt 0; then + echo "Found valid slot A, ${BOOT_A_LEFT} attempts remaining" + setexpr BOOT_A_LEFT ${BOOT_A_LEFT} - 1 + setenv load_uenv "load mmc 0:5 ${kernel_addr_r} /boot/uEnv.txt" + setenv load_fdt "load mmc 0:5 ${fdt_addr_r} /boot/${DTB_FILE}" + setenv load_kernel "load mmc 0:5 ${kernel_addr_r} /boot/Image" + raucargs="root=/dev/mmcblk0p5 rauc.slot=A" + fi + elif test "x${BOOT_SLOT}" = "xB"; then + if test ${BOOT_B_LEFT} -gt 0; then + echo "Found valid slot B, ${BOOT_B_LEFT} attempts remaining" + setexpr BOOT_B_LEFT ${BOOT_B_LEFT} - 1 + setenv load_uenv "load mmc 0:6 ${kernel_addr_r} /boot/uEnv.txt" + setenv load_fdt "load mmc 0:6 ${fdt_addr_r} /boot/${DTB_FILE}" + setenv load_kernel "load mmc 0:6 ${kernel_addr_r} /boot/Image" + raucargs="root=/dev/mmcblk0p6 rauc.slot=B" + fi + fi + done +fi + +if test "x${raucargs}" = "xunset"; then + echo "No valid slot found, resetting tries to 3" + setenv BOOT_A_LEFT 3 + setenv BOOT_B_LEFT 3 + saveenv + reset +fi + +# Examine the fdt loaded by the firmware +# Pass fw_dtb to use the dtb loaded by the firmware +fdt_live=unset +fdt addr ${fdt_addr} +fdt get value bootargs_fw /chosen bootargs +for arg in ${bootargs_fw} ; do + if test "x${arg}" = "xfw_dtb" ; then + fdt_live=${fdt_addr} + fi +done + +# Save bootargs_fw in a local variable for later use +bootargs_fw=${bootargs_fw} +env del bootargs_fw + +if test "x${rescue}" = "xtrue" -o "x${fdt_live}" = "xunset"; then + # Using device-tree from rootfs + # Check to see if we have any customizations in a uEnv.txt file + env del bootargs_force bootargs_extra + echo "Checking for /boot/uEnv.txt" + if run load_uenv ; then + echo "Importing uEnv.txt" + env import -t -r ${fileaddr} ${filesize} + fi + + # Load our actual device-tree file + echo "Loading device-tree" + run load_fdt + + # Point to run-time device-tree + fdt_live=${fdt_addr_r} + + # Setup kernel parameters + if test -n "${bootargs_force}" ; then + setenv bootargs "${bootargs_force} ${raucargs}" + else + setenv bootargs "${bootargs_default} ${bootargs_extra} ${raucargs}" + fi +else + # Using FW provided device-tree + # Append rauc boot arguments to FW generated command line + # This setting will override /chosen/bootargs in the device-tree + echo "Using firmware device-tree" + setenv bootargs "${bootargs_fw} ${raucargs}" +fi + +# Store updated boot state... +# ...above code should have modified BOOT_(AB)_LEFT and bootargs +saveenv + +echo "Loading kernel" +run load_kernel + +echo "Starting kernel" +booti ${kernel_addr_r} - ${fdt_live} diff --git a/board/beacon-cm4/users b/board/beacon-cm4/users new file mode 100644 index 0000000..7874c18 --- /dev/null +++ b/board/beacon-cm4/users @@ -0,0 +1,2 @@ +user 1000 user 1000 $6$XUtVBGdpmufH8R2H$olowG.5WTG7pEth5D..PyeKEmAze3SM9.I6Raf9k.OfS0OiS0wxbdOBJH.BgklLEKWH6REmXRUGyDylyWfDmg/ /home/user /bin/sh adm,audio,cdrom,dialout,floppy,plugdev,staff,sudo,video Default user + diff --git a/configs/beacon_cm4_rauc_defconfig b/configs/beacon_cm4_rauc_defconfig new file mode 100644 index 0000000..a2a8560 --- /dev/null +++ b/configs/beacon_cm4_rauc_defconfig @@ -0,0 +1,62 @@ +BR2_aarch64=y +BR2_cortex_a72=y +BR2_GLOBAL_PATCH_DIR="$(BR2_EXTERNAL_BEACON_PATH)/patches" +BR2_TOOLCHAIN_BUILDROOT_GLIBC=y +BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_6_6=y +BR2_TOOLCHAIN_BUILDROOT_CXX=y +BR2_GCC_ENABLE_LTO=y +BR2_TARGET_GENERIC_HOSTNAME="beacon" +BR2_TARGET_GENERIC_ISSUE="Welcome to Beacon Buildroot+RAUC" +BR2_INIT_SYSTEMD=y +# BR2_TARGET_ENABLE_ROOT_LOGIN is not set +# BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW is not set +BR2_SYSTEM_DHCP="eth0" +BR2_SYSTEM_DEFAULT_PATH="/bin:/sbin:/usr/bin:/usr/sbin" +BR2_ROOTFS_USERS_TABLES="$(BR2_EXTERNAL_BEACON_PATH)/board/beacon-cm4/users" +BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_BEACON_PATH)/board/beacon-cm4/rootfs-overlay" +BR2_ROOTFS_POST_BUILD_SCRIPT="$(BR2_EXTERNAL_BEACON_PATH)/board/beacon-cm4/post-build.sh" +BR2_ROOTFS_POST_IMAGE_SCRIPT="$(BR2_EXTERNAL_BEACON_PATH)/board/beacon-cm4/post-image.sh" +BR2_LINUX_KERNEL=y +BR2_LINUX_KERNEL_CUSTOM_GIT=y +BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/raspberrypi/linux" +BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="rpi-6.6.y" +BR2_LINUX_KERNEL_DEFCONFIG="bcm2711" +BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="$(BR2_EXTERNAL_BEACON_PATH)/board/beacon-cm4/linux.fragment" +BR2_LINUX_KERNEL_DTS_SUPPORT=y +BR2_LINUX_KERNEL_INTREE_DTS_NAME="broadcom/bcm2711-rpi-cm4" +BR2_LINUX_KERNEL_INSTALL_TARGET=y +BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y +BR2_PACKAGE_BUSYBOX_CONFIG_FRAGMENT_FILES="$(BR2_EXTERNAL_BEACON_PATH)/board/beacon-cm4/busybox.fragment" +BR2_PACKAGE_RPI_FIRMWARE=y +BR2_PACKAGE_RPI_FIRMWARE_VARIANT_PI4=y +BR2_PACKAGE_RPI_FIRMWARE_VARIANT_PI4_X=y +BR2_PACKAGE_RPI_FIRMWARE_CONFIG_FILE="$(BR2_EXTERNAL_BEACON_PATH)/board/beacon-cm4/config_cm4.txt" +BR2_PACKAGE_DTC=y +BR2_PACKAGE_DTC_PROGRAMS=y +BR2_PACKAGE_SUDO=y +BR2_PACKAGE_RAUC=y +BR2_PACKAGE_RAUC_DBUS=y +BR2_PACKAGE_RAUC_NETWORK=y +BR2_PACKAGE_RAUC_JSON=y +BR2_PACKAGE_DROPBEAR=y +BR2_PACKAGE_CRYPTSETUP=y +BR2_PACKAGE_UTIL_LINUX_WDCTL=y +BR2_TARGET_ROOTFS_EXT2=y +BR2_TARGET_ROOTFS_EXT2_4=y +BR2_TARGET_ROOTFS_EXT2_SIZE="250M" +BR2_TARGET_ROOTFS_SQUASHFS=y +# BR2_TARGET_ROOTFS_TAR is not set +BR2_TARGET_UBOOT=y +BR2_TARGET_UBOOT_BOARD_DEFCONFIG="rpi_arm64" +BR2_TARGET_UBOOT_CONFIG_FRAGMENT_FILES="$(BR2_EXTERNAL_BEACON_PATH)/board/beacon-cm4/u-boot.fragment" +BR2_PACKAGE_HOST_DOSFSTOOLS=y +BR2_PACKAGE_HOST_ENVIRONMENT_SETUP=y +BR2_PACKAGE_HOST_GENIMAGE=y +BR2_PACKAGE_HOST_MTOOLS=y +BR2_PACKAGE_HOST_RAUC=y +BR2_PACKAGE_HOST_UBOOT_TOOLS=y +BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE=y +BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SIZE="0x8000" +BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_REDUNDANT=y +BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT=y +BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT_SOURCE="$(BR2_EXTERNAL_BEACON_PATH)/board/beacon-cm4/u-boot_beacon.ush" diff --git a/external.desc b/external.desc new file mode 100644 index 0000000..ca40553 --- /dev/null +++ b/external.desc @@ -0,0 +1,2 @@ +name: BEACON +desc: Beacon Buildroot + RAUC for RPi CM4 diff --git a/external.mk b/external.mk new file mode 100644 index 0000000..e592d34 --- /dev/null +++ b/external.mk @@ -0,0 +1 @@ +include $(sort $(wildcard $(BR2_EXTERNAL_BEACON_PATH)/package/*/*.mk)) diff --git a/openssl-ca.sh b/openssl-ca.sh new file mode 100755 index 0000000..6ffd21f --- /dev/null +++ b/openssl-ca.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +set -xe + +ORG="${1:-Test Org}" +CA="${2:-rauc CA}" + +# After the CRL expires, signatures cannot be verified anymore +CRL="-crldays 5000" + +BASE="$(pwd)/openssl-ca" + +if [ -e $BASE ]; then + echo "$BASE already exists" + exit 1 +fi + +mkdir -p $BASE/dev/{private,certs} +touch $BASE/dev/index.txt +echo 01 > $BASE/dev/serial + +cat > $BASE/openssl.cnf < Building rpiboot from source..." + make -C "$USBBOOT_DIR" +fi + +# Step 1: Expose CM4 eMMC as USB mass storage +echo "==> Running rpiboot to expose CM4 eMMC (EMMC_DISABLE jumper must be bridged)..." +sudo "$USBBOOT_DIR/rpiboot" -d "$USBBOOT_DIR/mass-storage-gadget64" +echo "==> rpiboot done, waiting for block device..." + +# Step 2: Find the device (explicit arg or auto-detect USB disk ~8 GiB) +if [ -n "${1:-}" ]; then + DEVICE="$1" + echo "==> Using specified device: $DEVICE" +else + DEVICE="" + for i in $(seq 1 30); do + sleep 1 + # Detect USB block device of 7-8 GiB (CM4 eMMC) + DEVICE=$(lsblk -dno NAME,TRAN,SIZE \ + | awk '$2=="usb" && ($3~/^7\.[0-9]+G$/ || $3~/^8\.[0-9]+G$/) {print "/dev/"$1}' \ + | head -1) + [ -n "$DEVICE" ] && break + printf " waiting... (%ds)\r" "$i" + done + if [ -z "$DEVICE" ]; then + echo "ERROR: CM4 eMMC did not appear as a USB block device within 30s." + echo " Run 'lsblk' manually and re-run with explicit device: $0 /dev/sdX" + exit 1 + fi + echo "==> Auto-detected CM4 eMMC at $DEVICE" +fi + +# Step 3: Safety check - refuse to flash the host nvme/sata disk +if echo "$DEVICE" | grep -qE '^/dev/(nvme|sd[a-z]{2,}|sda$)'; then + lsblk -dno TRAN "$DEVICE" | grep -qx usb || { + echo "ERROR: $DEVICE does not appear to be a USB device. Aborting." + exit 1 + } +fi + +# Step 4: Unmount any auto-mounted partitions +echo "==> Unmounting $DEVICE partitions..." +sudo umount "${DEVICE}"?* 2>/dev/null || true +sudo umount "${DEVICE}"[0-9]* 2>/dev/null || true + +# Step 5: Flash via bmaptool +echo "==> Flashing $IMAGE -> $DEVICE ..." +sudo bmaptool copy "$IMAGE" "$DEVICE" +sudo sync + +echo "" +echo "==> Flash complete!" +echo " Remove the EMMC_DISABLE jumper, then power-cycle the CM4."