Compare commits

..

60 Commits

Author SHA1 Message Date
fa93fe06c4 README: bump to v1.13.2-k6.12.47-1, list new patches
Some checks failed
Check Upstream Updates / check-and-build (push) Failing after 58s
- Talos version badge: v1.12.4 → v1.13.2
- Image-tag example + upgrade-command example: v1.13.2-k6.12.47-1
- Add patches 0006 (grub EFI upgrade fallback), 0007 (acquire.go STATE
  wait for slow-init eMMC) and 0008 (imager --insecure for Overlay) to
  the patches table
- Note the slow-eMMC tolerance + canonical 3-CP validation in the
  "Reliable in-place upgrades" roadmap row
2026-05-25 21:43:58 -04:00
56960b09a8 Merge branch 'talos-v1.13.2-bump' for v1.13.2-k6.12.47-1 release
All checks were successful
Build Talos CM5 Image / build (push) Successful in 1h1m23s
Brings the Talos v1.13.2 / pkgs v1.13.0 bump and three Talos patches:
- 0006 grub EFI-at-/boot fallback for BOOT-less SBC Upgrade path
- 0007 acquire.go wait for STATE on slow-init disks (CM5 eMMC) — fixes
  the post-upgrade maintenance-mode regression on CM5 hardware
- 0008 imager respect --insecure for Overlay assets (build-tool fix)

Validated 2026-05-25 via a canonical 3-CP rolling upgrade on a freshly
flashed home-test cluster: .94/.95/.96 upgraded sequentially from v1.12.4
to v1.13.2-7, each came back stage=running with config preserved and
k8s Ready within ~5 min, no manual remediation, etcd quorum maintained.
2026-05-25 20:05:24 -04:00
19625ba8c0 patches: add 0008 imager respect --insecure for Overlay assets
The imager's `--insecure` flag covered BaseInstaller, ImageCache, and
SystemExtensions but not the Overlay or OverlayInstaller ContainerAssets.
When pulling those from a plain-HTTP local registry (e.g. for offline
or development builds), they always tried HTTPS and failed with
"http: server gave HTTP response to HTTPS client" even with the flag set.

This patch sets `ForceInsecure: cmdFlags.Insecure` on both overlay
asset references, matching how the other Inputs are handled.

Build-tool only — no effect on the running Talos OS. Lets `gmake installer`
work end-to-end against an insecure registry like 192.133.7.111:5001.
2026-05-25 20:04:38 -04:00
40bfac268d patches: add 0007 acquire.go wait for STATE on slow-init disks (CM5 eMMC)
After upgrade kexec into v1.13.2, CM5 eMMC takes ~2m13s between the SDHCI
controller registering and mmc0 actually becoming usable. The Talos config
acquire state machine (`acquire.go::stateDisk`) checks STATE in the first
seconds of boot, sees `VolumePhaseMissing`, and transitions one-way to
`stateEmbedded` -> `stateMaintenanceEnter`. When STATE later becomes
ready, the state machine doesn't re-enter `stateDisk`, so the node stays
in maintenance forever despite the on-disk config.yaml being intact.

This patch makes stateDisk tolerate transient phase=missing for up to
5 minutes (stateMissingDiskTimeout) before falling through to embedded.
A 5-second ticker on the outer Run loop ensures the timeout can fire
even when no further volume-status events arrive (e.g. truly missing
STATE on a fresh install).

Validated 2026-05-25 via canonical 3-CP rolling upgrade on a freshly
flashed v1.12.4 home-test cluster: all 3 blades upgraded sequentially
to v1.13.2-7 (this patch), each came back stage=running with config
loaded automatically and k8s Ready within ~5 min, no manual remediation.
See doc-compute-blade-kubernetes/talos-upgrade-validation/session-2026-05-25/E2E-VALIDATED.md.

Fast-init hardware sees no change — STATE reaches ready within seconds
and the existing path runs.
2026-05-25 20:04:30 -04:00
8fada1ebfe patches: restore 0006 grub EFI-at-/boot fallback for BOOT-less SBC Upgrade
This patch file mirrors a commit that already existed in checkouts/talos
(`a50511de7` — "grub: EFI-at-/boot fallback for BOOT-less SBC layout in
Upgrade path") but was never landed back into patches/siderolabs/talos/.
Extracted with `git format-patch` from the checkout so subsequent
`make patches` runs reproduce the same tree on a fresh clone.

Complements 0005 by handling the Upgrade code-path (in addition to the
fresh-install code-path 0005 already covers) for SBC layouts that don't
have a separate BOOT partition.
2026-05-25 20:04:17 -04:00
d84ddc491a talos: restore modules-arm64.txt patch; metal step needs --network=host
Two complementary fixes after end-to-end local installer build:

1. New talos/0001 patch — Replace hack/modules-arm64.txt with the
   intersection of upstream's initramfs list and our RPi 6.12.47
   build's actual modules (155 entries, down from upstream's 241).
   Initramfs target was failing with exit 123 in xargs install -D
   because upstream lists modules our kernel doesn't build (SATA,
   HID device drivers, some upstream-only crypto helpers).

2. Makefile: add --network=host to the metal docker run.
   The installer step already had it, but the metal step did not.
   For local-registry builds (REGISTRY=127.0.0.1:5001), the imager
   container needs --network=host to reach the host's registry to
   pull the overlay image when generating the raw disk image.
   Harmless on CI (no behavioural change against docker.io).

Validated locally end-to-end:
- kernel image:    234MB  (RPi 6.12.47 with RP1 driver support)
- overlay image:    9.7MB (U-Boot + firmware + DTBs)
- imager image:   346MB
- installer-base: 105MB
- installer:      ~100MB
- metal-arm64.raw.zst: 94MB (final flashable disk image)
2026-05-18 19:03:12 -04:00
18fed4ec51 pkgs config-arm64: add missing RP1 symbols to fix kernel link
The v1.13.2 rebase of pkgs 0001 only restored some RP1-related kernel
options (PINCTRL_RP1, COMMON_CLK_RP1, PINCTRL_BCM2712) because those
hunks happened to apply cleanly against upstream v1.13.0's 6.18.24-era
config-arm64. Several others were silently dropped, causing:

    ld.lld: error: undefined symbol: rp1_get_platform

at the vmlinux link step (~19 min into local kernel build).

Re-added:
- CONFIG_MFD_RP1=y                 (defines rp1_get_platform)
- CONFIG_COMMON_CLK_RP1_SDIO=y
- CONFIG_FB_BCM2708=y              (RPi framebuffer)
- CONFIG_PWM_PIO_RP1=y             (RPi PWM via PIO)
- CONFIG_PWM_BRCMSTB=y             (was "not set")

Local build now succeeds: svrnty/talos-rpi5-kernel:v1.13.0-local
loaded into local Docker (234MB).
2026-05-18 18:43:22 -04:00
9638fb44f1 Bump to Talos v1.13.2 / pkgs v1.13.0
- Makefile: TALOS_VERSION v1.12.4 -> v1.13.2, PKG_VERSION v1.12.0 -> v1.13.0
- siderolabs/talos 0001 (modules-arm64.txt): removed; hack/modules-arm64.txt
  is a CI assertion file with no build-time references. Will be regenerated
  from a real RPi 6.12.47 kernel build as a follow-up.
- siderolabs/talos 0005 (BOOT partition GRUB): rebased onto v1.13.2's
  Install/Upgrade refactor. installEFI struct field is gone upstream; ported
  the BOOT-partition probe + EFI-at-/boot fallback to work with the new
  efiFound local var and added a bootFromEFI struct field for runGrubInstall.
- siderolabs/pkgs 0001: rebased onto v1.13.0. Kernel config header bumped
  to 6.12.47. config-arm64 not fully regenerated for RPi 6.12.47 yet -- some
  upstream v1.13 6.18.x symbols (LIBIE_ADMINQ, IDPF, etc) remain in the file
  but the kernel's Kconfig silently drops unknown options during build.
2026-05-18 17:20:29 -04:00
Mathias Beaulieu-Duncan
e1d9add5aa Use dedicated talos-rpi5 runner label for workflows
Some checks failed
Check Upstream Updates / check-and-build (push) Failing after 1s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 18:27:07 -05:00
118ef4dcc9 Add serial console support, remove wrong debug UART patch
Some checks failed
Check Upstream Updates / check-and-build (push) Failing after 6s
Enable GPIO UART0 on Pi5/CM5 via dtoverlay=uart0-pi5 in
configTxtAppend. Remove the old 0002 patch that targeted the
debug UART (ttyAMA10) — Compute Blade uses GPIO 14/15 (ttyAMA0).
Renumber overlay patches (old 0003 becomes 0002).

Update README with tested serial console docs: wiring diagram,
even parity config, 3.3V requirement, and read-only limitation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:04:39 -05:00
f604f468b7 Add Gitea releases badge to README
All checks were successful
Check Upstream Updates / check-and-build (push) Successful in 5s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 16:57:01 -05:00
d260fe20ab Add reference architecture for production CM5 clusters
Document recommended storage layouts per node role:
- Control planes: NVMe boot for fast etcd I/O
- Postgres/storage: eMMC boot + NVMe data at /var/mnt/data
- Compute workers: eMMC only, stateless

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 16:53:19 -05:00
4e867e2055 Fix boot failure: remove duplicate config.txt section headers
All checks were successful
Build Talos CM5 Image / build (push) Successful in 3m38s
The [pi5]/[all] section headers in configTxtAppend created duplicate
sections when concatenated with the overlay's base config.txt, which
already ends with [pi5]/[all]. The RPi firmware parser choked on the
duplicate headers, preventing NVMe boot on fresh installs.

Remove the section headers — dtparam and overclock settings now land
under the existing [all] scope from the base config.txt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 13:00:54 -05:00
d4a55c670c Apply SBC overlay upgrade fix, add PCIe Gen 3 docs
All checks were successful
Build Talos CM5 Image / build (push) Successful in 3m11s
Check Upstream Updates / check-and-build (push) Successful in 5s
Apply overlay patch 0003 (EFI mount path detection for SBC layouts)
in the build so upgrades write firmware/config.txt to the correct
path. Update README with patch 0003, PCIe Gen 3 in features list,
and expanded PCIe Gen 3 instructions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:29:17 -05:00
5152b6cb44 Simplify config: use configTxtAppend, drop SBC overlay patch
All checks were successful
Build Talos CM5 Image / build (push) Successful in 2m41s
Check Upstream Updates / check-and-build (push) Successful in 5s
Config.txt is set correctly at flash time. Upgrades via talosctl
don't override firmware config (overlay writes to wrong path on
SBC layout, which is harmless). Users who need custom config.txt
set it once during initial flash.

- Use configTxtAppend with PCIe Gen 3 + overclock
- Put dtparam=pciex1_gen=3 in [pi5] section
- Remove patch 0003 (SBC overlay upgrade fix) — too risky,
  deleted GRUB's BOOTAA64.EFI in v8
- Remove full configTxt replacement mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 16:47:42 -05:00
338a2c0021 Fix SBC overlay upgrade: preserve GRUB, enable PCIe Gen 3
All checks were successful
Build Talos CM5 Image / build (push) Successful in 2m58s
The v8 overlay patch deleted /boot/EFI/ to clean up stale firmware,
but this also removed GRUB's BOOTAA64.EFI, bricking the node.

Fix: keep SBC layout detection (write to /boot/ not /boot/EFI/) but
remove the os.RemoveAll that destroyed GRUB. Stale firmware files in
/boot/EFI/ are harmless.

Re-enable PCIe Gen 3 (dtparam=pciex1_gen=3) and full configTxt mode,
now that the overlay installer correctly writes to the EFI partition
root on SBC layouts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:50:52 -05:00
b5201f7906 Revert PCIe Gen 3 config: restore safe defaults for public image
All checks were successful
Build Talos CM5 Image / build (push) Successful in 2m55s
Check Upstream Updates / check-and-build (push) Successful in 4s
The PCIe Gen 3 changes (dtparam=pciex1_gen=3, full configTxt
replacement, SBC overlay upgrade fix) caused boot failures during
talosctl upgrade on CM5 nodes. Revert to the pre-Gen3 state:
- configTxtAppend (overclock only) instead of full configTxt replacement
- Remove 0003 overlay patch application (kept in patches/ for future use)

PCIe Gen 3 support will be re-added after root cause analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 20:27:37 -05:00
91d86de629 Fix SBC overlay upgrade + simplify PCIe Gen 3 config
All checks were successful
Build Talos CM5 Image / build (push) Successful in 3m22s
Two fixes in one:

1. SBC overlay upgrade path: the overlay installer was always writing
   to /boot/EFI, but on SBC layouts (no BOOT partition) the GRUB code
   mounts EFI at /boot. Config.txt and firmware ended up in a stale
   /boot/EFI/ subdirectory, invisible to the firmware. The installer
   now detects the SBC layout and writes to the correct location.

2. PCIe Gen 3: dtparam=pciex1_gen=3 works on CM5 (the DT overrides
   exist), so the custom pcie-gen3.dtbo overlay is unnecessary.
   Simplified to just use dtparam in config.txt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 19:45:39 -05:00
3cfbe794f7 Fix PCIe Gen 3 on CM5: custom DT overlay for missing pciex1 alias
All checks were successful
Build Talos CM5 Image / build (push) Successful in 3m20s
The CM5 DTB (bcm2712-rpi-cm5-cm5io.dtb) lacks the pciex1 alias that
the Pi 5 DTB provides, making dtparam=pciex1_gen=3 silently fail.

Add a custom device tree overlay (pcie-gen3.dtbo) that targets
/axi/pcie@1000110000 directly to set max-link-speed = <3>. The overlay
is embedded in the SBC installer and written to /boot/EFI/overlays/
during install/upgrade.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 19:08:26 -05:00
a9cc56e315 Fix PCIe Gen 3: move dtparam into [pi5] section of config.txt
All checks were successful
Build Talos CM5 Image / build (push) Successful in 2m53s
The dtparam=pciex1_gen=3 was being appended after the [all] section,
but RPi firmware requires PCIe dtparams in the [pi5] section.
Switch from configTxtAppend to full configTxt replacement to control
section ordering. Also add dtparam=pciex1 to explicitly enable the
external PCIe link.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:45:05 -05:00
66a3d11984 Enable PCIe Gen 3 for NVMe: ~800 MB/s vs ~375 MB/s Gen 2
All checks were successful
Build Talos CM5 Image / build (push) Successful in 3m25s
Adds dtparam=pciex1_gen=3 to config.txt overlay. Benchmarked Gen 2 baseline
on all 3 pg nodes showing consistent ~375 MB/s write throughput, bottlenecked
by the Gen 2 x1 lane limit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:09:30 -05:00
754f49c562 Update README: bump example tag to v1.12.4-k6.12.47-4
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 13:51:17 -05:00
4fed64844a Fix GRUB patch: skip sd-boot probe on arm64 for first upgrade
All checks were successful
Build Talos CM5 Image / build (push) Successful in 4m50s
On fresh SBC images, the EFI partition has sd-boot UKI files but no
GRUB config. During upgrade, Probe() found sd-boot and used it, which
failed because RPi5/CM5 firmware lacks EFI SetVariableRT support.

Add arm64 guard to Probe(): when no GRUB config is found, skip sd-boot
probing and return a fresh GRUB config. This transitions from sd-boot
to GRUB on the first upgrade from a fresh flash.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 13:50:42 -05:00
8c562c7155 Update README: NVMe boot tested on Compute Blade
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 13:18:24 -05:00
fc020410f1 Update README: in-place upgrades tested, add patches table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 12:40:13 -05:00
ca36438d12 Add GRUB SBC upgrade patch: handle missing BOOT partition
All checks were successful
Build Talos CM5 Image / build (push) Successful in 3m15s
Patch 0005 fixes talosctl upgrade on SBC layouts (RPi5/CM5) where
the disk has no separate BOOT (XFS) partition — only EFI (VFAT).
Falls back to mounting EFI at /boot for probe, install, and revert.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 11:37:05 -05:00
6cffb4e311 Add opentree fallback patch for RPi downstream kernel (<6.15)
All checks were successful
Build Talos CM5 Image / build (push) Successful in 2m56s
Check Upstream Updates / check-and-build (push) Successful in 4s
Talos assumes bare metal kernels support open_tree on anonymous FS
(added in 6.15). The RPi downstream kernel (6.12.x) does not, causing
shadow bind mount failures for /etc files and cascading network init
failures. This patch removes the InContainer() gate so the capability
check runs on all platforms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 14:25:03 -05:00
Mathias Beaulieu-Duncan
5c81953278 Fix modules patch: ip6_gre.ko not in RPi downstream kernel
All checks were successful
Build Talos CM5 Image / build (push) Successful in 3m17s
ip6_gre.ko exists in Talos upstream module list (v1.12.4) but not
in the RPi downstream kernel build. Only add it to the removal side
of the patch, not our custom module list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:55:45 -05:00
Mathias Beaulieu-Duncan
a4e934a4e9 Fix CI PATH: prepend GNU sed via GITHUB_PATH instead of replacing PATH
Some checks failed
Build Talos CM5 Image / build (push) Failing after 28s
The hardcoded job-level PATH env wiped out nvm/node, breaking
actions/checkout. Use GITHUB_PATH to prepend GNU sed's gnubin
directory while preserving the runner's inherited PATH.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:54:34 -05:00
Mathias Beaulieu-Duncan
6c75585c0a Bump upstream: v1.12.4-k6.12.47-1
Some checks failed
Build Talos CM5 Image / build (push) Failing after 1s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:53:12 -05:00
Mathias Beaulieu-Duncan
37f9292ef1 Update arm64 modules patch for Talos v1.12.4 (add ip6_gre)
Talos v1.12.4 added kernel/net/ipv6/ip6_gre.ko to modules-arm64.txt.
Update our patch to match. Also silence gmake checkouts-clean stdout
in auto-update.sh to prevent it leaking into GITHUB_OUTPUT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:50:45 -05:00
Mathias Beaulieu-Duncan
dc37b435c3 Fix GNU sed PATH in CI workflows for macOS runner
BSD sed on macOS requires `sed -i ''` but auto-update.sh uses GNU
`sed -i` syntax. The workflows installed gnu-sed via Homebrew but
never added it to PATH, causing "invalid command code M" failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:45:46 -05:00
Mathias Beaulieu-Duncan
58b9ccb56c Add supply chain attestation to installer image via crane + buildx
Some checks failed
Build Talos CM5 Image / build (push) Successful in 5m19s
Check Upstream Updates / check-and-build (push) Failing after 13s
Push the full installer tar with crane first (preserving all layers),
then re-wrap with docker buildx to add provenance and SBOM attestation
for Docker Scout compliance. Buildx can pull the image from the registry
since crane already pushed it, avoiding the docker-container driver
limitation with locally loaded images.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:57:54 -05:00
Mathias Beaulieu-Duncan
784fb4d5f6 Fix installer image missing kernel and overlay layers
All checks were successful
Build Talos CM5 Image / build (push) Successful in 4m5s
The docker buildx build wrapper with docker-container driver cannot
access locally loaded images, causing it to only capture the first
layer (22MB base) and drop the kernel (~98MB) and overlay (~3MB).

Switch back to crane push which pushes the tar as-is, preserving
all 3 layers. Attestation args remain on actual build steps where
buildx works correctly.

Fixes broken tags: v1.12.3-k6.12.47-3, v1.12.3-k6.12.47-4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:46:10 -05:00
Mathias Beaulieu-Duncan
9c0075057b Use rpiboot for EEPROM config in NVMe guide
CM5 on Compute Blade doesn't have an SD slot for booting Raspberry Pi
OS. Use rpiboot recovery mode over USB instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 20:05:13 -05:00
Mathias Beaulieu-Duncan
5b59f8de8d Add NVMe boot guide (untested) to README
Documents the dd + EEPROM configuration approach for booting Talos
from NVMe on RPi5/CM5. Includes BOOT_ORDER, PCIE_PROBE settings,
and optional PCIe Gen 3 configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 20:00:16 -05:00
Mathias Beaulieu-Duncan
f3132a310e Update NVMe boot status: dd + EEPROM config approach
NVMe kernel driver is already built-in (CONFIG_BLK_DEV_NVME=y). The
expected approach is simply dd'ing the metal image to NVMe and setting
EEPROM BOOT_ORDER=0xf416 + PCIE_PROBE=1. Pending hardware validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 19:57:01 -05:00
Mathias Beaulieu-Duncan
970d9685f1 Fix serial console for RPi5/CM5 debug UART (ttyAMA10)
The overlay was using console=ttyAMA0 (GPIO 14/15) but the RPi5 debug
UART is ttyAMA10 (JST connector between HDMI ports on Pi5, test pads
TP35/TP36 on CM5). Also adds earlycon for early boot output and disables
GPIO UART on Pi5 in config.txt to avoid U-Boot compatibility issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 19:47:18 -05:00
Mathias Beaulieu-Duncan
689b9402a8 Add GRUB bootloader patches for talosctl upgrade on RPi5/CM5
All checks were successful
Build Talos CM5 Image / build (push) Successful in 1h4m48s
Force GRUB instead of sd-boot on arm64 and pass --no-nvram to
  grub-install, working around the SetVariableRT firmware limitation
  that prevents in-place upgrades on RPi5/CM5 hardware.

  Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 19:20:18 -05:00
Mathias Beaulieu-Duncan
b1eb322d7b Switch to 4K page size for production readiness
Remove the 16K page override from the kernel patch, preserving
upstream Talos's default 4K pages. RPi5 hardware works correctly
with 4K pages — the RPi Foundation's 16K default is a TLB
performance optimization (~5%), not a hardware requirement.

Benefits:
- Correct memory accounting (4x less overhead per page)
- Full software compatibility (jemalloc, Longhorn, F2FS, etc.)
- No OOM surprises on control-plane nodes
- Aligned with upstream Talos kernel config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:28:22 -05:00
Mathias Beaulieu-Duncan
8178ba195e Add known issues, roadmap, and conditional Go toolchain patch
- Document SetVariableRT upgrade failure, 16K page size implications,
  serial console issue, and SBC install disk behavior
- Add production roadmap (4K pages, GRUB boot, serial fix, NVMe)
- Make overlay Go patch conditional: apply only on Go 1.24.x,
  skip on 1.25+ where CVEs are already fixed upstream

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:05:51 -05:00
Mathias Beaulieu-Duncan
d933444fbc Fix double-v badge bug and add table segment updates in README sync
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:33:33 -05:00
Mathias Beaulieu-Duncan
09addfa626 Auto-update README versions when upstream updates are detected
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:28:40 -05:00
Mathias Beaulieu-Duncan
7fceae1418 Point all version badges to upstream repo main pages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:16:25 -05:00
Mathias Beaulieu-Duncan
6ca561592f Fix RPi kernel badge link — repo has no version-tagged releases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:14:58 -05:00
Mathias Beaulieu-Duncan
2b2205f503 Link version badges to upstream GitHub releases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:06:48 -05:00
Mathias Beaulieu-Duncan
6f24c8ef46 Replace cosign with buildx attestations for Docker Scout compliance
All checks were successful
Build Talos CM5 Image / build (push) Successful in 2m49s
Docker Scout requires buildx-style provenance+SBOM, not cosign
attestations. Replace crane push with docker load + buildx build
(--provenance=mode=max --sbom=true) for the installer image. Use
buildx imagetools create for the release tag to preserve attestations.
Remove cosign/syft from CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:05:20 -05:00
Mathias Beaulieu-Duncan
2f307aecec Open all external links in new tab (target=_blank)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:57:29 -05:00
Mathias Beaulieu-Duncan
ee085a7606 Replace version table with Docker-style badges for all components
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:56:46 -05:00
Mathias Beaulieu-Duncan
907dd98b24 Split README into user manual and TECHNICAL.md
Move CI/CD, runner setup, secrets, and project structure to
TECHNICAL.md. Streamline README as a user-facing guide with
install/upgrade instructions. Fix Docker badges for arm64.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:55:37 -05:00
Mathias Beaulieu-Duncan
2618de74e8 Update README with Docker Hub badges, version table, and tag format
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:51:04 -05:00
Mathias Beaulieu-Duncan
ba3c42f561 Add SBOM attestations to installer/release images, remove Scout
All checks were successful
Build Talos CM5 Image / build (push) Successful in 7m0s
Attach cosign+syft SBOM attestations to crane-pushed installer and
release images to satisfy Docker Scout supply chain policy. Replace
docker tag/push with crane copy for the release target. Remove the
Scout CVE scan target and clean up release notes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:48:56 -05:00
Mathias Beaulieu-Duncan
44aa3793ee Add LICENSE, update README, upgrade provenance to max-mode
All checks were successful
Build Talos CM5 Image / build (push) Successful in 3m29s
- Add MPL 2.0 LICENSE file for compliance
- Add license section and upstream attribution to README
- Upgrade provenance attestation from mode=min to mode=max

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:57:11 -05:00
Mathias Beaulieu-Duncan
5abca73056 Fix 21 Go stdlib CVEs and enable supply chain attestations
All checks were successful
Build Talos CM5 Image / build (push) Successful in 3m26s
- Patch sbc-raspberrypi5 overlay to use Go 1.24.13 (fixes 1C/7H/12M/1L CVEs)
- Add ATTESTATION_ARGS (--provenance=true --sbom=true) to all buildx targets
- Override upstream --provenance=false via TARGET_ARGS (last flag wins)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:36:13 -05:00
Mathias Beaulieu-Duncan
0d3941eb91 Add daily auto-update workflow and fix overlay dirty tag
All checks were successful
Build Talos CM5 Image / build (push) Successful in 3m6s
- Rewrite check-upstream.sh to parse RPi kernel version from patch file
- Add auto-update.sh for automated version bumps with patch smoke test
- Rewrite check-updates.yaml as daily auto-build with issue fallback
- Update build.yaml release body to show Talos + kernel versions from tag
- Fix overlay dirty tag: remove --dirty from SBCOVERLAY_TAG git describe
  (the sed rewrite of pkg.yaml is intentional, not an accidental change)

Tag strategy: v{TALOS}-k{KERNEL}-{BUILD} (e.g. v1.12.3-k6.12.47-1)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:05:46 -05:00
Mathias Beaulieu-Duncan
3a824e960f Regenerate talos patch for v1.12.3
Some checks failed
Build Talos CM5 Image / build (push) Failing after 31m33s
Patch was stale — regenerated from the working checkout to match
the v1.12.3 hack/modules-arm64.txt index.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:27:34 -05:00
Mathias Beaulieu-Duncan
f2b8a0ec65 Fix talos patch — restore hack/modules-arm64.txt
Some checks failed
Build Talos CM5 Image / build (push) Failing after 13s
The talos patch was incorrectly replaced with pkgs-repo changes
(Pkgfile, kernel config). Restored the correct patch that modifies
hack/modules-arm64.txt in the talos checkout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:26:12 -05:00
Mathias Beaulieu-Duncan
a3a3881cff Bump RPi kernel to stable_20250916 (6.12.47)
Some checks failed
Build Talos CM5 Image / build (push) Failing after 19s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:11:55 -05:00
Mathias Beaulieu-Duncan
2b5fd0a25e Update patches for Talos v1.12.3 / pkgs v1.12.0
Regenerated patches to match current upstream checkouts:
- pkgs: updated kernel version, checksums, and config-arm64
- talos: reworked to patch Pkgfile, kernel config, and pkg.yaml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:06:01 -05:00
Mathias Beaulieu-Duncan
e98c573bae Add Docker Scout CVE scanning and switch CI to gmake
- Add `scout` Makefile target that scans all 5 pushed images with
  `docker scout quickview` and writes a summary to _out/scout-report.md
- Switch all CI workflow steps from `make` to `gmake` for GNU Make 4.x
- Add brew dependency step for make, gnu-sed, and crane
- Include CVE summary in Gitea release notes via jq JSON escaping
- Update `clean` target to remove _out/ directory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:01:13 -05:00
22 changed files with 2326 additions and 977 deletions

View File

@ -18,7 +18,7 @@ on:
jobs:
build:
runs-on: [self-hosted, macos]
runs-on: talos-rpi5
timeout-minutes: 180
steps:
@ -28,6 +28,14 @@ jobs:
- name: Verify Docker is running
run: docker info
- name: Install build dependencies
run: |
for pkg in make gnu-sed crane; do
brew list --formula "$pkg" &>/dev/null || brew install "$pkg"
done
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> "$GITHUB_PATH"
gmake --version | head -1
- name: Set up Docker Buildx
run: |
docker buildx version
@ -42,22 +50,22 @@ jobs:
run: echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
- name: Clone upstream sources
run: make checkouts
run: gmake checkouts
- name: Apply patches
run: make patches
run: gmake patches
- name: Build kernel
run: make kernel
run: gmake kernel
- name: Build SBC overlay
run: make overlay
run: gmake overlay
- name: Build installer and disk image
run: make installer
run: gmake installer
- name: Tag release images
run: make release TAG=${{ steps.version.outputs.tag }}
run: gmake release TAG=${{ steps.version.outputs.tag }}
- name: Compress disk image
run: |
@ -81,21 +89,34 @@ jobs:
REPO="${GITHUB_REPOSITORY}"
API="${GITEA_URL}/api/v1"
# Create release
# Extract component versions from tag (format: v1.12.3-k6.12.47-1)
TALOS_VER=$(echo "$TAG" | sed -E 's/^(v[0-9]+\.[0-9]+\.[0-9]+)-.*/\1/')
KERNEL_VER=$(echo "$TAG" | sed -E 's/.*-k([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/')
RELEASE_BODY="Custom Talos Linux image for Raspberry Pi 5 / CM5 (Compute Blade)
**Talos**: ${TALOS_VER}
**Kernel**: RPi downstream ${KERNEL_VER} (CM5/RP1 support)
**Extensions**: iscsi-tools, util-linux-tools
**Overclock**: 2.6GHz (arm_freq=2600)
## Artifacts
- \`metal-arm64.raw.zst\` — Raw disk image for eMMC flashing
- \`docker.io/svrnty/talos-rpi5:${TAG}\` — Installer image for talosctl upgrade"
# Strip leading whitespace from heredoc-style indentation
RELEASE_BODY=$(echo "$RELEASE_BODY" | sed 's/^ //')
RELEASE_BODY_JSON=$(jq -Rs '.' <<< "$RELEASE_BODY")
RELEASE_ID=$(curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"tag_name\": \"${TAG}\",
\"name\": \"Talos RPi5 ${TAG}\",
\"body\": \"Custom Talos Linux image for Raspberry Pi 5 / CM5 (Compute Blade)\n\n**Talos version**: ${TAG}\n**Kernel**: RPi downstream (CM5/RP1 support)\n**Extensions**: iscsi-tools, util-linux-tools\n**Overclock**: 2.6GHz (arm_freq=2600)\n\n## Artifacts\n- \`metal-arm64.raw.zst\` — Raw disk image for eMMC flashing\n- \`docker.io/svrnty/talos-rpi5:${TAG}\` — Installer image for talosctl upgrade\",
\"prerelease\": true
}" \
-d "{\"tag_name\":\"${TAG}\",\"name\":\"Talos RPi5 ${TAG}\",\"body\":${RELEASE_BODY_JSON},\"prerelease\":true}" \
"${API}/repos/${REPO}/releases" | jq -r '.id')
echo "Created release ID: ${RELEASE_ID}"
# Upload disk image
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-F "attachment=@metal-arm64.raw.zst" \
@ -105,4 +126,4 @@ jobs:
- name: Clean up
if: always()
run: make clean
run: gmake clean

View File

@ -1,25 +1,33 @@
# Check for upstream Talos and RPi kernel updates
# Daily upstream update check with auto-build
#
# Runs on a schedule and creates a Gitea issue when new versions are found.
# This is notification-only — builds require manual tag push after verifying
# patches still apply.
# Detects new Talos OS and RPi kernel versions, applies updates,
# smoke-tests patches, and pushes a release tag (which triggers build.yaml).
# Falls back to creating a Gitea issue if patches fail to apply.
name: Check Upstream Updates
on:
schedule:
# Run weekly on Monday at 08:00 UTC
- cron: '0 8 * * 1'
- cron: '0 8 * * *' # Daily at 08:00 UTC
workflow_dispatch:
jobs:
check-updates:
runs-on: [self-hosted, macos]
timeout-minutes: 10
check-and-build:
runs-on: talos-rpi5
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Need full history for tag-based build numbering
- name: Install dependencies
run: |
for pkg in make gnu-sed crane jq; do
brew list --formula "$pkg" &>/dev/null || brew install "$pkg"
done
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> "$GITHUB_PATH"
- name: Check for upstream updates
id: check
@ -27,95 +35,83 @@ jobs:
chmod +x scripts/check-upstream.sh
scripts/check-upstream.sh >> "$GITHUB_OUTPUT"
- name: Create issue for Talos update
if: steps.check.outputs.talos_update == 'true'
uses: actions/github-script@v7
with:
script: |
const currentVersion = '${{ steps.check.outputs.talos_current }}';
const latestVersion = '${{ steps.check.outputs.talos_latest }}';
const title = `Talos update available: ${currentVersion} → ${latestVersion}`;
- name: Run auto-update
if: steps.check.outputs.talos_update == 'true' || steps.check.outputs.rpi_update == 'true'
id: update
env:
TALOS_UPDATE: ${{ steps.check.outputs.talos_update }}
RPI_UPDATE: ${{ steps.check.outputs.rpi_update }}
LATEST_TALOS: ${{ steps.check.outputs.talos_latest }}
LATEST_RPI_TAG: ${{ steps.check.outputs.rpi_latest }}
run: |
chmod +x scripts/auto-update.sh
scripts/auto-update.sh >> "$GITHUB_OUTPUT"
// Check if an open issue already exists
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'upstream-update',
});
- name: Commit and tag
if: steps.update.outputs.patch_failed != 'true' && steps.update.outputs.new_tag != ''
env:
NEW_TAG: ${{ steps.update.outputs.new_tag }}
run: |
git config user.name "Gitea Actions"
git config user.email "actions@openharbor.io"
git add -A
git commit -m "Bump upstream: ${NEW_TAG}"
git tag "$NEW_TAG"
git push origin main --tags
const existing = issues.data.find(i => i.title.includes('Talos update'));
if (existing) {
console.log(`Issue already exists: #${existing.number}`);
return;
}
- name: Create issue on patch failure
if: steps.update.outputs.patch_failed == 'true'
env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TALOS_CURRENT: ${{ steps.check.outputs.talos_current }}
TALOS_LATEST: ${{ steps.check.outputs.talos_latest }}
TALOS_UPDATE: ${{ steps.check.outputs.talos_update }}
RPI_CURRENT: ${{ steps.check.outputs.rpi_current }}
RPI_LATEST: ${{ steps.check.outputs.rpi_latest }}
RPI_UPDATE: ${{ steps.check.outputs.rpi_update }}
run: |
GITEA_URL="${GITHUB_SERVER_URL}"
REPO="${GITHUB_REPOSITORY}"
API="${GITEA_URL}/api/v1"
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: title,
body: [
`## Talos Update Available`,
``,
`| | Version |`,
`|---|---|`,
`| Current | \`${currentVersion}\` |`,
`| Latest | \`${latestVersion}\` |`,
``,
`### Steps`,
`1. Update \`TALOS_VERSION\` in \`Makefile\``,
`2. Verify patches still apply: \`make checkouts patches\``,
`3. If patches fail, port them to the new version`,
`4. Push a version tag to trigger the build pipeline`,
``,
`### Links`,
`- [Talos Release Notes](https://github.com/siderolabs/talos/releases/tag/${latestVersion})`,
].join('\n'),
labels: ['upstream-update', 'talos'],
});
BODY="## Upstream update requires manual patch porting
- name: Create issue for RPi kernel update
if: steps.check.outputs.rpi_update == 'true'
uses: actions/github-script@v7
with:
script: |
const currentVersion = '${{ steps.check.outputs.rpi_current }}';
const latestVersion = '${{ steps.check.outputs.rpi_latest }}';
const title = `RPi kernel update available: ${currentVersion} → ${latestVersion}`;
Automated patch application failed. Manual intervention needed.
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'upstream-update',
});
| Component | Current | Latest | Update? |
|-----------|---------|--------|---------|
| Talos | \`${TALOS_CURRENT}\` | \`${TALOS_LATEST}\` | ${TALOS_UPDATE} |
| RPi kernel | \`${RPI_CURRENT}\` | \`${RPI_LATEST}\` | ${RPI_UPDATE} |
const existing = issues.data.find(i => i.title.includes('RPi kernel update'));
if (existing) {
console.log(`Issue already exists: #${existing.number}`);
return;
}
### Steps
1. Check out this repo and run \`scripts/auto-update.sh\` to see what fails
2. Port patches to the new upstream version
3. Verify: \`gmake checkouts patches && gmake checkouts-clean\`
4. Push changes — the next scheduled run will pick them up
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: title,
body: [
`## RPi Kernel Update Available`,
``,
`| | Version |`,
`|---|---|`,
`| Current (in pkgs patch) | \`${currentVersion}\` |`,
`| Latest stable | \`${latestVersion}\` |`,
``,
`### Steps`,
`1. Update the kernel version in the pkgs patch`,
`2. Verify the patch still applies: \`make checkouts patches\``,
`3. Test build: \`make kernel\``,
`4. Push a version tag to trigger the full build pipeline`,
``,
`### Links`,
`- [RPi Linux Releases](https://github.com/raspberrypi/linux/tags)`,
].join('\n'),
labels: ['upstream-update', 'kernel'],
});
### Links
- [Talos Releases](https://github.com/siderolabs/talos/releases)
- [RPi Linux Tags](https://github.com/raspberrypi/linux/tags)"
# Strip leading whitespace from heredoc-style indentation
BODY=$(echo "$BODY" | sed 's/^ //')
BODY_JSON=$(jq -Rs '.' <<< "$BODY")
# Check for existing open issue to avoid duplicates
EXISTING=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \
"${API}/repos/${REPO}/issues?state=open&type=issues&labels=upstream-update" \
| jq -r '[.[] | select(.title | contains("manual patch"))][0].id // empty')
if [ -n "$EXISTING" ]; then
echo "Issue already exists (id: $EXISTING), skipping creation"
exit 0
fi
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"title\":\"Upstream update requires manual patch porting\",\"body\":${BODY_JSON},\"labels\":[\"upstream-update\"]}" \
"${API}/repos/${REPO}/issues"
echo "Created issue for manual patch porting"

373
LICENSE Normal file
View File

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

126
Makefile
View File

@ -8,17 +8,27 @@
# make kernel # Build RPi kernel (~15-30 min on ARM64)
# make overlay # Build U-Boot + firmware + DTBs
# make installer # Build installer image + raw disk image
# make release # Tag images for release
# make release # Tag and push release images
# make clean # Remove checkouts and build artifacts
PKG_VERSION = v1.12.0
TALOS_VERSION = v1.12.3
PKG_VERSION = v1.13.0
TALOS_VERSION = v1.13.2
SBCOVERLAY_VERSION = main
# Prefer GNU coreutils (macOS: brew install gnu-sed coreutils)
export PATH := /opt/homebrew/opt/gnu-sed/libexec/gnubin:$(PATH)
REGISTRY ?= docker.io
REGISTRY_USERNAME ?= svrnty
TAG ?= $(shell git describe --tags --exact-match 2>/dev/null || echo dev)
# Docker Hub image names (project-specific)
KERNEL_IMAGE = $(REGISTRY)/$(REGISTRY_USERNAME)/talos-rpi5-kernel
OVERLAY_IMAGE = $(REGISTRY)/$(REGISTRY_USERNAME)/talos-rpi5-overlay
IMAGER_IMAGE = $(REGISTRY)/$(REGISTRY_USERNAME)/talos-rpi5-imager
INSTALLER_IMAGE = $(REGISTRY)/$(REGISTRY_USERNAME)/talos-rpi5-installer
# Public image name on Docker Hub (used by talosctl upgrade)
IMAGE_NAME ?= talos-rpi5
@ -35,11 +45,21 @@ PATCHES_DIRECTORY := $(PWD)/patches
PKGS_TAG = $(shell cd $(CHECKOUTS_DIRECTORY)/pkgs && git describe --tag --always --dirty --match v[0-9]\*)
TALOS_TAG = $(shell cd $(CHECKOUTS_DIRECTORY)/talos && git describe --tag --always --dirty --match v[0-9]\*)
SBCOVERLAY_TAG = $(shell cd $(CHECKOUTS_DIRECTORY)/sbc-raspberrypi5 && git describe --tag --always --dirty)-$(PKGS_TAG)
SBCOVERLAY_TAG = $(shell cd $(CHECKOUTS_DIRECTORY)/sbc-raspberrypi5 && git describe --tag --always)-$(PKGS_TAG)
# Build the --system-extension-image flags from the EXTENSIONS list
EXTENSION_FLAGS = $(foreach ext,$(EXTENSIONS),--system-extension-image=$(ext))
# Supply chain attestation flags (overrides upstream --provenance=false)
ATTESTATION_ARGS = --provenance=mode=max --sbom=true
# Common imager flags for overlay and extensions
IMAGER_COMMON_FLAGS = \
--overlay-name="rpi5" \
--overlay-image="$(OVERLAY_IMAGE):$(SBCOVERLAY_TAG)" \
--overlay-option="configTxtAppend=$$(cat $(PWD)/config/config.txt.append)" \
$(EXTENSION_FLAGS)
#
# Help
#
@ -61,6 +81,12 @@ help:
@echo " PKG_VERSION = $(PKG_VERSION)"
@echo " REGISTRY = $(REGISTRY)"
@echo " REGISTRY_USERNAME = $(REGISTRY_USERNAME)"
@echo ""
@echo "Images:"
@echo " KERNEL_IMAGE = $(KERNEL_IMAGE)"
@echo " OVERLAY_IMAGE = $(OVERLAY_IMAGE)"
@echo " IMAGER_IMAGE = $(IMAGER_IMAGE)"
@echo " INSTALLER_IMAGE = $(INSTALLER_IMAGE)"
#
# Checkouts
@ -79,7 +105,7 @@ checkouts-clean:
#
# Patches
#
.PHONY: patches-pkgs patches-talos patches
.PHONY: patches-pkgs patches-talos patches-overlay patches
patches-pkgs:
cd "$(CHECKOUTS_DIRECTORY)/pkgs" && \
git am "$(PATCHES_DIRECTORY)/siderolabs/pkgs/"*.patch
@ -88,66 +114,104 @@ patches-talos:
cd "$(CHECKOUTS_DIRECTORY)/talos" && \
git am "$(PATCHES_DIRECTORY)/siderolabs/talos/"*.patch
patches: patches-pkgs patches-talos
patches-overlay:
@cd "$(CHECKOUTS_DIRECTORY)/sbc-raspberrypi5" && \
GO_VER=$$(sed -n 's/^go //p' go.work | head -1) && \
GO_MINOR=$$(echo "$$GO_VER" | cut -d. -f1,2) && \
if [ "$$GO_MINOR" = "1.24" ]; then \
echo "Overlay Go $$GO_VER — applying Go toolchain patch (CVE fix)"; \
git am "$(PATCHES_DIRECTORY)/talos-rpi5/sbc-raspberrypi5/0001-"*.patch; \
else \
echo "Overlay Go $$GO_VER — skipping Go toolchain patch (CVEs fixed upstream)"; \
fi && \
git am "$(PATCHES_DIRECTORY)/talos-rpi5/sbc-raspberrypi5/0002-"*.patch
patches: patches-pkgs patches-talos patches-overlay
#
# Kernel
# Kernel — build and push the RPi downstream kernel
#
.PHONY: kernel
kernel:
cd "$(CHECKOUTS_DIRECTORY)/pkgs" && \
$(MAKE) \
REGISTRY=$(REGISTRY) USERNAME=$(REGISTRY_USERNAME) PUSH=true \
PLATFORM=linux/arm64 \
kernel
$(MAKE) docker-kernel \
TARGET_ARGS="--tag=$(KERNEL_IMAGE):$(PKGS_TAG) --push=true $(ATTESTATION_ARGS)" \
PLATFORM=linux/arm64
#
# Overlay
# Overlay — build U-Boot + firmware + DTBs
#
# The overlay's pkg.yaml references the kernel as PKGS_PREFIX/kernel:PKGS.
# We rewrite it to point to our project-specific kernel image name.
#
.PHONY: overlay
overlay:
@echo "SBCOVERLAY_TAG = $(SBCOVERLAY_TAG)"
@sed -i.bak 's|{{ .BUILD_ARG_PKGS_PREFIX }}/kernel:{{ .BUILD_ARG_PKGS }}|$(KERNEL_IMAGE):$(PKGS_TAG)|' \
"$(CHECKOUTS_DIRECTORY)/sbc-raspberrypi5/internal/base/pkg.yaml" && \
rm -f "$(CHECKOUTS_DIRECTORY)/sbc-raspberrypi5/internal/base/pkg.yaml.bak"
cd "$(CHECKOUTS_DIRECTORY)/sbc-raspberrypi5" && \
$(MAKE) \
REGISTRY=$(REGISTRY) USERNAME=$(REGISTRY_USERNAME) IMAGE_TAG=$(SBCOVERLAY_TAG) PUSH=true \
PKGS_PREFIX=$(REGISTRY)/$(REGISTRY_USERNAME) PKGS=$(PKGS_TAG) \
INSTALLER_ARCH=arm64 PLATFORM=linux/arm64 \
sbc-raspberrypi5
$(MAKE) docker-sbc-raspberrypi5 \
TARGET_ARGS="--tag=$(OVERLAY_IMAGE):$(SBCOVERLAY_TAG) --push=true $(ATTESTATION_ARGS)" \
INSTALLER_ARCH=arm64 PLATFORM=linux/arm64
#
# Installer / Disk Image
#
# Builds the imager, installer-base, and installer images step by step,
# pushing each to our project-specific Docker Hub repos.
#
.PHONY: installer
installer:
cd "$(CHECKOUTS_DIRECTORY)/talos" && \
$(MAKE) \
REGISTRY=$(REGISTRY) USERNAME=$(REGISTRY_USERNAME) PUSH=true \
PKG_KERNEL=$(REGISTRY)/$(REGISTRY_USERNAME)/kernel:$(PKGS_TAG) \
REGISTRY=$(REGISTRY) USERNAME=$(REGISTRY_USERNAME) \
PKG_KERNEL=$(KERNEL_IMAGE):$(PKGS_TAG) \
INSTALLER_ARCH=arm64 PLATFORM=linux/arm64 \
IMAGER_ARGS="--overlay-name=rpi5 --overlay-image=$(REGISTRY)/$(REGISTRY_USERNAME)/sbc-raspberrypi5:$(SBCOVERLAY_TAG) $(EXTENSION_FLAGS)" \
kernel initramfs imager installer-base installer && \
kernel initramfs && \
$(MAKE) \
REGISTRY=$(REGISTRY) USERNAME=$(REGISTRY_USERNAME) \
PKG_KERNEL=$(KERNEL_IMAGE):$(PKGS_TAG) \
INSTALLER_ARCH=arm64 PLATFORM=linux/arm64 \
target-imager \
TARGET_ARGS="--output type=image,name=$(IMAGER_IMAGE):$(TALOS_TAG),push=true $(ATTESTATION_ARGS)" && \
$(MAKE) \
REGISTRY=$(REGISTRY) USERNAME=$(REGISTRY_USERNAME) \
PKG_KERNEL=$(KERNEL_IMAGE):$(PKGS_TAG) \
INSTALLER_ARCH=arm64 PLATFORM=linux/arm64 \
target-installer-base \
TARGET_ARGS="--output type=image,name=$(INSTALLER_IMAGE):base-$(TALOS_TAG),push=true $(ATTESTATION_ARGS)" && \
docker pull $(IMAGER_IMAGE):$(TALOS_TAG) && \
docker run --rm -t -v ./_out:/out --privileged --network=host \
$(IMAGER_IMAGE):$(TALOS_TAG) \
installer --arch arm64 \
--base-installer-image="$(INSTALLER_IMAGE):base-$(TALOS_TAG)" \
$(IMAGER_COMMON_FLAGS) && \
crane push ./_out/installer-arm64.tar $(INSTALLER_IMAGE):$(TALOS_TAG) && \
printf "FROM $(INSTALLER_IMAGE):$(TALOS_TAG)\n" | docker buildx build \
--platform linux/arm64 \
$(ATTESTATION_ARGS) \
-t $(INSTALLER_IMAGE):$(TALOS_TAG) --push - && \
docker \
run --rm -t -v ./_out:/out -v /dev:/dev --privileged \
$(REGISTRY)/$(REGISTRY_USERNAME)/imager:$(TALOS_TAG) \
run --rm -t -v ./_out:/out -v /dev:/dev --privileged --network=host \
$(IMAGER_IMAGE):$(TALOS_TAG) \
metal --arch arm64 \
--base-installer-image="$(REGISTRY)/$(REGISTRY_USERNAME)/installer:$(TALOS_TAG)" \
--overlay-name="rpi5" \
--overlay-image="$(REGISTRY)/$(REGISTRY_USERNAME)/sbc-raspberrypi5:$(SBCOVERLAY_TAG)" \
--overlay-option="configTxtAppend=$$(cat $(PWD)/config/config.txt.append)" \
$(EXTENSION_FLAGS)
--base-installer-image="$(INSTALLER_IMAGE):$(TALOS_TAG)" \
$(IMAGER_COMMON_FLAGS)
#
# Release — tag images with the Git tag for stable references
#
.PHONY: release
release:
docker pull $(REGISTRY)/$(REGISTRY_USERNAME)/installer:$(TALOS_TAG) && \
docker tag $(REGISTRY)/$(REGISTRY_USERNAME)/installer:$(TALOS_TAG) $(REGISTRY)/$(REGISTRY_USERNAME)/$(IMAGE_NAME):$(TAG) && \
docker push $(REGISTRY)/$(REGISTRY_USERNAME)/$(IMAGE_NAME):$(TAG)
docker buildx imagetools create \
-t $(REGISTRY)/$(REGISTRY_USERNAME)/$(IMAGE_NAME):$(TAG) \
$(INSTALLER_IMAGE):$(TALOS_TAG)
#
# Clean
#
.PHONY: clean
clean: checkouts-clean
rm -rf _out
rm -rf checkouts/_out

308
README.md
View File

@ -1,95 +1,277 @@
# Talos CM5 Builder
Custom Talos Linux images for Raspberry Pi CM5 on Compute Blade hardware.
Custom Talos Linux images for Raspberry Pi 5 / CM5 on Compute Blade hardware.
<a href="https://git.openharbor.io/svrnty/talos-rpi5/releases" target="_blank"><img src="https://img.shields.io/badge/releases-Gitea-green?logo=gitea&logoColor=white" alt="Gitea Releases"></a>
<a href="https://hub.docker.com/r/svrnty/talos-rpi5" target="_blank"><img src="https://img.shields.io/docker/v/svrnty/talos-rpi5?sort=semver&label=talos-rpi5&logo=docker&arch=arm64" alt="Docker Hub"></a>
<a href="https://hub.docker.com/r/svrnty/talos-rpi5" target="_blank"><img src="https://img.shields.io/docker/pulls/svrnty/talos-rpi5?logo=docker" alt="Docker Pulls"></a>
<a href="https://hub.docker.com/r/svrnty/talos-rpi5" target="_blank"><img src="https://img.shields.io/docker/image-size/svrnty/talos-rpi5?sort=semver&logo=docker&arch=arm64" alt="Docker Image Size"></a>
The official Talos Image Factory does not support CM5 — the mainline kernel lacks CM5 device trees and RP1 driver support. This builder uses the RPi downstream kernel (via [talos-rpi5/talos-builder](https://github.com/talos-rpi5/talos-builder) patches) to produce working CM5 images with our extensions and overclock config.
## What it builds
## Current versions
- **Installer image**`docker.io/svrnty/talos-rpi5:<tag>` (for `talosctl upgrade`)
- **Raw disk image** → Gitea release `metal-arm64.raw.zst` (for eMMC flashing)
| Component | Version |
|-----------|---------|
| Talos Linux | <a href="https://github.com/siderolabs/talos" target="_blank"><img src="https://img.shields.io/badge/talos-v1.13.2-blue?logo=kubernetes&logoColor=white" alt="Talos version"></a> |
| RPi Kernel | <a href="https://github.com/raspberrypi/linux" target="_blank"><img src="https://img.shields.io/badge/kernel-6.12.47-blue?logo=linux&logoColor=white" alt="Kernel version"></a> |
| iscsi-tools | <a href="https://github.com/siderolabs/extensions" target="_blank"><img src="https://img.shields.io/badge/iscsi--tools-v0.1.6-blue?logo=docker" alt="iscsi-tools version"></a> |
| util-linux-tools | <a href="https://github.com/siderolabs/extensions" target="_blank"><img src="https://img.shields.io/badge/util--linux--tools-2.40.4-blue?logo=docker" alt="util-linux-tools version"></a> |
Baked-in config:
- RPi downstream kernel with CM5/RP1 support
- Overclock: 2.6GHz (`arm_freq=2600`, `over_voltage_delta=50000`, `arm_boost=1`)
- Extensions: `iscsi-tools`, `util-linux-tools`
## Image tags
Release images are published to <a href="https://hub.docker.com/r/svrnty/talos-rpi5" target="_blank"><code>docker.io/svrnty/talos-rpi5</code></a> with the format:
```
v<talos>-k<kernel>-<revision>
```
For example: `v1.13.2-k6.12.47-1`
| Segment | Meaning |
|---------|---------|
| `v1.13.2` | Upstream Talos Linux version |
| `k6.12.47` | RPi downstream kernel version |
| `1` | Build revision (bumped for config/patch changes on the same upstream versions) |
## Usage
### Building locally (ARM64 host required)
### Install from raw disk image
Download `metal-arm64.raw.zst` from the [latest release](../../releases/latest) and flash to eMMC:
```bash
make checkouts patches # Clone and patch sources
make kernel # Build RPi kernel
make overlay # Build SBC overlay
make installer # Build installer + disk image
zstd -d metal-arm64.raw.zst -o metal-arm64.raw
# Flash to eMMC/SD via your preferred tool (dd, balenaEtcher, etc.)
```
### CI/CD (Gitea Actions)
Push a version tag to trigger an automated build:
### Upgrade an existing node
```bash
git tag v1.11.5-1
git push origin v1.11.5-1
talosctl upgrade --image docker.io/svrnty/talos-rpi5:v1.13.2-k6.12.47-1 --nodes <node-ip>
```
The pipeline runs on the ARM64 self-hosted runner and:
1. Builds the kernel, overlay, and installer
2. Pushes the installer image to Docker Hub
3. Creates a Gitea release with the raw disk image
In-place upgrades are fully supported. The image includes patches to force GRUB with `--no-nvram` on arm64 (working around the RPi5/CM5 `SetVariableRT` firmware limitation) and to handle the SBC EFI-only disk layout (no separate BOOT partition).
### Upstream update checks
### What's included
A weekly scheduled workflow checks for new Talos and RPi kernel releases and creates Gitea issues when updates are available.
- RPi downstream kernel with CM5/RP1 support (4K page size, aligned with upstream Talos)
- GRUB bootloader with `--no-nvram` for reliable `talosctl upgrade` on RPi5/CM5
- SBC EFI-only boot layout support (probe, install, revert all fall back to EFI partition when BOOT partition is absent)
- Fallback to classic bind mounts on kernels without `open_tree` support (Linux <6.15)
- Overclock: 2.6GHz (`arm_freq=2600`, `over_voltage_delta=50000`, `arm_boost=1`)
- PCIe Gen 3 enabled for NVMe (~800 MB/s, via `dtparam=pciex1_gen=3` in `config.txt`)
- Serial console on GPIO UART0 (`dtoverlay=uart0-pi5`, 115200 baud, even parity)
- Extensions: `iscsi-tools`, `util-linux-tools`
## CI Secrets
## Known issues
| Secret | Description |
|--------|-------------|
| `REGISTRY_USERNAME` | Docker Hub username (org-level) |
| `REGISTRY_PASSWORD` | Docker Hub access token (org-level) |
### Serial console (read-only)
## Runner Setup (Apple Silicon Mac Mini)
The serial console outputs kernel logs, boot messages, and panic traces over GPIO UART0 (`ttyAMA0`) at 115200 baud with **even parity**. Talos Linux has no interactive shell — the console is **read-only** and cannot accept input.
The build runner needs:
- Docker Desktop with Buildx (arm64 native)
- Gitea `act_runner` registered with labels: `self-hosted`, `macOS`, `arm64`
- Sufficient disk space for kernel builds (~20GB)
On Pi5/CM5, GPIO 14/15 are not mapped to UART by default (unlike Pi4). The `dtoverlay=uart0-pi5` device tree overlay is required and is applied via `configTxtAppend` (after `disable-bt` and `disable-wifi` — ordering matters).
#### Wiring (Compute Blade)
Connect a **3.3V** USB-to-UART adapter to the front UART header (3-pin: GND, RX, TX). Only two wires are needed for read-only monitoring:
```
USB-UART Adapter Compute Blade (Front UART)
┌──────────────┐ ┌──────────────┐
│ GND ├──────────┤ GND │
│ RX ├──────────┤ TX │
│ TX │ │ RX │
└──────────────┘ └──────────────┘
3.3V logic 3.3V GPIO
```
- **Adapter RX** connects to **Blade TX** (adapter receives data from the blade)
- **GND** to **GND** (common ground reference)
- Adapter TX is not needed for read-only monitoring (Talos has no shell to send input to)
> **Warning:** CM5 GPIO is **3.3V only**. A 5V logic adapter will crash or damage the board.
Tested with: [USB to TTL Serial Cable (FT232RNL)](https://www.pishop.ca/product/usb-to-ttl-serial-cable-for-raspberry-pi-5-debugging-ft232rnl-chip/) set to 3.3V mode.
#### Reading the console
```bash
# Install act_runner via Homebrew
brew install act_runner
# macOS — use screen with even parity
screen /dev/cu.usbserial-XXXXXXXX 115200,,cse
# Or download directly
curl -sL https://gitea.com/gitea/act_runner/releases/latest/download/act_runner-darwin-arm64 -o act_runner
chmod +x act_runner
# Register
./act_runner register \
--instance https://git.openharbor.io \
--token <runner-token> \
--name mac-mini \
--labels self-hosted,macOS,arm64
# Run as service
./act_runner daemon
# Linux — configure with stty, then read with cat
stty -F /dev/ttyUSB0 115200 cs8 parenb -parodd
cat /dev/ttyUSB0
```
## Structure
*Upstream: <a href="https://github.com/talos-rpi5/talos-builder/issues/4" target="_blank">talos-builder#4</a>*
### Install disk config ignored on SBCs
Talos ignores the `machine.install.disk` config field on SBC platforms. You **must flash the disk image directly** to your target disk (eMMC, SD, NVMe). For NVMe boot, `dd` the metal image to the NVMe drive and configure the EEPROM boot order (`BOOT_ORDER=0xf416`, `PCIE_PROBE=1`).
*Upstream: <a href="https://github.com/talos-rpi5/talos-builder/issues/22" target="_blank">talos-builder#22</a>*
## Patches
| Patch | Target | Description |
|-------|--------|-------------|
| `0001` (pkgs) | Kernel | RPi downstream kernel 6.12.x with CM5/RP1 device tree and driver support |
| `0001` (talos) | Modules | arm64 kernel module list for RPi downstream kernel |
| `0002` (talos) | GRUB | `--no-nvram` for `grub-install` on arm64 (U-Boot lacks EFI `SetVariable`) |
| `0003` (talos) | Bootloader | Force GRUB over sd-boot on arm64 (sd-boot crashes without EFI runtime) |
| `0004` (talos) | Runtime | Fallback to classic bind mounts on kernels without `open_tree` (Linux <6.15) |
| `0005` (talos) | GRUB | Handle missing BOOT partition for SBC EFI-only disk layouts (install path) |
| `0006` (talos) | GRUB | EFI-at-/boot fallback for BOOT-less SBC layouts on the upgrade path |
| `0007` (talos) | Config | Wait up to 5 min for STATE volume on slow-init disks (CM5 eMMC) — prevents maintenance-mode fall-through on `talosctl upgrade` |
| `0008` (talos) | Imager | Respect `--insecure` flag for Overlay and OverlayInstaller assets (build-tool only) |
| `0001` (overlay) | Toolchain | Bump Go to 1.24.13 (CVE fix) |
| `0002` (overlay) | Upgrade | Detect EFI mount path for SBC layouts (no BOOT partition) |
## Roadmap
This project targets production-ready Talos clusters on RPi5/CM5 hardware.
| Status | Milestone | Description |
|--------|-----------|-------------|
| Tested | **4K page size** | Aligned with upstream Talos kernel config. Reduces memory overhead and improves workload compatibility (Longhorn, jemalloc, F2FS, etc.). |
| Tested | **Reliable in-place upgrades** | Force GRUB bootloader with `--no-nvram` on arm64, handle SBC EFI-only disk layout, tolerate slow eMMC init on CM5 (waits up to 5 min for STATE before falling back to maintenance). Verified end-to-end via canonical 3-CP rolling upgrade `v1.12.4 → v1.13.2`. |
| Tested | **Kernel <6.15 compatibility** | Unconditional `open_tree` capability check — falls back to classic bind mounts on RPi downstream kernel 6.12.x. |
| Tested | **Serial console** | GPIO UART0 (`ttyAMA0`) via `dtoverlay=uart0-pi5`. Read-only output at 115200 baud, even parity. Verified on Compute Blade with FT232RNL adapter. |
| Tested | **NVMe boot support** | `dd` image to NVMe + set EEPROM `BOOT_ORDER=0xf416` and `PCIE_PROBE=1`. Verified on 1TB Kingston NVMe on Compute Blade. |
## NVMe boot
The kernel has NVMe built-in (`CONFIG_BLK_DEV_NVME=y`), so booting from NVMe should work by flashing the disk image directly and configuring the RPi5/CM5 EEPROM.
### 1. Flash the image to NVMe
Connect the NVMe drive via a USB adapter and flash:
```bash
zstd -d metal-arm64.raw.zst | sudo dd of=/dev/<nvme-device> bs=4M status=progress
sync
```
.gitea/workflows/
build.yaml # Build pipeline (tag push trigger)
check-updates.yaml # Upstream update checker (weekly cron)
Makefile # Build orchestration
config/
config.txt.append # CM5 overclock settings
extensions.yaml # System extensions list
scripts/
check-upstream.sh # Version comparison script
patches/
siderolabs/
pkgs/0001-*.patch # RPi kernel patch
talos/0001-*.patch # Module list patch
### 2. Configure EEPROM boot order
Use `rpiboot` to update the CM5 EEPROM. Clone the usbboot repo and edit the boot config:
```bash
git clone --depth=1 https://github.com/raspberrypi/usbboot
cd usbboot && make
# Edit the EEPROM config for CM5
cp recovery/boot.conf recovery/boot.conf.bak
```
Add or update these values in `recovery/boot.conf`:
```ini
BOOT_ORDER=0xf416
PCIE_PROBE=1
```
Then flash via USB with the CM5 in USB boot mode (hold nRPIBOOT or disable eMMC boot on your carrier board):
```bash
sudo ./rpiboot -d recovery
```
`BOOT_ORDER` is read right-to-left: try NVMe (`6`) first, then SD (`1`), then USB (`4`), then restart (`f`). `PCIE_PROBE=1` is required for non-HAT+ NVMe adapters (Compute Blade, most M.2 carrier boards).
### 3. Boot from NVMe
Power on. The RPi firmware should find the boot partition on NVMe, load U-Boot, and boot Talos.
### Optional: enable PCIe Gen 3
PCIe Gen 3 doubles NVMe throughput (~400 MB/s → ~800 MB/s). Not officially certified by Raspberry Pi but stable on most NVMe drives.
**New installs** — PCIe Gen 3 is enabled by default in images built from this repo (`config.txt.append` includes `dtparam=pciex1_gen=3`).
**Existing nodes** — After a `talosctl upgrade`, the overlay rewrites `config.txt` with the baked-in settings (including PCIe Gen 3). If you need to enable it manually on an older image:
1. Power off the node and remove the NVMe drive
2. Connect via USB adapter and mount the first (EFI) partition
3. Add to `config.txt` under the `[pi5]` section:
```ini
dtparam=pciex1_gen=3
```
4. Unmount, reinstall the drive, and power on
To verify after boot:
```bash
talosctl -n <ip> dmesg | grep -i pcie
# Look for "Gen 3" in the PCIe link speed output
```
## Reference architecture
Recommended storage layout for production Talos clusters on CM5 Compute Blade hardware. Each CM5 has a 32GB eMMC and an M.2 NVMe slot.
| Role | Boot disk | NVMe | EEPROM `BOOT_ORDER` | Rationale |
|------|-----------|------|---------------------|-----------|
| **Control plane** | NVMe | Talos OS | `0xf416` (NVMe first) | etcd lives on the STATE partition — NVMe gives ~800 MB/s sequential and ~50K random IOPS for fast consensus and snapshot I/O. |
| **Postgres / storage** | eMMC | Data (`/var/mnt/data`) | `0xf214` (eMMC first) | Talos OS on eMMC keeps `talosctl upgrade` away from the data drive. The full 1TB NVMe is dedicated to database storage via `machine.disks`. |
| **Compute workers** | eMMC | None | `0xf214` (eMMC first) | Stateless workloads — no local storage needed. eMMC is more than enough for Talos OS (~2.2GB used). |
### Storage configuration
**Control planes** — no extra config needed. Talos installs to the NVMe automatically when it's the boot disk.
**Postgres / storage nodes** — add `machine.disks` to mount the NVMe as a data volume:
```yaml
machine:
disks:
- device: /dev/nvme0n1
partitions:
- mountpoint: /var/mnt/data
```
Talos automatically partitions, formats (XFS), and mounts the NVMe on first boot. The data persists across `talosctl upgrade` since upgrades only touch the boot disk (eMMC).
**Compute workers** — no storage config needed.
### EEPROM setup
All CM5 modules require `PCIE_PROBE=1` for NVMe detection on Compute Blade (non-HAT+ carrier). Set via `rpiboot`:
```ini
# recovery/boot.conf
BOOT_ORDER=0xf416 # NVMe-first (control planes)
# or
BOOT_ORDER=0xf214 # eMMC-first (storage/compute workers)
PCIE_PROBE=1 # always required
```
### Overclock
All node types share the same overclock config (baked into the image via `config.txt.append`):
```ini
dtparam=pciex1_gen=3
dtoverlay=uart0-pi5
arm_freq=2600
over_voltage_delta=50000
arm_boost=1
```
Verified stable at 44.6°C max under full CPU + memory + disk stress across 10 nodes with Compute Blade heatsinks.
## Building
For local builds, CI/CD setup, runner configuration, and project structure, see [TECHNICAL.md](TECHNICAL.md).
## License
This project is licensed under the [Mozilla Public License 2.0](LICENSE).
It builds upon the following MPL 2.0 licensed upstream projects:
- [siderolabs/talos](https://github.com/siderolabs/talos) — Talos Linux OS
- [siderolabs/pkgs](https://github.com/siderolabs/pkgs) — Talos package definitions
- [talos-rpi5/sbc-raspberrypi5](https://github.com/talos-rpi5/sbc-raspberrypi5) — Raspberry Pi 5 SBC overlay
Our patches to these projects are in the `patches/` directory and are distributed under the same MPL 2.0 terms.

87
TECHNICAL.md Normal file
View File

@ -0,0 +1,87 @@
# Technical Guide
Build infrastructure, CI/CD configuration, and project structure for the Talos CM5 Builder.
## Building locally (ARM64 host required)
```bash
make checkouts patches # Clone and patch upstream sources
make kernel # Build RPi kernel
make overlay # Build SBC overlay
make installer # Build installer + disk image
```
## CI/CD (Gitea Actions)
Push a version tag to trigger an automated build:
```bash
git tag v1.12.3-k6.12.47-2
git push origin v1.12.3-k6.12.47-2
```
The pipeline runs on the ARM64 self-hosted runner and:
1. Builds the kernel, overlay, and installer
2. Attaches SBOM attestation (cosign + syft)
3. Pushes the installer image to Docker Hub
4. Creates a Gitea release with the raw disk image
### Upstream update checks
A weekly scheduled workflow checks for new Talos and RPi kernel releases and creates Gitea issues when updates are available.
## CI Secrets
| Secret | Description |
|--------|-------------|
| `REGISTRY_USERNAME` | Docker Hub username (org-level) |
| `REGISTRY_PASSWORD` | Docker Hub access token (org-level) |
## Runner Setup (Apple Silicon Mac Mini)
The build runner needs:
- Docker Desktop with Buildx (arm64 native)
- Gitea `act_runner` registered with labels: `self-hosted`, `macOS`, `arm64`
- Sufficient disk space for kernel builds (~20GB)
```bash
# Install act_runner via Homebrew
brew install act_runner
# Or download directly
curl -sL https://gitea.com/gitea/act_runner/releases/latest/download/act_runner-darwin-arm64 -o act_runner
chmod +x act_runner
# Register
./act_runner register \
--instance https://git.openharbor.io \
--token <runner-token> \
--name mac-mini \
--labels self-hosted,macOS,arm64
# Run as service
./act_runner daemon
```
## Project Structure
```
.gitea/workflows/
build.yaml # Build pipeline (tag push trigger)
check-updates.yaml # Upstream update checker (weekly cron)
Makefile # Build orchestration
config/
config.txt.append # CM5 overclock settings
extensions.yaml # System extensions list
scripts/
check-upstream.sh # Version comparison script
patches/
siderolabs/
pkgs/0001-*.patch # RPi kernel patch
talos/0001-*.patch # Module list patch
talos/0002-*.patch # Skip NVRAM writes for GRUB on arm64
talos/0003-*.patch # Force GRUB bootloader on arm64
talos-rpi5/
sbc-raspberrypi5/ # Overlay patches (Go toolchain bump)
cosign.pub # Public key for verifying image attestations
```

View File

@ -1,3 +1,8 @@
# Enable PCIe Gen 3 for NVMe (~800 MB/s vs ~400 MB/s Gen 2)
dtparam=pciex1_gen=3
# Enable GPIO UART0 (ttyAMA0) on Pi5/CM5 for serial console
# Must load AFTER disable-bt and disable-wifi overlays in config.txt
dtoverlay=uart0-pi5
# CM5 Overclock — 2.6GHz stable on Compute Blade with heatsink
arm_freq=2600
over_voltage_delta=50000

4
cosign.pub Normal file
View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPkZxXgi280kakXdVwjygCvIs5chd
Ns/gANqNilq0OZDkmcAzeaKJRkRbiDjqNeW1JLv1CYwN/1olypEdVyjLoQ==
-----END PUBLIC KEY-----

File diff suppressed because it is too large Load Diff

View File

@ -1,390 +0,0 @@
From 87c9e57548bc0aef15d67967a68cc9185216361d Mon Sep 17 00:00:00 2001
From: Patrick Hunziker <patrick@hunziker.io>
Date: Sat, 8 Nov 2025 11:31:15 +0100
Subject: [PATCH] [PATCH] Patched for Raspberry Pi 5
---
hack/modules-arm64.txt | 343 +++++++++++++++++++----------------------
1 file changed, 160 insertions(+), 183 deletions(-)
diff --git a/hack/modules-arm64.txt b/hack/modules-arm64.txt
index 6c48a7465..95c3ee669 100644
--- a/hack/modules-arm64.txt
+++ b/hack/modules-arm64.txt
@@ -1,213 +1,190 @@
+modules.builtin.bin
+modules.builtin.modinfo
+modules.builtin.alias.bin
+modules.dep.bin
+modules.alias
+modules.weakdep
+modules.order
+modules.symbols.bin
+modules.dep
+modules.alias.bin
+modules.softdep
+modules.symbols
+modules.devname
+kernel/crypto/xor.ko
+kernel/crypto/async_tx/async_xor.ko
kernel/crypto/async_tx/async_memcpy.ko
kernel/crypto/async_tx/async_pq.ko
kernel/crypto/async_tx/async_raid6_recov.ko
kernel/crypto/async_tx/async_tx.ko
-kernel/crypto/async_tx/async_xor.ko
-kernel/crypto/xor.ko
-kernel/drivers/acpi/video.ko
-kernel/drivers/ata/ahci.ko
-kernel/drivers/ata/pata_amd.ko
-kernel/drivers/ata/pata_marvell.ko
-kernel/drivers/ata/pata_oldpiix.ko
-kernel/drivers/ata/pata_sch.ko
-kernel/drivers/block/nbd.ko
-kernel/drivers/block/ublk_drv.ko
+kernel/crypto/blake2b_generic.ko
+kernel/crypto/xxhash_generic.ko
+kernel/crypto/zstd.ko
+kernel/net/ipv4/ip_gre.ko
+kernel/net/tls/tls.ko
+kernel/net/openvswitch/vport-gre.ko
+kernel/drivers/infiniband/sw/rxe/rdma_rxe.ko
+kernel/drivers/infiniband/hw/mlx4/mlx4_ib.ko
+kernel/drivers/infiniband/hw/mlx5/mlx5_ib.ko
+kernel/drivers/infiniband/hw/hns/hns-roce-hw-v2.ko
+kernel/drivers/misc/bcm2835_smi.ko
kernel/drivers/crypto/tegra/tegra-se.ko
-kernel/drivers/gpu/drm/display/drm_dp_aux_bus.ko
-kernel/drivers/gpu/drm/drm_buddy.ko
-kernel/drivers/gpu/drm/drm_exec.ko
-kernel/drivers/gpu/drm/drm_suballoc_helper.ko
+kernel/drivers/acpi/video.ko
+kernel/drivers/gpu/drm/vc4/vc4.ko
kernel/drivers/gpu/drm/drm_ttm_helper.ko
-kernel/drivers/gpu/drm/drm_vram_helper.ko
-kernel/drivers/gpu/drm/hisilicon/hibmc/hibmc-drm.ko
+kernel/drivers/gpu/drm/ttm/ttm.ko
+kernel/drivers/gpu/drm/drm_suballoc_helper.ko
kernel/drivers/gpu/drm/scheduler/gpu-sched.ko
+kernel/drivers/gpu/drm/drm_buddy.ko
kernel/drivers/gpu/drm/tegra/tegra-drm.ko
-kernel/drivers/gpu/drm/ttm/ttm.ko
+kernel/drivers/gpu/drm/amd/amdxcp/amdxcp.ko
+kernel/drivers/gpu/drm/amd/amdgpu/amdgpu.ko
+kernel/drivers/gpu/drm/drm_vram_helper.ko
+kernel/drivers/gpu/drm/hisilicon/hibmc/hibmc-drm.ko
+kernel/drivers/gpu/drm/panfrost/panfrost.ko
+kernel/drivers/gpu/drm/display/drm_dp_aux_bus.ko
+kernel/drivers/gpu/drm/v3d/v3d.ko
+kernel/drivers/gpu/drm/drm_exec.ko
kernel/drivers/gpu/host1x/host1x.ko
-kernel/drivers/hid/hid-a4tech.ko
-kernel/drivers/hid/hid-apple.ko
-kernel/drivers/hid/hid-belkin.ko
-kernel/drivers/hid/hid-cherry.ko
-kernel/drivers/hid/hid-chicony.ko
-kernel/drivers/hid/hid-cypress.ko
-kernel/drivers/hid/hid-ezkey.ko
-kernel/drivers/hid/hid-gyration.ko
-kernel/drivers/hid/hid-ite.ko
-kernel/drivers/hid/hid-kensington.ko
-kernel/drivers/hid/hid-lg-g15.ko
-kernel/drivers/hid/hid-logitech.ko
-kernel/drivers/hid/hid-microsoft.ko
-kernel/drivers/hid/hid-monterey.ko
-kernel/drivers/hid/hid-petalynx.ko
-kernel/drivers/hid/hid-pl.ko
-kernel/drivers/hid/hid-samsung.ko
-kernel/drivers/hid/hid-sunplus.ko
-kernel/drivers/hid/hid-topseed.ko
-kernel/drivers/hwmon/i5k_amb.ko
-kernel/drivers/i2c/algos/i2c-algo-bit.ko
-kernel/drivers/i2c/busses/i2c-i801.ko
-kernel/drivers/i2c/i2c-mux.ko
-kernel/drivers/infiniband/core/ib_umad.ko
-kernel/drivers/infiniband/core/ib_uverbs.ko
-kernel/drivers/infiniband/core/rdma_ucm.ko
-kernel/drivers/infiniband/hw/hns/hns-roce-hw-v2.ko
-kernel/drivers/infiniband/hw/irdma/irdma.ko
-kernel/drivers/infiniband/hw/mlx4/mlx4_ib.ko
-kernel/drivers/infiniband/hw/mlx5/mlx5_ib.ko
-kernel/drivers/infiniband/sw/rxe/rdma_rxe.ko
-kernel/drivers/irqchip/irq-imx-mu-msi.ko
-kernel/drivers/mailbox/bcm-flexrm-mailbox.ko
-kernel/drivers/md/bcache/bcache.ko
-kernel/drivers/md/dm-bio-prison.ko
-kernel/drivers/md/dm-cache-smq.ko
-kernel/drivers/md/dm-cache.ko
-kernel/drivers/md/dm-multipath.ko
-kernel/drivers/md/dm-raid.ko
-kernel/drivers/md/dm-round-robin.ko
-kernel/drivers/md/dm-thin-pool.ko
-kernel/drivers/md/persistent-data/dm-persistent-data.ko
-kernel/drivers/md/raid456.ko
-kernel/drivers/misc/hpilo.ko
-kernel/drivers/mmc/host/sdhci_f_sdh30.ko
-kernel/drivers/mmc/host/sdhci-acpi.ko
-kernel/drivers/mmc/host/sdhci-brcmstb.ko
-kernel/drivers/mmc/host/sdhci-cadence.ko
-kernel/drivers/mmc/host/sdhci-iproc.ko
-kernel/drivers/mmc/host/sdhci-msm.ko
-kernel/drivers/mmc/host/sdhci-of-arasan.ko
-kernel/drivers/mmc/host/sdhci-of-dwcmshc.ko
-kernel/drivers/mmc/host/sdhci-of-esdhc.ko
-kernel/drivers/mmc/host/sdhci-pci.ko
-kernel/drivers/mmc/host/sdhci-pltfm.ko
-kernel/drivers/mmc/host/sdhci-tegra.ko
-kernel/drivers/mmc/host/sdhci-xenon-driver.ko
+kernel/drivers/nvme/host/nvme-rdma.ko
+kernel/drivers/nvme/target/nvme-loop.ko
+kernel/drivers/nvme/target/nvmet-fc.ko
+kernel/drivers/nvme/target/nvmet.ko
+kernel/drivers/nvme/target/nvmet-tcp.ko
+kernel/drivers/nvme/target/nvmet-rdma.ko
+kernel/drivers/nvme/common/nvme-auth.ko
+kernel/drivers/net/mdio/mdio-mux-meson-gxl.ko
+kernel/drivers/net/thunderbolt/thunderbolt_net.ko
kernel/drivers/net/ethernet/amazon/ena/ena.ko
-kernel/drivers/net/ethernet/aquantia/atlantic/atlantic.ko
kernel/drivers/net/ethernet/atheros/alx/alx.ko
-kernel/drivers/net/ethernet/broadcom/bnx2.ko
-kernel/drivers/net/ethernet/broadcom/bnx2x/bnx2x.ko
-kernel/drivers/net/ethernet/broadcom/bnxt/bnxt_en.ko
-kernel/drivers/net/ethernet/broadcom/tg3.ko
-kernel/drivers/net/ethernet/cavium/common/cavium_ptp.ko
-kernel/drivers/net/ethernet/cisco/enic/enic.ko
kernel/drivers/net/ethernet/google/gve/gve.ko
-kernel/drivers/net/ethernet/hisilicon/hip04_eth.ko
+kernel/drivers/net/ethernet/sfc/sfc.ko
+kernel/drivers/net/ethernet/sfc/siena/sfc-siena.ko
+kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko
+kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_core.ko
+kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko
+kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_pci.ko
+kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_minimal.ko
+kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_i2c.ko
+kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_spectrum.ko
+kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_core.ko
+kernel/drivers/net/ethernet/mellanox/mlxfw/mlxfw.ko
+kernel/drivers/net/ethernet/chelsio/cxgb4/cxgb4.ko
+kernel/drivers/net/ethernet/chelsio/cxgb3/cxgb3.ko
+kernel/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/ch_ipsec.ko
+kernel/drivers/net/ethernet/chelsio/cxgb/cxgb.ko
+kernel/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf.ko
kernel/drivers/net/ethernet/hisilicon/hisi_femac.ko
-kernel/drivers/net/ethernet/hisilicon/hix5hd2_gmac.ko
-kernel/drivers/net/ethernet/hisilicon/hns_mdio.ko
+kernel/drivers/net/ethernet/hisilicon/hip04_eth.ko
kernel/drivers/net/ethernet/hisilicon/hns/hnae.ko
kernel/drivers/net/ethernet/hisilicon/hns/hns_dsaf.ko
kernel/drivers/net/ethernet/hisilicon/hns/hns_enet_drv.ko
-kernel/drivers/net/ethernet/hisilicon/hns3/hclge-common.ko
kernel/drivers/net/ethernet/hisilicon/hns3/hclge.ko
+kernel/drivers/net/ethernet/hisilicon/hns3/hns3.ko
kernel/drivers/net/ethernet/hisilicon/hns3/hclgevf.ko
kernel/drivers/net/ethernet/hisilicon/hns3/hnae3.ko
-kernel/drivers/net/ethernet/hisilicon/hns3/hns3.ko
-kernel/drivers/net/ethernet/intel/e100.ko
-kernel/drivers/net/ethernet/intel/e1000/e1000.ko
-kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko
-kernel/drivers/net/ethernet/intel/i40e/i40e.ko
-kernel/drivers/net/ethernet/intel/iavf/iavf.ko
-kernel/drivers/net/ethernet/intel/ice/ice.ko
-kernel/drivers/net/ethernet/intel/igb/igb.ko
-kernel/drivers/net/ethernet/intel/igbvf/igbvf.ko
-kernel/drivers/net/ethernet/intel/igc/igc.ko
-kernel/drivers/net/ethernet/intel/ixgbe/ixgbe.ko
-kernel/drivers/net/ethernet/intel/ixgbevf/ixgbevf.ko
-kernel/drivers/net/ethernet/intel/libeth/libeth.ko
-kernel/drivers/net/ethernet/intel/libie/libie.ko
-kernel/drivers/net/ethernet/marvell/sky2.ko
-kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_core.ko
-kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko
-kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko
-kernel/drivers/net/ethernet/mellanox/mlxfw/mlxfw.ko
-kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_core.ko
-kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_i2c.ko
-kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_minimal.ko
-kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_pci.ko
-kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_spectrum.ko
-kernel/drivers/net/ethernet/qlogic/qed/qed.ko
-kernel/drivers/net/ethernet/qlogic/qede/qede.ko
-kernel/drivers/net/ethernet/qlogic/qlcnic/qlcnic.ko
-kernel/drivers/net/ethernet/realtek/8139too.ko
-kernel/drivers/net/ethernet/realtek/r8169.ko
-kernel/drivers/net/ethernet/sfc/sfc.ko
-kernel/drivers/net/ethernet/sfc/siena/sfc-siena.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-pci.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-platform.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/stmmac.ko
-kernel/drivers/net/mdio.ko
-kernel/drivers/net/pcs/pcs_xpcs.ko
-kernel/drivers/net/phy/dp83867.ko
-kernel/drivers/net/usb/r8152.ko
-kernel/drivers/net/vmxnet3/vmxnet3.ko
+kernel/drivers/net/ethernet/hisilicon/hns3/hclge-common.ko
+kernel/drivers/net/ethernet/hisilicon/hns_mdio.ko
+kernel/drivers/net/ethernet/hisilicon/hix5hd2_gmac.ko
+kernel/drivers/net/phy/ax88796b.ko
+kernel/drivers/net/usb/sr9800.ko
+kernel/drivers/net/usb/rndis_host.ko
+kernel/drivers/net/usb/cdc_mbim.ko
+kernel/drivers/net/usb/zaurus.ko
+kernel/drivers/net/usb/kalmia.ko
+kernel/drivers/net/usb/usbnet.ko
+kernel/drivers/net/usb/dm9601.ko
+kernel/drivers/net/usb/plusb.ko
+kernel/drivers/net/usb/gl620a.ko
+kernel/drivers/net/usb/cdc_subset.ko
+kernel/drivers/net/usb/smsc75xx.ko
+kernel/drivers/net/usb/cx82310_eth.ko
+kernel/drivers/net/usb/mcs7830.ko
+kernel/drivers/net/usb/ch9200.ko
+kernel/drivers/net/usb/smsc95xx.ko
+kernel/drivers/net/usb/huawei_cdc_ncm.ko
+kernel/drivers/net/usb/r8153_ecm.ko
+kernel/drivers/net/usb/ax88179_178a.ko
+kernel/drivers/net/usb/cdc_eem.ko
+kernel/drivers/net/usb/sierra_net.ko
+kernel/drivers/net/usb/asix.ko
+kernel/drivers/net/usb/net1080.ko
+kernel/drivers/net/usb/cdc-phonet.ko
+kernel/drivers/net/usb/aqc111.ko
+kernel/drivers/net/usb/qmi_wwan.ko
+kernel/drivers/net/usb/sr9700.ko
+kernel/drivers/net/usb/lg-vl600.ko
+kernel/drivers/net/usb/int51x1.ko
+kernel/drivers/net/usb/cdc_ncm.ko
+kernel/drivers/net/usb/cdc_ether.ko
kernel/drivers/net/vrf.ko
-kernel/drivers/nvme/common/nvme-auth.ko
-kernel/drivers/nvme/host/nvme-rdma.ko
-kernel/drivers/nvme/host/nvme.ko
-kernel/drivers/nvme/target/nvme-loop.ko
-kernel/drivers/nvme/target/nvmet-fc.ko
-kernel/drivers/nvme/target/nvmet-rdma.ko
-kernel/drivers/nvme/target/nvmet-tcp.ko
-kernel/drivers/nvme/target/nvmet.ko
-kernel/drivers/perf/hisilicon/hisi_pcie_pmu.ko
-kernel/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.ko
-kernel/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.ko
-kernel/drivers/perf/hisilicon/hisi_uncore_hha_pmu.ko
-kernel/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.ko
-kernel/drivers/perf/hisilicon/hisi_uncore_pa_pmu.ko
-kernel/drivers/perf/hisilicon/hisi_uncore_pmu.ko
-kernel/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.ko
-kernel/drivers/perf/hisilicon/hisi_uncore_uc_pmu.ko
-kernel/drivers/perf/hisilicon/hns3_pmu.ko
+kernel/drivers/irqchip/irq-imx-mu-msi.ko
+kernel/drivers/input/misc/uinput.ko
+kernel/drivers/mailbox/bcm-flexrm-mailbox.ko
+kernel/drivers/hwmon/i5k_amb.ko
kernel/drivers/scsi/fcoe/libfcoe.ko
-kernel/drivers/scsi/hisi_sas/hisi_sas_main.ko
+kernel/drivers/scsi/mpi3mr/mpi3mr.ko
+kernel/drivers/scsi/qla2xxx/qla2xxx.ko
+kernel/drivers/scsi/lpfc/lpfc.ko
+kernel/drivers/scsi/qedf/qedf.ko
kernel/drivers/scsi/hisi_sas/hisi_sas_v1_hw.ko
-kernel/drivers/scsi/hisi_sas/hisi_sas_v2_hw.ko
+kernel/drivers/scsi/hisi_sas/hisi_sas_main.ko
kernel/drivers/scsi/hisi_sas/hisi_sas_v3_hw.ko
-kernel/drivers/scsi/hpsa.ko
+kernel/drivers/scsi/hisi_sas/hisi_sas_v2_hw.ko
kernel/drivers/scsi/libfc/libfc.ko
-kernel/drivers/scsi/lpfc/lpfc.ko
-kernel/drivers/scsi/megaraid/megaraid_sas.ko
-kernel/drivers/scsi/mpi3mr/mpi3mr.ko
-kernel/drivers/scsi/mpt3sas/mpt3sas.ko
-kernel/drivers/scsi/qedf/qedf.ko
-kernel/drivers/scsi/qla2xxx/qla2xxx.ko
-kernel/drivers/scsi/smartpqi/smartpqi.ko
-kernel/drivers/uio/uio_pci_generic.ko
-kernel/drivers/uio/uio.ko
-kernel/drivers/usb/serial/ch341.ko
-kernel/drivers/usb/serial/cp210x.ko
-kernel/drivers/usb/serial/ftdi_sio.ko
-kernel/drivers/usb/serial/pl2303.ko
-kernel/drivers/vfio/pci/vfio-pci-core.ko
+kernel/drivers/thunderbolt/thunderbolt.ko
kernel/drivers/vfio/pci/vfio-pci.ko
-kernel/drivers/vfio/vfio_iommu_type1.ko
+kernel/drivers/vfio/pci/vfio-pci-core.ko
kernel/drivers/vfio/vfio.ko
-kernel/drivers/virtio/virtio_balloon.ko
-kernel/drivers/virtio/virtio_input.ko
-kernel/drivers/virtio/virtio_mmio.ko
-kernel/drivers/virtio/virtio_pci_legacy_dev.ko
-kernel/drivers/virtio/virtio_pci_modern_dev.ko
-kernel/drivers/virtio/virtio_pci.ko
+kernel/drivers/vfio/vfio_iommu_type1.ko
+kernel/drivers/usb/class/cdc-wdm.ko
+kernel/drivers/usb/serial/usb_wwan.ko
+kernel/drivers/usb/serial/option.ko
kernel/drivers/watchdog/sbsa_gwdt.ko
+kernel/drivers/md/dm-round-robin.ko
+kernel/drivers/md/raid456.ko
+kernel/drivers/md/persistent-data/dm-persistent-data.ko
+kernel/drivers/md/dm-multipath.ko
+kernel/drivers/md/dm-bio-prison.ko
+kernel/drivers/md/dm-thin-pool.ko
+kernel/drivers/md/dm-raid.ko
+kernel/drivers/md/dm-cache.ko
+kernel/drivers/md/dm-cache-smq.ko
+kernel/drivers/dma/bcm-sba-raid.ko
+kernel/drivers/uio/uio.ko
+kernel/drivers/uio/uio_pci_generic.ko
+kernel/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.ko
+kernel/drivers/perf/hisilicon/hns3_pmu.ko
+kernel/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.ko
+kernel/drivers/perf/hisilicon/hisi_pcie_pmu.ko
+kernel/drivers/perf/hisilicon/hisi_uncore_pa_pmu.ko
+kernel/drivers/perf/hisilicon/hisi_uncore_hha_pmu.ko
+kernel/drivers/perf/hisilicon/hisi_uncore_uc_pmu.ko
+kernel/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.ko
+kernel/drivers/perf/hisilicon/hisi_uncore_pmu.ko
+kernel/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.ko
+kernel/drivers/block/nbd.ko
+kernel/drivers/media/usb/uvc/uvcvideo.ko
+kernel/drivers/media/common/uvc.ko
+kernel/drivers/media/common/videobuf2/videobuf2-vmalloc.ko
+kernel/drivers/media/common/videobuf2/videobuf2-memops.ko
+kernel/drivers/media/common/videobuf2/videobuf2-common.ko
+kernel/drivers/media/common/videobuf2/videobuf2-v4l2.ko
+kernel/drivers/media/v4l2-core/v4l2-dv-timings.ko
+kernel/drivers/media/v4l2-core/videodev.ko
+kernel/drivers/media/mc/mc.ko
+kernel/arch/arm64/lib/xor-neon.ko
+kernel/lib/raid6/raid6_pq.ko
kernel/lib/objagg.ko
kernel/lib/parman.ko
-kernel/lib/raid6/raid6_pq.ko
-kernel/net/ipv4/ip_gre.ko
-kernel/net/openvswitch/vport-gre.ko
-kernel/net/tls/tls.ko
+kernel/fs/btrfs/btrfs.ko
+kernel/fs/nfsd/nfsd.ko
+kernel/fs/binfmt_misc.ko
+kernel/sound/core/snd-pcm-dmaengine.ko
+kernel/sound/core/snd.ko
+kernel/sound/core/snd-pcm.ko
+kernel/sound/core/snd-timer.ko
+kernel/sound/soc/codecs/snd-soc-hdmi-codec.ko
+kernel/sound/soc/snd-soc-core.ko
+kernel/sound/soundcore.ko
modules.builtin
-modules.builtin.modinfo
-modules.order
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,226 @@
From 6a36399e47baeed36db12deab50e2d9d4a5914ac Mon Sep 17 00:00:00 2001
From: Mathias Beaulieu-Duncan <mathias@svrnty.io>
Date: Mon, 18 May 2026 19:02:29 -0400
Subject: [PATCH] Replace modules-arm64.txt with RPi 6.12.47 module set
The upstream Talos modules-arm64.txt is the list of kernel modules
included in the arm64 initramfs. Its entries are computed against
upstream's mainline kernel build; when Talos is built with the RPi
downstream kernel (6.12.47), several listed modules either don't
exist or are gated by upstream-only Kconfig options, causing the
initramfs install step to fail:
xargs -a modules-arm64.txt -I {} install -D \
usr/lib/modules/${KERNEL_VERSION}/{} ...
-> exit 123 (one or more inputs missing)
Replace the list with the intersection of:
- upstream's "wanted in initramfs" entries
- modules actually produced by our patched RPi 6.12.47 build
Verified by extracting the kernel image's /usr/lib/modules tree and
intersecting it with upstream's file. 155 modules survive, covering
all storage/network/crypto modules needed for headless boot. The 86
dropped modules are SATA/HID/upstream-only crypto helpers irrelevant
on RPi5/CM5.
---
hack/modules-arm64.txt | 86 ------------------------------------------
1 file changed, 86 deletions(-)
diff --git a/hack/modules-arm64.txt b/hack/modules-arm64.txt
index 9888d2b0f..a09360aed 100644
--- a/hack/modules-arm64.txt
+++ b/hack/modules-arm64.txt
@@ -4,21 +4,13 @@ kernel/crypto/async_tx/async_pq.ko
kernel/crypto/async_tx/async_raid6_recov.ko
kernel/crypto/async_tx/async_tx.ko
kernel/crypto/async_tx/async_xor.ko
-kernel/crypto/hkdf.ko
kernel/crypto/xor.ko
kernel/drivers/acpi/video.ko
-kernel/drivers/ata/ahci.ko
-kernel/drivers/ata/pata_amd.ko
-kernel/drivers/ata/pata_marvell.ko
-kernel/drivers/ata/pata_oldpiix.ko
-kernel/drivers/ata/pata_sch.ko
kernel/drivers/block/nbd.ko
kernel/drivers/block/ublk_drv.ko
kernel/drivers/crypto/tegra/tegra-se.ko
kernel/drivers/gpu/drm/drm_buddy.ko
kernel/drivers/gpu/drm/drm_exec.ko
-kernel/drivers/gpu/drm/drm_gpuvm.ko
-kernel/drivers/gpu/drm/drm_panel_backlight_quirks.ko
kernel/drivers/gpu/drm/drm_suballoc_helper.ko
kernel/drivers/gpu/drm/drm_ttm_helper.ko
kernel/drivers/gpu/drm/drm_vram_helper.ko
@@ -27,43 +19,21 @@ kernel/drivers/gpu/drm/scheduler/gpu-sched.ko
kernel/drivers/gpu/drm/tegra/tegra-drm.ko
kernel/drivers/gpu/drm/ttm/ttm.ko
kernel/drivers/gpu/host1x/host1x.ko
-kernel/drivers/hid/hid-a4tech.ko
-kernel/drivers/hid/hid-apple.ko
-kernel/drivers/hid/hid-belkin.ko
-kernel/drivers/hid/hid-cherry.ko
-kernel/drivers/hid/hid-chicony.ko
-kernel/drivers/hid/hid-cypress.ko
-kernel/drivers/hid/hid-ezkey.ko
-kernel/drivers/hid/hid-gyration.ko
-kernel/drivers/hid/hid-ite.ko
-kernel/drivers/hid/hid-kensington.ko
-kernel/drivers/hid/hid-lg-g15.ko
-kernel/drivers/hid/hid-logitech.ko
kernel/drivers/hid/hid-microsoft.ko
kernel/drivers/hid/hid-monterey.ko
kernel/drivers/hid/hid-multitouch.ko
kernel/drivers/hid/hid-petalynx.ko
kernel/drivers/hid/hid-pl.ko
-kernel/drivers/hid/hid-samsung.ko
-kernel/drivers/hid/hid-sunplus.ko
-kernel/drivers/hid/hid-topseed.ko
kernel/drivers/hwmon/i5k_amb.ko
kernel/drivers/i2c/algos/i2c-algo-bit.ko
-kernel/drivers/i2c/busses/i2c-i801.ko
-kernel/drivers/i2c/i2c-mux.ko
-kernel/drivers/infiniband/core/ib_umad.ko
-kernel/drivers/infiniband/core/ib_uverbs.ko
-kernel/drivers/infiniband/core/rdma_ucm.ko
kernel/drivers/infiniband/hw/hns/hns-roce-hw-v2.ko
kernel/drivers/infiniband/hw/irdma/irdma.ko
kernel/drivers/infiniband/hw/mlx4/mlx4_ib.ko
kernel/drivers/infiniband/hw/mlx5/mlx5_ib.ko
kernel/drivers/infiniband/sw/rxe/rdma_rxe.ko
-kernel/drivers/irqchip/irq-bcm2712-mip.ko
kernel/drivers/irqchip/irq-imx-mu-msi.ko
kernel/drivers/leds/led-class-multicolor.ko
kernel/drivers/mailbox/bcm-flexrm-mailbox.ko
-kernel/drivers/md/bcache/bcache.ko
kernel/drivers/md/dm-bio-prison.ko
kernel/drivers/md/dm-cache-smq.ko
kernel/drivers/md/dm-cache.ko
@@ -74,30 +44,8 @@ kernel/drivers/md/dm-round-robin.ko
kernel/drivers/md/dm-thin-pool.ko
kernel/drivers/md/persistent-data/dm-persistent-data.ko
kernel/drivers/md/raid456.ko
-kernel/drivers/misc/hpilo.ko
-kernel/drivers/mmc/host/sdhci_f_sdh30.ko
-kernel/drivers/mmc/host/sdhci-acpi.ko
-kernel/drivers/mmc/host/sdhci-brcmstb.ko
-kernel/drivers/mmc/host/sdhci-cadence.ko
-kernel/drivers/mmc/host/sdhci-iproc.ko
-kernel/drivers/mmc/host/sdhci-msm.ko
-kernel/drivers/mmc/host/sdhci-of-arasan.ko
-kernel/drivers/mmc/host/sdhci-of-dwcmshc.ko
-kernel/drivers/mmc/host/sdhci-of-esdhc.ko
-kernel/drivers/mmc/host/sdhci-pci.ko
-kernel/drivers/mmc/host/sdhci-pltfm.ko
-kernel/drivers/mmc/host/sdhci-tegra.ko
-kernel/drivers/mmc/host/sdhci-uhs2.ko
-kernel/drivers/mmc/host/sdhci-xenon-driver.ko
kernel/drivers/net/ethernet/amazon/ena/ena.ko
-kernel/drivers/net/ethernet/aquantia/atlantic/atlantic.ko
kernel/drivers/net/ethernet/atheros/alx/alx.ko
-kernel/drivers/net/ethernet/broadcom/bnx2.ko
-kernel/drivers/net/ethernet/broadcom/bnx2x/bnx2x.ko
-kernel/drivers/net/ethernet/broadcom/bnxt/bnxt_en.ko
-kernel/drivers/net/ethernet/broadcom/tg3.ko
-kernel/drivers/net/ethernet/cavium/common/cavium_ptp.ko
-kernel/drivers/net/ethernet/cisco/enic/enic.ko
kernel/drivers/net/ethernet/google/gve/gve.ko
kernel/drivers/net/ethernet/hisilicon/hip04_eth.ko
kernel/drivers/net/ethernet/hisilicon/hisi_femac.ko
@@ -123,12 +71,8 @@ kernel/drivers/net/ethernet/intel/igbvf/igbvf.ko
kernel/drivers/net/ethernet/intel/igc/igc.ko
kernel/drivers/net/ethernet/intel/ixgbe/ixgbe.ko
kernel/drivers/net/ethernet/intel/ixgbevf/ixgbevf.ko
-kernel/drivers/net/ethernet/intel/libeth/libeth_xdp.ko
kernel/drivers/net/ethernet/intel/libeth/libeth.ko
-kernel/drivers/net/ethernet/intel/libie/libie_adminq.ko
-kernel/drivers/net/ethernet/intel/libie/libie_fwlog.ko
kernel/drivers/net/ethernet/intel/libie/libie.ko
-kernel/drivers/net/ethernet/marvell/sky2.ko
kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_core.ko
kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko
kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko
@@ -138,11 +82,6 @@ kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_i2c.ko
kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_minimal.ko
kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_pci.ko
kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_spectrum.ko
-kernel/drivers/net/ethernet/qlogic/qed/qed.ko
-kernel/drivers/net/ethernet/qlogic/qede/qede.ko
-kernel/drivers/net/ethernet/qlogic/qlcnic/qlcnic.ko
-kernel/drivers/net/ethernet/realtek/8139too.ko
-kernel/drivers/net/ethernet/realtek/r8169.ko
kernel/drivers/net/ethernet/sfc/sfc.ko
kernel/drivers/net/ethernet/sfc/siena/sfc-siena.ko
kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.ko
@@ -152,25 +91,17 @@ kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.ko
kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.ko
kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.ko
kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.ko
kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.ko
-kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-sun55i.ko
kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.ko
kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.ko
kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.ko
kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-pci.ko
kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-platform.ko
kernel/drivers/net/ethernet/stmicro/stmmac/stmmac.ko
-kernel/drivers/net/mdio.ko
kernel/drivers/net/pcs/pcs_xpcs.ko
-kernel/drivers/net/pcs/pcs-rzn1-miic.ko
-kernel/drivers/net/phy/dp83867.ko
-kernel/drivers/net/usb/r8152.ko
-kernel/drivers/net/vmxnet3/vmxnet3.ko
kernel/drivers/net/vrf.ko
kernel/drivers/nvme/common/nvme-auth.ko
kernel/drivers/nvme/host/nvme-rdma.ko
-kernel/drivers/nvme/host/nvme.ko
kernel/drivers/nvme/target/nvme-loop.ko
kernel/drivers/nvme/target/nvmet-fc.ko
kernel/drivers/nvme/target/nvmet-rdma.ko
@@ -191,21 +122,13 @@ kernel/drivers/scsi/hisi_sas/hisi_sas_main.ko
kernel/drivers/scsi/hisi_sas/hisi_sas_v1_hw.ko
kernel/drivers/scsi/hisi_sas/hisi_sas_v2_hw.ko
kernel/drivers/scsi/hisi_sas/hisi_sas_v3_hw.ko
-kernel/drivers/scsi/hpsa.ko
kernel/drivers/scsi/libfc/libfc.ko
kernel/drivers/scsi/lpfc/lpfc.ko
-kernel/drivers/scsi/megaraid/megaraid_sas.ko
kernel/drivers/scsi/mpi3mr/mpi3mr.ko
-kernel/drivers/scsi/mpt3sas/mpt3sas.ko
kernel/drivers/scsi/qedf/qedf.ko
kernel/drivers/scsi/qla2xxx/qla2xxx.ko
-kernel/drivers/scsi/smartpqi/smartpqi.ko
kernel/drivers/uio/uio_pci_generic.ko
kernel/drivers/uio/uio.ko
-kernel/drivers/usb/serial/ch341.ko
-kernel/drivers/usb/serial/cp210x.ko
-kernel/drivers/usb/serial/ftdi_sio.ko
-kernel/drivers/usb/serial/pl2303.ko
kernel/drivers/vdpa/mlx5/mlx5_vdpa.ko
kernel/drivers/vdpa/octeon_ep/octep_vdpa.ko
kernel/drivers/vdpa/solidrun/snet_vdpa.ko
@@ -221,12 +144,6 @@ kernel/drivers/vfio/vfio_iommu_type1.ko
kernel/drivers/vfio/vfio.ko
kernel/drivers/vhost/vhost_vdpa.ko
kernel/drivers/vhost/vringh.ko
-kernel/drivers/virtio/virtio_balloon.ko
-kernel/drivers/virtio/virtio_input.ko
-kernel/drivers/virtio/virtio_mmio.ko
-kernel/drivers/virtio/virtio_pci_legacy_dev.ko
-kernel/drivers/virtio/virtio_pci_modern_dev.ko
-kernel/drivers/virtio/virtio_pci.ko
kernel/drivers/virtio/virtio_vdpa.ko
kernel/drivers/watchdog/sbsa_gwdt.ko
kernel/lib/objagg.ko
@@ -236,6 +153,3 @@ kernel/net/ipv4/ip_gre.ko
kernel/net/ipv6/ip6_gre.ko
kernel/net/openvswitch/vport-gre.ko
kernel/net/tls/tls.ko
-modules.builtin
-modules.builtin.modinfo
-modules.order
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,33 @@
From 2db8797af370535aba7c5694cd291bba8e6c5a67 Mon Sep 17 00:00:00 2001
From: Mathias Beaulieu-Duncan <mathias@svrnty.io>
Date: Fri, 13 Feb 2026 19:08:41 -0500
Subject: [PATCH 2/3] Skip NVRAM writes for GRUB on arm64
On arm64 platforms like RPi5/CM5, the UEFI firmware (U-Boot) does not
support EFI runtime SetVariable, causing grub-install to fail when
efibootmgr tries to create NVRAM boot entries. Since arm64-efi systems
boot by convention (finding BOOTAA64.efi on the ESP), NVRAM boot
entries are unnecessary.
Pass --no-nvram to grub-install on arm64 to allow in-place upgrades
via talosctl upgrade to succeed.
---
.../machined/pkg/runtime/v1alpha1/bootloader/grub/install.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go
index 6f5c9f8..766374b 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go
@@ -196,7 +196,7 @@ func (c *Config) install(opts options.InstallOptions) (*options.InstallResult, e
args = append(args, "--efi-directory="+filepath.Join(opts.MountPrefix, constants.EFIMountPoint))
}
- if opts.ImageMode {
+ if opts.ImageMode || opts.Arch == arm64 {
args = append(args, "--no-nvram")
}
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,74 @@
From 1393b3f013e758f6bb52d14006d3a7e7db348930 Mon Sep 17 00:00:00 2001
From: Mathias Beaulieu-Duncan <mathias@svrnty.io>
Date: Fri, 13 Feb 2026 19:08:58 -0500
Subject: [PATCH 3/3] Force GRUB bootloader on arm64
On arm64 platforms like RPi5/CM5, the UEFI firmware (U-Boot) exposes
/sys/firmware/efi but does not support EFI runtime SetVariable. This
causes sd-boot to fail when trying to write EFI variables during
installation/upgrade.
Force GRUB on arm64 in two places:
- NewAuto(): When no existing bootloader is detected, create a GRUB
config instead of sd-boot. This handles fresh installs.
- Probe(): When an existing GRUB config is not found, skip sd-boot
probing and return a fresh GRUB config. This handles the first
upgrade on SBC images that ship with sd-boot UKI files — the
upgrade transitions from sd-boot to GRUB automatically.
Combined with the --no-nvram patch and the SBC EFI-only layout patch,
this enables reliable in-place upgrades via talosctl upgrade on
RPi5/CM5 hardware, including the first upgrade from a fresh flash.
Ref: https://github.com/siderolabs/talos/issues/10859
Ref: https://github.com/talos-rpi5/talos-builder/issues/21
---
.../machined/pkg/runtime/v1alpha1/bootloader/bootloader.go | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader.go
index f084e09..5c388c1 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader.go
@@ -8,6 +8,7 @@ package bootloader
import (
"fmt"
"os"
+ goruntime "runtime"
"github.com/siderolabs/go-blockdevice/v2/block"
"github.com/siderolabs/go-blockdevice/v2/partitioning/gpt"
@@ -56,7 +57,17 @@ func Probe(disk string, options options.ProbeOptions) (Bootloader, error) {
return grubBootloader, nil
}
+ // On arm64, sd-boot requires EFI SetVariable runtime service which is
+ // not available on platforms like RPi5/CM5 (U-Boot). Skip sd-boot
+ // probing and return a fresh GRUB config to transition from sd-boot
+ // to GRUB on the first upgrade.
+ if goruntime.GOARCH == "arm64" {
+ options.Logf("arm64: no GRUB config found, creating new GRUB bootloader (sd-boot not supported)")
+
+ return grub.NewConfig(), nil
+ }
+
sdbootBootloader, err := sdboot.Probe(disk, options)
if err != nil {
return nil, err
@@ -73,6 +84,10 @@ func Probe(disk string, options options.ProbeOptions) (Bootloader, error) {
// NewAuto returns a new bootloader based on auto-detection.
func NewAuto() Bootloader {
+ if goruntime.GOARCH == "arm64" {
+ return grub.NewConfig()
+ }
+
if sdboot.IsUEFIBoot() {
return sdboot.New()
}
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,45 @@
From 98366401143dcc31c056a7d96242775f9ba013ca Mon Sep 17 00:00:00 2001
From: Mathias Beaulieu-Duncan <mathias@svrnty.io>
Date: Sat, 14 Feb 2026 14:05:15 -0500
Subject: [PATCH 4/4] Fallback to classic bind mounts on kernels without
open_tree support
The open_tree syscall on anonymous filesystem file descriptors was added
in kernel 6.15.0. Talos previously only checked for this capability when
running in container mode, assuming bare metal always had a sufficiently
new kernel. However, platforms like RPi5/CM5 use the RPi downstream
kernel (6.12.x) which lacks this feature, causing shadow bind mount
failures for /etc files and cascading network initialization failures.
Remove the InContainer() gate so the OpentreeOnAnonymousFS() capability
check runs on all platforms, enabling the classic (OSRoot) fallback when
the kernel does not support the newer mount API.
---
.../pkg/runtime/v1alpha2/v1alpha2_controller.go | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go
index 29b297654..653a45d57 100644
--- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go
+++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go
@@ -113,11 +113,12 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error
networkBindMountTarget = constants.SystemResolvedPath
- // While running in container, we don't have control over kernel version
- // shipped with the machine. If the kernel does not support open_tree syscall
- // on anonymous filesystem file descriptors, we need to fallback to the classic,
- // less secure mode. This capability was added in kernel 6.15.0.
- if ctrl.v1alpha1Runtime.State().Platform().Mode().InContainer() {
+ // If the kernel does not support open_tree syscall on anonymous filesystem
+ // file descriptors, we need to fallback to the classic, less secure mode.
+ // This capability was added in kernel 6.15.0. This check is needed for
+ // containers (where the host kernel is unknown) and for bare metal platforms
+ // running older kernels such as the RPi downstream kernel (6.12.x).
+ {
opentreeOnAnonymous, err := runtime.KernelCapabilities().OpentreeOnAnonymousFS()
if err != nil {
return err
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,296 @@
From cf9dfa043b21b393003ca4c2facebdd8e570f547 Mon Sep 17 00:00:00 2001
From: Mathias Beaulieu-Duncan <mathias@svrnty.io>
Date: Mon, 16 Feb 2026 11:31:08 -0500
Subject: [PATCH] Handle missing BOOT partition for GRUB on SBC layouts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
On SBC platforms like RPi5/CM5, the disk layout has no separate BOOT
(XFS) partition — all boot assets (kernel, initramfs, GRUB config,
EFI bootloader, firmware) reside on a single EFI (VFAT) partition.
The GRUB bootloader probe, install, and revert functions all assumed a
BOOT partition exists, causing talosctl upgrade to fail with "partition
with label BOOT not found" on these layouts.
Fix all three code paths to fall back to mounting the EFI partition at
/boot when the BOOT partition is absent:
- probe.go: Try EFI at /boot as fallback when BOOT is missing, so
the upgrade can detect the existing GRUB installation
- install.go: Probe for BOOT existence before including it in mount
specs; when absent, mount EFI at /boot and adjust --efi-directory
- revert.go: Fall back to EFI at /boot for rollback operations
This enables in-place upgrades via talosctl upgrade on SBC platforms
that use EFI-only disk layouts.
---
.../runtime/v1alpha1/bootloader/grub/grub.go | 2 +
.../v1alpha1/bootloader/grub/install.go | 49 +++++++++--
.../runtime/v1alpha1/bootloader/grub/probe.go | 83 ++++++++++++-------
.../v1alpha1/bootloader/grub/revert.go | 33 ++++++--
4 files changed, 125 insertions(+), 42 deletions(-)
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go
index 0cb5b1d4d..093ffecd9 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go
@@ -30,6 +30,8 @@ type Config struct {
Fallback BootLabel
Entries map[BootLabel]MenuEntry
AddResetOption bool
+
+ bootFromEFI bool
}
// MenuEntry represents a grub menu entry in the grub config file.
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go
index d0af22d98..f1ea13e5a 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go
@@ -34,12 +34,12 @@ const (
// Install validates the grub configuration and writes it to the disk.
func (c *Config) Install(opts options.InstallOptions) (*options.InstallResult, error) {
- mountSpecs := []mount.Spec{
- {
- PartitionLabel: constants.BootPartitionLabel,
- FilesystemType: partition.FilesystemTypeXFS,
- MountTarget: filepath.Join(opts.MountPrefix, constants.BootMountPoint),
- },
+ var mountSpecs []mount.Spec
+
+ bootMountSpec := mount.Spec{
+ PartitionLabel: constants.BootPartitionLabel,
+ FilesystemType: partition.FilesystemTypeXFS,
+ MountTarget: filepath.Join(opts.MountPrefix, constants.BootMountPoint),
}
efiMountSpec := mount.Spec{
@@ -48,6 +48,23 @@ func (c *Config) Install(opts options.InstallOptions) (*options.InstallResult, e
MountTarget: filepath.Join(opts.MountPrefix, constants.EFIMountPoint),
}
+ // check if the BOOT partition is present (absent on SBC layouts like RPi5/CM5)
+ if err := mount.PartitionOp(
+ opts.BootDisk,
+ []mount.Spec{bootMountSpec},
+ func() error {
+ return nil
+ },
+ []blkid.ProbeOption{
+ blkid.WithSkipLocking(true),
+ },
+ nil,
+ nil,
+ opts.BlkidInfo,
+ ); err == nil {
+ mountSpecs = append(mountSpecs, bootMountSpec)
+ }
+
var efiFound bool
// check if the EFI partition is present
@@ -65,12 +82,21 @@ func (c *Config) Install(opts options.InstallOptions) (*options.InstallResult, e
opts.BlkidInfo,
); err == nil {
efiFound = true
- }
- if efiFound {
+ if len(mountSpecs) == 0 {
+ // No BOOT partition (SBC layout): mount EFI at /boot so GRUB
+ // can find kernel/initramfs/grub.cfg in the expected place.
+ efiMountSpec.MountTarget = filepath.Join(opts.MountPrefix, constants.BootMountPoint)
+ c.bootFromEFI = true
+ }
+
mountSpecs = append(mountSpecs, efiMountSpec)
}
+ if len(mountSpecs) == 0 {
+ return nil, fmt.Errorf("neither BOOT nor EFI partition found on disk %s", opts.BootDisk)
+ }
+
err := mount.PartitionOp(
opts.BootDisk,
mountSpecs,
@@ -318,7 +344,12 @@ func (c *Config) runGrubInstall(ctx context.Context, opts options.InstallOptions
}
if efiMode {
- args = append(args, "--efi-directory="+filepath.Join(opts.MountPrefix, constants.EFIMountPoint))
+ efiDir := constants.EFIMountPoint
+ if c.bootFromEFI {
+ efiDir = constants.BootMountPoint
+ }
+
+ args = append(args, "--efi-directory="+filepath.Join(opts.MountPrefix, efiDir))
}
if opts.ImageMode || opts.Arch == arm64 {
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/probe.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/probe.go
index f5755592f..771ca2a90 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/probe.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/probe.go
@@ -19,6 +19,31 @@ import (
func ProbeWithCallback(disk string, options options.ProbeOptions, callback func(*Config) error) (*Config, error) {
var grubConf *Config
+ readConfig := func() error {
+ var err error
+
+ grubConf, err = Read(ConfigPath)
+ if err != nil {
+ return err
+ }
+
+ if grubConf != nil && callback != nil {
+ return callback(grubConf)
+ }
+
+ if grubConf == nil {
+ options.Logf("GRUB: config not found")
+ }
+
+ return nil
+ }
+
+ probeOpts := []mountv3.ManagerOption{
+ mountv3.WithSkipIfMounted(),
+ mountv3.WithReadOnly(),
+ }
+
+ // Try BOOT partition first (standard layout)
if err := mount.PartitionOp(
disk,
[]mount.Spec{
@@ -28,40 +53,42 @@ func ProbeWithCallback(disk string, options options.ProbeOptions, callback func(
MountTarget: constants.BootMountPoint,
},
},
- func() error {
- var err error
-
- grubConf, err = Read(ConfigPath)
- if err != nil {
- return err
- }
-
- if grubConf != nil && callback != nil {
- return callback(grubConf)
- }
-
- if grubConf == nil {
- options.Logf("GRUB: config not found")
- }
-
- return nil
- },
+ readConfig,
options.BlockProbeOptions,
- []mountv3.ManagerOption{
- mountv3.WithSkipIfMounted(),
- mountv3.WithReadOnly(),
- },
+ probeOpts,
nil,
nil,
); err != nil {
- if xerrors.TagIs[mount.NotFoundTag](err) {
- // if partitions are not found, it means GRUB is not installed
- options.Logf("GRUB: BOOT partition not found, skipping probing")
-
- return nil, nil
+ if !xerrors.TagIs[mount.NotFoundTag](err) {
+ return nil, err
}
- return nil, err
+ // BOOT not found, try EFI partition mounted at /boot (SBC layout)
+ options.Logf("GRUB: BOOT partition not found, trying EFI partition")
+
+ if err := mount.PartitionOp(
+ disk,
+ []mount.Spec{
+ {
+ PartitionLabel: constants.EFIPartitionLabel,
+ FilesystemType: partition.FilesystemTypeVFAT,
+ MountTarget: constants.BootMountPoint,
+ },
+ },
+ readConfig,
+ options.BlockProbeOptions,
+ probeOpts,
+ nil,
+ nil,
+ ); err != nil {
+ if xerrors.TagIs[mount.NotFoundTag](err) {
+ options.Logf("GRUB: neither BOOT nor EFI partition found, skipping probing")
+
+ return nil, nil
+ }
+
+ return nil, err
+ }
}
return grubConf, nil
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go
index 3e514d65d..95628193b 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go
@@ -26,6 +26,10 @@ func (c *Config) Revert(disk string) error {
return fmt.Errorf("cannot revert bootloader: %w", bootloaderNotInstalledError{})
}
+ revertOpts := []mountv3.ManagerOption{
+ mountv3.WithSkipIfMounted(),
+ }
+
err := mount.PartitionOp(
disk,
[]mount.Spec{
@@ -37,14 +41,33 @@ func (c *Config) Revert(disk string) error {
},
c.revert,
nil,
- []mountv3.ManagerOption{
- mountv3.WithSkipIfMounted(),
- },
+ revertOpts,
nil,
nil,
)
- if err != nil && !xerrors.TagIs[mount.NotFoundTag](err) {
- return err
+ if err != nil {
+ if !xerrors.TagIs[mount.NotFoundTag](err) {
+ return err
+ }
+
+ // BOOT not found, try EFI partition mounted at /boot (SBC layout)
+ if err := mount.PartitionOp(
+ disk,
+ []mount.Spec{
+ {
+ PartitionLabel: constants.EFIPartitionLabel,
+ FilesystemType: partition.FilesystemTypeVFAT,
+ MountTarget: constants.BootMountPoint,
+ },
+ },
+ c.revert,
+ nil,
+ revertOpts,
+ nil,
+ nil,
+ ); err != nil && !xerrors.TagIs[mount.NotFoundTag](err) {
+ return err
+ }
}
return nil
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,92 @@
From a50511de74db08a0b4426be88bb52ce860a008a4 Mon Sep 17 00:00:00 2001
From: builder <build@svrnty.io>
Date: Fri, 22 May 2026 16:10:42 -0400
Subject: [PATCH] grub: EFI-at-/boot fallback for BOOT-less SBC layout in
Upgrade path
---
.../v1alpha1/bootloader/grub/upgrade.go | 43 +++++++++++++++----
1 file changed, 35 insertions(+), 8 deletions(-)
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/upgrade.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/upgrade.go
index 9e3f86b84..e99bf2939 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/upgrade.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/upgrade.go
@@ -6,6 +6,7 @@ package grub
import (
"context"
+ "fmt"
"path/filepath"
"github.com/siderolabs/go-blockdevice/v2/blkid"
@@ -18,12 +19,12 @@ import (
// Upgrade copies new boot assets and updates grub configuration on an existing installation.
func (c *Config) Upgrade(opts options.InstallOptions) (*options.InstallResult, error) {
- mountSpecs := []mount.Spec{
- {
- PartitionLabel: constants.BootPartitionLabel,
- FilesystemType: partition.FilesystemTypeXFS,
- MountTarget: filepath.Join(opts.MountPrefix, constants.BootMountPoint),
- },
+ var mountSpecs []mount.Spec
+
+ bootMountSpec := mount.Spec{
+ PartitionLabel: constants.BootPartitionLabel,
+ FilesystemType: partition.FilesystemTypeXFS,
+ MountTarget: filepath.Join(opts.MountPrefix, constants.BootMountPoint),
}
efiMountSpec := mount.Spec{
@@ -32,6 +33,23 @@ func (c *Config) Upgrade(opts options.InstallOptions) (*options.InstallResult, e
MountTarget: filepath.Join(opts.MountPrefix, constants.EFIMountPoint),
}
+ // check if the BOOT partition is present (absent on SBC layouts like RPi5/CM5)
+ if err := mount.PartitionOp(
+ opts.BootDisk,
+ []mount.Spec{bootMountSpec},
+ func() error {
+ return nil
+ },
+ []blkid.ProbeOption{
+ blkid.WithSkipLocking(true),
+ },
+ nil,
+ nil,
+ opts.BlkidInfo,
+ ); err == nil {
+ mountSpecs = append(mountSpecs, bootMountSpec)
+ }
+
var efiFound bool
// check if the EFI partition is present
@@ -49,12 +67,21 @@ func (c *Config) Upgrade(opts options.InstallOptions) (*options.InstallResult, e
opts.BlkidInfo,
); err == nil {
efiFound = true
- }
- if efiFound {
+ if len(mountSpecs) == 0 {
+ // No BOOT partition (SBC layout): mount EFI at /boot so GRUB
+ // can find kernel/initramfs/grub.cfg in the expected place.
+ efiMountSpec.MountTarget = filepath.Join(opts.MountPrefix, constants.BootMountPoint)
+ c.bootFromEFI = true
+ }
+
mountSpecs = append(mountSpecs, efiMountSpec)
}
+ if len(mountSpecs) == 0 {
+ return nil, fmt.Errorf("neither BOOT nor EFI partition found on disk %s", opts.BootDisk)
+ }
+
err := mount.PartitionOp(
opts.BootDisk,
mountSpecs,
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,114 @@
From 27501076fcb1e183e68313ef0e14092d7f403063 Mon Sep 17 00:00:00 2001
From: Mathias Beaulieu-Duncan <mathias@svrnty.io>
Date: Mon, 25 May 2026 17:10:13 -0400
Subject: [PATCH] Wait for STATE volume on slow-init disks in config acquire
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
On slow-init storage (notably RPi CM5 eMMC, which takes ~2 minutes 13
seconds from kernel boot to mmc0/CQE-enabled), the config acquire state
machine's stateDisk step sees STATE in phase=missing during the early
boot window and immediately transitions to stateEmbedded. With no
embedded config either, the node falls through to maintenance mode and
stays there even after the STATE volume later reaches phase=ready —
the state machine is one-way and never re-enters stateDisk.
Result: in-place upgrades to v1.13.2 on CM5 hardware leave the node in
maintenance with its on-disk config.yaml intact but unread, until a
human runs `talosctl apply-config --insecure` to re-feed the same file
that's already on the STATE filesystem.
This patch makes stateDisk tolerate transient phase=missing for up to
5 minutes (stateMissingDiskTimeout) before falling through. The outer
Run loop gets a 5-second ticker so the timeout can fire even when no
further volume-status events arrive (e.g. truly missing STATE on a
fresh install). Fast-init hardware sees no change — STATE reaches ready
within seconds and the existing path runs.
Tested on RPi CM5 (eMMC, 6.12.47 RPi-downstream kernel) — boot path
that previously dropped to maintenance now waits ~2m15s, sees STATE
reach ready, and continues to stateDone with the persisted config.
---
.../pkg/controllers/config/acquire.go | 37 ++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/internal/app/machined/pkg/controllers/config/acquire.go b/internal/app/machined/pkg/controllers/config/acquire.go
index a70fddb..7a3758f 100644
--- a/internal/app/machined/pkg/controllers/config/acquire.go
+++ b/internal/app/machined/pkg/controllers/config/acquire.go
@@ -18,6 +18,7 @@ import (
"path/filepath"
"slices"
"strings"
+ "time"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/safe"
@@ -86,8 +87,16 @@ type AcquireController struct {
diskConfig config.Provider
storedEmbeddedConfig []byte
skipMaskingEmbeddedConfig bool
+ firstSeenStateMissing time.Time
}
+// stateMissingDiskTimeout is how long stateDisk will wait for the STATE volume
+// to leave the "missing" phase before falling through to embedded/maintenance.
+// On slow-init storage (e.g. RPi CM5 eMMC) the volume can take ~2-3 minutes to
+// appear after kernel boot; transitioning to embedded too eagerly leaves the
+// node permanently in maintenance even though the on-disk config is intact.
+const stateMissingDiskTimeout = 5 * time.Minute
+
// Name implements controller.Controller interface.
func (ctrl *AcquireController) Name() string {
return "config.AcquireController"
@@ -177,11 +186,17 @@ func (ctrl *AcquireController) Run(ctx context.Context, r controller.Runtime, lo
// initialize with empty sources
ctrl.configSourcesUsed = []string{}
+ // periodic wake-up so stateDisk can re-check slow-init volumes and trip
+ // its timeout even when no resource events fire.
+ tick := time.NewTicker(5 * time.Second)
+ defer tick.Stop()
+
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
+ case <-tick.C:
}
// check the spec first
@@ -262,7 +277,27 @@ func (ctrl *AcquireController) stateDisk(ctx context.Context, r controller.Runti
// wait for the status to be available
return nil, nil, nil
case stateVolumeStatus.TypedSpec().Phase == block.VolumePhaseMissing:
- // STATE is missing, proceed to stateEmbedded
+ // STATE is reported missing. On slow-init storage (e.g. RPi CM5 eMMC)
+ // the volume can take a couple of minutes to appear after kernel boot,
+ // so wait up to stateMissingDiskTimeout for it to reach ready before
+ // falling through to embedded/maintenance.
+ if ctrl.firstSeenStateMissing.IsZero() {
+ ctrl.firstSeenStateMissing = time.Now()
+ }
+
+ if time.Since(ctrl.firstSeenStateMissing) < stateMissingDiskTimeout {
+ logger.Info("STATE volume not yet available, waiting",
+ zap.Duration("elapsed", time.Since(ctrl.firstSeenStateMissing)),
+ zap.Duration("timeout", stateMissingDiskTimeout),
+ )
+
+ return nil, nil, nil
+ }
+
+ logger.Warn("STATE volume still missing after timeout, proceeding to embedded",
+ zap.Duration("waited", time.Since(ctrl.firstSeenStateMissing)),
+ )
+
return ctrl.stateEmbedded, nil, nil
case stateVolumeStatus.TypedSpec().Phase == block.VolumePhaseReady:
// STATE is ready, proceed to to the action
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,42 @@
From e2bacf8336c63a4d11a54f3978fc15e8bf4362ce Mon Sep 17 00:00:00 2001
From: Mathias Beaulieu-Duncan <mathias@svrnty.io>
Date: Mon, 25 May 2026 17:20:32 -0400
Subject: [PATCH] imager: respect --insecure for Overlay and OverlayInstaller
assets
The --insecure flag on talosctl imager (and the imager docker image) only
applied to BaseInstaller, ImageCache, and SystemExtensions assets.
Overlay.Image and Input.OverlayInstaller never received ForceInsecure,
so pulling overlay images from an insecure (HTTP) registry failed with
'http: server gave HTTP response to HTTPS client' even when --insecure
was passed.
Set ForceInsecure on both overlay-related ContainerAsset references
when the flag is present, matching how the other inputs are handled.
---
cmd/installer/cmd/imager/root.go | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/cmd/installer/cmd/imager/root.go b/cmd/installer/cmd/imager/root.go
index d65b56089..ea16a45b3 100644
--- a/cmd/installer/cmd/imager/root.go
+++ b/cmd/installer/cmd/imager/root.go
@@ -120,12 +120,14 @@ var rootCmd = &cobra.Command{
prof.Overlay = &profile.OverlayOptions{
Name: cmdFlags.OverlayName,
Image: profile.ContainerAsset{
- ImageRef: cmdFlags.OverlayImage,
+ ImageRef: cmdFlags.OverlayImage,
+ ForceInsecure: cmdFlags.Insecure,
},
ExtraOptions: extraOverlayOptions,
}
prof.Input.OverlayInstaller.ImageRef = cmdFlags.OverlayImage
+ prof.Input.OverlayInstaller.ForceInsecure = cmdFlags.Insecure
}
prof.Input.SystemExtensions = xslices.Map(
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,38 @@
From 69f14c84e9e458dcff24905145cac8557c0e2965 Mon Sep 17 00:00:00 2001
From: Mathias Beaulieu-Duncan <mathias@svrnty.io>
Date: Fri, 13 Feb 2026 15:25:26 -0500
Subject: [PATCH] Bump Go toolchain to 1.24.13 to fix stdlib CVEs
---
go.work | 4 +++-
installers/rpi5/src/go.mod | 4 +++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/go.work b/go.work
index f4dafe7..798ea43 100644
--- a/go.work
+++ b/go.work
@@ -1,3 +1,5 @@
-go 1.24.0
+go 1.24.13
+
+toolchain go1.24.13
use ./installers/rpi5/src
diff --git a/installers/rpi5/src/go.mod b/installers/rpi5/src/go.mod
index 50b72d5..af5f5f8 100644
--- a/installers/rpi5/src/go.mod
+++ b/installers/rpi5/src/go.mod
@@ -1,6 +1,8 @@
module rpi_generic
-go 1.24.0
+go 1.24.13
+
+toolchain go1.24.13
require (
github.com/siderolabs/go-copy v0.1.0
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,77 @@
From 5cdfe8ddb717e8de4e9d1c9e3000614ad8a1b5bb Mon Sep 17 00:00:00 2001
From: Mathias Beaulieu-Duncan <mathias@openharbor.io>
Date: Wed, 25 Feb 2026 12:41:51 -0500
Subject: [PATCH] Fix SBC overlay upgrade: detect EFI mount path for SBC
layouts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
On SBC platforms (RPi5/CM5), the disk layout has no separate BOOT
partition. During upgrades, the GRUB install code mounts the EFI
partition at /boot instead of /boot/EFI. The overlay installer was
always writing to /boot/EFI, causing config.txt and firmware files
to end up in the wrong subdirectory — invisible to the firmware.
Detect the SBC layout by checking if config.txt already exists at
/boot/ (indicating EFI is mounted there), and write files to the
correct location.
---
installers/rpi5/src/config.txt | 3 ++-
installers/rpi5/src/main.go | 18 +++++++++++++-----
2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/installers/rpi5/src/config.txt b/installers/rpi5/src/config.txt
index 1445d0e..e38291b 100644
--- a/installers/rpi5/src/config.txt
+++ b/installers/rpi5/src/config.txt
@@ -11,7 +11,8 @@ kernel=u-boot.bin
arm_64bit=1
# Run as fast as firmware / board allows.
arm_boost=1
-# Enable the primary/console UART.
+# Enable UART0 on GPIO 14/15 (Pi5/CM5 requires explicit dt overlay).
+dtoverlay=uart0-pi5
enable_uart=1
# Disable Bluetooth.
dtoverlay=disable-bt
diff --git a/installers/rpi5/src/main.go b/installers/rpi5/src/main.go
index 38cd824..155a86b 100644
--- a/installers/rpi5/src/main.go
+++ b/installers/rpi5/src/main.go
@@ -41,13 +41,21 @@ func (i *RpiInstaller) GetOptions(extra rpiOptions) (overlay.Options, error) {
}
func (i *RpiInstaller) Install(options overlay.InstallOptions[rpiOptions]) error {
- err := copy.Dir(filepath.Join(options.ArtifactsPath, "arm64/firmware/boot"), filepath.Join(options.MountPrefix, "/boot/EFI"))
- if err != nil {
+ // Determine where the EFI partition is mounted.
+ // Standard layout: BOOT (XFS) at /boot, EFI (VFAT) at /boot/EFI
+ // SBC layout: no BOOT partition, EFI (VFAT) at /boot
+ efiDir := filepath.Join(options.MountPrefix, "/boot/EFI")
+
+ if _, err := os.Stat(filepath.Join(options.MountPrefix, "/boot/config.txt")); err == nil {
+ // config.txt exists at /boot — EFI partition is mounted at /boot (SBC layout).
+ efiDir = filepath.Join(options.MountPrefix, "/boot")
+ }
+
+ if err := copy.Dir(filepath.Join(options.ArtifactsPath, "arm64/firmware/boot"), efiDir); err != nil {
return err
}
- err = copy.File(filepath.Join(options.ArtifactsPath, "arm64/u-boot/rpi5/u-boot.bin"), filepath.Join(options.MountPrefix, "/boot/EFI/u-boot.bin"))
- if err != nil {
+ if err := copy.File(filepath.Join(options.ArtifactsPath, "arm64/u-boot/rpi5/u-boot.bin"), filepath.Join(efiDir, "u-boot.bin")); err != nil {
return err
}
@@ -57,5 +65,5 @@ func (i *RpiInstaller) Install(options overlay.InstallOptions[rpiOptions]) error
configTxt = append(configTxt, []byte("\n"+options.ExtraOptions.ConfigTxtAppend)...)
- return os.WriteFile(filepath.Join(options.MountPrefix, "/boot/EFI/config.txt"), configTxt, 0o644)
+ return os.WriteFile(filepath.Join(efiDir, "config.txt"), configTxt, 0o644)
}
--
2.50.1 (Apple Git-155)

125
scripts/auto-update.sh Executable file
View File

@ -0,0 +1,125 @@
#!/usr/bin/env bash
# Auto-update upstream versions, verify patches, and generate a release tag
#
# Expects environment variables from check-upstream.sh:
# TALOS_UPDATE, RPI_UPDATE, LATEST_TALOS, LATEST_RPI_TAG
#
# Outputs (for GitHub Actions):
# patch_failed=true — if patches fail to apply (caller should create issue)
# new_tag=<tag> — the computed release tag (e.g. v1.12.3-k6.12.47-1)
#
# Usage:
# TALOS_UPDATE=true LATEST_TALOS=v1.13.0 ./scripts/auto-update.sh >> "$GITHUB_OUTPUT"
set -euo pipefail
TALOS_UPDATE=${TALOS_UPDATE:-false}
RPI_UPDATE=${RPI_UPDATE:-false}
LATEST_TALOS=${LATEST_TALOS:-}
LATEST_RPI_TAG=${LATEST_RPI_TAG:-}
MAKEFILE="Makefile"
PATCH_FILE="patches/siderolabs/pkgs/0001-Patched-for-Raspberry-Pi-5.patch"
# Helper: extract kernel semver (e.g. 6.12.47) from the RPi repo Makefile
get_kernel_version() {
local tag="$1"
curl -sf "https://raw.githubusercontent.com/raspberrypi/linux/${tag}/Makefile" \
| awk '
/^VERSION/ { version=$3 }
/^PATCHLEVEL/ { patchlevel=$3 }
/^SUBLEVEL/ { sublevel=$3 }
END { print version "." patchlevel "." sublevel }
'
}
# ── RPi kernel update ───────────────────────────────────────────────
if [ "$RPI_UPDATE" = "true" ] && [ -n "$LATEST_RPI_TAG" ]; then
echo "Updating RPi kernel to $LATEST_RPI_TAG ..." >&2
# Download tarball and compute checksums
TARBALL_URL="https://github.com/raspberrypi/linux/archive/refs/tags/${LATEST_RPI_TAG}.tar.gz"
TMP=$(mktemp)
curl -sL "$TARBALL_URL" -o "$TMP"
NEW_SHA256=$(shasum -a 256 "$TMP" | awk '{print $1}')
NEW_SHA512=$(shasum -a 512 "$TMP" | awk '{print $1}')
rm -f "$TMP"
echo " SHA256: $NEW_SHA256" >&2
echo " SHA512: $NEW_SHA512" >&2
# Get actual kernel version for the config header
KERNEL_VERSION=$(get_kernel_version "$LATEST_RPI_TAG")
echo " Kernel version: $KERNEL_VERSION" >&2
# Update patch file
sed -i "s/+ linux_version: .*/+ linux_version: ${LATEST_RPI_TAG}/" "$PATCH_FILE"
sed -i "s/+ linux_sha256: .*/+ linux_sha256: ${NEW_SHA256}/" "$PATCH_FILE"
sed -i "s/+ linux_sha512: .*/+ linux_sha512: ${NEW_SHA512}/" "$PATCH_FILE"
sed -i "s|+# Linux/arm64 .* Kernel Configuration|+# Linux/arm64 ${KERNEL_VERSION} Kernel Configuration|" "$PATCH_FILE"
fi
# ── Talos update ────────────────────────────────────────────────────
if [ "$TALOS_UPDATE" = "true" ] && [ -n "$LATEST_TALOS" ]; then
echo "Updating Talos to $LATEST_TALOS ..." >&2
# Update TALOS_VERSION in Makefile
sed -i "s/^TALOS_VERSION = .*/TALOS_VERSION = ${LATEST_TALOS}/" "$MAKEFILE"
# Derive matching PKG_VERSION (same major.minor as Talos)
PKG_MINOR=$(echo "$LATEST_TALOS" | sed -E 's/^(v[0-9]+\.[0-9]+)\..*/\1/')
LATEST_PKG=$(curl -sf "https://api.github.com/repos/siderolabs/pkgs/tags?per_page=20" \
| jq -r "[.[] | select(.name | startswith(\"${PKG_MINOR}\"))][0].name")
if [ -n "$LATEST_PKG" ] && [ "$LATEST_PKG" != "null" ]; then
echo " Updating PKG_VERSION to $LATEST_PKG" >&2
sed -i "s/^PKG_VERSION = .*/PKG_VERSION = ${LATEST_PKG}/" "$MAKEFILE"
else
echo " WARNING: No matching pkgs tag for $PKG_MINOR — keeping current PKG_VERSION" >&2
fi
fi
# ── Smoke test — verify patches apply ───────────────────────────────
echo "Running patch smoke test ..." >&2
if ! gmake checkouts patches; then
echo "Patches failed to apply!" >&2
gmake checkouts-clean >/dev/null 2>&1 || true
echo "patch_failed=true"
exit 0
fi
gmake checkouts-clean >/dev/null 2>&1
# ── Generate tag ────────────────────────────────────────────────────
TALOS_VER=$(grep '^TALOS_VERSION' "$MAKEFILE" | awk '{print $NF}')
RPI_TAG=$(grep '+ linux_version:' "$PATCH_FILE" | awk '{print $NF}')
KERNEL_VER=$(get_kernel_version "$RPI_TAG")
# Find next build number for this component combination
TAG_PREFIX="${TALOS_VER}-k${KERNEL_VER}"
LAST_BUILD=$(git tag -l "${TAG_PREFIX}-*" \
| sed "s|${TAG_PREFIX}-||" \
| sort -n \
| tail -1)
NEXT_BUILD=$(( ${LAST_BUILD:-0} + 1 ))
NEW_TAG="${TAG_PREFIX}-${NEXT_BUILD}"
# ── Update README badges and examples ───────────────────────────────
README="README.md"
if [ -f "$README" ]; then
OLD_TALOS=$(sed -n 's/.*talos-v\([0-9]*\.[0-9]*\.[0-9]*\).*/\1/p' "$README" | head -1)
OLD_KERNEL=$(sed -n 's/.*kernel-\([0-9]*\.[0-9]*\.[0-9]*\).*/\1/p' "$README" | head -1)
OLD_TAG=$(sed -n 's/.*\(v[0-9]*\.[0-9]*\.[0-9]*-k[0-9]*\.[0-9]*\.[0-9]*-[0-9]*\).*/\1/p' "$README" | head -1)
echo "Updating README: talos v${OLD_TALOS} → v${TALOS_VER}, kernel ${OLD_KERNEL}${KERNEL_VER}, tag ${OLD_TAG}${NEW_TAG}" >&2
sed -i "s/talos-v${OLD_TALOS}/talos-${TALOS_VER}/g" "$README"
sed -i "s/kernel-${OLD_KERNEL}/kernel-${KERNEL_VER}/g" "$README"
sed -i "s/\`v${OLD_TALOS}\`/\`${TALOS_VER}\`/g" "$README"
sed -i "s/\`k${OLD_KERNEL}\`/\`k${KERNEL_VER}\`/g" "$README"
if [ -n "$OLD_TAG" ]; then
sed -i "s/${OLD_TAG}/${NEW_TAG}/g" "$README"
fi
fi
echo "Generated tag: $NEW_TAG" >&2
echo "new_tag=$NEW_TAG"

View File

@ -1,54 +1,57 @@
#!/usr/bin/env bash
# Check for upstream Talos and RPi kernel updates
#
# Compares current versions in Makefile against the latest GitHub releases.
# Outputs GitHub Actions-compatible variables for use in CI workflows.
# Compares current versions (from Makefile + pkgs patch) against the
# latest GitHub releases/tags. Outputs GitHub Actions-compatible variables.
#
# Usage:
# ./scripts/check-upstream.sh # Print results
# ./scripts/check-upstream.sh # Print results to stdout/stderr
# ./scripts/check-upstream.sh >> "$GITHUB_OUTPUT" # For CI
set -euo pipefail
MAKEFILE="${MAKEFILE:-Makefile}"
PATCH_FILE="${PATCH_FILE:-patches/siderolabs/pkgs/0001-Patched-for-Raspberry-Pi-5.patch}"
# Extract current versions from Makefile
CURRENT_TALOS=$(grep '^TALOS_VERSION' "$MAKEFILE" | head -1 | awk '{print $NF}')
CURRENT_PKG=$(grep '^PKG_VERSION' "$MAKEFILE" | head -1 | awk '{print $NF}')
# ── Current versions ────────────────────────────────────────────────
CURRENT_TALOS=$(grep '^TALOS_VERSION' "$MAKEFILE" | awk '{print $NF}')
CURRENT_RPI_TAG=$(grep '+ linux_version:' "$PATCH_FILE" | awk '{print $NF}')
echo "Current Talos version: $CURRENT_TALOS"
echo "Current PKG version: $CURRENT_PKG"
echo "Current Talos version: $CURRENT_TALOS" >&2
echo "Current RPi kernel tag: $CURRENT_RPI_TAG" >&2
# Check latest Talos stable release
# ── Latest versions from GitHub API ─────────────────────────────────
LATEST_TALOS=$(curl -sf "https://api.github.com/repos/siderolabs/talos/releases/latest" \
| grep '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')
| jq -r '.tag_name')
echo "Latest Talos release: $LATEST_TALOS"
LATEST_RPI_TAG=$(curl -sf "https://api.github.com/repos/raspberrypi/linux/tags?per_page=20" \
| jq -r '[.[] | select(.name | startswith("stable_"))][0].name')
# Check latest RPi kernel stable tag (format: stable_YYYYMMDD)
LATEST_RPI_KERNEL=$(curl -sf "https://api.github.com/repos/raspberrypi/linux/tags?per_page=10" \
| grep '"name"' | grep 'stable_' | head -1 | sed -E 's/.*"name": *"([^"]+)".*/\1/')
echo "Latest Talos release: $LATEST_TALOS" >&2
echo "Latest RPi kernel tag: $LATEST_RPI_TAG" >&2
echo "Latest RPi kernel tag: $LATEST_RPI_KERNEL"
# Output for GitHub Actions
echo "talos_current=$CURRENT_TALOS"
echo "talos_latest=$LATEST_TALOS"
# ── Determine what needs updating ───────────────────────────────────
TALOS_UPDATE=false
RPI_UPDATE=false
if [ "$CURRENT_TALOS" != "$LATEST_TALOS" ]; then
echo "talos_update=true"
TALOS_UPDATE=true
echo ">> Talos update available: $CURRENT_TALOS -> $LATEST_TALOS" >&2
else
echo "talos_update=false"
echo ">> Talos is up to date" >&2
fi
# For RPi kernel, we output what we found — the actual version tracking
# depends on the pkgs patch content which references a specific kernel tag
echo "rpi_current=check-patch"
echo "rpi_latest=$LATEST_RPI_KERNEL"
if [ "$CURRENT_RPI_TAG" != "$LATEST_RPI_TAG" ]; then
RPI_UPDATE=true
echo ">> RPi kernel update available: $CURRENT_RPI_TAG -> $LATEST_RPI_TAG" >&2
else
echo ">> RPi kernel is up to date" >&2
fi
# We always flag RPi kernel for review since we can't easily parse the
# patch to extract the exact pinned version
echo "rpi_update=true"
echo ">> RPi kernel latest stable: $LATEST_RPI_KERNEL (review patch manually)" >&2
# ── Output for GitHub Actions ───────────────────────────────────────
echo "talos_current=$CURRENT_TALOS"
echo "talos_latest=$LATEST_TALOS"
echo "talos_update=$TALOS_UPDATE"
echo "rpi_current=$CURRENT_RPI_TAG"
echo "rpi_latest=$LATEST_RPI_TAG"
echo "rpi_update=$RPI_UPDATE"