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>
287 lines
12 KiB
Scheme
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
|