Files
ssmithxandClaude Opus 5.5 cf228218ac uci late-defaults: TollGate's setup once the WiFi config exists
On a fresh image 99-tollgate-setup ran at activation, before wifi-setup
had created /etc/config/wireless, so the open TollGate SSID never
appeared (the Pi release image came up with `guix-router`).  uci-service-
type gains late-defaults, run by wifi-setup after `wifi config` and the
first config, then one reboot if any ran.  TollGate's setup and the
MT6000's 99_zz-wired-private move there.  Tested on the Pi (fresh WiFi
state: TollGate-C69B after one automatic reboot); MT6000 generation 7
(scripts already done there: no reboot).  PITFALLS #57.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 16:50:11 -04:00

287 lines
12 KiB
Scheme

;; Guix system for the GL.iNet GL-MT6000 (Flint 2): MediaTek MT7986A, 1 GB
;; RAM, 8 GB eMMC, 2.5G WAN + 5 switch ports, MT7976 dual-band WiFi 6.
;;
;; Boot chain: BL2 (eMMC boot0) -> ATF + GL.iNet's stock U-Boot (`fip'
;; partition) -> the FIT image in the `kernel' partition (p6): kernel, DTB
;; carrying the Guix command line in /chosen/bootargs-append, initrd. GL's
;; U-Boot has no menu: deploying writes a new FIT, rolling back rewrites it.
;; Recovery: hold reset at power-on for GL's web failsafe. Root: ext4
;; labelled "Guix" in the `rootfs' partition (p7).
;; See plans/guix-openwrt-mt6000-plan.md.
;;
;; Network (as OpenWrt's board config):
;; WAN eth1 2.5G port, DHCP (netifd)
;; LAN br-lan lan1-lan5 (MT7531 DSA ports) + WiFi APs, 192.168.1.1/24,
;; dnsmasq DHCPv4/DNS, odhcpd RA/DHCPv6
;; firewall4, LuCI, the OpenWrt userland as on the Pi (rpi4.scm).
;;
;; Load with GUIX_PACKAGE_PATH pointing at this directory's packages/.
;; Must come first: makes every cross build use a glibc whose system()/popen()
;; run /bin/sh, not the build host's bash (packages/cross-libc-fix.scm).
(use-modules (cross-libc-fix))
(use-modules (gnu)
(gnu bootloader u-boot)
(guix packages)
(guix utils)
((guix config) #:select (%guix-version))
(nongnu packages linux)
(nonguix licenses)
(mt6000-linux)
(wireless-regdb-signed)
(openwrt)
(openwrt-services)
(router-base)
(tollgate-services))
(use-service-modules avahi networking shepherd ssh sysctl)
(use-package-modules admin base bash linux networking)
(define %mt6000-linux-firmware
;; linux-firmware subset: the MT7986's WiFi (mt7915e: WM/WA/ROM patch,
;; fallback EEPROMs) and WED offload (WO) firmware.
(package
(inherit linux-firmware)
(name "linux-firmware-mt7986")
(arguments
(substitute-keyword-arguments (package-arguments linux-firmware)
((#:phases phases #~%standard-phases)
#~(modify-phases #$phases
(add-after 'unpack 'select-firmware
(lambda _
(use-modules (ice-9 regex))
(define (wanted? path)
(string-match "^\"?mediatek/mt7986_" path))
(substitute* "WHENCE"
(("^(File|RawFile|Link|Symlink): *(.*)" _ type rest)
(string-append (if (wanted? rest) type "Skip")
": " rest)))))))))
(synopsis "MT7986 WiFi and WED firmware")
(description
"Subset of the linux-firmware package with the MT7986 firmware: the
mt7915e driver's WiFi microcode and WED (Wireless Ethernet Dispatch)
offload firmware.")
(license (nonfree
"https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/WHENCE"))))
(define gl-u-boot-bootloader
;; Nothing to install: GL's U-Boot stays as it is and boots the FIT that
;; the deploy script writes to the `kernel' partition. (The extlinux.conf
;; Guix generates is unused.)
(bootloader
(inherit u-boot-bootloader)
(name 'gl-u-boot)
(package #f)
(installer #f)
(disk-image-installer #f)))
;; /etc/board-base.json, as OpenWrt's board_detect writes it; wifi-setup adds
;; the radios (wlan) into /tmp/sysinfo/board.json = /etc/board.json.
(define %board-base.json
(plain-file "board-base.json" "\
{
\"model\": { \"id\": \"glinet,gl-mt6000\",
\"name\": \"GL.iNet GL-MT6000\" },
\"network\": {
\"lan\": { \"ports\": [ \"lan1\", \"lan2\", \"lan3\", \"lan4\", \"lan5\" ],
\"protocol\": \"static\" },
\"wan\": { \"device\": \"eth1\", \"protocol\": \"dhcp\" }
}
}
"))
;; After `wifi config' on first boot: WPA2 APs named guix-router on both
;; radios (radio0 2.4 GHz, radio1 5 GHz), in br-lan, with one random key --
;; read it with `uci get wireless.default_radio0.key'. mt7915 does ACS, so
;; the channels `wifi config' chose stay.
(define %wifi-first-config
(plain-file "wifi-first-config" "\
key=$(od -An -tx1 -N12 /dev/urandom | tr -d ' \\n')
for r in 0 1; do
uci -q batch <<EOF
set wireless.radio$r.disabled='0'
set wireless.radio$r.country='US'
set wireless.default_radio$r.ssid='guix-router'
set wireless.default_radio$r.encryption='psk2'
set wireless.default_radio$r.key='$key'
set wireless.default_radio$r.network='lan'
set wireless.default_radio$r.disabled='0'
EOF
done
uci commit wireless
"))
;; The root file system fills the `rootfs' partition: the image's is smaller.
(define %resize-root-service
(simple-service 'resize-root shepherd-root-service-type
(list (shepherd-service
(documentation "Grow the root ext4 file system to its partition.")
(provision '(resize-root))
(requirement '(file-systems))
(one-shot? #t)
;; resize2fs is a no-op once the file system fills the partition.
(start #~(lambda _
(zero? (system* #$(file-append bash "/bin/bash") "-c"
"\"$0\" \"$(\"$1\" -no SOURCE /)\""
#$(file-append e2fsprogs "/sbin/resize2fs")
#$(file-append util-linux "/bin/findmnt")))))))))
;; Packet steering. The frame engine delivers everything through one RX
;; ring, both of its IRQs land on CPU0 and RPS is off, so one core did all
;; the forwarding: bidirectional routing peaked at ~1.06 Gbit/s with a core
;; at 96% softirq. IRQs on CPU1 and CPU2, RPS on CPUs 0, 1 and 3 (not the
;; RX IRQ's): ~1.45 Gbit/s, no core above ~30%.
(define %packet-steering-service
(simple-service 'packet-steering shepherd-root-service-type
(list (shepherd-service
(documentation "Spread Ethernet IRQs and RPS over the CPUs.")
(provision '(packet-steering))
(requirement '(udev))
(one-shot? #t)
(start #~(lambda _
(zero? (system* #$(file-append bash "/bin/bash") "-c" "
for i in $(seq 1 30); do [ -e /sys/class/net/eth1 ] && break; sleep 1; done
cpu=1
for irq in $(awk -F: '/15100000[.]ethernet/ { print $1 + 0 }' /proc/interrupts); do
echo $((1 << cpu)) > /proc/irq/$irq/smp_affinity
cpu=$((cpu + 1))
done
for d in eth0 eth1; do echo b > /sys/class/net/$d/queues/rx-0/rps_cpus; done"))))))))
(define %serial-console "ttyS0")
(define %mt6000-os
(operating-system
(host-name "mt6000")
(timezone "Etc/UTC")
(locale "en_US.utf8")
(bootloader (bootloader-configuration
(bootloader gl-u-boot-bootloader)
(targets '())))
(kernel linux-mt6000)
(firmware (list %mt6000-linux-firmware wireless-regdb-signed))
;; eMMC, its clocks, the serial console and ramoops (the kernel log
;; across resets, in /sys/fs/pstore) are built into linux-mt6000.
(initrd-modules '())
;; Headless: an initrd error must not wait at a REPL on the serial
;; console. It panics instead, and panic=10 reboots -- into GL U-Boot's
;; TFTP recovery window again.
(initrd (lambda (file-systems . rest)
(apply base-initrd file-systems #:on-error 'backtrace rest)))
(kernel-arguments
(list (string-append "console=" %serial-console ",115200")
"panic=10"
;; mtk_eth_soc is a module, loaded long after the kernel turns
;; off the clocks nobody has claimed; without this its MDIO bus
;; times out ("mdio: MDIO timeout") and Ethernet never probes.
;; (OpenWrt builds the driver in.)
"clk_ignore_unused"))
(file-systems (cons* (file-system
(mount-point "/")
(device (file-system-label "Guix"))
(type "ext4"))
%base-file-systems))
(swap-devices '())
(users (cons* %router-root-account %base-user-accounts))
(packages (append (list usbutils iw ethtool ucode jsonfilter)
%base-packages))
(services (cons*
;; No RTC: start from the last known time, not 1970. First,
;; so its activation runs before the others.
(service sysfixtime-service-type)
(service agetty-service-type
(agetty-configuration
(tty %serial-console)
(term "vt100")
(baud-rate "115200")))
(service avahi-service-type)
(service netifd-service-type
(netifd-configuration
(resolv-conf "/etc/resolv.conf")))
(service openwrt-wireless-service-type
(openwrt-wireless-configuration
(board-base %board-base.json)
(first-config %wifi-first-config)))
(service openwrt-dnsmasq-service-type
(openwrt-dnsmasq-configuration
(dnsmasq dnsmasq/cross-fixed)))
(service firewall-service-type)
(simple-service 'router-sysctl sysctl-service-type
%router-sysctls)
(service ubus-service-type)
(service procd-compat-service-type
(procd-compat-configuration
(release `(("distribution" . "Guix System")
("version" . ,%guix-version)
("target" . "mediatek/filogic")
("description"
. "Guix System, OpenWrt userland")))))
(service uci-service-type
(uci-configuration
(seeds `(("system" ,(local-file "files/uci-mt6000/system"))
("network" ,(local-file "files/uci-mt6000/network"))
("dhcp" ,(local-file "files/uci-mt6000/dhcp"))
("firewall"
,(local-file "files/uci-mt6000/firewall"))))
(defaults
`(("12_network-generate-ula"
,(file-append %openwrt-tree
"/package/base-files/files/etc/"
"uci-defaults/12_network-generate-ula"))))
;; After TollGate's 99-tollgate-setup (a late default
;; too): wired ports into its private network, no
;; admin services on the open SSID's br-lan.
(late-defaults
`(("99_zz-wired-private"
,(local-file "files/uci-mt6000/99_zz-wired-private"))))))
(service odhcpd-service-type)
(service uhttpd-service-type)
(service luci-service-type)
(service nodogsplash-service-type)
;; TollGate (Cashu/Nostr paid access through nodogsplash).
;; Its first-boot setup (99-tollgate-setup) makes both radios
;; an open TollGate-XXXX AP on br-lan, adds a private WPA2
;; network (br-private), moves uhttpd to :8080 and opens tcp
;; 2121. Wallet and identity in /etc/tollgate (state, not in
;; the store).
(service tollgate-service-type)
%resize-root-service
%packet-steering-service
%chrony-accounts
%chrony-service
;; Keys only: the open TollGate SSID shares br-lan with sshd,
;; so anyone nearby can reach it. (LuCI still takes the root
;; password: set a strong one with `passwd' on the router.)
(service openssh-service-type
(openssh-configuration
(permit-root-login 'prohibit-password)
(password-authentication? #f)
(authorized-keys
`(("root" ,(local-file "files/workstation.pub"))))))
(modify-services %base-services
(delete console-font-service-type)
;; Accept store items signed by the build workstation, for
;; `guix copy' deploys.
(guix-service-type
config => (guix-configuration
(inherit config)
(authorized-keys
(cons (local-file "files/workstation-guix.pub")
%default-authorized-guix-keys)))))))))
%mt6000-os