refine agents and readme
This commit is contained in:
48
AGENTS.md
Normal file
48
AGENTS.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Beacon CM4 — Quick Reference
|
||||||
|
|
||||||
|
> Full docs: `beacon-buildroot/README.md`
|
||||||
|
|
||||||
|
## Build
|
||||||
|
```bash
|
||||||
|
cd ~/repos/buildroot-beacon
|
||||||
|
make -C rpi-buildroot-fork O=$(pwd)/output BR2_EXTERNAL=$(pwd)/beacon-buildroot -j$(nproc)
|
||||||
|
# outputs: output/images/rootfs.raucb update.raucb sdcard.img.xz
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flash (initial, EMMC_DISABLE jumper bridged)
|
||||||
|
```bash
|
||||||
|
./beacon-buildroot/scripts/flash-cm4.sh # auto-detect
|
||||||
|
./beacon-buildroot/scripts/flash-cm4.sh /dev/sda # explicit device
|
||||||
|
```
|
||||||
|
|
||||||
|
## SSH / find IP
|
||||||
|
```bash
|
||||||
|
CM4=$(ip neigh show dev enp0s31f6 | awk '/e4:5f:01:e9:13:96/{print $1}')
|
||||||
|
sshpass -p beacon ssh user@$CM4 # login: user / beacon
|
||||||
|
```
|
||||||
|
|
||||||
|
## OTA Update
|
||||||
|
```bash
|
||||||
|
# transfer (scp broken on Dropbear — use tee pipe):
|
||||||
|
sshpass -p beacon ssh user@$CM4 'sudo tee /upload/rootfs.raucb >/dev/null' \
|
||||||
|
< output/images/rootfs.raucb
|
||||||
|
# install + reboot:
|
||||||
|
sshpass -p beacon ssh user@$CM4 'rauc install /upload/rootfs.raucb && sudo reboot'
|
||||||
|
# after reboot — find new IP, then:
|
||||||
|
sshpass -p beacon ssh user@$CM4 'rauc status mark-good && rauc status'
|
||||||
|
```
|
||||||
|
|
||||||
|
## UART
|
||||||
|
```bash
|
||||||
|
picocom -b 115200 /dev/ttyUSB1 # interactive (GPIO14/15)
|
||||||
|
socat -u /dev/ttyUSB1,b115200,rawer,crnl OPEN:/tmp/uart.log,creat,trunc & # headless capture
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rescue
|
||||||
|
Short GPIO4 (pin 7) → GND (pin 9) during power-on → boots `/dev/mmcblk0p2`.
|
||||||
|
|
||||||
|
## Secure Boot (Milestone 2)
|
||||||
|
```bash
|
||||||
|
update-pieeprom.sh -k private.pem && rpiboot -d secure-boot-recovery
|
||||||
|
```
|
||||||
|
> Use existing `private.pem` — never regenerate it.
|
||||||
262
README.md
Normal file
262
README.md
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
# Beacon CM4 — Buildroot + RAUC
|
||||||
|
|
||||||
|
Buildroot BR2_EXTERNAL for a Raspberry Pi CM4 with A/B OTA updates via [RAUC](https://rauc.io/).
|
||||||
|
|
||||||
|
## Partition layout
|
||||||
|
|
||||||
|
| # | Label | Size | Content |
|
||||||
|
|---|-------|------|---------|
|
||||||
|
| p1 | boot_a | 64 MiB | FAT32 — firmware + U-Boot (slot A) |
|
||||||
|
| p2 | boot_b | 64 MiB | FAT32 — firmware + U-Boot (slot B) |
|
||||||
|
| p3 | data | 256 MiB | ext4 — persistent data (`/data`) |
|
||||||
|
| p4 | (extended) | — | — |
|
||||||
|
| p5 | rootfs0 | 250 MiB | ext4 — rootfs slot A |
|
||||||
|
| p6 | rootfs1 | 250 MiB | ext4 — rootfs slot B |
|
||||||
|
| p7 | upload | 256 MiB | ext4 — staging area for bundles (`/upload`) |
|
||||||
|
|
||||||
|
U-Boot reads `BOOT_ORDER`/`BOOT_x_LEFT` env vars from eMMC and selects the active slot before loading the kernel. RAUC uses `boot-mbr-switch` to toggle between slots.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## One-time setup
|
||||||
|
|
||||||
|
### 1. Generate RAUC signing certificates
|
||||||
|
|
||||||
|
Run **once** from the `beacon-buildroot/` directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/repos/buildroot-beacon/beacon-buildroot
|
||||||
|
./openssl-ca.sh "Beacon" "Beacon RAUC CA"
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates `openssl-ca/dev/` with:
|
||||||
|
|
||||||
|
```
|
||||||
|
openssl-ca/dev/
|
||||||
|
ca.cert.pem ← keyring installed into target /etc/rauc/keyring.pem
|
||||||
|
development-1.cert.pem ← signing cert (build host)
|
||||||
|
private/
|
||||||
|
development-1.key.pem ← signing key (build host, keep secret)
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Do not regenerate** the CA once devices are flashed — bundles signed with
|
||||||
|
> a new CA will be rejected by devices that have the old keyring.
|
||||||
|
|
||||||
|
### 2. Initial full build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/repos/buildroot-beacon
|
||||||
|
|
||||||
|
make -C rpi-buildroot-fork \
|
||||||
|
O=$(pwd)/output \
|
||||||
|
BR2_EXTERNAL=$(pwd)/beacon-buildroot \
|
||||||
|
beacon_cm4_rauc_defconfig
|
||||||
|
|
||||||
|
make -C rpi-buildroot-fork \
|
||||||
|
O=$(pwd)/output \
|
||||||
|
BR2_EXTERNAL=$(pwd)/beacon-buildroot \
|
||||||
|
-j$(nproc)
|
||||||
|
```
|
||||||
|
|
||||||
|
Output artifacts in `output/images/`:
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `sdcard.img.xz` | Full eMMC image for initial flash |
|
||||||
|
| `sdcard.img.bmap` | Block map for fast flash with bmaptool |
|
||||||
|
| `rootfs.raucb` | **OTA bundle** — rootfs only |
|
||||||
|
| `update.raucb` | OTA bundle — bootfs + rootfs (full system) |
|
||||||
|
|
||||||
|
### 3. Initial flash (EMMC_DISABLE jumper bridged)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./beacon-buildroot/scripts/flash-cm4.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove the jumper and power-cycle after the script completes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Creating an OTA update
|
||||||
|
|
||||||
|
### What triggers a new bundle
|
||||||
|
|
||||||
|
Any source change that results in a different `rootfs.ext4` or `boot.vfat` will produce a new bundle on the next build. Typical triggers:
|
||||||
|
|
||||||
|
- Package version bump / new package in defconfig
|
||||||
|
- File added/changed under `rootfs-overlay/`
|
||||||
|
- `post-build.sh` changes
|
||||||
|
- Kernel or U-Boot update
|
||||||
|
|
||||||
|
### Build the update
|
||||||
|
|
||||||
|
Incremental build — only changed packages and the rootfs/image stage are rebuilt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/repos/buildroot-beacon
|
||||||
|
|
||||||
|
# Optional: set a human-readable version string
|
||||||
|
export VERSION="1.2.0"
|
||||||
|
|
||||||
|
make -C rpi-buildroot-fork \
|
||||||
|
O=$(pwd)/output \
|
||||||
|
BR2_EXTERNAL=$(pwd)/beacon-buildroot \
|
||||||
|
-j$(nproc)
|
||||||
|
```
|
||||||
|
|
||||||
|
`post-image.sh` runs automatically at the end and:
|
||||||
|
1. Builds `boot.vfat` from U-Boot + firmware blobs
|
||||||
|
2. Creates `rootfs.raucb` (rootfs-only bundle)
|
||||||
|
3. Creates `update.raucb` (full bootfs+rootfs bundle)
|
||||||
|
4. Signs both bundles with `development-1.key.pem`
|
||||||
|
5. Assembles `sdcard.img.xz` + `.bmap`
|
||||||
|
|
||||||
|
### Bundle contents
|
||||||
|
|
||||||
|
`rootfs.raucb` manifest (`format=verity`):
|
||||||
|
```ini
|
||||||
|
[update]
|
||||||
|
compatible=beacon-cm4
|
||||||
|
version=<VERSION>
|
||||||
|
[bundle]
|
||||||
|
format=verity
|
||||||
|
[image.rootfs]
|
||||||
|
filename=rootfs.ext4
|
||||||
|
```
|
||||||
|
|
||||||
|
`update.raucb` additionally contains `[image.bootloader]` → `boot.vfat`.
|
||||||
|
|
||||||
|
### Signing details
|
||||||
|
|
||||||
|
Signing is done by the host `rauc` binary during `post-image.sh`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rauc bundle \
|
||||||
|
--cert openssl-ca/dev/development-1.cert.pem \
|
||||||
|
--key openssl-ca/dev/private/development-1.key.pem \
|
||||||
|
--keyring openssl-ca/dev/ca.cert.pem \
|
||||||
|
<bundle-dir>/ <output>.raucb
|
||||||
|
```
|
||||||
|
|
||||||
|
The target verifies the bundle signature against `/etc/rauc/keyring.pem`
|
||||||
|
(= `ca.cert.pem` installed during build by `post-build.sh`).
|
||||||
|
|
||||||
|
To inspect a bundle without installing it:
|
||||||
|
```bash
|
||||||
|
output/host/bin/rauc \
|
||||||
|
--keyring beacon-buildroot/openssl-ca/dev/ca.cert.pem \
|
||||||
|
info output/images/rootfs.raucb
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deploying the update to the CM4
|
||||||
|
|
||||||
|
### Find the CM4's IP
|
||||||
|
|
||||||
|
The CM4 gets a DHCP address on eth0 (changes on each reboot):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# By MAC address:
|
||||||
|
ip neigh show dev enp0s31f6 | grep e4:5f:01:e9:13:96
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transfer the bundle
|
||||||
|
|
||||||
|
Dropbear has no sftp-server — standard `scp` does **not** work.
|
||||||
|
Use stdin pipe instead:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CM4=10.11.0.xx # replace with actual IP
|
||||||
|
|
||||||
|
sshpass -p beacon ssh user@$CM4 \
|
||||||
|
'sudo tee /upload/rootfs.raucb > /dev/null' \
|
||||||
|
< output/images/rootfs.raucb
|
||||||
|
```
|
||||||
|
|
||||||
|
~51 MiB transfers in ~5 s on LAN.
|
||||||
|
|
||||||
|
### Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sshpass -p beacon ssh user@$CM4 'rauc install /upload/rootfs.raucb'
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
0% Installing
|
||||||
|
20% Checking bundle done.
|
||||||
|
40% Determining target install group done.
|
||||||
|
46% Checking slot rootfs.1 done.
|
||||||
|
99% Copying image to rootfs.1 done.
|
||||||
|
100% Installing done.
|
||||||
|
Installing `/upload/rootfs.raucb` succeeded
|
||||||
|
```
|
||||||
|
|
||||||
|
RAUC automatically selects the **inactive** slot as the target.
|
||||||
|
|
||||||
|
### Reboot into the new slot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sshpass -p beacon ssh user@$CM4 'rauc status && sudo reboot'
|
||||||
|
```
|
||||||
|
|
||||||
|
`rauc status` should show `Activated: rootfs.1 (B)` before the reboot.
|
||||||
|
|
||||||
|
### Confirm and mark-good
|
||||||
|
|
||||||
|
After reboot the system boots into the new slot in **trial mode**
|
||||||
|
(U-Boot decrements `BOOT_x_LEFT`). You must mark it good or it will
|
||||||
|
roll back on the next reboot.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find new IP (DHCP address changes):
|
||||||
|
CM4_NEW=$(ip neigh show dev enp0s31f6 | awk '/e4:5f:01:e9:13:96/{print $1}')
|
||||||
|
|
||||||
|
sshpass -p beacon ssh user@$CM4_NEW 'rauc status mark-good && rauc status'
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected final `rauc status`:
|
||||||
|
```
|
||||||
|
Booted from: rootfs.1 (B)
|
||||||
|
Activated: rootfs.1 (B)
|
||||||
|
x [rootfs.1] boot status: good ← currently running, committed
|
||||||
|
o [rootfs.0] boot status: good ← fallback
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback behaviour
|
||||||
|
|
||||||
|
If `mark-good` is **not** called after a reboot, U-Boot decrements
|
||||||
|
`BOOT_x_LEFT`. After 3 failed attempts it switches back to the previous
|
||||||
|
slot automatically — no manual intervention needed.
|
||||||
|
|
||||||
|
To force an immediate rollback:
|
||||||
|
```bash
|
||||||
|
# On the CM4:
|
||||||
|
sudo fw_setenv BOOT_ORDER "A B" # or "B A" depending on current slot
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rescue partition
|
||||||
|
|
||||||
|
Short **GPIO4** (pin 7) to **GND** (pin 9) on the 40-pin header during
|
||||||
|
power-on. U-Boot detects this and boots the read-only rescue rootfs from
|
||||||
|
`/dev/mmcblk0p2`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
| Path | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `configs/beacon_cm4_rauc_defconfig` | Buildroot defconfig |
|
||||||
|
| `board/beacon-cm4/genimage.cfg` | Partition layout |
|
||||||
|
| `board/beacon-cm4/post-image.sh` | Bundle creation + signing |
|
||||||
|
| `board/beacon-cm4/post-build.sh` | Target rootfs customisation |
|
||||||
|
| `board/beacon-cm4/rootfs-overlay/etc/rauc/system.conf` | RAUC slot config |
|
||||||
|
| `openssl-ca/dev/` | Signing certificates (generated once) |
|
||||||
|
| `scripts/flash-cm4.sh` | Automated initial flash script |
|
||||||
99
agents.md
99
agents.md
@@ -1,99 +0,0 @@
|
|||||||
# 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@<cm4-ip>
|
|
||||||
```
|
|
||||||
**Note**: Dropbear has no sftp-server — `scp` does NOT work. Transfer files via stdin pipe:
|
|
||||||
```bash
|
|
||||||
sshpass -p beacon ssh user@<cm4-ip> '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@<cm4-ip> '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.
|
|
||||||
Reference in New Issue
Block a user