From fb86b1dfe762405e00d06f20f28ec3466cc90f32 Mon Sep 17 00:00:00 2001 From: q Date: Wed, 7 Oct 2026 00:44:19 +0300 Subject: [PATCH] =?UTF-8?q?B11=E2=80=93B22:=20LED/buttons=20audit,=20Alpin?= =?UTF-8?q?e=20primary=20root,=20router,=20Wayland=20+=20aurora-ui,=20VPN?= =?UTF-8?q?=20(WireGuard=20via=20TURN=20relay,=20direct-first),=20standalo?= =?UTF-8?q?ne/SSH,=20USB=20OTG,=20netwatch/blackbox?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NOTES.md | 77 ++ README.md | 31 +- b11/audit/b11-ro.sh | 14 + b11/run/b11-btn-watch.sh | 14 + b11/run/b11-led-run.sh | 65 ++ b12/alpine/chroot-down.sh | 9 + b12/alpine/chroot-up.sh | 21 + .../overlay/etc/X11/xorg-modesetting.conf | 19 + b12/alpine/overlay/etc/X11/xorg.conf | 24 + b12/alpine/overlay/usr/local/bin/aurora-x | 28 + b12/b12p/B12P-rollback.sh | 12 + b12/b12p/B12P-write.sh | 53 + b12/b12p/b12p-boot.sh | 40 + b12/b12p/build-b12p.sh | 28 + b12/b12p/cache/extlinux-b12p.conf | 6 + b12/b12p/cache/mkfs-cache-b12p.sh | 27 + b12/b12q/B12Q-rollback.sh | 12 + b12/b12q/B12Q-write.sh | 53 + .../alpine-overlay/etc/aurora/build-epoch | 1 + b12/b12q/alpine-overlay/etc/aurora/modem.conf | 12 + .../alpine-overlay/etc/aurora/primary-root | 1 + b12/b12q/alpine-overlay/etc/fstab | 1 + b12/b12q/alpine-overlay/etc/hostname | 1 + .../alpine-overlay/etc/init.d/aurora-bootok | 5 + .../alpine-overlay/etc/init.d/aurora-display | 8 + .../alpine-overlay/etc/init.d/aurora-firmware | 15 + .../alpine-overlay/etc/init.d/aurora-modem | 8 + b12/b12q/alpine-overlay/etc/init.d/aurora-ncm | 22 + b12/b12q/alpine-overlay/etc/init.d/aurora-ro | 12 + .../alpine-overlay/etc/init.d/aurora-wifi | 8 + b12/b12q/alpine-overlay/etc/inittab | 9 + .../usr/libexec/aurora/svc/aurora-display | 13 + .../usr/libexec/aurora/svc/aurora-modem | 27 + .../usr/libexec/aurora/svc/aurora-wifi | 16 + b12/b12q/alpine-overlay/usr/local/bin/b4b2-mm | 43 + .../alpine-overlay/usr/local/bin/nv-check | 12 + b12/b12q/alpine-overlay/usr/local/bin/qmi-b3 | 20 + .../alpine-overlay/usr/sbin/aurora-display | 44 + b12/b12q/alpine-overlay/usr/sbin/aurora-modem | 399 +++++++ b12/b12q/alpine-overlay/usr/sbin/aurora-wifi | 67 ++ b12/b12q/b12q-boot.sh | 45 + b12/b12q/build-b12q.sh | 25 + b12/b12q/cache/extlinux-b12q.conf | 6 + b12/b12q/cache/mkfs-cache-b12q.sh | 27 + b12/b12q/install-p27.sh | 14 + b12/b12r/B12R-rollback.sh | 12 + b12/b12r/B12R-write.sh | 52 + b12/b12r/b12r-boot.sh | 45 + b12/b12r/build-b12r.sh | 12 + b12/b12r/cache/extlinux-b12r.conf | 6 + b12/b12r/cache/mkfs-cache-b12r.sh | 27 + b12/b12r/install-p27.sh | 8 + b12/b12r/overlay/etc/aurora/router.conf | 10 + b12/b12r/overlay/etc/init.d/aurora-router | 13 + b12/b12r/overlay/usr/sbin/aurora-router | 91 ++ b12/mk-alpine-rootfs.sh | 23 + b12/mk-e2fs-bundle.sh | 14 + b12/mk-router-bundle.sh | 17 + b12/persist/alpine-provision.sh | 34 + b12/pull.sh | 3 + b12/push.sh | 16 + b12/router/router-common.sh | 7 + b12/router/router-down.sh | 11 + b12/router/router-status.sh | 12 + b12/router/router-up.sh | 77 ++ b12/router/router.conf | 12 + b12/tn.sh | 3 + b12/vncshot.py | 38 + b13/b13w-vnc-sway.txt | 9 + b13/run/cage-down.sh | 4 + b13/run/cage-up.sh | 8 + b13/run/pattern.sh | 19 + b13/run/sway-down.sh | 4 + b13/run/sway-up.sh | 8 + b13/run/sway.conf | 15 + b13/run/wayvnc-down.sh | 4 + b13/run/wayvnc-up.sh | 10 + b13/run/wl-common.sh | 19 + b13/vnc-vencrypt.py | 60 + b14/README.md | 19 + b14/config/aurora-ui.conf | 3 + b14/run/aurora-ui.openrc | 14 + b14/run/soak.sh | 24 + b14/run/ui-status.sh | 8 + b14/run/ui-stop.sh | 10 + b14/run/ui-up.sh | 32 + b14/src/Makefile | 8 + b14/src/aurora-ui.c | 379 ++++++ b14/src/font8x16.h | 259 +++++ b14/src/sdlprobe.c | 19 + b15/B15-rollback.sh | 12 + b15/B15-write.sh | 52 + b15/b15-boot.sh | 15 + b15/build-b15.sh | 11 + b15/cache/extlinux-b15.conf | 6 + b15/cache/mkfs-cache-b15.sh | 27 + b15/run/aurora-router | 95 ++ b15/run/aurora-vpn | 130 +++ b15/run/aurora-vpn-diag | 16 + b15/run/aurora-vpn.openrc | 7 + b15/run/free-turn.openrc | 11 + b15/run/vpn.conf | 4 + b16/README.md | 13 + b16/src/Makefile | 8 + b16/src/aurora-ui.c | 1016 +++++++++++++++++ b16/src/font6x8.h | 259 +++++ b16/src/font8x16.h | 259 +++++ b17/aurora-modem | 405 +++++++ b17/aurora-ssh.openrc | 19 + b17/modem.conf.add | 3 + b18/B18-rollback.sh | 12 + b18/B18-write.sh | 52 + b18/aurora-usb | 16 + b18/b18-boot.sh | 15 + b18/build-b18.sh | 13 + b18/cache/emu/run-emu-b18.sh | 11 + b18/cache/extlinux-b18.conf | 6 + b18/cache/mkfs-cache-b18.sh | 27 + b18/out/SHA256SUMS | 7 + b20/aurora-blackbox | 48 + b20/aurora-blackbox.openrc | 10 + b20/aurora-netwatch | 74 ++ b20/aurora-netwatch.openrc | 10 + b22/aurora-vpn | 146 +++ b22/aurora-vpnsel | 68 ++ b22/aurora-vpnsel.openrc | 10 + linux/aurora-b12.config | 30 + linux/aurora-b15.config | 8 + linux/aurora-b18.config | 15 + logs/b11/B11-LED-AUDIT.md | 82 ++ logs/b11/B11-LED-RESULT.md | 73 ++ logs/b11/b11c/B11C-TRACE-PLAN.md | 139 +++ logs/b12/b12a/B12A-AUDIO-AUDIT.md | 34 + logs/b12/b12p/B12P-RESULT.md | 16 + logs/b12/b12q/B12Q-ALPINE-PRIMARY-ROOT.md | 62 + logs/b12/b12r/B12R-ROUTER-RESULT.md | 41 + logs/b13/B13-WAYLAND-WLROOTS-RESULT.md | 57 + logs/b14/B14-UI-RESULT.md | 79 ++ logs/b15/B15-AUDIT.md | 35 + 139 files changed, 6432 insertions(+), 4 deletions(-) create mode 100644 b11/audit/b11-ro.sh create mode 100644 b11/run/b11-btn-watch.sh create mode 100644 b11/run/b11-led-run.sh create mode 100644 b12/alpine/chroot-down.sh create mode 100644 b12/alpine/chroot-up.sh create mode 100644 b12/alpine/overlay/etc/X11/xorg-modesetting.conf create mode 100644 b12/alpine/overlay/etc/X11/xorg.conf create mode 100644 b12/alpine/overlay/usr/local/bin/aurora-x create mode 100644 b12/b12p/B12P-rollback.sh create mode 100644 b12/b12p/B12P-write.sh create mode 100644 b12/b12p/b12p-boot.sh create mode 100644 b12/b12p/build-b12p.sh create mode 100644 b12/b12p/cache/extlinux-b12p.conf create mode 100644 b12/b12p/cache/mkfs-cache-b12p.sh create mode 100644 b12/b12q/B12Q-rollback.sh create mode 100644 b12/b12q/B12Q-write.sh create mode 100644 b12/b12q/alpine-overlay/etc/aurora/build-epoch create mode 100644 b12/b12q/alpine-overlay/etc/aurora/modem.conf create mode 100644 b12/b12q/alpine-overlay/etc/aurora/primary-root create mode 100644 b12/b12q/alpine-overlay/etc/fstab create mode 100644 b12/b12q/alpine-overlay/etc/hostname create mode 100644 b12/b12q/alpine-overlay/etc/init.d/aurora-bootok create mode 100644 b12/b12q/alpine-overlay/etc/init.d/aurora-display create mode 100644 b12/b12q/alpine-overlay/etc/init.d/aurora-firmware create mode 100644 b12/b12q/alpine-overlay/etc/init.d/aurora-modem create mode 100644 b12/b12q/alpine-overlay/etc/init.d/aurora-ncm create mode 100644 b12/b12q/alpine-overlay/etc/init.d/aurora-ro create mode 100644 b12/b12q/alpine-overlay/etc/init.d/aurora-wifi create mode 100644 b12/b12q/alpine-overlay/etc/inittab create mode 100644 b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-display create mode 100644 b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-modem create mode 100644 b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-wifi create mode 100644 b12/b12q/alpine-overlay/usr/local/bin/b4b2-mm create mode 100644 b12/b12q/alpine-overlay/usr/local/bin/nv-check create mode 100644 b12/b12q/alpine-overlay/usr/local/bin/qmi-b3 create mode 100644 b12/b12q/alpine-overlay/usr/sbin/aurora-display create mode 100644 b12/b12q/alpine-overlay/usr/sbin/aurora-modem create mode 100644 b12/b12q/alpine-overlay/usr/sbin/aurora-wifi create mode 100644 b12/b12q/b12q-boot.sh create mode 100644 b12/b12q/build-b12q.sh create mode 100644 b12/b12q/cache/extlinux-b12q.conf create mode 100644 b12/b12q/cache/mkfs-cache-b12q.sh create mode 100644 b12/b12q/install-p27.sh create mode 100644 b12/b12r/B12R-rollback.sh create mode 100644 b12/b12r/B12R-write.sh create mode 100644 b12/b12r/b12r-boot.sh create mode 100644 b12/b12r/build-b12r.sh create mode 100644 b12/b12r/cache/extlinux-b12r.conf create mode 100644 b12/b12r/cache/mkfs-cache-b12r.sh create mode 100644 b12/b12r/install-p27.sh create mode 100644 b12/b12r/overlay/etc/aurora/router.conf create mode 100644 b12/b12r/overlay/etc/init.d/aurora-router create mode 100644 b12/b12r/overlay/usr/sbin/aurora-router create mode 100644 b12/mk-alpine-rootfs.sh create mode 100644 b12/mk-e2fs-bundle.sh create mode 100644 b12/mk-router-bundle.sh create mode 100644 b12/persist/alpine-provision.sh create mode 100644 b12/pull.sh create mode 100644 b12/push.sh create mode 100644 b12/router/router-common.sh create mode 100644 b12/router/router-down.sh create mode 100644 b12/router/router-status.sh create mode 100644 b12/router/router-up.sh create mode 100644 b12/router/router.conf create mode 100644 b12/tn.sh create mode 100644 b12/vncshot.py create mode 100644 b13/b13w-vnc-sway.txt create mode 100644 b13/run/cage-down.sh create mode 100644 b13/run/cage-up.sh create mode 100644 b13/run/pattern.sh create mode 100644 b13/run/sway-down.sh create mode 100644 b13/run/sway-up.sh create mode 100644 b13/run/sway.conf create mode 100644 b13/run/wayvnc-down.sh create mode 100644 b13/run/wayvnc-up.sh create mode 100644 b13/run/wl-common.sh create mode 100644 b13/vnc-vencrypt.py create mode 100644 b14/README.md create mode 100644 b14/config/aurora-ui.conf create mode 100644 b14/run/aurora-ui.openrc create mode 100644 b14/run/soak.sh create mode 100644 b14/run/ui-status.sh create mode 100644 b14/run/ui-stop.sh create mode 100644 b14/run/ui-up.sh create mode 100644 b14/src/Makefile create mode 100644 b14/src/aurora-ui.c create mode 100644 b14/src/font8x16.h create mode 100644 b14/src/sdlprobe.c create mode 100644 b15/B15-rollback.sh create mode 100644 b15/B15-write.sh create mode 100644 b15/b15-boot.sh create mode 100644 b15/build-b15.sh create mode 100644 b15/cache/extlinux-b15.conf create mode 100644 b15/cache/mkfs-cache-b15.sh create mode 100644 b15/run/aurora-router create mode 100644 b15/run/aurora-vpn create mode 100644 b15/run/aurora-vpn-diag create mode 100644 b15/run/aurora-vpn.openrc create mode 100644 b15/run/free-turn.openrc create mode 100644 b15/run/vpn.conf create mode 100644 b16/README.md create mode 100644 b16/src/Makefile create mode 100644 b16/src/aurora-ui.c create mode 100644 b16/src/font6x8.h create mode 100644 b16/src/font8x16.h create mode 100644 b17/aurora-modem create mode 100644 b17/aurora-ssh.openrc create mode 100644 b17/modem.conf.add create mode 100644 b18/B18-rollback.sh create mode 100644 b18/B18-write.sh create mode 100644 b18/aurora-usb create mode 100644 b18/b18-boot.sh create mode 100644 b18/build-b18.sh create mode 100644 b18/cache/emu/run-emu-b18.sh create mode 100644 b18/cache/extlinux-b18.conf create mode 100644 b18/cache/mkfs-cache-b18.sh create mode 100644 b18/out/SHA256SUMS create mode 100644 b20/aurora-blackbox create mode 100644 b20/aurora-blackbox.openrc create mode 100644 b20/aurora-netwatch create mode 100644 b20/aurora-netwatch.openrc create mode 100644 b22/aurora-vpn create mode 100644 b22/aurora-vpnsel create mode 100644 b22/aurora-vpnsel.openrc create mode 100644 linux/aurora-b12.config create mode 100644 linux/aurora-b15.config create mode 100644 linux/aurora-b18.config create mode 100644 logs/b11/B11-LED-AUDIT.md create mode 100644 logs/b11/B11-LED-RESULT.md create mode 100644 logs/b11/b11c/B11C-TRACE-PLAN.md create mode 100644 logs/b12/b12a/B12A-AUDIO-AUDIT.md create mode 100644 logs/b12/b12p/B12P-RESULT.md create mode 100644 logs/b12/b12q/B12Q-ALPINE-PRIMARY-ROOT.md create mode 100644 logs/b12/b12r/B12R-ROUTER-RESULT.md create mode 100644 logs/b13/B13-WAYLAND-WLROOTS-RESULT.md create mode 100644 logs/b14/B14-UI-RESULT.md create mode 100644 logs/b15/B15-AUDIT.md diff --git a/NOTES.md b/NOTES.md index d8017b2..ac56451 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1354,6 +1354,83 @@ fast MSS restart re-attach needs investigation; single unreproduced cases (pc1 n - Limitations: clamp is no hard-off below 4.00 V; timer is no charge stop; no thermal derating; battery contact unreliable. - Next: router stack; optional charger follow-ups (derating, hard-stop test, CV tuning) by separate GO. +# Session 39 — B11 LED audit + all-SAFE-LED run (2026-10-04) — **PASS (scope: 1 SAFE line)** — logs/b11/ +- Read-only audit (B11-LED-AUDIT.md): the stock DT/Android/system/MPSS/LK have no indicator LEDs ("gpio-leds" = SIM mux only). The only proven LED line is MPP4 (LCD backlight). + MPP3 = current sink off (UNKNOWN), MPP2 = digital out HIGH and MPP1 = analog out (DO NOT TOUCH), PM GPIO1–4/VIB/unclaimed TLMM = UNKNOWN, GPIO105 out-low/106 pull-up = modem RF. +- Run r1 on the live production boot (no image, no eMMC): OFF → ON 4 s → OFF → chase 2× → restore, END OK, monitor 0 FAIL, operator saw the backlight follow the plan; + PMIC state identical pre/post. pm8916-thermal reads ~7 °C lower while the MPP4 sink is off (artefact). +- Buttons (read-only): POWER = KPDPWR (pwrkey IRQ), RESET = RESIN (PON RT bit1), MENU probably = KPDPWR too; GPIO37/107 (stock key_f2/f3) never toggle. + +# Session 39b — B11C closed / B12A audio audit (2026-10-04) +- B11C: 15 populated LEDs (+~12 empty positions) inside the display frame on BOT = display backlight array (operator); LED topic closed. Annotated photo logs/b11/b11c/B11C2-annotated-bot.jpg. +- B12A (read-only, CLOSED n/a — operator: no speaker/mic in the case): stock audio = generic 8916 MTP config (PM8916 Tombak + digital codec, mixer_paths unmodified); no speaker/mic/jack visible in photos; CONFIG_SOUND=n. logs/b12/b12a/B12A-AUDIO-AUDIT.md. + +# Session 40 — B12P persistent Alpine on userdata (2026-10-04) — **PASS** — logs/b12/b12p/B12P-RESULT.md +- userdata p27 = ext4 `aurora-alpine` (Alpine 3.24.2 + X11), auto-mounted at /alpine by the B12P initramfs; only p27 RW (disk RO flag lifted, all other partitions RO-verified). +- Cache **98d061ef** (B10 kernel/DTB/cmdline + overlay) B12P_CACHE_WRITE_VERIFIED, backup B10 c3732515, rollback b12/b12p/B12P-rollback.sh. RAM ram2 + cold1 PASS. +- Also built (untested on HW): kernel out-b12 (b10b5 + nftables), b12/router/ router-up/down/status + nft/dnsmasq bundle. NCM bulk transfers stall → b12/push.sh. + +# Session 40b — B12Q Alpine primary rootfs (2026-10-04) — **RAM VALIDATED** — logs/b12/b12q/B12Q-ALPINE-PRIMARY-ROOT.md +- initramfs stage0 → switch_root into Alpine 3.24.2 on p27 (busybox init + OpenRC 0.63.2); LFS init kept byte-identical as /init.lfs rescue + (aurora.alpine=off, any check failure, ≥3 unconfirmed boots). p27 got 44 pkgs (bit-identical modem binaries) + OpenRC wrappers around the unchanged B6–B10 scripts. +- RAM normal1/off1/failtest1 all PASS. **Cache 59272cd3 B12Q_CACHE_WRITE_VERIFIED (backup B12P 98d061ef), cold1 PASS — Alpine is the primary rootfs.** Rollback b12/b12q/B12Q-rollback.sh. + +# Session 40c — B12R router inside Alpine (2026-10-04) — **RAM VALIDATED** — logs/b12/b12r/B12R-ROUTER-RESULT.md +- Kernel 7.2.7-aurora-b12 (b10b5 + nf_tables) RAM-booted; p27 got nftables+dnsmasq+aurora-router (OpenRC, not in runlevel). Phone: DHCP .199, ya.ru OK, NAT OK, mgmt isolation drop OK. +- Beeline RSTs foreign hosts (whitelist mode) — apk works via mirror.yandex.ru. **Cache e85d862e B12R_CACHE_WRITE_VERIFIED, cold1 PASS, aurora-router in default runlevel.** Rollback b12/b12r/B12R-rollback.sh → B12Q. + +# Session 41 — B13W Wayland/wlroots (2026-10-04) — **PASS** — logs/b13/B13-WAYLAND-WLROOTS-RESULT.md +- Alpine apk (mirror.yandex.ru): wlroots 0.20.2, cage 0.3.0, sway 1.12_rc3, wayvnc 0.10.0, seatd 0.9.3, foot. Pixman renderer on DRM card0 (SPI-1 128x128), no GPU. +- Cage PASS (47.5 MB, 0 % idle), Sway PASS (56.8 MB), wayvnc on Sway and on Cage PASS (172.16.42.1 only, VeNCrypt TLS + password, input OK), X11 regression PASS. +- wlroots needs mesa libgallium/LLVM files even with Pixman → `apk fix` restored the B13-pruned mesa/LLVM. Recommendation: Cage for UI, Sway+wayvnc for debug, Xorg = fallback. + +# Session 42 — B14 Cage + SDL2 modem UI (2026-10-05) — **USERSPACE VALIDATED** — logs/b14/B14-UI-RESULT.md +- aurora-ui (C, SDL2 2.32.10 wayland driver, llvmpipe via EGL/wl_shm) under Cage: LTE/IP/Wi-Fi/traffic/VBAT+state/PMIC/NAT/clock on 128x128; soak 15+5 min stable. +- Pitfalls: SDL2 segfaults without wayland-libs-egl; bam-dmux wwan0 netdev counters are always 0 (UI uses wlan0); CMA EBUSY warnings came from page cache after apk installs (0 with UI after drop_caches). +- aurora-ui autostart ENABLED (GO 2026-10-05): cold1 UI up at 9.5 s uptime, router autostart ROUTER UP OK 88 s. + +# Session 43 — B15 VPN: WireGuard over a TURN relay (2026-10-05) — **PASS (cold boot)** — logs/b15/B15-AUDIT.md +- Kernel 7.2.7-aurora-b15 = b10b5 sources + WIREGUARD + IP_MULTIPLE_TABLES (fragment linux/aurora-b15.config). +- Path: Wi-Fi LAN → wg0 → local relay client (127.0.0.1) → public TURN relays → own WireGuard server. Relay client = third-party static + arm64 binary (free-turn, not included); its config, the WireGuard config/keys and the server address are private and not published. +- aurora-vpn (b15/run): policy-routes only the LAN (rule 100: LAN subnet → table 51820 → wg0); the board's own traffic (incl. the relay + uplink) stays in main → wwan0, so no loop and no fwmark. Fail-closed for the LAN (no LTE leak). MTU from the WG config, MSS clamp. +- OpenRC: free-turn (supervise-daemon, respawn) + aurora-vpn in default. First ~2 min after boot: DTLS timeouts until LTE + clock are up, then self-recovers. +- Race fix: aurora-router came up after aurora-vpn and re-added its forward chain that dropped LAN→wg0 → router now skips its ruleset if table inet aurora_vpn exists. +- Cache **212e0ca9** (rollback b15/B15-rollback.sh → B12R). + +# Session 44 — B16 UI pages + POWER key (2026-10-05) — b16/README.md +- aurora-ui: status row with WG handshake age; POWER key (pm8941_pwrkey evdev) wakes and cycles STATUS → CLIENTS → relay log → WG; backlight off after 180 s. + +# Session 45 — B17 standalone operation + SSH (2026-10-05) +- aurora-modem: USB management-ping check only with MGMT_CHECK=1 (default 0) — it rolled the modem back when no host was attached. +- SSH (dropbear via own /etc/init.d/aurora-ssh); never `rc-service dropbear` on this image: Alpine's script needs net → starts + `networking` and a second udevd whose exit deletes /run/udev/control. nft allows tcp/22 only to the LAN address. +- Board usable without a host: SSH over Wi-Fi, telnet over USB NCM (no auth, dev). + +# Session 46 — B18 USB host / OTG (2026-10-06) — **PASS, persistent** +- Kernel out-b18 = b15 + CONFIG_USB/EHCI/CHIPIDEA_HOST, usb-storage/UAS, HID, exFAT (linux/aurora-b18.config — CONFIG_USB itself was off before). +- DTB b18 = b10b3 + &usb dr_mode "otg" + usb-role-switch; boots as device (NCM); `aurora-usb host|device|status` switches the role. + No VBUS output on this board → powered hub needed (keyboard/mouse receiver verified; X with libinput). +- Cache **ac82811a** (b18/B18-write.sh), cold boot PASS; rollback b18/B18-rollback.sh → B15. + +# Session 47 — B19 OpenSSH instead of dropbear (2026-10-06) +- Alpine dropbear has no X11 forwarding → openssh-server + sftp-server + xauth, host keys converted (same fingerprint), X11Forwarding yes. + `ssh -Y` over Wi-Fi works; scp/sftp work. + +# Session 48 — B20 aurora-netwatch + aurora-blackbox (2026-10-06) +- Day test on battery: operator detach (`emm-detached`) left the bearer down for 30+ min → aurora-netwatch: bearer + ping every 30 s, + 3 failures → disconnect + start + default route; OFF → retry after a grace period. Simulated disconnect: detected in ~80 s, recovered in 9 s. +- aurora-blackbox: /var/log/aurora/status.log once a minute + full kernel log per boot (boot counter), rotation at 2 MB. + +# Session 49 — B21 Stack game, B22 direct-first VPN (2026-10-06) +- B21: a one-button game on aurora-ui page 5 (b16/src/aurora-ui.c). +- B22: second WireGuard interface wg1 directly to the server; aurora-vpnsel moves the LAN route to wg1 while its handshake is fresh, + stops the relay; falls back to wg0 via the relay when stale. LTE whitelist indicator: periodic TCP probes to domestic vs foreign hosts + → UI shows WL / NET / LTE!. With the operator in whitelist mode the direct path is blocked → relay is used. + +# Session 50 — B23 published separately (see below); B11–B22 published 2026-10-07 (scripts, configs, sources, *.md reports only). + # Session B23 — faster charging, 720 mA (2026-10-07) — **PASS, persistent** — logs/b23/B23-RESULT.md - DTB b23 = B18 DTB + `qcom,fast-charge-safe-current = <720000>` → IBAT_MAX/IBAT_SAFE 0x04 → 0x07; kernel b18, initramfs B12Q, cmdline unchanged. - RAM ram2 (b23/b23-boot.sh, observer b23/b23-mon.sh): laptop USB meter 0.724 A, USBIN 4.67–4.78 V, CC 55+ min, PMIC max 55.7 °C, CV from ≈4.10 V. diff --git a/README.md b/README.md index 02b9efe..3b3aa4a 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,14 @@ | B6P — автозапуск модема из flash (persistent) | **PASS / CLOSED** | | B7 — время от сети (DMS/NITZ, без RTC) | **PASS / CLOSED** | | Wi-Fi (WCNSS/Pronto + wcn36xx, тестовая AP WPA2, автозапуск из flash) — B8 | **PASS / CLOSED** (см. ниже) | -| Router stack (NAT/firewall/VPN) | TODO (следующий этап) | +| Индикация (LED = подсветка дисплея), кнопки POWER/RESET; аудио n/a — B11/B12A | **CLOSED** | +| Alpine Linux как основной root на userdata (OpenRC), откат в initramfs-rescue — B12P/B12Q | **PASS / persistent** | +| Router stack: NAT/firewall (nftables) + dnsmasq для Wi-Fi LAN — B12R | **PASS / persistent** | +| Wayland (Cage/Sway/wayvnc) и UI на экране 128×128 (aurora-ui, SDL2), кнопка POWER — B13/B14/B16 | **PASS** | +| VPN для LAN: WireGuard через TURN-relay (fail-closed) — B15; прямой WG с откатом на relay — B22 | **PASS** | +| Автономная работа без хоста, SSH (OpenSSH, X11 forwarding) — B17/B19 | **PASS** | +| USB host / OTG (role switch, HID, накопители через хаб с питанием) — B18 | **PASS / persistent** | +| Сторож LTE-bearer (netwatch) и журнал blackbox — B20 | **PASS** | | Дисплей ST7735S 128×128 (SPI): DRM/fbcon tty1, подсветка, автозапуск из flash — B9 | **PASS / CLOSED** (см. ниже) | | Батарея / зарядка: супервизор CHARGE → HOLD → батарея, FAULT safe clamp, автозапуск из flash — B10 | **PASS / CLOSED** (см. ниже) | | Ускоренная зарядка: ток CC 450 → 720 mA (только DTB) — B23 | **PASS / persistent** (см. ниже) | @@ -35,7 +42,8 @@ ``` Power → stock SBL1 → DB410c TZ (TZ.BF.3.0) → qhypstub → lk1st (aboot, autoboot, splash на ST7735S через ~0.4 с) - → cache: ext2 + extlinux/extlinux.conf → B10B-5 (ядро b10b5 + DTB b10b3 + initramfs B9C: модем, время, Wi-Fi, дисплей) → ARM64 Linux → LTE + Wi-Fi AP + консоль на экране + зарядка + → cache: ext2 + extlinux/extlinux.conf → B23 (ядро b18 + DTB b23 + initramfs B12Q) → stage0 → switch_root в Alpine на userdata (p27, OpenRC) + → модем, время, Wi-Fi AP + роутер + VPN, дисплей + aurora-ui, зарядка 720 mA, USB OTG (rescue: initramfs B9C-init при сбое Alpine) Fallback: extlinux не найден / не парсится / не грузится → Android boot image из раздела boot (emmc1 rescue) ``` @@ -49,13 +57,26 @@ Fallback: extlinux не найден / не парсится / не грузит | hyp | qhypstub, до 512 KiB | `1a963047` | | aboot | lk1st (lk2nd 23.1 + патч B9L: SPI-панель ST7735S + splash) autoboot, подписан qtestsign, до 1 MiB | `15208dbb` (откат: B5a lk1st `3b8cd266`) | | boot | emmc1 rescue image (+ нули до 16 MiB) | `e9579f33` | -| cache | ext2 + extlinux + B10B-5 (ядро 7.2.7-aurora-b10b5 = B9C + зарядник, DTB b10b3, initramfs B9C) | `c3732515` (откат: B9C `b9ce13c9` → B8F `857c9c30` → B7C `14fe453a` → T4 `9d3bc890` → B6P `9fea693a`) | +| cache | ext2 + extlinux + B23 (ядро 7.2.7-aurora-b18, DTB b23, initramfs B12Q → Alpine) | `01bfe522` (откат: B18 `ac82811a` → B15 `212e0ca9` → B12R `e85d862e` → B12Q `59272cd3` → B12P `98d061ef` → B10B-5 `c3732515` → B9C `b9ce13c9` → …) | | recovery | lk2nd 23.1 (fastboot) | `0dfa60a0` | -| modem, NV/EFS, persist, system, userdata | не менялись (Linux монтирует modem FAT только RO) | — | +| userdata (p27) | ext4 `aurora-alpine`: Alpine 3.24 root (B12P+), единственный RW-раздел | — (изменяемый) | +| modem, NV/EFS, persist, system | не менялись (Linux монтирует modem FAT только RO) | — | Golden mm1 (Android boot image, для `fastboot boot`): `aurora-mm1.img` sha256 `38c960ef…`, ядро `7.2.7-aurora-bam1 #5`. Все хэши целиком: `b5a/SHA256SUMS`, `b5b/SHA256SUMS`, `bootchain-migration/SHA256SUMS`, `linux/*/SHA256SUMS`, [docs/BLOBS.md](docs/BLOBS.md). +## B11–B22 — Alpine, роутер, VPN, UI, USB host (2026-10-04…06) + +Подробно — NOTES.md (сессии 39–49) и отчёты в `logs/b11`…`logs/b15`. + +- **B11** (`logs/b11/`): индикаторных LED на плате нет — «LED» это подсветка дисплея (MPP4); кнопки POWER = KPDPWR, RESET = RESIN. **B12A**: аудио n/a (нет динамика/микрофона). +- **B12P/Q/R** (`b12/`, `logs/b12/`): Alpine 3.24 на userdata p27; initramfs stage0 делает switch_root в Alpine (OpenRC), откат в прежний init при сбое проверок или ≥3 неподтверждённых загрузках; роутер nftables + dnsmasq для Wi-Fi LAN, изоляция управляющей сети. +- **B13/B14/B16/B21** (`b13/`, `b14/`, `b16/`): wlroots (Pixman, без GPU): Cage для UI, Sway+wayvnc для отладки; `aurora-ui` (C, SDL2) — LTE/IP/Wi-Fi/трафик/VBAT/PMIC/NAT/VPN на 128×128, страницы по кнопке POWER, гашение подсветки, игра. +- **B15/B22** (`b15/`, `b22/`): VPN только для LAN: policy routing (table 51820), fail-closed; WireGuard через TURN-relay, с B22 — прямой WG, если доступен (оператор в режиме белого списка его режет), индикатор белого списка. +- **B17/B19**: работа без хоста; SSH (OpenSSH, X11 forwarding) по Wi-Fi. +- **B18** (`b18/`): USB OTG с переключением роли (`aurora-usb`), ядро b18; VBUS платой не выдаётся. +- **B20** (`b20/`): `aurora-netwatch` (восстановление bearer после detach) и `aurora-blackbox` (`/var/log/aurora`). + ## B23 status — ускоренная зарядка 720 mA: PASS, persistent (2026-10-07) Отчёт: [`logs/b23/B23-RESULT.md`](logs/b23/B23-RESULT.md), DTS: [`linux/dts/msm8916-jz08-aurora-b23.dts`](linux/dts/msm8916-jz08-aurora-b23.dts). @@ -171,6 +192,7 @@ Safety: | `b8/` | B8: Wi-Fi — аудит, WCNSS/NV, wcn36xx, hostapd, сервис aurora-wifi, cache B8F (запись/откат/эмулятор) | | `b9/` | B9: дисплей — аудит stock LK/DT, panel-mipi-dbi firmware, fbcon/сервис aurora-display, cache B9C, патч lk2nd (SPI-панель в LK) | | `b10/` | B10: батарея/зарядка — аудит, супервизор v1–v4 (патчи/diff), тестовые DTB/сборки/скрипты, хуки B10B-4/5 (только тест), cache B10B-5 (запись/откат/эмулятор) | +| `b11/`…`b22/` | B11 LED/кнопки; B12 Alpine (rootfs, overlay, cache B12P/Q/R, роутер); B13 Wayland; B14/B16 aurora-ui (исходники); B15 VPN; B17 standalone; B18 USB OTG + cache; B20 netwatch/blackbox; B22 выбор VPN-пути | | `b23/` | B23: ток зарядки 720 mA — сборка RAM-образа, монитор, cache B23 (сборка/эмулятор/запись/откат) | | `tools/` | утилиты: UART-логгер, Sahara probe, распаковка boot.img, sanitizer публикации | | `logs/` | sanitized логи: UART, QMI, ModemManager, EDL, результаты тестов | @@ -181,6 +203,7 @@ Safety: Полные дампы eMMC и разделов, NV/EFS (`modemst1/2`, `fsg`, `fsc`), `persist`, `userdata`, файлы прошивки модема, стоковые SBL1/RPM/TZ/aboot/boot/recovery/system, Firehose-программер, DB410c TZ, собранные бинарники, Wi-Fi NV (`WCNSS_qcom_wlan_nv.bin`), PSK тестовой AP и образы с ним (B8F/B9C/B10 initramfs/cache/RAM-образы), логотип из stock LK. +С B12: `wifi.conf` (SSID/PSK), конфиги и ключи WireGuard/relay, адрес VPN-сервера, сторонний relay-клиент и его исходники, учётные данные и TLS-ключ wayvnc, `authorized_keys`, образы rootfs/cache (B12–B23). Начиная с B10 сырые логи прогонов (мониторы, dmesg, snapshot, UART, журналы действий, дампы регистров, выводы эмулятора) не публикуются — только отчёты `*.md` и компактные таблицы `*-diff.txt` (правила в `tools/publish-sanitize.py` и `.gitignore`). Идентификаторы (IMEI, IMSI, ICCID, eMMC CID/serial, Sahara serial, MAC, номера сот, cookies) заменены на ``. diff --git a/b11/audit/b11-ro.sh b/b11/audit/b11-ro.sh new file mode 100644 index 0000000..239f036 --- /dev/null +++ b/b11/audit/b11-ro.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# B11 read-only snapshot: PMIC MPP/GPIO/VIB/LPG regs, PON RT status, inputs, backlight, gpio. NO writes. +D=/sys/kernel/debug; ls $D/regmap >/dev/null +echo "== uptime $(cut -d' ' -f1 /proc/uptime)"; ls $D/regmap +rd() { # sid base count + R=$D/regmap/$1/registers; dd if=$R bs=9 skip=$(($2)) count=$(($3)) 2>/dev/null; } +echo "== PON 0x0800-0x084f (sid0)"; rd 0-00 0x0800 0x50 +for b in 0xa000 0xa100 0xa200 0xa300 0xc000 0xc100 0xc200 0xc300; do echo "== sid0 $b"; rd 0-00 $b 0x50; done +for b in 0xbc00 0xc000; do echo "== sid1 $b"; rd 0-01 $b 0x50; done +echo "== input"; cat /proc/bus/input/devices +echo "== backlight"; for f in /sys/class/backlight/*/*; do [ -f $f ] && echo "$f=$(cat $f 2>/dev/null)"; done +echo "== leds"; ls -l /sys/class/leds/ +echo "== keygpio"; grep -E "gpio(37|106|107|116|118)\b|gpio1 |gpio20|gpio22|gpio23|gpio105" $D/gpio +echo B11-RO-END diff --git a/b11/run/b11-btn-watch.sh b/b11/run/b11-btn-watch.sh new file mode 100644 index 0000000..df01be9 --- /dev/null +++ b/b11/run/b11-btn-watch.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# B11 read-only button watcher: polls TLMM GPIO37/107 (debugfs) and PM8916 PON RT 0x0810 every ~0.1 s, logs changes. NO writes. +T=${1:-b1}; DUR=${2:-90}; LOG=/tmp/b11-btn-$T.log; D=/sys/kernel/debug +exec >$LOG 2>&1 +up() { cut -d' ' -f1 /proc/uptime; } +st() { g=$(grep -E "^ gpio(37|107) " $D/gpio | awk '{print $1"="$4}' | tr '\n' ' ') + p=$(dd if=$D/regmap/0-00/registers bs=9 skip=$((0x810)) count=1 2>/dev/null | cut -d' ' -f2) + echo "$g pon_rt=$p"; } +E0=$(cat /proc/interrupts | grep -i pwrkey | awk '{print $2}') +echo "B11BTN $T $(up) START dur=$DUR"; last="" +end=$(( $(cut -d. -f1 /proc/uptime) + DUR )) +while [ $(cut -d. -f1 /proc/uptime) -lt $end ]; do s=$(st); [ "$s" != "$last" ] && echo "B11BTN $T $(up) $s"; last=$s; usleep 100000; done +echo "B11BTN $T $(up) pwrkey-irq before=$E0 after=$(cat /proc/interrupts | grep -i pwrkey | awk '{print $2}')" +echo "B11BTN $T $(up) END" diff --git a/b11/run/b11-led-run.sh b/b11/run/b11-led-run.sh new file mode 100644 index 0000000..48d0deb --- /dev/null +++ b/b11/run/b11-led-run.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# B11 single all-SAFE-LED run (RAM state only; no eMMC/cache writes, no PMIC/TLMM raw writes). +# SAFE set from B11-LED-AUDIT.md: L1 = PM8916 MPP4 LCD backlight via /sys/class/backlight/backlight (0/1). No PWM line confirmed. +# usage: sh b11-led-run.sh TAG -> log /tmp/b11-run-TAG.log, markers "B11 TAG ..." +T=${1:-r1}; LOG=/tmp/b11-run-$T.log; FAIL=/tmp/b11-fail-$T; rm -f $FAIL +D=/sys/kernel/debug +LEDS="L1:/sys/class/backlight/backlight/brightness:1:0" # name:path:on:off +STEP=0.7; ALLON=4 +exec >$LOG 2>&1 +up() { cut -d' ' -f1 /proc/uptime; } +say() { echo "B11 $T $(up) $*"; } +mpp4() { dd if=$D/regmap/0-00/registers bs=9 skip=$((0xa340)) count=13 2>/dev/null | grep -E "^a3(40|41|46|4c)" | tr '\n' ' '; } +snap() { say "SNAP $1 mpp4[ $(mpp4)] bl=$(cat /sys/class/backlight/backlight/brightness) bl_power=$(cat /sys/class/backlight/backlight/bl_power)"; } +DM0=$(dmesg | wc -l) +check() { # one pass; prints status, returns 1 on critical loss + rc=0; s="" + ping -c2 -W2 -I wwan0 77.88.8.8 >/dev/null 2>&1 && s="$s lte=ok" || { s="$s lte=FAIL"; rc=1; } + [ "$(cat /sys/class/remoteproc/remoteproc0/state)" = running ] && s="$s mpss=ok" || { s="$s mpss=FAIL"; rc=1; } + [ "$(cat /sys/class/remoteproc/remoteproc1/state)" = running ] && s="$s wcnss=ok" || { s="$s wcnss=FAIL"; rc=1; } + [ "$(cat /sys/class/net/wlan0/operstate 2>/dev/null)" = up ] && pidof hostapd >/dev/null && s="$s wifi=ok" || { s="$s wifi=FAIL"; rc=1; } + [ "$(cat /sys/class/net/usb0/operstate 2>/dev/null)" = up ] && s="$s ncm=ok" || { s="$s ncm=FAIL"; rc=1; } + readlink /sys/bus/spi/devices/spi0.0/driver | grep -q panel-mipi-dbi && [ -e /dev/fb0 ] && s="$s disp=ok" || { s="$s disp=FAIL"; rc=1; } + c=$(cat /sys/class/power_supply/pm8916-lbc-chgr/status 2>/dev/null); s="$s chg=$c" + t=$(cat /sys/class/thermal/thermal_zone5/temp); s="$s pmic=$t"; [ "$t" -lt 70000 ] || { s="$s TEMP-HIGH"; rc=1; } + e=$(dmesg | tail -n +$((DM0+1)) | grep -v " HB " | grep -ciE "oops|bug:|panic|crash|fatal|watchdog|fault|error|reset|fail") + s="$s kerr=$e"; [ "$e" -eq 0 ] || rc=1 + echo "$s"; return $rc +} +restore() { for l in $LEDS; do IFS=: read n p on off < "$p"; done; snap RESTORED; } +mon() { while [ ! -e /tmp/b11-stop-$T ]; do r=$(check); st=$?; say "MON$r"; [ $st -eq 0 ] || { touch $FAIL; say "MON CRITICAL"; }; sleep 1; done; } +guard() { [ -e $FAIL ] && { say "ABORT at $1"; restore; dmesg | tail -n +$((DM0+1)) | grep -v " HB " | tail -20; touch /tmp/b11-stop-$T; say "END ABORT"; exit 2; }; } +set_all() { for l in $LEDS; do IFS=: read n p on off < $p || echo $off > $p; done; } +trap 'touch $FAIL; guard SIGNAL' INT TERM HUP +# --- pre-state +for l in $LEDS; do IFS=: read n p on off < no LED touched"; say "END PRECHECK-FAIL"; exit 3; }; say "PRECHECK$r" +rm -f /tmp/b11-stop-$T; mon & MP=$! +# 1. all off +say "STAGE1 ALL-OFF"; set_all off; snap S1; sleep 2; guard S1 +# 2. all on 4 s +say "STAGE2 ALL-ON ${ALLON}s"; set_all on; snap S2; sleep $ALLON; guard S2 +# 3. all off +say "STAGE3 ALL-OFF"; set_all off; snap S3; sleep 2; guard S3 +# 4. chase, 2 cycles +for c in 1 2; do for l in $LEDS; do IFS=: read n p on off < $p; sleep $STEP; guard "S4c$c$n"; echo $off > $p; say "STAGE4 CHASE c$c $n OFF"; sleep $STEP; guard "S4c$c${n}off"; done; done +# 5. brightness: skipped - no PWM-confirmed line +say "STAGE5 SKIPPED no PWM-confirmed line" +# 6. restore +say "STAGE6 RESTORE"; restore; sleep 2; guard S6 +touch /tmp/b11-stop-$T; wait $MP 2>/dev/null +r=$(check); say "POSTCHECK$r" +say "END OK" diff --git a/b12/alpine/chroot-down.sh b/b12/alpine/chroot-down.sh new file mode 100644 index 0000000..5f3b70e --- /dev/null +++ b/b12/alpine/chroot-down.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# B12: stop everything running inside /tmp/alpine, unmount in reverse order, free the tmpfs. +R=/tmp/alpine +[ -x $R/usr/local/bin/aurora-x ] && grep -q " $R/proc " /proc/mounts && chroot $R /usr/local/bin/aurora-x stop +inroot() { for p in /proc/[0-9]*; do [ "$(readlink $p/root 2>/dev/null)" = $R ] && echo ${p#/proc/}; done; } +P=$(inroot); [ -n "$P" ] && kill $P 2>/dev/null; sleep 2; P=$(inroot); [ -n "$P" ] && kill -9 $P 2>/dev/null; sleep 1 +for m in $(grep " $R" /proc/mounts | awk '{print $2}' | sort -r); do umount $m || umount -l $m; done +rmdir $R 2>/dev/null +echo "CHROOT-DOWN OK left_mounts=$(grep -c " $R" /proc/mounts) left_procs=[$(inroot)] vt=$(cat /sys/class/tty/tty0/active)" diff --git a/b12/alpine/chroot-up.sh b/b12/alpine/chroot-up.sh new file mode 100644 index 0000000..1f70b07 --- /dev/null +++ b/b12/alpine/chroot-up.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# B12 (board, LFS host side): RAM-only Alpine chroot at /tmp/alpine on its own tmpfs (size-capped; umount frees everything). +# chroot-up.sh (- = stream from stdin) +# Mounts (only what X/apk need): proc, sysfs (ro), /dev (bind), /dev/pts (bind). No eMMC partition is touched. +set -u; R=/tmp/alpine; SZ=${ALPINE_TMPFS_SIZE:-160m}; SRC=${1:-} +log() { echo "[$(cut -d' ' -f1 /proc/uptime)] $*"; } +mnt() { grep -q " $1 " /proc/mounts; } +[ -n "$SRC" ] || { echo "usage: $0 "; exit 2; } +mkdir -p $R +mnt $R || mount -t tmpfs -o size=$SZ,mode=0755 alpine $R || { echo "CHROOT-UP FAIL tmpfs"; exit 1; } +if [ ! -x $R/bin/busybox ]; then + if [ "$SRC" = - ]; then tar xzf - -C $R; else tar xzf "$SRC" -C $R; fi || { echo "CHROOT-UP FAIL extract"; exit 1; } +fi +mnt $R/proc || mount -t proc proc $R/proc +mnt $R/sys || { mount -o bind /sys $R/sys && mount -o remount,bind,ro $R/sys; } +mnt $R/dev || mount -o bind /dev $R/dev +mnt $R/dev/pts || mount -o bind /dev/pts $R/dev/pts +# DNS for apk inside the chroot: bearer DNS from the modem controller (falls back to 77.88.8.8) +DNS=$(sed -n 's/^DNS=//p' /run/aurora-modem/ipcfg 2>/dev/null | tr ',' ' '); [ -n "$DNS" ] || DNS=77.88.8.8 +: > $R/etc/resolv.conf; for d in $DNS; do echo "nameserver $d" >> $R/etc/resolv.conf; done +log "CHROOT-UP OK $(cat $R/etc/alpine-release) used=$(df -k $R | awk 'NR==2{print $3}')k mounts=$(grep -c " $R" /proc/mounts)" diff --git a/b12/alpine/overlay/etc/X11/xorg-modesetting.conf b/b12/alpine/overlay/etc/X11/xorg-modesetting.conf new file mode 100644 index 0000000..e0e5adf --- /dev/null +++ b/b12/alpine/overlay/etc/X11/xorg-modesetting.conf @@ -0,0 +1,19 @@ +# B13 alternative: modesetting on /dev/dri/card0 (dumb buffers + shadow/dirtyfb), no glamor. +Section "ServerFlags" + Option "AutoAddDevices" "false" + Option "AutoEnableDevices" "false" + Option "AllowEmptyInput" "true" + Option "BlankTime" "0" +EndSection +Section "Device" + Identifier "panel" + Driver "modesetting" + Option "kmsdev" "/dev/dri/card0" + Option "AccelMethod" "none" + Option "ShadowFB" "true" +EndSection +Section "Screen" + Identifier "screen" + Device "panel" + DefaultDepth 16 +EndSection diff --git a/b12/alpine/overlay/etc/X11/xorg.conf b/b12/alpine/overlay/etc/X11/xorg.conf new file mode 100644 index 0000000..f1124ba --- /dev/null +++ b/b12/alpine/overlay/etc/X11/xorg.conf @@ -0,0 +1,24 @@ +# B13 Aurora: Xorg on the ST7735S 128x128 (panel-mipi-dbi DRM, fbdev emulation /dev/fb0, RGB565). No input devices (VNC injects via XTEST). +Section "ServerFlags" + Option "AutoAddDevices" "false" + Option "AutoEnableDevices" "false" + Option "AllowEmptyInput" "true" + Option "BlankTime" "0" + Option "StandbyTime" "0" + Option "SuspendTime" "0" + Option "OffTime" "0" +EndSection +Section "Device" + Identifier "panel" + Driver "fbdev" + Option "fbdev" "/dev/fb0" + Option "ShadowFB" "true" +EndSection +Section "Screen" + Identifier "screen" + Device "panel" + DefaultDepth 16 + SubSection "Display" + Depth 16 + EndSubSection +EndSection diff --git a/b12/alpine/overlay/usr/local/bin/aurora-x b/b12/alpine/overlay/usr/local/bin/aurora-x new file mode 100644 index 0000000..be73217 --- /dev/null +++ b/b12/alpine/overlay/usr/local/bin/aurora-x @@ -0,0 +1,28 @@ +#!/bin/sh +# B13 (inside the Alpine chroot): aurora-x start [fbdev|modesetting] | stop | status +# Xorg :0 on vt2 -> twm + xterm + xsetroot; x11vnc on the USB mgmt address only (172.16.42.1:5900). +# x11vnc: IPv4 172.16.42.1 only. NOTE libvncserver 0.9.15 listens on [::]:5900 even with -no6/-noipv6 unless -rfbportv6 -1. +L=/var/log/aurora-x; mkdir -p $L /tmp/.X11-unix; chmod 1777 /tmp/.X11-unix +VNC_ADDR=${VNC_ADDR:-172.16.42.1} +pids() { for n in x11vnc xterm twm Xorg; do pidof $n; done | tr '\n' ' '; } +case "$1" in +start) + CONF=/etc/X11/xorg.conf; [ "$2" = modesetting ] && CONF=/etc/X11/xorg-modesetting.conf + pidof Xorg >/dev/null && { echo "X already running: $(pids)"; exit 0; } + for d in /usr/share/fonts/misc /usr/share/fonts/cursor; do [ -d $d ] && [ ! -f $d/fonts.dir ] && mkfontscale $d && mkfontdir $d; done + Xorg :0 vt2 -config $CONF -nolisten tcp -noreset -s 0 -dpms > $L/Xorg.out 2>&1 & + i=0; while [ ! -S /tmp/.X11-unix/X0 ] && [ $i -lt 30 ]; do sleep 1; i=$((i+1)); done + [ -S /tmp/.X11-unix/X0 ] || { echo "X-START FAIL (see $L/Xorg.out, /var/log/Xorg.0.log)"; tail -20 /var/log/Xorg.0.log; exit 1; } + export DISPLAY=:0 + xsetroot -solid '#203050' + twm > $L/twm.out 2>&1 & + xterm -geometry 21x9+0+0 -fa '' -fn 6x10 -bg black -fg green -e sh -c 'echo aurora B13; uname -r; exec sh' > $L/xterm.out 2>&1 & + x11vnc -display :0 -listen $VNC_ADDR -no6 -rfbport 5900 -rfbportv6 -1 -forever -shared -nopw -noxdamage -quiet -bg -o $L/x11vnc.log > $L/x11vnc.out 2>&1 + sleep 2; echo "X-START OK after ${i}s: $(pids) $(xdpyinfo 2>/dev/null | grep dimensions)"; grep -E "dimensions|depth of root" $L/x11vnc.log | head -2 ;; +stop) + for n in x11vnc xterm twm; do killall $n 2>/dev/null; done; sleep 1; killall Xorg 2>/dev/null + i=0; while pidof Xorg >/dev/null && [ $i -lt 10 ]; do sleep 1; i=$((i+1)); done; pidof Xorg >/dev/null && killall -9 Xorg + echo "X-STOP done: left=[$(pids)]" ;; +status) echo "pids: $(pids)"; netstat -ltn 2>/dev/null | grep -E ":59|:60" ; tail -3 $L/x11vnc.log 2>/dev/null ;; +*) echo "usage: $0 start [fbdev|modesetting]|stop|status"; exit 2 ;; +esac diff --git a/b12/b12p/B12P-rollback.sh b/b12/b12p/B12P-rollback.sh new file mode 100644 index 0000000..109185e --- /dev/null +++ b/b12/b12p/B12P-rollback.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# B12P rollback - restore the B10 cache (c3732515, operational since 2026-10-04). Needs 9008 via HW EDL (D+->GND). boot/aboot/userdata untouched +# (B10 init sets every eMMC device RO and never mounts userdata, so the ext4 Alpine there is inert). Deeper rollback: b10/b10b5/B10-rollback.sh (B9C). +cd ~/doc/modem/jz08-aurora || exit 1 +L=firehose/007050e100000000_394a2e47cf830150_fhprg_peek.bin ; O=logs/b12/b12p/rollback ; mkdir -p $O +B=b10/b10b3/cache-v4/cache-extlinux-b10b5.img +[ "$(sha256sum $B | cut -d' ' -f1)" = c37325156a6f4c013982cf01bcb5b2ddb026b0298e9c3ec0be87f57d693464b8 ] || { echo "STOP: rollback image hash"; exit 1; } +lsusb | grep -q 05c6:9008 || { echo "STOP: no 9008"; exit 1; } +edl --loader=$L printgpt > $O/00-printgpt.log 2>&1 +edl --loader=$L w cache $B > $O/cache-w.log 2>&1 +edl --loader=$L r cache $O/cache-rb.bin > /dev/null 2>&1 ; cmp $B $O/cache-rb.bin && echo CACHE_RESTORED_B10 +sha256sum $B $O/cache-rb.bin diff --git a/b12/b12p/B12P-write.sh b/b12/b12p/B12P-write.sh new file mode 100644 index 0000000..72fa891 --- /dev/null +++ b/b12/b12p/B12P-write.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# B12P — the ONLY write: cache := B12P candidate (B10 production kernel/DTB/cmdline unchanged + initramfs overlay: persistent Alpine on +# userdata auto-mounted at /alpine). Run on 480s ONLY after explicit GO. Derived 1:1 from b10/b10b5/B10-write.sh (B10_CACHE_WRITE_VERIFIED 2026-10-04); +# changed: image/hashes, rollback refs, log dir, and userdata is no longer compared to A4pre (it was formatted as ext4 'aurora-alpine' in B12P-1) +# - only its superblock (magic + label) is checked. The pre-write readback of the live cache (must be B10 c3732515) is kept as backup. +# Entry: HW EDL (battery on, D+->GND, plug USB, release). Never `edl reset`. Stops at first failed gate. +cd ~/doc/modem/jz08-aurora || exit 1 +L=firehose/007050e100000000_394a2e47cf830150_fhprg_peek.bin +O=logs/b12/b12p/W ; A3=logs/b5a/A3 ; P=logs/b5a/A4pre ; mkdir -p $O +NEW=b12/b12p/cache/cache-extlinux-b12p.img ; NEW_SHA=98d061eff0951ef5d05c9a4356bec959726f9462525325f7908a0bcc0e2e7394 +OLD_SHA=c37325156a6f4c013982cf01bcb5b2ddb026b0298e9c3ec0be87f57d693464b8 # current cache = B10 extlinux +BOOT_SHA=e9579f33c7304cd47f487f2b61cd9226dc04fd3a63775d62b4b95fc15bd3ebff +ABOOT_SHA=15208dbb4feafccd72d2b06db5714cd746c216701f0ef8822c58bb47105d37c4 # aboot = lk1st-b9l (B9L-PERSIST) +die(){ echo "STOP: $*"; exit 1; } +sha(){ sha256sum "$1" | cut -d" " -f1; } +E(){ edl --loader=$L "$@"; } +echo "== G0 inputs" +[ "$(sha $L)" = 53f193500c03248f0d671ab57bfe9ca8a42967e97f28403294b4b3f854075aca ] || die loader +[ "$(sha $NEW)" = $NEW_SHA ] && [ $(stat -c %s $NEW) = 134217728 ] || die candidate +[ "$(sha b10/b10b3/cache-v4/cache-extlinux-b10b5.img)" = $OLD_SHA ] || die rollback-image +[ "$(sha logs/b10/b10b5/W/11-cache-readback.bin)" = $OLD_SHA ] || die b10-readback-backup +lsusb | grep -q 05c6:9008 || die "no 9008" +echo "== G1 identity + geometry + baseline" +E printgpt > $O/00-printgpt.log 2>&1 || die printgpt +grep -aq "Serial: *0x" $O/00-printgpt.log || grep -aq "Mode detected: firehose" $O/00-printgpt.log || die serial +grep -a "^cache:" $O/00-printgpt.log | grep -q "Offset 0x000000004118c000, Length 0x0000000008000000" || die cache-geometry +E rs 0 34 $O/pre-gptp.bin >/dev/null 2>&1; cmp $O/pre-gptp.bin $A3/gpt-primary-34.bin || die gpt-changed +E r boot $O/pre-boot.bin >/dev/null 2>&1; [ "$(sha $O/pre-boot.bin)" = $BOOT_SHA ] || die boot-not-emmc1 +E r aboot $O/pre-aboot.bin >/dev/null 2>&1; [ "$(sha $O/pre-aboot.bin)" = $ABOOT_SHA ] || die aboot-not-b9l +E r cache $O/pre-cache.bin >/dev/null 2>&1; [ "$(sha $O/pre-cache.bin)" = $OLD_SHA ] || die cache-not-backup +echo "== W cache := candidate (LBA 2133088, 262144 sectors)" +E w cache $NEW > $O/10-cache-write.log 2>&1; echo "rc=$?"; grep -a "Wrote" $O/10-cache-write.log +E r cache $O/11-cache-readback.bin > $O/11-cache-readback.log 2>&1 || die readback-rc +sha256sum $NEW $O/11-cache-readback.bin +cmp $NEW $O/11-cache-readback.bin || die "CACHE READBACK MISMATCH — do NOT reboot; run b12/b12p/B12P-rollback.sh" +echo CACHE_MATCH +echo "== V everything else unchanged" +E rs 0 34 $O/post-gptp.bin >/dev/null 2>&1; cmp $O/post-gptp.bin $A3/gpt-primary-34.bin || die gptp +E rs 7634911 33 $O/post-gptb.bin >/dev/null 2>&1; cmp $O/post-gptb.bin $A3/gpt-backup-33.bin || die gptb +E rs 305184 1024 $O/post-tzbak.bin >/dev/null 2>&1; cmp $O/post-tzbak.bin $A3/tzbak-305184.bin || die tzbak +E r boot $O/post-boot.bin >/dev/null 2>&1; [ "$(sha $O/post-boot.bin)" = $BOOT_SHA ] || die boot +E r aboot $O/post-aboot.bin >/dev/null 2>&1; [ "$(sha $O/post-aboot.bin)" = $ABOOT_SHA ] || die aboot +for p in sbl1 rpm tz hyp recovery modemst1 modemst2 fsg fsc persist; do + E r $p $O/post-$p.bin >/dev/null 2>&1; cmp $O/post-$p.bin $A3/$p.bin || die "$p changed"; echo "$p == A3" +done +for p in modem system; do + E r $p $O/post-$p.bin >/dev/null 2>&1; cmp $O/post-$p.bin $P/$p.bin || die "$p changed"; echo "$p == A4pre"; rm -f $O/post-$p.bin +done +E rs 2428000 8 $O/post-userdata-sb.bin >/dev/null 2>&1 +[ "$(dd if=$O/post-userdata-sb.bin bs=1 skip=1080 count=2 2>/dev/null | od -An -tx1 | tr -d ' \n')" = 53ef ] && [ "$(dd if=$O/post-userdata-sb.bin bs=1 skip=1144 count=16 2>/dev/null | tr -d '\000')" = aurora-alpine ] || die "userdata superblock is not ext4 aurora-alpine" +echo "userdata = ext4 aurora-alpine (superblock)" +(cd $O && sha256sum *.bin > SHA256SUMS) +echo "B12P_CACHE_WRITE_VERIFIED — power off fully (USB, then battery) before first boot" diff --git a/b12/b12p/b12p-boot.sh b/b12/b12p/b12p-boot.sh new file mode 100644 index 0000000..a42cce0 --- /dev/null +++ b/b12/b12p/b12p-boot.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# 480s side, B12P RAM test (nothing written to eMMC boot partitions). RESET-held power-on -> lk1st fastboot -> `fastboot boot` B12P RAM image. +# Checks: modem START OK (patched safety gate), Wi-Fi AP, ALPINE OK (/alpine mounted from userdata, RW only p27), display, eMMC write stats. +K=$1; MODE=${2:-ram}; [ -n "$K" ] || { echo "usage: $0 K [ram|cold]"; exit 2; } # cold = normal power-on from eMMC cache, no fastboot +cd ~/doc/modem/jz08-aurora; O=logs/b12/b12p/$MODE$K; mkdir -p $O; IMG=b12/b12p/out/aurora-b12p.img +A=$O/host-actions.txt; act() { echo "$(date -u +%H:%M:%S) $*" | tee -a $A; } +tn() { { sleep 1; sed "s/\$/\r/" "$1"; sleep $2; } | ncat 172.16.42.1 23 | tr -d "\000\r"; } +if [ $MODE = ram ]; then +echo "00af916024671553f873a92d6ba7057443a239eaf3e38384d0b51845af5689b9 $IMG" | sha256sum -c || exit 1 +act "B12P ram$K: waiting for fastboot (power off, then hold RESET while powering on)" +i=0; until fastboot devices 2>/dev/null | grep -q ; do i=$((i+1)); [ $i -ge 900 ] && { act "ABORT: no fastboot"; exit 1; }; sleep 2; done +act "fastboot present"; { fastboot boot $IMG 2>&1; echo "rc=$?"; } | tee -a $A +else +act "B12P cold$K: waiting for normal power-on WITHOUT RESET (cache B12P 98d061ef)" +i=0; until ping -c1 -W1 172.16.42.1 >/dev/null 2>&1; do fastboot devices 2>/dev/null | grep -q && { act "ABORT: board in fastboot - RESET held?"; exit 1; }; i=$((i+1)); [ $i -ge 1800 ] && { act "ABORT: no NCM"; exit 1; }; sleep 1; done +fi +i=0; until ping -c1 -W1 172.16.42.1 >/dev/null 2>&1; do i=$((i+1)); [ $i -ge 180 ] && { act "ABORT: NCM not up"; exit 1; }; sleep 1; done; act "NCM ping ok" +printf 'grep -E "START OK|FAIL in|rollback|SAFETY" /run/aurora-modem/lifecycle.log | tail -2\n' > $O/.w +i=0; until tn $O/.w 3 | grep -qE "^\[[0-9.]+\] .*(=== START OK|FAIL in|rollback)"; do i=$((i+1)); [ $i -ge 80 ] && { act "no START OK/FAIL after ~12 min"; break; }; sleep 6; done +act "modem: $(tn $O/.w 3 | grep -E '^\[[0-9.]+\]' | tail -2 | tr '\n' ' ')" +printf 'grep -E "AP ENABLED|FAIL" /run/aurora-wifi/wifi.log | tail -1\n' > $O/.a +i=0; until tn $O/.a 3 | grep -qE "^\[[0-9.]+\] (=== AP ENABLED|FAIL)"; do i=$((i+1)); [ $i -ge 30 ] && break; sleep 6; done +act "wifi: $(tn $O/.a 3 | grep -E '^\[[0-9.]+\]' | tail -1)" +cat > $O/.c <<'C' +echo SNAP-BEGIN; cat /proc/uptime; uname -a; cat /proc/cmdline +echo ---alpine; cat /run/aurora-alpine/alpine.log /run/aurora-alpine/service.log; /etc/init.d/aurora-alpine status +echo ---ro; for x in /dev/mmcblk0 /dev/mmcblk0p* /dev/mmcblk0boot0 /dev/mmcblk0boot1; do printf "%s=%s " ${x#/dev/} $(blockdev --getro $x); done; echo +echo ---chroot; chroot /alpine /bin/sh -c 'cat /etc/alpine-release /etc/aurora-alpine-release; ls /usr/libexec/Xorg; cat /root/persist-test.txt' +echo ---modem; /etc/init.d/aurora-modem status; grep -E "SAFETY|START OK" /run/aurora-modem/lifecycle.log +echo ---ping; ping -c4 -W3 -I wwan0 77.88.8.8 | tail -2 +echo ---wifi; /etc/init.d/aurora-wifi status | tail -3 +echo ---display; /etc/init.d/aurora-display status; cat /sys/class/tty/tty0/active +echo ---charger; cat /sys/kernel/debug/pm8916_lbc/state 2>/dev/null || { mount -t debugfs debugfs /sys/kernel/debug; cat /sys/kernel/debug/pm8916_lbc/state; } +echo ---emmc; cat /sys/block/mmcblk0/stat; for p in /sys/class/block/mmcblk0p*; do s=$(awk '{print $5}' $p/stat); [ "$s" != 0 ] && echo "$(basename $p) writes=$s"; done +echo ---mem; free; dmesg | grep -iE "ext4|mmcblk0p27|aurora-alpine" | tail +echo SNAP-END +C +tn $O/.c 25 > $O/snapshot.txt; act "snapshot: $(grep -c SNAP-END $O/snapshot.txt) end; $(grep -m1 'ALPINE OK' $O/snapshot.txt)" +grep -A1 -- "---ro" $O/snapshot.txt | tail -1 | tr ' ' '\n' | grep "=0" | tee -a $A +act "B12P $MODE$K END" diff --git a/b12/b12p/build-b12p.sh b/b12/b12p/build-b12p.sh new file mode 100644 index 0000000..b580f47 --- /dev/null +++ b/b12/b12p/build-b12p.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# B12P image: production B10 (kernel 7.2.7-aurora-b10b5 + prod DTB e47762d1 + B9C cmdline, all unchanged) + initramfs overlay: +# /init (+ aurora-alpine autostart), aurora-modem (safety gate accepts a lifted disk flag only if every partition except userdata is RO), +# aurora-alpine service, e2fsck bundle (Alpine 3.24 e2fsprogs 1.47.4, own musl loader), mountpoint /alpine. +# Outputs RAM image (fastboot boot pretest) + parts for the cache candidate. out/ contains the B8F initramfs (Wi-Fi PSK): never publish. +set -e; cd "$(dirname "$0")"; O=out; F=../../b10/b10b3/out-v4; GEN=../../linux/out/usr/gen_init_cpio; TS=1790726400 +mkdir -p $O +echo "b0ea04666c4ab979067fe9c0015bda0e9c8ac742678321e57b18e841c9c69f95 $F/Image.gz-dtb" | sha256sum -c +echo "e47762d1dcd0af6e2acb711fa47ecc13769cd6bcb81259ce50635e8b18b6514a $F/aurora-b10b3.dtb" | sha256sum -c +echo "ac518e27e2957ad5e4515969b9a3808024e11ef8d1a79433a899a67dbc644558 $F/initramfs.cpio.gz" | sha256sum -c +echo "0fef41b262917c8ae308fa222da0017e2b3cb3ec5da1ca8ba18d0318476761a3 $F/cmdline.txt" | sha256sum -c +diff -u ../../b9/b9c/rootfs/init rootfs/init > $O/init.diff || true +diff -u ../../b7/b7c/rootfs/usr/sbin/aurora-modem rootfs/usr/sbin/aurora-modem > $O/aurora-modem.diff || true +[ $(grep -c '^+[^+]' $O/init.diff) = 2 ] && [ $(grep -c '^-[^-]' $O/init.diff) = 0 ] || { echo "STOP: init diff"; exit 1; } +[ $(grep -c '^-[^-]' $O/aurora-modem.diff) = 1 ] || { echo "STOP: aurora-modem diff"; exit 1; } +cp $F/Image.gz-dtb $F/aurora-b10b3.dtb $F/cmdline.txt $O/ +{ echo "file /init rootfs/init 0755 0 0" + echo "file /usr/sbin/aurora-modem rootfs/usr/sbin/aurora-modem 0755 0 0" + echo "file /usr/sbin/aurora-alpine rootfs/usr/sbin/aurora-alpine 0755 0 0" + echo "file /etc/init.d/aurora-alpine rootfs/etc/init.d/aurora-alpine 0755 0 0" + echo "dir /alpine 0755 0 0" + echo "dir /usr/libexec/aurora/e2fs 0755 0 0"; echo "dir /usr/libexec/aurora/e2fs/lib 0755 0 0"; echo "dir /usr/libexec/aurora/e2fs/sbin 0755 0 0" + for f in e2fs/lib/* e2fs/sbin/*; do echo "file /usr/libexec/aurora/$f $f 0755 0 0"; done; } > $O/overlay-b12p.list +$GEN -t $TS $O/overlay-b12p.list > $O/overlay-b12p.cpio +gzip -9 -n -c $O/overlay-b12p.cpio > $O/overlay-b12p.cpio.gz +cat $F/initramfs.cpio.gz $O/overlay-b12p.cpio.gz > $O/initramfs-b12p.cpio.gz +python3 ../../b6p/mkbootimg.py $O/Image.gz-dtb $O/initramfs-b12p.cpio.gz $O/cmdline.txt $O/aurora-b12p.img +(cd $O && sha256sum Image.gz-dtb aurora-b10b3.dtb cmdline.txt overlay-b12p.cpio.gz initramfs-b12p.cpio.gz aurora-b12p.img > SHA256SUMS; cat SHA256SUMS); ls -l $O/aurora-b12p.img diff --git a/b12/b12p/cache/extlinux-b12p.conf b/b12/b12p/cache/extlinux-b12p.conf new file mode 100644 index 0000000..bf160d4 --- /dev/null +++ b/b12/b12p/cache/extlinux-b12p.conf @@ -0,0 +1,6 @@ +default b12p +label b12p + linux /Image.gz-dtb + fdt /aurora-b10b3.dtb + initrd /initramfs-b12p.cpio.gz + append earlycon console=tty0 console=ttyMSM0,115200n8 ignore_loglevel loglevel=8 rdinit=/init fbcon=font:6x8 consoleblank=0 diff --git a/b12/b12p/cache/mkfs-cache-b12p.sh b/b12/b12p/cache/mkfs-cache-b12p.sh new file mode 100644 index 0000000..53aa3cf --- /dev/null +++ b/b12/b12p/cache/mkfs-cache-b12p.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# B12P cache CANDIDATE (NOT for writing without a separate GO): exact recipe of b10/b10b3/cache-v4/mkfs-cache-b10b5.sh (c3732515); +# differs only in the initrd (B9C initramfs + B12P overlay: aurora-alpine autostart, modem safety gate, e2fsck bundle), file names, +# extlinux label, fs label and UUIDs. Kernel/DTB/append = B10 production unchanged. Runs on 480s (mke2fs 1.47.2). Offline only. +# The image contains the AP PSK (in the initramfs) - never publish it. +set -e +cd ~/doc/modem/jz08-aurora/b12/b12p/cache +OUT=${1:-cache-extlinux-b12p.img}; F=../out +echo "0fef41b262917c8ae308fa222da0017e2b3cb3ec5da1ca8ba18d0318476761a3 $F/cmdline.txt" | sha256sum -c +echo "b0ea04666c4ab979067fe9c0015bda0e9c8ac742678321e57b18e841c9c69f95 $F/Image.gz-dtb" | sha256sum -c +echo "e47762d1dcd0af6e2acb711fa47ecc13769cd6bcb81259ce50635e8b18b6514a $F/aurora-b10b3.dtb" | sha256sum -c +echo "40df25a0818244c8ff72fa045d2f4ac9679263456550accc9555548c3ba0f142 $F/initramfs-b12p.cpio.gz" | sha256sum -c +ST=$(mktemp -d); mkdir -p $ST/extlinux +cp $F/Image.gz-dtb $F/aurora-b10b3.dtb $F/initramfs-b12p.cpio.gz $ST/ +printf "default b12p\nlabel b12p\n\tlinux /Image.gz-dtb\n\tfdt /aurora-b10b3.dtb\n\tinitrd /initramfs-b12p.cpio.gz\n\tappend %s\n" "$(cat $F/cmdline.txt)" > $ST/extlinux/extlinux.conf +cp $ST/extlinux/extlinux.conf extlinux-b12p.conf +chmod 0644 $ST/*.* $ST/extlinux/extlinux.conf; chmod 0755 $ST $ST/extlinux +find $ST -exec touch -h -d @1790726400 {} + +rm -f $OUT; truncate -s 134217728 $OUT +E2FSPROGS_FAKE_TIME=1790726400 /sbin/mke2fs -F -q -t ext2 -O none,filetype,sparse_super \ + -b 4096 -I 128 -N 64 -m 0 -L aurora-b12p -U 4a5a3038-b6f0-4000-8000-0000b12b1200 \ + -E root_owner=0:0,hash_seed=4a5a3038-b6f0-4000-8000-0000b12b1201 -d $ST $OUT 32768 +rm -rf $ST +for f in / /lost+found /extlinux /extlinux/extlinux.conf /Image.gz-dtb /aurora-b10b3.dtb /initramfs-b12p.cpio.gz; do for t in atime ctime mtime; do echo "sif $f $t @1790726400"; done; done > /tmp/b12p-sif.$$ +/sbin/debugfs -w -f /tmp/b12p-sif.$$ $OUT >/dev/null 2>&1; rm -f /tmp/b12p-sif.$$ +/sbin/e2fsck -fn $OUT >/dev/null 2>&1 && echo "e2fsck -fn clean" +stat -c "%n %s" $OUT; sha256sum $OUT diff --git a/b12/b12q/B12Q-rollback.sh b/b12/b12q/B12Q-rollback.sh new file mode 100644 index 0000000..6c6e985 --- /dev/null +++ b/b12/b12q/B12Q-rollback.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# B12Q rollback - restore the B12P cache (98d061ef: LFS PID1 + Alpine chroot at /alpine). Needs 9008 via HW EDL (D+->GND). boot/aboot/userdata untouched +# (p27 keeps the B12Q packages/OpenRC; under B12P it is only mounted at /alpine). Deeper: b12/b12p/B12P-rollback.sh (B10 c3732515). +cd ~/doc/modem/jz08-aurora || exit 1 +L=firehose/007050e100000000_394a2e47cf830150_fhprg_peek.bin ; O=logs/b12/b12q/rollback ; mkdir -p $O +B=b12/b12p/cache/cache-extlinux-b12p.img +[ "$(sha256sum $B | cut -d' ' -f1)" = 98d061eff0951ef5d05c9a4356bec959726f9462525325f7908a0bcc0e2e7394 ] || { echo "STOP: rollback image hash"; exit 1; } +lsusb | grep -q 05c6:9008 || { echo "STOP: no 9008"; exit 1; } +edl --loader=$L printgpt > $O/00-printgpt.log 2>&1 +edl --loader=$L w cache $B > $O/cache-w.log 2>&1 +edl --loader=$L r cache $O/cache-rb.bin > /dev/null 2>&1 ; cmp $B $O/cache-rb.bin && echo CACHE_RESTORED_B12P +sha256sum $B $O/cache-rb.bin diff --git a/b12/b12q/B12Q-write.sh b/b12/b12q/B12Q-write.sh new file mode 100644 index 0000000..66b17ac --- /dev/null +++ b/b12/b12q/B12Q-write.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# B12Q — the ONLY write: cache := B12Q candidate (B10 kernel/DTB/cmdline unchanged; initramfs = stage0 switch_root into Alpine on p27, +# LFS rescue /init.lfs on any failure / aurora.alpine=off / 3 unconfirmed boots). Run on 480s ONLY after explicit GO. +# Derived 1:1 from b12/b12p/B12P-write.sh (B12P_CACHE_WRITE_VERIFIED 2026-10-04); changed: image/hashes, rollback refs, log dir. +# The pre-write readback of the live cache (must be B12P 98d061ef) is kept as backup. +# Entry: HW EDL (battery on, D+->GND, plug USB, release). Never `edl reset`. Stops at first failed gate. +cd ~/doc/modem/jz08-aurora || exit 1 +L=firehose/007050e100000000_394a2e47cf830150_fhprg_peek.bin +O=logs/b12/b12q/W ; A3=logs/b5a/A3 ; P=logs/b5a/A4pre ; mkdir -p $O +NEW=b12/b12q/cache/cache-extlinux-b12q.img ; NEW_SHA=59272cd3fb45a9388934110b345a0e320d62a3199b52aa0c47ec10eb79352aae +OLD_SHA=98d061eff0951ef5d05c9a4356bec959726f9462525325f7908a0bcc0e2e7394 # current cache = B12P extlinux +BOOT_SHA=e9579f33c7304cd47f487f2b61cd9226dc04fd3a63775d62b4b95fc15bd3ebff +ABOOT_SHA=15208dbb4feafccd72d2b06db5714cd746c216701f0ef8822c58bb47105d37c4 # aboot = lk1st-b9l (B9L-PERSIST) +die(){ echo "STOP: $*"; exit 1; } +sha(){ sha256sum "$1" | cut -d" " -f1; } +E(){ edl --loader=$L "$@"; } +echo "== G0 inputs" +[ "$(sha $L)" = 53f193500c03248f0d671ab57bfe9ca8a42967e97f28403294b4b3f854075aca ] || die loader +[ "$(sha $NEW)" = $NEW_SHA ] && [ $(stat -c %s $NEW) = 134217728 ] || die candidate +[ "$(sha b12/b12p/cache/cache-extlinux-b12p.img)" = $OLD_SHA ] || die rollback-image +[ "$(sha logs/b12/b12p/W/11-cache-readback.bin)" = $OLD_SHA ] || die b12p-readback-backup +lsusb | grep -q 05c6:9008 || die "no 9008" +echo "== G1 identity + geometry + baseline" +E printgpt > $O/00-printgpt.log 2>&1 || die printgpt +grep -aq "Serial: *0x" $O/00-printgpt.log || grep -aq "Mode detected: firehose" $O/00-printgpt.log || die serial +grep -a "^cache:" $O/00-printgpt.log | grep -q "Offset 0x000000004118c000, Length 0x0000000008000000" || die cache-geometry +E rs 0 34 $O/pre-gptp.bin >/dev/null 2>&1; cmp $O/pre-gptp.bin $A3/gpt-primary-34.bin || die gpt-changed +E r boot $O/pre-boot.bin >/dev/null 2>&1; [ "$(sha $O/pre-boot.bin)" = $BOOT_SHA ] || die boot-not-emmc1 +E r aboot $O/pre-aboot.bin >/dev/null 2>&1; [ "$(sha $O/pre-aboot.bin)" = $ABOOT_SHA ] || die aboot-not-b9l +E r cache $O/pre-cache.bin >/dev/null 2>&1; [ "$(sha $O/pre-cache.bin)" = $OLD_SHA ] || die cache-not-backup +echo "== W cache := candidate (LBA 2133088, 262144 sectors)" +E w cache $NEW > $O/10-cache-write.log 2>&1; echo "rc=$?"; grep -a "Wrote" $O/10-cache-write.log +E r cache $O/11-cache-readback.bin > $O/11-cache-readback.log 2>&1 || die readback-rc +sha256sum $NEW $O/11-cache-readback.bin +cmp $NEW $O/11-cache-readback.bin || die "CACHE READBACK MISMATCH — do NOT reboot; run b12/b12q/B12Q-rollback.sh" +echo CACHE_MATCH +echo "== V everything else unchanged" +E rs 0 34 $O/post-gptp.bin >/dev/null 2>&1; cmp $O/post-gptp.bin $A3/gpt-primary-34.bin || die gptp +E rs 7634911 33 $O/post-gptb.bin >/dev/null 2>&1; cmp $O/post-gptb.bin $A3/gpt-backup-33.bin || die gptb +E rs 305184 1024 $O/post-tzbak.bin >/dev/null 2>&1; cmp $O/post-tzbak.bin $A3/tzbak-305184.bin || die tzbak +E r boot $O/post-boot.bin >/dev/null 2>&1; [ "$(sha $O/post-boot.bin)" = $BOOT_SHA ] || die boot +E r aboot $O/post-aboot.bin >/dev/null 2>&1; [ "$(sha $O/post-aboot.bin)" = $ABOOT_SHA ] || die aboot +for p in sbl1 rpm tz hyp recovery modemst1 modemst2 fsg fsc persist; do + E r $p $O/post-$p.bin >/dev/null 2>&1; cmp $O/post-$p.bin $A3/$p.bin || die "$p changed"; echo "$p == A3" +done +for p in modem system; do + E r $p $O/post-$p.bin >/dev/null 2>&1; cmp $O/post-$p.bin $P/$p.bin || die "$p changed"; echo "$p == A4pre"; rm -f $O/post-$p.bin +done +E rs 2428000 8 $O/post-userdata-sb.bin >/dev/null 2>&1 +[ "$(dd if=$O/post-userdata-sb.bin bs=1 skip=1080 count=2 2>/dev/null | od -An -tx1 | tr -d ' \n')" = 53ef ] && [ "$(dd if=$O/post-userdata-sb.bin bs=1 skip=1144 count=16 2>/dev/null | tr -d '\000')" = aurora-alpine ] || die "userdata superblock is not ext4 aurora-alpine" +echo "userdata = ext4 aurora-alpine (superblock)" +(cd $O && sha256sum *.bin > SHA256SUMS) +echo "B12Q_CACHE_WRITE_VERIFIED — power off fully (USB, then battery) before first boot" diff --git a/b12/b12q/alpine-overlay/etc/aurora/build-epoch b/b12/b12q/alpine-overlay/etc/aurora/build-epoch new file mode 100644 index 0000000..c1aed6e --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/aurora/build-epoch @@ -0,0 +1 @@ +1790935600 diff --git a/b12/b12q/alpine-overlay/etc/aurora/modem.conf b/b12/b12q/alpine-overlay/etc/aurora/modem.conf new file mode 100644 index 0000000..d392a9c --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/aurora/modem.conf @@ -0,0 +1,12 @@ +# Aurora modem operator profile (read by /usr/sbin/aurora-modem). Logic lives in the controller; only this file names an operator. +# Empty APN => start refuses (exit 2): no APN guessing. +# Current test profile: Beeline RU (public, operator-published default credentials). +APN=internet.beeline.ru +USER=beeline +PASSWORD=beeline +AUTH=pap +IP_TYPE=ipv4 +# registration deadline (s): wait for registered + PS attached; searching/limited/denied/detached are not failures before it. +REG_TIMEOUT=600 +# extra /32 host routes via the bearer interface (bearer DNS servers are always added). No default route is created. +ROUTES="77.88.8.8 1.1.1.1" diff --git a/b12/b12q/alpine-overlay/etc/aurora/primary-root b/b12/b12q/alpine-overlay/etc/aurora/primary-root new file mode 100644 index 0000000..be88977 --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/aurora/primary-root @@ -0,0 +1 @@ +B12Q: this p27 is an Aurora primary root (stage0 switch_root target) diff --git a/b12/b12q/alpine-overlay/etc/fstab b/b12/b12q/alpine-overlay/etc/fstab new file mode 100644 index 0000000..a6e8e83 --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/fstab @@ -0,0 +1 @@ +tmpfs /tmp tmpfs nosuid,nodev,mode=1777,size=64m 0 0 diff --git a/b12/b12q/alpine-overlay/etc/hostname b/b12/b12q/alpine-overlay/etc/hostname new file mode 100644 index 0000000..9f2e4b9 --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/hostname @@ -0,0 +1 @@ +aurora diff --git a/b12/b12q/alpine-overlay/etc/init.d/aurora-bootok b/b12/b12q/alpine-overlay/etc/init.d/aurora-bootok new file mode 100644 index 0000000..4437278 --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/init.d/aurora-bootok @@ -0,0 +1,5 @@ +#!/sbin/openrc-run +# B12Q: default runlevel reached -> reset stage0's boot-attempt counter (anti boot-loop: 3 unconfirmed boots -> LFS rescue). +description="Aurora boot confirmation" +depend() { after aurora-display aurora-modem aurora-wifi aurora-ncm; } +start() { echo 0 > /var/lib/aurora/boot-attempts; sync; echo "<5>[aurora-bootok] ALPINE PRIMARY ROOT BOOT OK" > /dev/kmsg; einfo "boot confirmed"; } diff --git a/b12/b12q/alpine-overlay/etc/init.d/aurora-display b/b12/b12q/alpine-overlay/etc/init.d/aurora-display new file mode 100644 index 0000000..4adb763 --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/init.d/aurora-display @@ -0,0 +1,8 @@ +#!/sbin/openrc-run +# B12Q: OpenRC wrapper around the validated LFS service script (unchanged copy in /usr/libexec/aurora/svc/aurora-display). +description="Aurora display service (B6P..B10 logic)" +S=/usr/libexec/aurora/svc/aurora-display +depend() { after aurora-ncm; } +start() { ebegin "aurora-display"; $S start; eend $?; } +stop() { :; } +status() { $S status; } diff --git a/b12/b12q/alpine-overlay/etc/init.d/aurora-firmware b/b12/b12q/alpine-overlay/etc/init.d/aurora-firmware new file mode 100644 index 0000000..60b63c3 --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/init.d/aurora-firmware @@ -0,0 +1,15 @@ +#!/sbin/openrc-run +# B12Q: B1 from the LFS init - /dev/disk/by-partlabel links, modem FAT RO at /firmware, firmware_class path. Unchanged logic. +description="Aurora modem firmware FAT (RO)" +depend() { need aurora-ro; } +start() { + ebegin "Mounting modem FAT RO at /firmware" + mkdir -p /dev/disk/by-partlabel /firmware + for p in /sys/class/block/mmcblk0p*; do n=$(sed -n 's/^PARTNAME=//p' $p/uevent); [ -n "$n" ] && ln -sf /dev/$(basename $p) /dev/disk/by-partlabel/$n; done + M=$(readlink /dev/disk/by-partlabel/modem) + [ "$(blockdev --getro $M)" = 1 ] || { eend 1 "modem partition not RO"; return 1; } + grep -q " /firmware " /proc/mounts || mount -t vfat -o ro,nosuid,nodev,noexec,shortname=lower $M /firmware || { eend 1 mount; return 1; } + grep " /firmware " /proc/mounts | grep -q " ro," || { umount /firmware; eend 1 "not ro"; return 1; } + echo -n /firmware/image > /sys/module/firmware_class/parameters/path + eend 0 +} diff --git a/b12/b12q/alpine-overlay/etc/init.d/aurora-modem b/b12/b12q/alpine-overlay/etc/init.d/aurora-modem new file mode 100644 index 0000000..8059617 --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/init.d/aurora-modem @@ -0,0 +1,8 @@ +#!/sbin/openrc-run +# B12Q: OpenRC wrapper around the validated LFS service script (unchanged copy in /usr/libexec/aurora/svc/aurora-modem). +description="Aurora modem service (B6P..B10 logic)" +S=/usr/libexec/aurora/svc/aurora-modem +depend() { need aurora-ro aurora-firmware; after aurora-ncm aurora-display; } +start() { ebegin "aurora-modem"; $S start; eend $?; } +stop() { ebegin "aurora-modem stop"; $S stop; eend $?; } +status() { $S status; } diff --git a/b12/b12q/alpine-overlay/etc/init.d/aurora-ncm b/b12/b12q/alpine-overlay/etc/init.d/aurora-ncm new file mode 100644 index 0000000..04bb002 --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/init.d/aurora-ncm @@ -0,0 +1,22 @@ +#!/sbin/openrc-run +# B12Q: usb_up + telnetd from the LFS init (DEVELOPMENT IDs 18d1:d001, NCM 172.16.42.1/24, root telnet bound to NCM only). Unchanged logic. +description="Aurora USB NCM management + telnetd" +depend() { after aurora-ro; } +start() { + ebegin "USB NCM gadget" + grep -q " /sys/kernel/config " /proc/mounts || mount -t configfs configfs /sys/kernel/config + for i in $(seq 1 50); do udc=$(ls /sys/class/udc 2>/dev/null | head -1); [ -n "$udc" ] && break; sleep 0.2; done + [ -n "$udc" ] || { eend 1 "no UDC"; return 1; } + G=/sys/kernel/config/usb_gadget/aurora + if [ ! -d $G ]; then + mkdir -p $G; echo 0x18d1 > $G/idVendor; echo 0xd001 > $G/idProduct + mkdir -p $G/strings/0x409; echo Aurora > $G/strings/0x409/manufacturer; echo "Aurora RAM Boot" > $G/strings/0x409/product; echo > $G/strings/0x409/serialnumber + mkdir -p $G/functions/ncm.usb0 $G/configs/c.1/strings/0x409; echo NCM > $G/configs/c.1/strings/0x409/configuration + ln -s $G/functions/ncm.usb0 $G/configs/c.1/ + fi + [ -n "$(cat $G/UDC)" ] || echo "$udc" > $G/UDC || { eend 1 "bind UDC"; return 1; } + ifc=$(cat $G/functions/ncm.usb0/ifname) + ip link set lo up; ip addr show dev "$ifc" | grep -q " 172.16.42.1/24 " || ip addr add 172.16.42.1/24 dev "$ifc"; ip link set "$ifc" up + pidof telnetd >/dev/null || telnetd -l /bin/sh -b 172.16.42.1 -p 23 + eend $? "telnetd" +} diff --git a/b12/b12q/alpine-overlay/etc/init.d/aurora-ro b/b12/b12q/alpine-overlay/etc/init.d/aurora-ro new file mode 100644 index 0000000..9d3c0ac --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/init.d/aurora-ro @@ -0,0 +1,12 @@ +#!/sbin/openrc-run +# B12Q: eMMC write policy check after switch_root (stage0 left only userdata RW) + /tmp on tmpfs (no eMMC writes for scratch). +description="Aurora eMMC RO policy check" +depend() { before aurora-firmware aurora-modem aurora-wifi; } +start() { + ebegin "Checking eMMC RO policy" + ud=; for p in /sys/class/block/mmcblk0p*; do [ "$(sed -n 's/^PARTNAME=//p' $p/uevent)" = userdata ] && ud=/dev/$(basename $p); done + bad=; for x in /dev/mmcblk0p* /dev/mmcblk0boot0 /dev/mmcblk0boot1; do [ "$x" = "$ud" ] && continue; [ "$(blockdev --getro $x)" = 1 ] || bad="$bad $x"; done + [ -z "$bad" ] || { eend 1 "NOT RO:$bad"; return 1; } + grep -q " /tmp tmpfs" /proc/mounts || mount -t tmpfs -o nosuid,nodev,mode=1777,size=64m tmpfs /tmp + echo "<5>[aurora-ro] all eMMC devices RO except $ud (root)" > /dev/kmsg; eend 0 +} diff --git a/b12/b12q/alpine-overlay/etc/init.d/aurora-wifi b/b12/b12q/alpine-overlay/etc/init.d/aurora-wifi new file mode 100644 index 0000000..c0a057b --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/init.d/aurora-wifi @@ -0,0 +1,8 @@ +#!/sbin/openrc-run +# B12Q: OpenRC wrapper around the validated LFS service script (unchanged copy in /usr/libexec/aurora/svc/aurora-wifi). +description="Aurora wifi service (B6P..B10 logic)" +S=/usr/libexec/aurora/svc/aurora-wifi +depend() { need aurora-ro aurora-firmware; after aurora-modem; } +start() { ebegin "aurora-wifi"; $S start; eend $?; } +stop() { ebegin "aurora-wifi stop"; $S stop; eend $?; } +status() { $S status; } diff --git a/b12/b12q/alpine-overlay/etc/inittab b/b12/b12q/alpine-overlay/etc/inittab new file mode 100644 index 0000000..6ad7aea --- /dev/null +++ b/b12/b12q/alpine-overlay/etc/inittab @@ -0,0 +1,9 @@ +# Aurora B12Q (Alpine primary root): busybox init + OpenRC +::sysinit:/sbin/openrc sysinit +::sysinit:/sbin/openrc boot +::wait:/sbin/openrc default +# display console (fbcon tty1, as in B9C) and root shell on the UART (as in the LFS init) +tty1::respawn:/sbin/getty 38400 tty1 linux +ttyMSM0::respawn:/sbin/getty -n -l /bin/sh -L 115200 ttyMSM0 vt100 +::ctrlaltdel:/sbin/reboot +::shutdown:/sbin/openrc shutdown diff --git a/b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-display b/b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-display new file mode 100644 index 0000000..7ff912d --- /dev/null +++ b/b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-display @@ -0,0 +1,13 @@ +#!/bin/sh +# Aurora display service (B9C). Started once by /init; no OpenRC. +# /etc/init.d/aurora-display start | status — kernel cmdline 'aurora.display=off' disables autostart. +C=/usr/sbin/aurora-display; D=/run/aurora-display; L=$D/service.log +mkdir -p $D +case "$1" in +start) + grep -qw aurora.display=off /proc/cmdline && { echo "aurora-display: disabled by cmdline"; exit 0; } + ( $C start >> $L 2>&1; echo "=== service start rc=$? $(cut -d' ' -f1 /proc/uptime)" >> $L ) & + echo "aurora-display: starting (pid $!, log $D/display.log)";; +status) $C status; tail -3 $D/display.log 2>/dev/null;; +*) echo "usage: $0 start|status"; exit 2;; +esac diff --git a/b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-modem b/b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-modem new file mode 100644 index 0000000..4ca3134 --- /dev/null +++ b/b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-modem @@ -0,0 +1,27 @@ +#!/bin/sh +# Aurora modem service (B6P). Started once by /init after USB NCM; no OpenRC in this image. +# /etc/init.d/aurora-modem start | stop | status +# start: runs '/usr/sbin/aurora-modem start' in the background (registration may take up to REG_TIMEOUT s); returns at once. +# stop: ends a start still in progress, then '/usr/sbin/aurora-modem stop' (bearer down -> MM -> BAM idle -> MSS offline -> rmtfs last). +# status: service state + controller status. Reconnect without MSS restart: 'aurora-modem disconnect' then 'aurora-modem start'. +# Kernel cmdline 'aurora.modem=off' disables autostart. +C=/usr/sbin/aurora-modem; D=/run/aurora-modem; P=$D/service.pid; L=$D/service.log +mkdir -p $D +running() { [ -f $P ] && kill -0 $(cat $P) 2>/dev/null; } +case "$1" in +start) + grep -qw aurora.modem=off /proc/cmdline && { echo "aurora-modem: disabled by cmdline"; exit 0; } + running && { echo "aurora-modem: start already in progress (pid $(cat $P))"; exit 0; } + echo "=== service start $(cut -d' ' -f1 /proc/uptime)" >> $L + ( $C start >> $L 2>&1; echo "=== service start rc=$? $(cut -d' ' -f1 /proc/uptime)" >> $L ) & + echo $! > $P; echo "aurora-modem: starting (pid $!, log $L)";; +stop) + if running; then echo "aurora-modem: aborting start in progress (pid $(cat $P))"; kill -TERM $(cat $P) 2>/dev/null + pkill -TERM -P $(cat $P) 2>/dev/null; sleep 1; fi + rm -f $P; echo "=== service stop $(cut -d' ' -f1 /proc/uptime)" >> $L + $C stop >> $L 2>&1; r=$?; echo "=== service stop rc=$r" >> $L; echo "aurora-modem: stop rc=$r"; exit $r;; +status) + running && echo "service: start in progress (pid $(cat $P))" || echo "service: idle" + $C status;; +*) echo "usage: $0 start|stop|status"; exit 2;; +esac diff --git a/b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-wifi b/b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-wifi new file mode 100644 index 0000000..efbdb0b --- /dev/null +++ b/b12/b12q/alpine-overlay/usr/libexec/aurora/svc/aurora-wifi @@ -0,0 +1,16 @@ +#!/bin/sh +# Aurora Wi-Fi service (B8F). Started once by /init after the modem service; no OpenRC. +# /etc/init.d/aurora-wifi start | stop | status — kernel cmdline 'aurora.wifi=off' disables autostart. +C=/usr/sbin/aurora-wifi; D=/run/aurora-wifi; P=$D/service.pid; L=$D/service.log +mkdir -p $D +running() { [ -f $P ] && kill -0 $(cat $P) 2>/dev/null; } +case "$1" in +start) + grep -qw aurora.wifi=off /proc/cmdline && { echo "aurora-wifi: disabled by cmdline"; exit 0; } + running && { echo "aurora-wifi: start already in progress (pid $(cat $P))"; exit 0; } + ( $C start >> $L 2>&1; echo "=== service start rc=$? $(cut -d' ' -f1 /proc/uptime)" >> $L ) & + echo $! > $P; echo "aurora-wifi: starting (pid $!, log $L)";; +stop) running && kill -TERM $(cat $P) 2>/dev/null; rm -f $P; $C stop;; +status) running && echo "service: start in progress (pid $(cat $P))" || echo "service: idle"; $C status;; +*) echo "usage: $0 start|stop|status"; exit 2;; +esac diff --git a/b12/b12q/alpine-overlay/usr/local/bin/b4b2-mm b/b12/b12q/alpine-overlay/usr/local/bin/b4b2-mm new file mode 100644 index 0000000..fb4f022 --- /dev/null +++ b/b12/b12q/alpine-overlay/usr/local/bin/b4b2-mm @@ -0,0 +1,43 @@ +#!/bin/sh +# B4b2 via ModemManager: ONE simple-connect (apn=internet, ip-type=ipv4), manual IPv4 in routing table 100 only +# (main table / usb0 untouched), IP ping, DNS, clean disconnect. Long digit runs (IMEI/ICCID/IMSI) masked on-device. +O=/tmp/b4b2mm; mkdir -p $O; t() { cut -d' ' -f1 /proc/uptime; } +mask() { sed -E 's/([0-9]{6,})([0-9]{4})/******\2/g'; } +B=/sys/bus/platform/devices/4080000.remoteproc:bam-dmux +pm() { echo "### $(t) PM=$(cat $B/power/runtime_status) active_ms=$(cat $B/power/runtime_active_time) rproc=$(cat /sys/class/remoteproc/remoteproc0/state)"; } +cnt() { echo "### $(t) CNT $(for f in rx_packets rx_bytes tx_packets tx_bytes; do printf '%s=%s ' $f $(cat /sys/class/net/wwan0/statistics/$f); done)"; ip -s link show wwan0 2>&1; } +nv() { for want in modemst1 modemst2 fsg fsc persist; do for p in /sys/class/block/mmcblk0p*; do + [ "$(sed -n 's/^PARTNAME=//p' $p/uevent)" = "$want" ] && echo "$want $(sha256sum /dev/$(basename $p) | cut -d' ' -f1)"; done; done; } +nv > $O/nv-before.txt; cat /sys/block/mmcblk0/stat > $O/stat-before.txt; wc -l < /tmp/rmtfs.log > $O/rmtfs-offset.txt +{ mmcli -L; mmcli -m 0; mmcli -m 0 --signal-get 2>&1; pm; ip link show wwan0; ip route; ip rule; } 2>&1 | mask > $O/pre.txt +grep -qE "state: .*registered" $O/pre.txt && grep -q "packet service state: attached" $O/pre.txt || { echo "PRECHECK FAILED"; echo B4B2MM-STOPPED; exit 1; } +# --- single connect +{ pm; echo "### $(t) mmcli -m 0 --simple-connect=apn=internet,ip-type=ipv4"; timeout 150 mmcli -m 0 --simple-connect="apn=internet,ip-type=ipv4" 2>&1; echo "### rc=$?"; pm; } > $O/connect.txt +BR=$(mmcli -m 0 | grep -oE "Bearer/[0-9]+" | tail -1 | cut -d/ -f2) +{ mmcli -m 0; [ -n "$BR" ] && mmcli -b $BR; } 2>&1 | mask > $O/after-connect.txt +grep -q "^### rc=0" $O/connect.txt && [ -n "$BR" ] && mmcli -b $BR | grep -q "connected: yes" || { echo "CONNECT FAILED (bearer '$BR')"; echo B4B2MM-STOPPED; exit 1; } +T=$(mmcli -b $BR); f() { echo "$T" | awk -F": " "/ $1:/{print \$2; exit}" | tr -d " "; } +IF=$(f interface); A=$(f address); PF=$(f prefix); GW=$(f gateway); DNS=$(f dns); MTU=$(f mtu); D1=${DNS%%,*}; D2=${DNS#*,}; [ "$D2" = "$DNS" ] && D2= +echo "BR=$BR IF=$IF A=$A PF=$PF GW=$GW DNS=$DNS MTU=$MTU" > $O/bearer-ip.txt +[ "$IF" = wwan0 ] && [ -n "$A" ] && [ -n "$PF" ] || { echo "BEARER HAS NO USABLE IPv4 on wwan0"; } +# --- manual IPv4 (table 100 + source rule; host routes only for DNS servers in main) +{ cnt; ip addr add $A/$PF dev wwan0; echo "addr rc=$?"; [ -n "$MTU" ] && ip link set wwan0 mtu $MTU; ip link set wwan0 up; echo "up rc=$?"; sleep 2; pm + ip route add default via $GW dev wwan0 table 100; echo "rt100 rc=$?"; ip rule add from $A lookup 100 priority 100; echo "rule rc=$?" + for d in $D1 $D2; do ip route add $d/32 dev wwan0 src $A; done + ip addr show wwan0; echo "--- main:"; ip route; echo "--- t100:"; ip route show table 100; ip rule; } > $O/cfg.txt 2>&1 +# --- IP-only ping (source = LTE address -> table 100) +{ for h in 77.88.8.8 1.1.1.1 8.8.8.8; do echo "### $(t) ping -I $A $h"; ping -c 5 -W 3 -I $A $h 2>&1; pm; done; cnt; } > $O/ping.txt 2>&1 +# --- DNS via operator DNS +{ for s in $D1 $D2; do echo "### $(t) nslookup ya.ru $s"; nslookup ya.ru $s 2>&1; done; pm; cnt; } > $O/dns.txt 2>&1 +{ mmcli -b $BR; mmcli -m 0 --signal-get; } 2>&1 | mask > $O/during.txt +# --- clean disconnect +{ echo "### $(t) mmcli -m 0 --simple-disconnect"; timeout 60 mmcli -m 0 --simple-disconnect 2>&1; echo "### rc=$?"; mmcli -b $BR 2>&1 | grep -E "connected|interface|address" + ip rule del from $A lookup 100 priority 100; ip route flush table 100; for d in $D1 $D2; do ip route del $d/32 dev wwan0 2>/dev/null; done + ip addr flush dev wwan0; ip link set wwan0 down; echo "down rc=$?"; ip link show wwan0; ip route; ip rule; sleep 10; pm; } > $O/disconnect.txt 2>&1 +{ mmcli -m 0; pm; } 2>&1 | mask > $O/post.txt +nv > $O/nv-after.txt; cat /sys/block/mmcblk0/stat > $O/stat-after.txt +tail -n +$(( $(cat $O/rmtfs-offset.txt) + 1 )) /tmp/rmtfs.log > $O/rmtfs-delta.log +dmesg | grep -iE "bam|dmux|wwan|resume|-110|-22|Resource temporarily|Oops|panic|fatal|q6v5|watchdog|ssr" > $O/dmesg.txt +mask < /tmp/mm.log > $O/mm.log +cmp -s $O/nv-before.txt $O/nv-after.txt && echo "NV BEFORE==AFTER" > $O/nv-result.txt || echo "NV DIFFERENT" > $O/nv-result.txt +tar -C /tmp -cf /tmp/b4b2mm.tar b4b2mm; echo B4B2MM-DONE diff --git a/b12/b12q/alpine-overlay/usr/local/bin/nv-check b/b12/b12q/alpine-overlay/usr/local/bin/nv-check new file mode 100644 index 0000000..12b23ee --- /dev/null +++ b/b12/b12q/alpine-overlay/usr/local/bin/nv-check @@ -0,0 +1,12 @@ +#!/bin/sh +# Recompute NV hashes + eMMC stat and compare with the boot-time snapshot (read-only). +o=/tmp/nv-after.txt; : > $o +for want in modemst1 modemst2 fsg fsc persist; do + for p in /sys/class/block/mmcblk0p*; do + [ "$(sed -n 's/^PARTNAME=//p' $p/uevent)" = "$want" ] && echo "$want $(basename $p) $(sha256sum /dev/$(basename $p) | cut -d' ' -f1)" >> $o + done +done +echo "stat $(cat /sys/block/mmcblk0/stat)" >> $o +grep -v ^stat /tmp/nv-before.txt > /tmp/nvb; grep -v ^stat $o > /tmp/nva +cmp -s /tmp/nvb /tmp/nva && echo "NV-CHECK: BEFORE == AFTER ($(wc -l < /tmp/nva) partitions)" || { echo "NV-CHECK: DIFFERENT"; diff /tmp/nvb /tmp/nva; } +set -- $(sed -n 's/^stat //p' $o); echo "NV-CHECK: disk writes_completed=$5 sectors_written=$7" diff --git a/b12/b12q/alpine-overlay/usr/local/bin/qmi-b3 b/b12/b12q/alpine-overlay/usr/local/bin/qmi-b3 new file mode 100644 index 0000000..a7b1b04 --- /dev/null +++ b/b12/b12q/alpine-overlay/usr/local/bin/qmi-b3 @@ -0,0 +1,20 @@ +#!/bin/sh +# B3: READ-ONLY QMI queries only (DMS/UIM/NAS "get"). No scan, no registration, no set-*, no PIN ops, no reset. +# ICCID/IMSI are masked on-device before being written anywhere. +D=${1:-/dev/wwan0qmi0} +Q="qmicli -p -d $D" +mask_iccid() { sed -E "s/([0-9]{15,22})/\1\n/; s/[0-9]+([0-9]{4})\n/************\1/"; } +mask_imsi() { sed -E "s/'([0-9]{5})[0-9]+([0-9]{3})'/'\1******\2'/"; } +run() { echo "### $*"; timeout 30 $Q "$@" 2>&1; echo "### rc=$?"; } +run --dms-get-operating-mode +run --dms-get-revision +run --dms-uim-get-state +run --uim-get-card-status +echo "### --dms-uim-get-iccid (masked)"; timeout 30 $Q --dms-uim-get-iccid 2>&1 | mask_iccid +echo "### --dms-uim-get-imsi (masked)"; timeout 30 $Q --dms-uim-get-imsi 2>&1 | mask_imsi +run --nas-get-home-network +run --nas-get-serving-system +run --nas-get-system-info +run --nas-get-signal-info +run --nas-get-system-selection-preference +run --dms-get-operating-mode diff --git a/b12/b12q/alpine-overlay/usr/sbin/aurora-display b/b12/b12q/alpine-overlay/usr/sbin/aurora-display new file mode 100644 index 0000000..b5198cd --- /dev/null +++ b/b12/b12q/alpine-overlay/usr/sbin/aurora-display @@ -0,0 +1,44 @@ +#!/bin/sh +# Aurora display controller (B9C). ST7735S on SPI via panel-mipi-dbi (DRM) -> fb0 -> fbcon (tty1) + MPP4 backlight. +# aurora-display start : wait for fb0, make sure the display pipe is enabled (fbcon takeover modeset, or one fbdev +# unblank if nothing enabled it), then switch the backlight on through the gpio-backlight driver. +# aurora-display status : one-line state summary +# aurora-display bl on|off +# Never writes PMIC/GPIO registers directly: only fb0 'blank' and backlight sysfs attributes of the kernel drivers. +D=/run/aurora-display; L=$D/display.log; mkdir -p $D +FB=/sys/class/graphics/fb0; CON=/sys/class/drm/card0-SPI-1; BL=/sys/class/backlight/backlight +t() { cut -d' ' -f1 /proc/uptime; } +log() { echo "[$(t)] $*" >> $L; echo "[aurora-display] $*" > /dev/kmsg 2>/dev/null; } +fbcon_bound() { for v in /sys/class/vtconsole/vtcon*; do grep -q "frame buffer" $v/name 2>/dev/null && [ "$(cat $v/bind)" = 1 ] && return 0; done; return 1; } +l17_users() { for r in /sys/class/regulator/regulator.*; do [ "$(cat $r/name 2>/dev/null)" = l17 ] && { cat $r/num_users; return; }; done; echo 0; } +pipe_on() { [ "$(cat $CON/enabled 2>/dev/null)" = enabled ] && [ "$(cat $CON/dpms 2>/dev/null)" = On ] && [ "$(l17_users)" -ge 1 ]; } +state() { echo "fb0=$([ -e $FB ] && cat $FB/name) fbcon=$(fbcon_bound && echo bound || echo no) conn=$(cat $CON/enabled 2>/dev/null)/$(cat $CON/dpms 2>/dev/null) l17_users=$(l17_users) bl_power=$(cat $BL/bl_power 2>/dev/null) bl=$(cat $BL/actual_brightness 2>/dev/null)"; } +wait_for() { n=$1; shift; i=0; until "$@"; do i=$((i+1)); [ $i -ge $n ] && return 1; sleep 0.2; done; } +bl() { + [ -d $BL ] || { log "FAIL: no backlight device"; return 1; } + case $1 in + on) echo 0 > $BL/bl_power; echo 1 > $BL/brightness;; + off) echo 0 > $BL/brightness;; + esac + log "backlight $1 -> bl_power=$(cat $BL/bl_power) actual_brightness=$(cat $BL/actual_brightness)" +} +case "$1" in +start) + log "start: $(state)" + wait_for 100 test -e $FB || { log "FAIL: fb0 did not appear within 20 s"; exit 1; } + fbcon_bound && log "fbcon bound to fb0" || log "fbcon NOT bound (no framebuffer console)" + if wait_for 25 pipe_on; then + log "display pipe already enabled (by fbcon takeover) - no unblank needed" + else + log "display pipe not enabled -> fbdev unblank" + echo 0 > $FB/blank + wait_for 25 pipe_on || { log "FAIL: pipe still not enabled after unblank: $(state)"; exit 1; } + log "display pipe enabled by unblank" + fi + bl on + [ "$(cat $BL/actual_brightness)" = 1 ] || { log "FAIL: backlight did not turn on: $(state)"; exit 1; } + log "=== DISPLAY READY $(state)";; +status) state;; +bl) bl "$2";; +*) echo "usage: $0 start|status|bl on|off"; exit 2;; +esac diff --git a/b12/b12q/alpine-overlay/usr/sbin/aurora-modem b/b12/b12q/alpine-overlay/usr/sbin/aurora-modem new file mode 100644 index 0000000..dfc7e3d --- /dev/null +++ b/b12/b12q/alpine-overlay/usr/sbin/aurora-modem @@ -0,0 +1,399 @@ +#!/bin/sh +# Aurora (JZ08AU, MSM8916) modem lifecycle controller - B6P production build of the B6N-proven controller (b6j/aurora-modem). +# aurora-modem start | disconnect | stop | status | addroute +# start: [rmtfs -r -P -s -v -> MSS -> wwan0qmi0 (+2 s port-race guard) -> SIM ready -> DMS online (one SET, retried only on DeviceNotReady) +# -> registration state machine (registered + PS attached, deadline REG_TIMEOUT, default 600 s)] (skipped when rmtfs+MSS already run) +# -> udevd/dbus/polkitd/ModemManager (reused if running) -> ONE simple-connect -> bearer IPv4 on wwanN + /32 routes only (no default route). +# disconnect: simple-disconnect, our routes, flush+down all wwan*, wait BAM-DMUX suspended. MSS, rmtfs, MM and radio stay up; 'start' reconnects. +# stop: disconnect -> stop MM (+qmi-proxy) -> BAM-DMUX suspended -> SIGTERM rmtfs (rmtfs writes 'stop' -> MSS offline) -> rmtfs exits last. +# Never writes eMMC/NV: rmtfs -r keeps EFS writes in RAM; refuses to start unless modemst1/2, fsc, fsg, persist, mmcblk0 are getro=1 and /firmware is ro. +# Does not need a valid wall clock (all timing from /proc/uptime). No automatic MSS/rmtfs retry. +# B7C: time bootstrap. At fixed points of start (QMI_READY, SIM_READY, RADIO_ONLINE, each REG transition, REGISTERED, BEARER_CONNECTED) logs QMI DMS +# + 3GPP NITZ. The first valid DMS (>= 2026, >= image build, plausible, |DMS-NITZ| < 2 s; NITZ required unless TIME_REQUIRE_NITZ=0) steps +# CLOCK_REALTIME once with date -s (never backwards, never RTC). After START OK, a background SNTP check (VNIIFTRI, query only) logs the residual +# and steps only if |offset| >= TIME_NTP_STEP s. No slew/discipline: STA_UNSYNC stays set, so the kernel RTC_SYSTOHC 11-min RTC write never engages. +# Exit: 0 ok, 1 failure/timeout (start rolls back with a full stop unless MODEM_KEEP_ON_FAIL=1), 2 usage/config error. + +CONF=${MODEM_CONF:-/etc/aurora/modem.conf} +D=/run/aurora-modem; mkdir -p $D +LOG=$D/lifecycle.log; STATE=$D/state; ROUTES=$D/routes; BEARER=$D/bearer +RPNAME=4080000.remoteproc +BAM=/sys/bus/platform/devices/4080000.remoteproc:bam-dmux +Q="qmicli -p -d /dev/wwan0qmi0" + +# --- timeouts (s); override in $CONF +T_RMTFS=5 T_MSS=60 T_QMI_DEV=30 T_QMI=45 T_SIM=60 T_ONLINE=30 T_REG=180 T_MM_DETECT=150 T_MM_REG=60 T_CONNECT=150 +T_DISCONNECT=60 T_DISABLE=30 T_MM_EXIT=30 T_BAM_IDLE=15 T_MSS_STOP=30 T_RMTFS_EXIT=15 +APN= USER= PASSWORD= AUTH= IP_TYPE=ipv4 REG_TIMEOUT=600 ROUTES= KEEP=${MODEM_KEEP_ON_FAIL:-0} +TIME_BOOTSTRAP=1 TIME_REQUIRE_NITZ=1 TIME_MAX_DMS_NITZ=2 TIME_NTP="ntp1.vniiftri.ru ntp2.vniiftri.ru ntp3.vniiftri.ru" TIME_NTP_STEP=1 +[ -f "$CONF" ] && . "$CONF" +MODEM_APN=$APN MODEM_USER=$USER MODEM_PASSWORD=$PASSWORD MODEM_AUTH=$AUTH MODEM_IP_TYPE=${IP_TYPE:-ipv4} MODEM_TEST_ROUTES=$ROUTES T_REG=${REG_TIMEOUT:-600} +MODEM_KEEP_ON_FAIL=${MODEM_KEEP_ON_FAIL:-$KEEP} + +mmc() { mmcli "$@" 2>/dev/null | tr -d '\033' | sed 's/\[[0-9;]*m//g'; } # mmcli colours values even without a tty +now() { cut -d' ' -f1 /proc/uptime; } +log() { m="[$(now)] $*"; echo "$m" >> $LOG; echo "$m"; echo "<5>[aurora-modem] $*" > /dev/kmsg 2>/dev/null; } +setstate() { o=$(cat $STATE 2>/dev/null); echo "$1" > $STATE; log "STATE ${o:-OFF} -> $1"; } +die() { log "FAIL: $*"; exit 1; } +# wait_for TIMEOUT INTERVAL cmd... : poll a real condition, no blind sleeps +wait_for() { _to=$1; _iv=$2; shift 2; _t0=$(now) + while :; do "$@" && return 0 + [ "$(awk -v a="$(now)" -v b="$_t0" -v t="$_to" 'BEGIN{print (a-b>=t)}')" = 1 ] && return 1 + sleep $_iv; done; } +since() { awk -v a="$(now)" -v b="$1" 'BEGIN{printf "%.1f", a-b}'; } + +rp() { for r in /sys/class/remoteproc/remoteproc*; do [ "$(cat $r/name 2>/dev/null)" = $RPNAME ] && echo $r && return; done; } +mss_state() { r=$(rp); [ -n "$r" ] && cat $r/state || echo none; } +mss_is() { [ "$(mss_state)" = "$1" ]; } +rmtfs_pid() { pidof rmtfs; } +mm_pid() { pidof ModemManager; } +bam_pm() { cat $BAM/power/runtime_status 2>/dev/null || echo none; } +bam_suspended() { [ "$(bam_pm)" = suspended ]; } +qmi_dev() { [ -e /dev/wwan0qmi0 ] && [ -e /sys/class/net/wwan0 ]; } +link_up() { [ $(( $(cat /sys/class/net/$1/flags) & 1 )) -ne 0 ]; } +mm_idx() { mmc -L | grep -oE "Modem/[0-9]+" | head -1 | cut -d/ -f2; } +mm_has_modem() { [ -n "$(mm_idx)" ]; } +mm_state() { mmc -m "$(mm_idx)" | awk -F': ' '/\| +state: /{gsub(/ /,"",$2); print $2; exit}'; } +# MM initialized: proven session 22 pass 1 connected from 'disabled' (Simple.Connect enables the modem itself) +mm_usable() { case "$(mm_state)" in disabled|enabling|enabled|searching|registered|connected|connecting) return 0;; esac; return 1; } +qmi_ok() { timeout 20 $Q --dms-get-operating-mode > $D/qmi-mode.txt 2>&1 && grep -q "Mode:" $D/qmi-mode.txt; } +qmi_reg() { timeout 20 $Q --nas-get-serving-system > $D/qmi-ss.txt 2>&1 + grep -q "Registration state: 'registered'" $D/qmi-ss.txt && grep -q "PS: 'attached'" $D/qmi-ss.txt; } +sim_ready() { timeout 20 $Q --uim-get-card-status > $D/qmi-uim.txt 2>&1 + grep -q "Card state: 'present'" $D/qmi-uim.txt && grep -q "Application state: 'ready'" $D/qmi-uim.txt; } +set_online() { timeout 20 $Q --dms-set-operating-mode=online > $D/qmi-set.txt 2>&1; r=$?; cat $D/qmi-set.txt >> $LOG + [ $r = 0 ] && return 0; grep -q "DeviceNotReady" $D/qmi-set.txt || { echo hard > $D/qmi-set.err; return 0; }; return 1; } +# this mmcli has no --list-bearers ("no actions specified"); use 'Bearer | paths:' of mmcli -m, not the initial (attach) bearer +bearers() { mmc -m "$(mm_idx)" | grep -v "initial bearer" | grep -oE "Bearer/[0-9]+" | cut -d/ -f2; } +any_bearer_connected() { for b in $(bearers); do mmc -b $b | grep -q "connected: yes" && return 0; done; return 1; } +qv() { grep -m1 -oE "$1: *'[^']*'" $2 2>/dev/null | sed -E "s/.*'([^']*)'/\1/"; } +# B6M registration state machine. rc 0 = registered+PS attached, 1 = REGISTRATION_TIMEOUT, 2 = local failure (reason in $D/reg-fail) +reg_sm() { + T=$D/reg-trans.tsv; t0=$1; last=; n=0; nd=0; rm -f $D/reg-fail; D0=$(dmesg | wc -l) + printf 'uptime t_online reg ps status true_status domain plmn earfcn pci si_tac si_eci cl_tac cl_eci rsrp rsrq snr +' > $T + lf() { echo "$*" > $D/reg-fail; return 2; } + while :; do n=$((n+1)) + mss_is running || { lf "MSS state $(mss_state)"; return 2; } + [ -e /dev/wwan0qmi0 ] || { lf "wwan0qmi0 disappeared"; return 2; } + [ -n "$(rmtfs_pid)" ] || { lf "rmtfs died"; return 2; } + [ "$(bam_pm)" = error ] && { lf "BAM-DMUX runtime_status=error"; return 2; } + dmesg | tail -n +$((D0+1)) | grep -qE 'crash detected|fatal error|[Ww]atchdog|wdog|Oops|BUG:|Failed to resume' && { lf "dmesg: $(dmesg | tail -n +$((D0+1)) | grep -m1 -E 'crash detected|fatal error|[Ww]atchdog|wdog|Oops|BUG:|Failed to resume')"; return 2; } + if [ $((n % 5)) = 1 ]; then + sim_ready || { lf "SIM not ready: $(grep -E 'Card state|Application state' $D/qmi-uim.txt | head -2 | tr -s ' \t\n' ' ')"; return 2; } + ping -c1 -W2 172.16.42.2 >/dev/null 2>&1 || { sleep 1; ping -c1 -W2 172.16.42.2 >/dev/null 2>&1 || { lf "USB management (usb0 ping) lost"; return 2; }; } + fi + timeout 20 $Q --nas-get-serving-system > $D/qmi-ss.txt 2>&1 + timeout 20 $Q --nas-get-system-info > $D/qmi-si-all.txt 2>&1; sed -n '/LTE service:/,/SIM reject/p' $D/qmi-si-all.txt > $D/qmi-si.txt + RG=$(qv 'Registration state' $D/qmi-ss.txt); PS=$(qv PS $D/qmi-ss.txt); ST=$(qv Status $D/qmi-si.txt); TS=$(qv 'True Status' $D/qmi-si.txt) + DM=$(qv Domain $D/qmi-si.txt); PL=$(qv MCC $D/qmi-si.txt)-$(qv MNC $D/qmi-si.txt); SIT=$(qv 'Tracking Area Code' $D/qmi-si.txt); SIE=$(qv 'Cell ID' $D/qmi-si.txt) + key="$RG|$PS|$ST|$TS|$DM|$PL|$SIT|$SIE" + if [ "$key" != "$last" ]; then + timeout 20 $Q --nas-get-cell-location-info > $D/qmi-cl.txt 2>&1; timeout 20 $Q --nas-get-signal-info > $D/qmi-sig.txt 2>&1 + [ "$RG" = registration-denied ] && [ "${last%%|*}" != registration-denied ] && nd=$((nd+1)) + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' $(now) $(since $t0) "$RG" "$PS" "$ST" "$TS" "$DM" "$PL" \ + "$(qv 'EUTRA Absolute RF Channel Number' $D/qmi-cl.txt)" "$(qv 'Serving Cell ID' $D/qmi-cl.txt)" "$SIT" "$SIE" \ + "$(qv 'Tracking Area Code' $D/qmi-cl.txt)" "$(qv 'Global Cell ID' $D/qmi-cl.txt)" "$(qv RSRP $D/qmi-sig.txt)" "$(qv RSRQ $D/qmi-sig.txt)" "$(qv SNR $D/qmi-sig.txt)" >> $T + log "REG t+$(since $t0)s: $RG / PS $PS / $ST ($TS, $DM) $PL pci=$(qv 'Serving Cell ID' $D/qmi-cl.txt) si=$SIT/$SIE cl=$(qv 'Tracking Area Code' $D/qmi-cl.txt)/$(qv 'Global Cell ID' $D/qmi-cl.txt)" + last=$key; tprobe "REG:$RG/$PS" + fi + echo $nd > $D/reg-denied-count + [ "$RG" = registered ] && [ "$PS" = attached ] && return 0 + [ "$(awk -v a="$(now)" -v b="$t0" -v t="$T_REG" 'BEGIN{print (a-b>=t)}')" = 1 ] && { lf "REGISTRATION_TIMEOUT after ${T_REG}s ($RG, PS $PS)"; return 1; } + sleep 3 + done +} +bearer_field() { awk -F': ' -v k="$1" '$1 ~ " "k"$" {gsub(/ /,"",$2); print $2; exit}' $BEARER.txt; } + +# --- B7C time bootstrap. Variables are _t-prefixed: tprobe runs inside reg_sm (n, T, last, t0 belong to it). +NTPBB=/usr/libexec/aurora/busybox; GPS_EPOCH=315964800; Y2026=1767225600 +time_floor() { _tf=$(cat /etc/aurora/build-epoch 2>/dev/null); case "$_tf" in ''|*[!0-9]*) _tf=$Y2026;; esac; [ $_tf -lt $Y2026 ] && _tf=$Y2026; echo $_tf; } +# tprobe PHASE: read DMS + NITZ (raw kept in $D/time-probe-N.*), validate, log; first VALID sample while the clock is still pre-build -> tset +tprobe() { + [ "$TIME_BOOTSTRAP" = 1 ] || return 0 + _tn=$(( $(cat $D/time-probes 2>/dev/null || echo 0) + 1 )); echo $_tn > $D/time-probes; _tp=$D/time-probe-$_tn + _u0=$(now); timeout 5 $Q --dms-get-time > $_tp.dms 2>&1; _u1=$(now); timeout 5 $Q --nas-get-network-time > $_tp.nitz 2>&1; _u2=$(now) + _ms=$(sed -n "s/.*System time: '\([0-9]*\).*/\1/p" $_tp.dms); _nd=$(qv '3GPP Date' $_tp.nitz); _nt=$(qv '3GPP Time' $_tp.nitz) + _ne=; [ -n "$_nd" ] && [ -n "$_nt" ] && _ne=$(date -u -d "$_nd $_nt" +%s 2>/dev/null); _clk=$(date -u +%s) + # -> "VERDICT dms_utc dms_uptime dms-nitz reason"; dms-nitz compares DMS projected to the NITZ read instant with whole-second NITZ + _v=$(awk -v ms="$_ms" -v g=$GPS_EPOCH -v u0=$_u0 -v u1=$_u1 -v u2=$_u2 -v ne="$_ne" -v fl=$(time_floor) -v y=$Y2026 -v rn=$TIME_REQUIRE_NITZ -v mx=$TIME_MAX_DMS_NITZ 'BEGIN{ + if (ms !~ /^[0-9]+$/) { print "INVALID - - - dms-unreadable"; exit } + dms=ms/1000+g; at=(u0+u1)/2; r="ok"; d="-" + if (dms < y) r="year<2026"; else if (dms < fl) r="before-build"; else if (dms > fl+5*365*86400) r="implausible" + if (ne ~ /^[0-9]+$/) { dd=dms+((u1+u2)/2-at)-ne; d=sprintf("%+.2f", dd); if (r=="ok" && (dd<-mx || dd>mx)) r="dms-nitz-mismatch" } + else if (r=="ok" && rn==1) r="no-nitz" + printf "%s %.3f %.2f %s %s\n", (r=="ok" ? "VALID" : "INVALID"), dms, at, d, r }') + set -- "$1" $_v + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' $_tn "$1" $_u0 "${_ms:--}" "$3" "${_nd:--}T${_nt:--}" "$5" $_clk "$2/$6" >> $D/time.tsv + log "TIME probe $_tn [$1]: dms_ms=${_ms:-none} dms_utc=$3 nitz=${_nd:-none} ${_nt} dms-nitz=$5 clock=$_clk -> $2 ($6)" + [ "$2" = VALID ] && [ ! -f $D/time-set ] && [ $_clk -lt $(time_floor) ] && tset "$1" $3 $4 $5 + return 0; } +# tset PHASE DMS_UTC DMS_UPTIME DIFF: step CLOCK_REALTIME once, landing on a whole UTC second (uptime-based wait, no fixed bias correction) +tset() { + _N=$(awk -v d=$2 -v a=$3 -v u=$(now) 'BEGIN{printf "%d", int(d+(u-a))+1}') + _w=$(awk -v N=$_N -v d=$2 -v a=$3 -v u=$(now) 'BEGIN{w=N-(d+(u-a)); printf "%.3f", (w>0 ? w : 0)}') + sleep $_w; _old=$(date -u +%s); [ $_N -gt $_old ] || { log "TIME SET skipped: target $_N not after clock $_old (never backwards)"; return 0; } + date -u -s @$_N > /dev/null 2>&1; _rc=$? + [ $_rc = 0 ] && echo "$_N $(now) $1 qmi-dms" > $D/time-set + log "TIME SET rc=$_rc source=qmi-dms phase=$1 old=$_old ($(date -u -d @$_old '+%F %T')) new=$_N ($(date -u '+%F %T')) uptime=$(now) dms-nitz=$4 validation=VALID rtc=untouched"; } +# after START OK, background: resolve VNIIFTRI via bearer DNS, /32 routes via addroute (removed by stop/disconnect), ntpd -w (query only) +time_sntp() { + [ "$TIME_BOOTSTRAP" = 1 ] && [ -x $NTPBB ] && [ -n "$TIME_NTP" ] || { log "SNTP: skipped (bootstrap=$TIME_BOOTSTRAP bin=$([ -x $NTPBB ] && echo yes || echo no))"; return 0; } + . $D/ipcfg 2>/dev/null || return 0; _dns=${DNS%%,*}; _ips= + for _h in $TIME_NTP; do _ip=$(nslookup $_h $_dns 2>/dev/null | awk '/^Name:/{f=1} f && /^Address/{print $2; exit}') + case "$_ip" in [0-9]*.[0-9]*.[0-9]*.[0-9]*) addroute $_ip && _ips="$_ips $_ip";; *) log "SNTP: $_h not resolved via $_dns";; esac; done + [ -n "$_ips" ] || return 0 + _a=; for _ip in $_ips; do _a="$_a -p $_ip"; done + timeout 15 $NTPBB ntpd -w -d $_a > $D/sntp.txt 2>&1 + _b=$(awk '/reply from/{o="";d=""; for(i=1;i<=NF;i++){ if($i~/^offset:/) o=substr($i,8); if($i~/^delay:/) d=substr($i,7) } if(o!="" && (bd=="" || d+0 0 = clock behind)" + [ -n "$_b" ] || return 0 + if [ "$(awk -v o=${_b%% *} -v s=$TIME_NTP_STEP 'BEGIN{print (o>=s || o<=-s)}')" = 1 ]; then + _old=$(date -u +%s); _N=$(awk -v c=$_old -v o=${_b%% *} 'BEGIN{printf "%d", c+o+0.5}'); date -u -s @$_N > /dev/null 2>&1 + log "SNTP STEP rc=$? old=$_old new=$_N (|offset| >= ${TIME_NTP_STEP}s) rtc=untouched" + else log "SNTP: |offset| < ${TIME_NTP_STEP}s - clock kept (no slew: STA_UNSYNC stays, RTC_SYSTOHC never engages)"; fi; } + +check_safety() { + for p in modemst1 modemst2 fsc fsg persist; do + d=/dev/disk/by-partlabel/$p; [ -e $d ] || return 1 + [ "$(blockdev --getro $(readlink -f $d))" = 1 ] || { log "SAFETY: $p not RO"; return 1; } + done + if [ "$(blockdev --getro /dev/mmcblk0)" != 1 ]; then # B12P: the disk flag may be lifted only for the Alpine userdata mount + _ud=$(readlink -f /dev/disk/by-partlabel/userdata 2>/dev/null) + for x in /dev/mmcblk0p* /dev/mmcblk0boot0 /dev/mmcblk0boot1; do [ "$x" = "$_ud" ] && continue + [ "$(blockdev --getro $x)" = 1 ] || { log "SAFETY: mmcblk0 not RO ($x rw)"; return 1; }; done + fi + grep -q " /firmware .* ro," /proc/mounts || { log "SAFETY: /firmware not mounted ro"; return 1; } + [ "$(cat /sys/module/firmware_class/parameters/path)" = /firmware/image ] || { log "SAFETY: firmware path"; return 1; } + return 0 +} +connect_args() { + for v in "$MODEM_APN" "$MODEM_USER" "$MODEM_PASSWORD" "$MODEM_AUTH" "$MODEM_IP_TYPE"; do + case "$v" in *[,\"\'=]*) echo "BAD"; return;; esac; done + a="apn=$MODEM_APN" + [ -n "$MODEM_USER" ] && a="$a,user=$MODEM_USER" + [ -n "$MODEM_PASSWORD" ] && a="$a,password=$MODEM_PASSWORD" + [ -n "$MODEM_AUTH" ] && a="$a,allowed-auth=$MODEM_AUTH" + echo "$a,ip-type=$MODEM_IP_TYPE" +} +addroute() { # /32 via the bearer interface, recorded so stop removes exactly what we added + ip=$1; . $D/ipcfg 2>/dev/null || { echo "no bearer ipcfg"; return 1; } + grep -qx "$ip" $ROUTES 2>/dev/null && return 0 + ip route add $ip/32 dev $IF src $A && echo $ip >> $ROUTES +} +diag() { # snapshot before rollback + { echo "### diag $(now) state=$(cat $STATE 2>/dev/null) mss=$(mss_state) rmtfs=$(rmtfs_pid) mm=$(mm_pid) bam=$(bam_pm)" + [ -n "$(mm_pid)" ] && { mmc -L; mmc -m "$(mm_idx)"; } + cat $D/qmi-mode.txt $D/qmi-ss.txt 2>/dev/null; dmesg | tail -40; } > $D/diag-$(now).txt 2>&1 +} +fail_start() { log "FAIL in $(cat $STATE 2>/dev/null): $*"; diag + if [ "$MODEM_KEEP_ON_FAIL" = 1 ]; then log "MODEM_KEEP_ON_FAIL=1: leaving stack as is" + else log "rollback: full stop"; do_stop; fi + exit 1; } + +do_start() { + T0=$(now); log "=== START (conf $CONF, apn '${MODEM_APN}', ip-type $MODEM_IP_TYPE)" + [ -n "$MODEM_APN" ] || { log "config: MODEM_APN empty - refusing (no APN guessing)"; exit 2; } + CA=$(connect_args); [ "$CA" = BAD ] && { log "config: forbidden character in APN/user/password/auth"; exit 2; } + check_safety || { log "FAIL: safety gate (RO) - nothing started"; exit 1; } + [ -n "$(rp)" ] || { log "FAIL: remoteproc $RPNAME not found"; exit 1; } + + # OFF -> RMTFS_READY -> MPSS_BOOTING -> MPSS_RUNNING + if [ -n "$(rmtfs_pid)" ] && mss_is running; then log "rmtfs $(rmtfs_pid) + MSS running already - reuse" + elif [ -n "$(rmtfs_pid)" ] || ! mss_is offline; then + log "FAIL: inconsistent (rmtfs='$(rmtfs_pid)' MSS=$(mss_state)); run stop first"; exit 1 + else + : > $ROUTES; rm -f $D/ipcfg + n0=$(grep -c 'registering services' $D/rmtfs.log 2>/dev/null) + LD_PRELOAD=/usr/lib/linebuf.so rmtfs -r -P -s -v >> $D/rmtfs.log 2>&1 & + echo $! > $D/rmtfs.pid + wait_for $T_RMTFS 0.2 sh -c "kill -0 $(cat $D/rmtfs.pid) 2>/dev/null && [ \$(grep -c 'registering services' $D/rmtfs.log) -gt ${n0:-0} ]" \ + || fail_start "rmtfs did not start (pid $(cat $D/rmtfs.pid))" + setstate RMTFS_READY + setstate MPSS_BOOTING + t=$(now); wait_for $T_MSS 0.2 mss_is running || fail_start "MSS not running after ${T_MSS}s (state $(mss_state))" + log "MSS running after $(since $t)s" + fi + setstate MPSS_RUNNING + + # -> QMI_READY + t=$(now); wait_for $T_QMI_DEV 0.2 qmi_dev || fail_start "wwan0qmi0/wwan0 missing after ${T_QMI_DEV}s" + log "wwan0qmi0 + wwan0 present after $(since $t)s; +2 s (JZ02 rpmsg_wwan_ctrl port-race guard, proven start_post)" + sleep 2 + [ "$(bam_pm)" = error ] && fail_start "BAM-DMUX runtime_status=error" + + # -> RADIO_ONLINE -> REGISTERED (Aurora-proven: qmicli online + NAS poll before ModemManager) + if [ -n "$(mm_pid)" ]; then + log "ModemManager already running - skip qmicli, use MM state" + setstate QMI_READY + else + t=$(now); wait_for $T_QMI 1 qmi_ok || fail_start "QMI DMS not answering after ${T_QMI}s" + setstate QMI_READY; log "DMS $(grep -o "Mode: '[^']*'" $D/qmi-mode.txt) after $(since $t)s"; tprobe QMI_READY + t=$(now); wait_for $T_SIM 1 sim_ready || fail_start "SIM/USIM not ready after ${T_SIM}s ($(grep -E "Card state|Application state" $D/qmi-uim.txt | head -2 | tr -s ' \t\n' ' '))" + setstate SIM_READY; log "UIM card present, USIM app ready after $(since $t)s"; tprobe SIM_READY + if ! grep -q "Mode: 'online'" $D/qmi-mode.txt; then + log "SET: $Q --dms-set-operating-mode=online (retried only on DeviceNotReady, <= ${T_ONLINE}s)" + rm -f $D/qmi-set.err; t=$(now); wait_for $T_ONLINE 1 set_online || fail_start "SET online: DeviceNotReady for ${T_ONLINE}s" + [ -f $D/qmi-set.err ] && fail_start "SET online failed: $(grep -i error $D/qmi-set.txt | head -1)" + qmi_ok && grep -q "Mode: 'online'" $D/qmi-mode.txt || fail_start "mode not online after SET" + log "DMS online after $(since $t)s" + fi + setstate RADIO_ONLINE; tprobe RADIO_ONLINE + t=$(now); reg_sm $t; rr=$? + [ $rr = 2 ] && fail_start "LOCAL FAILURE during registration: $(cat $D/reg-fail)" + [ $rr = 1 ] && fail_start "$(cat $D/reg-fail)" + + log "registered: $(grep -oE "(MCC|MNC|Description): '[^']*'" $D/qmi-ss.txt | tr '\n' ' ')after $(since $t)s" + setstate REGISTERED; tprobe REGISTERED + killall qmi-proxy 2>/dev/null; wait_for 5 0.2 sh -c '! pidof qmi-proxy >/dev/null' + fi + + # -> MM_READY (mm-up order: udevd, trigger, settle, dbus, polkitd, ModemManager) + pidof udevd >/dev/null || { udevd --daemon; udevadm trigger --action=add; udevadm settle --timeout=15; log "udevd started"; } + if ! pidof dbus-daemon >/dev/null; then + mkdir -p /run/dbus /var/lib/dbus /var/lib/polkit; rm -f /run/dbus/pid; dbus-uuidgen --ensure + dbus-daemon --system --fork || fail_start "dbus-daemon"; log "dbus started"; fi + pidof polkitd >/dev/null || { /usr/lib/polkit-1/polkitd --no-debug >> $D/polkitd.log 2>&1 & log "polkitd started"; } + if [ -z "$(mm_pid)" ]; then + N=$(ls $D/mm-*.log 2>/dev/null | wc -l) + ModemManager --debug > $D/mm-$N.log 2>&1 & + echo $! > $D/mm.pid; log "ModemManager started pid $! (log mm-$N.log)" + fi + t=$(now); wait_for $T_MM_DETECT 2 mm_has_modem || fail_start "MM: no modem after ${T_MM_DETECT}s" + log "MM modem $(mm_idx) detected after $(since $t)s" + mmc -m "$(mm_idx)" > $D/mm-modem.txt + grep -q "plugin: qcom-soc" $D/mm-modem.txt || fail_start "MM plugin is not qcom-soc" + grep -q "primary port: wwan0qmi0" $D/mm-modem.txt || fail_start "MM primary port is not wwan0qmi0" + setstate MM_READY + t=$(now); wait_for $T_MM_REG 2 mm_usable || fail_start "MM state '$(mm_state)' not usable after ${T_MM_REG}s" + log "MM state $(mm_state) after $(since $t)s" + + # -> BEARER_CONNECTED (exactly one simple-connect) + if any_bearer_connected; then log "bearer already connected - reuse" + else + log "mmcli -m $(mm_idx) --simple-connect=\"$(echo "$CA" | sed 's/password=[^,]*/password=***/')\"" + timeout $T_CONNECT mmcli -m "$(mm_idx)" --simple-connect="$CA" >> $LOG 2>&1 || fail_start "simple-connect failed/timeout" + fi + BR=; for b in $(bearers); do mmc -b $b | grep -q "connected: yes" && BR=$b; done + [ -n "$BR" ] || fail_start "no connected bearer after simple-connect" + mmc -m "$(mm_idx)" > $D/mm-modem.txt + grep -qE "registration: (home|roaming)" $D/mm-modem.txt || fail_start "MM registration not home/roaming after connect" + log "MM state $(mm_state), $(grep -oE "registration: [a-z]+" $D/mm-modem.txt)" + mmc -b $BR > $BEARER.txt + IF=$(bearer_field interface); A=$(bearer_field address); PF=$(bearer_field prefix); GW=$(bearer_field gateway) + DNS=$(bearer_field dns); MTU=$(bearer_field mtu) + echo "$IF" | grep -qE '^wwan[0-7]$' && [ -n "$A" ] && [ -n "$PF" ] || fail_start "bearer $BR bad ipv4 config IF=$IF A=$A PF=$PF" + printf 'BR=%s\nIF=%s\nA=%s\nPF=%s\nGW=%s\nDNS=%s\nMTU=%s\n' "$BR" "$IF" "$A" "$PF" "$GW" "$DNS" "$MTU" > $D/ipcfg + log "bearer $BR: $IF $A/$PF gw $GW dns $DNS mtu $MTU" + ip addr flush dev $IF; ip addr add $A/$PF dev $IF || fail_start "ip addr add" + [ -n "$MTU" ] && ip link set $IF mtu $MTU + ip link set $IF up || fail_start "ip link set $IF up (bam-dmux runtime PM?)" + link_up $IF || fail_start "$IF not UP" + for h in $(echo "$DNS" | tr ',' ' ') $MODEM_TEST_ROUTES; do addroute $h || fail_start "route $h"; done + ip route get 172.16.42.2 2>&1 | grep -q "dev usb0" || fail_start "management route no longer via usb0" + ip route | grep -q "^default" && log "WARNING: a default route exists (not created by us)" + setstate BEARER_CONNECTED + log "=== START OK in $(since $T0)s ($IF $A/$PF, routes: $(tr '\n' ' ' < $ROUTES))" + tprobe BEARER_CONNECTED; ( time_sntp ) & +} + +do_stop() { + T0=$(now); log "=== STOP (from $(cat $STATE 2>/dev/null || echo OFF): mss=$(mss_state) rmtfs=$(rmtfs_pid) mm=$(mm_pid) bam=$(bam_pm))" + rc=0 + # 1. disconnect bearer(s) + if [ -n "$(mm_pid)" ] && mm_has_modem; then + timeout $T_DISCONNECT mmcli -m "$(mm_idx)" --simple-disconnect >> $LOG 2>&1; log "simple-disconnect rc=$? (bearers: $(bearers | tr '\n' ' '))" + wait_for 15 1 sh -c "! sh $0 _anyconn" && log "no bearer connected" || { log "WARNING: bearer still connected"; rc=1; } + fi + # 2. our /32 routes, 3. flush + DOWN every wwan* + for h in $(cat $ROUTES 2>/dev/null); do ip route del $h/32 2>/dev/null; done; : > $ROUTES + for d in /sys/class/net/wwan*; do [ -e "$d" ] || continue; w=${d##*/} + ip addr flush dev $w 2>/dev/null; ip link set $w down 2>/dev/null + link_up $w && { log "ERROR: $w still UP"; rc=1; }; done + rm -f $D/ipcfg + ip route | grep -q " dev wwan" && { log "ERROR: routes via wwan remain: $(ip route | grep ' dev wwan' | tr '\n' ';')"; rc=1; } + log "wwan links down, addresses/routes removed" + [ -n "$(cat $STATE 2>/dev/null)" ] && setstate WWAN_DOWN + # 4. stop ModemManager (+ its qmi-proxy) + if [ -n "$(mm_pid)" ]; then + kill -TERM $(mm_pid); wait_for $T_MM_EXIT 0.5 sh -c '! pidof ModemManager >/dev/null' \ + || { log "MM did not exit in ${T_MM_EXIT}s - SIGKILL"; kill -KILL $(mm_pid) 2>/dev/null; rc=1; } + log "ModemManager stopped" + fi + pidof qmi-proxy >/dev/null && { killall qmi-proxy; wait_for 5 0.2 sh -c '! pidof qmi-proxy >/dev/null'; log "qmi-proxy stopped"; } + setstate MM_STOPPED + # 5. BAM-DMUX idle + if [ -e $BAM ] && ! mss_is offline; then + t=$(now); if wait_for $T_BAM_IDLE 0.5 bam_suspended; then log "BAM-DMUX suspended after $(since $t)s" + else log "ERROR: BAM-DMUX '$(bam_pm)' not suspended after ${T_BAM_IDLE}s - NOT stopping MSS"; exit 1; fi + fi + # 6. MSS stop via rmtfs -s (SIGTERM -> pwrite "stop" to remoteproc state -> exit), 7. rmtfs exits last + P=$(rmtfs_pid) + if [ -n "$P" ]; then + t=$(now); kill -TERM $P; log "SIGTERM rmtfs $P (rmtfs stops MSS, then exits)" + wait_for $T_MSS_STOP 0.2 mss_is offline && log "MSS offline after $(since $t)s (rmtfs alive: $(kill -0 $P 2>/dev/null && echo yes || echo no))" \ + || { log "ERROR: MSS '$(mss_state)' after ${T_MSS_STOP}s"; rc=1; } + setstate MPSS_STOPPED + wait_for $T_RMTFS_EXIT 0.2 sh -c "! kill -0 $P 2>/dev/null" && log "rmtfs exited after $(since $t)s" \ + || { log "rmtfs still alive - second SIGTERM"; kill -TERM $P; wait_for 5 0.2 sh -c "! kill -0 $P 2>/dev/null" || { kill -KILL $P; rc=1; }; } + elif ! mss_is offline && [ -n "$(rp)" ]; then + log "MSS $(mss_state) without rmtfs - echo stop > state"; echo stop > $(rp)/state + wait_for $T_MSS_STOP 0.2 mss_is offline || rc=1 + fi + mss_is offline || mss_is none || { log "ERROR: MSS state $(mss_state)"; rc=1; } + setstate OFF + log "=== STOP done rc=$rc in $(since $T0)s (mss=$(mss_state) rmtfs='$(rmtfs_pid)' mm='$(mm_pid)' bam=$(bam_pm))" + return $rc +} + +do_disconnect() { # B6N: bearer down only; MSS, rmtfs, MM (and DMS radio) stay up. Next 'start' reuses them and only simple-connects. + T0=$(now); log "=== DISCONNECT (from $(cat $STATE 2>/dev/null || echo OFF): mss=$(mss_state) rmtfs=$(rmtfs_pid) mm=$(mm_pid) bam=$(bam_pm))"; rc=0 + [ -n "$(mm_pid)" ] && mm_has_modem || { log "DISCONNECT: no MM modem"; return 1; } + timeout $T_DISCONNECT mmcli -m "$(mm_idx)" --simple-disconnect >> $LOG 2>&1; log "simple-disconnect rc=$? (bearers: $(bearers | tr '\n' ' '))" + wait_for 15 1 sh -c "! sh $0 _anyconn" && log "no bearer connected" || { log "ERROR: bearer still connected"; rc=1; } + for h in $(cat $ROUTES 2>/dev/null); do ip route del $h/32 2>/dev/null; done; : > $ROUTES + for d in /sys/class/net/wwan*; do [ -e "$d" ] || continue; w=${d##*/} + ip addr flush dev $w 2>/dev/null; ip link set $w down 2>/dev/null + link_up $w && { log "ERROR: $w still UP"; rc=1; }; done + rm -f $D/ipcfg + ip route | grep -q " dev wwan" && { log "ERROR: routes via wwan remain: $(ip route | grep ' dev wwan' | tr '\n' ';')"; rc=1; } + t=$(now); wait_for $T_BAM_IDLE 0.5 bam_suspended && log "BAM-DMUX suspended after $(since $t)s" || { log "ERROR: BAM-DMUX '$(bam_pm)' not suspended"; rc=1; } + setstate MM_IDLE + log "=== DISCONNECT done rc=$rc in $(since $T0)s (mss=$(mss_state) rmtfs='$(rmtfs_pid)' mm='$(mm_pid)' mm_state=$(mm_state) bam=$(bam_pm))" + return $rc +} + +do_status() { + r=$(rp) + echo "controller: $(cat $STATE 2>/dev/null || echo OFF)" + echo "remoteproc: $RPNAME $(mss_state) ($r) fw=$(cat $r/firmware 2>/dev/null)" + echo "rmtfs: pid='$(rmtfs_pid)' writes_logged=$(grep -c 'write ' $D/rmtfs.log 2>/dev/null)" + echo "qmi dev: $(ls /dev/wwan0qmi0 2>/dev/null || echo absent) qmi-proxy='$(pidof qmi-proxy)'" + echo "daemons: udevd='$(pidof udevd)' dbus='$(pidof dbus-daemon)' polkitd='$(pidof polkitd)' ModemManager='$(mm_pid)'" + if [ -n "$(mm_pid)" ] && mm_has_modem; then + mmc -m "$(mm_idx)" | grep -E "\| +(state|power state|registration|operator name|access tech|signal quality|plugin|primary port): " \ + | sed -E 's/^[^|]*\| */ mm /' + for b in $(bearers); do echo " bearer $b: $(mmc -b $b | grep -E 'connected:|interface:|address:' | tr -s ' |' ' ' | tr '\n' ';')"; done + else echo " mm: n/a"; fi + for d in /sys/class/net/wwan*; do [ -e "$d" ] || continue; w=${d##*/} + link_up $w && echo "wwan: $w UP $(ip -4 addr show $w | awk '/inet /{print $2}')"; done + echo "wwan up: $(n=0; for d in /sys/class/net/wwan*; do [ -e $d ] && link_up ${d##*/} && n=$((n+1)); done; echo $n) of $(ls -d /sys/class/net/wwan* 2>/dev/null | wc -l)" + echo "routes: $(ip route | grep ' dev wwan' | tr '\n' ';')" + echo "bam-dmux: $(bam_pm) active_ms=$(cat $BAM/power/runtime_active_time 2>/dev/null) suspended_ms=$(cat $BAM/power/runtime_suspended_time 2>/dev/null) control=$(cat $BAM/power/control 2>/dev/null)" + echo "usb0: $(ip -4 addr show usb0 | awk '/inet /{print $2}') route-to-host: $(ip route get 172.16.42.2 2>&1 | head -1)" + echo "emmc: $(awk '{print "writes_completed="$5" sectors_written="$7}' /sys/block/mmcblk0/stat)" + echo "time: $(date -u '+%F %T') UTC; set: $(cat $D/time-set 2>/dev/null || echo no); probes: $(cat $D/time-probes 2>/dev/null || echo 0)" + echo "dmesg-errs: $(dmesg | grep -cE 'Failed to resume|Resource temporarily|crash detected|fatal error|[Ww]atchdog|Oops|Kernel panic|Unable to handle')" +} + +case "$1" in + start) do_start;; + stop) do_stop; exit $?;; + disconnect) do_disconnect; exit $?;; + status) do_status;; + addroute) [ -n "$2" ] || exit 2; addroute "$2";; + _anyconn) any_bearer_connected;; + _mmstate) mm_state;; + *) echo "usage: $0 start|stop|status|addroute "; exit 2;; +esac diff --git a/b12/b12q/alpine-overlay/usr/sbin/aurora-wifi b/b12/b12q/alpine-overlay/usr/sbin/aurora-wifi new file mode 100644 index 0000000..f698e1f --- /dev/null +++ b/b12/b12q/alpine-overlay/usr/sbin/aurora-wifi @@ -0,0 +1,67 @@ +#!/bin/sh +# Aurora Wi-Fi controller (B8F). Pronto/WCNSS remoteproc -> wcn36xx wlan0 -> minimal hostapd AP (WPA2-PSK/CCMP). +# No DHCP/NAT/DNS/firewall. Everything proven in B8B..B8E; order = B8C..B8E: WCNSS is started after the modem reached +# START OK (or FAIL/rollback, or WAIT_MODEM s elapsed), from the RO modem FAT (/firmware/image/wcnss.mdt), NV from initramfs. +# aurora-wifi start | stop | status +D=/run/aurora-wifi; LOG=$D/wifi.log; CONF=/etc/aurora/wifi.conf; X=/usr/libexec/aurora/wifi +ML=/run/aurora-modem/lifecycle.log; RPNAME=a204000.remoteproc +mkdir -p $D +now() { cut -d' ' -f1 /proc/uptime; } +log() { m="[$(now)] $*"; echo "$m" >> $LOG; echo "$m"; echo "<5>[aurora-wifi] $*" > /dev/kmsg 2>/dev/null; } +rp() { for r in /sys/class/remoteproc/remoteproc*; do [ "$(cat $r/name 2>/dev/null)" = $RPNAME ] && echo $r && return; done; } +hpid() { ps w | grep "[h]ostapd -t .*$D/hostapd.conf" | awk '{print $1}'; } +WAIT_MODEM=300; SSID=; PSK=; CHANNEL=6 +[ -f $CONF ] && . $CONF +case "$1" in +start) + T0=$(now); log "=== WIFI START (wait for modem START OK/FAIL up to ${WAIT_MODEM}s)" + i=0; while [ $i -lt $WAIT_MODEM ]; do + grep -qE "=== START OK|FAIL in|rollback" $ML 2>/dev/null && break; sleep 1; i=$((i+1)); done + log "modem gate after ${i}s: $(grep -E '=== START OK|FAIL in|rollback' $ML 2>/dev/null | tail -1 | cut -c1-80)" + [ "$(cat /sys/module/firmware_class/parameters/path)" = /firmware/image ] && [ -f /firmware/image/wcnss.mdt ] || { log "FAIL: /firmware/image/wcnss.mdt not available"; exit 1; } + [ -f /lib/firmware/wlan/prima/WCNSS_qcom_wlan_nv.bin ] || { log "FAIL: NV missing"; exit 1; } + R=$(rp); [ -n "$R" ] || { log "FAIL: remoteproc $RPNAME not found"; exit 1; } + if [ "$(cat $R/state)" = offline ]; then echo start > $R/state || { log "FAIL: WCNSS start write"; exit 1; }; fi + log "WCNSS $R state=$(cat $R/state)" + i=0; while [ ! -e /sys/class/net/wlan0 ] && [ $i -lt 30 ]; do sleep 1; i=$((i+1)); done + [ -e /sys/class/net/wlan0 ] || { log "FAIL: wlan0 did not appear in 30 s"; exit 1; } + ip link set wlan0 up || { log "FAIL: wlan0 up"; exit 1; } + log "wlan0 up addr=$(cat /sys/class/net/wlan0/address) ($(dmesg | grep -o "wcn36xx: firmware WLAN version.*XO'" | tail -1))" + [ -n "$SSID" ] && [ ${#PSK} -ge 8 ] || { log "AP not configured (SSID/PSK) - wlan0 left up, managed"; exit 0; } + umask 077; cat > $D/hostapd.conf <> %s/hostapd.log 2>&1\n' $X $X $D $D > $D/hostapd.run + setsid sh $D/hostapd.run < /dev/null > /dev/null 2>&1 & + i=0; while ! grep -q "AP-ENABLED" $D/hostapd.log 2>/dev/null && [ $i -lt 20 ]; do sleep 1; i=$((i+1)); done + if grep -q "AP-ENABLED" $D/hostapd.log; then log "=== AP ENABLED ssid=$SSID ch=$CHANNEL pid=$(hpid) in $(awk "BEGIN{print $(now)-$T0}")s" + else log "FAIL: hostapd did not reach AP-ENABLED: $(tail -3 $D/hostapd.log | tr '\n' ' ')"; exit 1; fi;; +stop) + P=$(hpid); [ -n "$P" ] && kill $P; sleep 1 + [ -e /sys/class/net/wlan0 ] && ip link set wlan0 down + R=$(rp); [ -n "$R" ] && [ "$(cat $R/state)" = running ] && echo stop > $R/state + log "=== WIFI STOP (hostapd=${P:-none}, WCNSS $(cat $R/state 2>/dev/null))";; +status) + R=$(rp); echo "remoteproc: $RPNAME $(cat $R/state 2>/dev/null) ($R)" + echo "crash=$(dmesg | grep -cE 'crash detected in a204000') watchdog=$(dmesg | grep -cE 'Watchdog startup') fatal=$(dmesg | grep -cE 'qcom-wcnss-pil.*fatal')" + echo "wlan0: $(ip -o link show wlan0 2>/dev/null | cut -d' ' -f2-3) $(iw dev wlan0 info 2>/dev/null | grep -E 'ssid|type|channel' | tr -s '\t\n' ' ')" + echo "hostapd: pid=$(hpid) $(grep -cE 'AP-STA-CONNECTED' $D/hostapd.log 2>/dev/null) connects, $(grep -cE 'AP-STA-DISCONNECTED' $D/hostapd.log 2>/dev/null) disconnects" + iw dev wlan0 station dump 2>/dev/null | grep -E "^Station|authorized|signal:|connected time" + tail -3 $LOG;; +*) echo "usage: $0 start|stop|status"; exit 2;; +esac diff --git a/b12/b12q/b12q-boot.sh b/b12/b12q/b12q-boot.sh new file mode 100644 index 0000000..9cc5e3b --- /dev/null +++ b/b12/b12q/b12q-boot.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# 480s: B12Q RAM test. b12q-boot.sh K normal|off|failtest — waits for lk1st fastboot, `fastboot boot` the variant, then checks over telnet. +# Nothing is flashed. normal: expect Alpine PID1 on p27; off/failtest: expect LFS rescue (stage0 fallback) with p27 untouched/relocked. +K=$1; V=$2; case "$V" in normal) IMG=aurora-b12q.img;; off) IMG=aurora-b12q-off.img;; failtest) IMG=aurora-b12q-failtest.img;; cold) IMG=;; *) echo "usage: $0 K normal|off|failtest"; exit 2;; esac +cd ~/doc/modem/jz08-aurora; O=logs/b12/b12q/$V$K; mkdir -p $O; [ -n "$IMG" ] && IMG=b12/b12q/out/$IMG +A=$O/host-actions.txt; act() { echo "$(date -u +%H:%M:%S) $*" | tee -a $A; } +tn() { { sleep 1; sed "s/\$/\r/" "$1"; sleep $2; } | ncat 172.16.42.1 23 | tr -d "\000\r"; } +if [ $V != cold ]; then +grep " $(basename $IMG)\$" b12/b12q/out/SHA256SUMS | sed "s| | b12/b12q/out/|" | sha256sum -c || exit 1 +act "B12Q $V$K: waiting for fastboot (RESET held at power-on)" +i=0; until fastboot devices 2>/dev/null | grep -q ; do i=$((i+1)); [ $i -ge 900 ] && { act "ABORT: no fastboot"; exit 1; }; sleep 2; done +act "fastboot present"; { fastboot boot $IMG 2>&1; echo "rc=$?"; } | tee -a $A +else +act "B12Q cold$K: waiting for normal power-on WITHOUT RESET (cache B12Q 59272cd3)" +i=0; until ping -c1 -W1 172.16.42.1 >/dev/null 2>&1; do fastboot devices 2>/dev/null | grep -q && { act "ABORT: board in fastboot - RESET held?"; exit 1; }; i=$((i+1)); [ $i -ge 1800 ] && { act "ABORT: no NCM"; exit 1; }; sleep 1; done +fi +i=0; until ping -c1 -W1 172.16.42.1 >/dev/null 2>&1; do i=$((i+1)); [ $i -ge 240 ] && { act "ABORT: NCM not up"; exit 1; }; sleep 1; done; act "NCM ping ok" +printf 'grep -E "START OK|FAIL in|rollback|SAFETY" /run/aurora-modem/lifecycle.log | tail -2\n' > $O/.w +i=0; until tn $O/.w 3 | grep -qE "^\[[0-9.]+\] .*(=== START OK|FAIL in|rollback)"; do i=$((i+1)); [ $i -ge 80 ] && { act "no START OK/FAIL after ~12 min"; break; }; sleep 6; done +act "modem: $(tn $O/.w 3 | grep -E '^\[[0-9.]+\]' | tail -2 | tr '\n' ' ')" +printf 'grep -E "AP ENABLED|FAIL" /run/aurora-wifi/wifi.log | tail -1\n' > $O/.a +i=0; until tn $O/.a 3 | grep -qE "^\[[0-9.]+\] (=== AP ENABLED|FAIL)"; do i=$((i+1)); [ $i -ge 30 ] && break; sleep 6; done +act "wifi: $(tn $O/.a 3 | grep -E '^\[[0-9.]+\]' | tail -1)" +cat > $O/.c <<'C' +echo SNAP-BEGIN; cat /proc/uptime; uname -a; cat /proc/cmdline +echo ---pid1; ls -l /proc/1/exe; cat /proc/1/cmdline | tr '\0' ' '; echo; ls -l /sbin/init; cat /etc/alpine-release 2>/dev/null; cat /etc/os-release 2>/dev/null | head -2 +echo ---root; grep -E " / |mmcblk0p27|/alpine" /proc/mounts; df -k / | tail -1; cat /proc/mounts +echo ---stage0; dmesg | grep -E "aurora-stage0|aurora-ro|aurora-bootok|stage0"; cat /var/lib/aurora/boot-attempts 2>/dev/null; cat /var/lib/aurora/stage0-fsck.txt 2>/dev/null | tail -1 +echo ---openrc; rc-status -a 2>/dev/null; rc-status --crashed 2>/dev/null +echo ---ro; for x in /dev/mmcblk0 /dev/mmcblk0p* /dev/mmcblk0boot0 /dev/mmcblk0boot1; do printf "%s=%s " ${x#/dev/} $(blockdev --getro $x); done; echo +echo ---modem; /usr/libexec/aurora/svc/aurora-modem status 2>/dev/null || /etc/init.d/aurora-modem status; grep -E "SAFETY|START OK" /run/aurora-modem/lifecycle.log +echo ---ping; ping -c4 -W3 -I wwan0 77.88.8.8 | tail -2 +echo ---wifi; iw dev; grep -E "AP ENABLED" /run/aurora-wifi/wifi.log; for r in /sys/class/remoteproc/*; do echo "RP $(cat $r/name)=$(cat $r/state)"; done +echo ---display; cat /run/aurora-display/display.log 2>/dev/null | tail -3; cat /sys/class/tty/tty0/active; cat /sys/class/backlight/backlight/bl_power; cat /sys/class/drm/card0-SPI-1/enabled +echo ---charger; grep -q " /sys/kernel/debug " /proc/mounts || mount -t debugfs debugfs /sys/kernel/debug; head -3 /sys/kernel/debug/pm8916_lbc/state +echo ---apk; apk --version 2>/dev/null && apk info 2>/dev/null | wc -l; cat /root/persist-test.txt 2>/dev/null +echo ---procs; ps -o pid,user,args 2>/dev/null | grep -vE "\[|ps -o" | head -40 +echo ---mem; free +echo ---emmc; cat /sys/block/mmcblk0/stat; for p in /sys/class/block/mmcblk0p* /sys/class/block/mmcblk0boot0 /sys/class/block/mmcblk0boot1; do s=$(awk '{print $5}' $p/stat); [ "$s" != 0 ] && echo "$(basename $p) writes=$s"; done +echo SNAP-END +C +tn $O/.c 30 > $O/snapshot.txt; act "snapshot: $(grep -c SNAP-END $O/snapshot.txt) end" +grep -A3 -- "---pid1" $O/snapshot.txt | tail -3 | tee -a $A; grep -E "^/dev/mmcblk0p27 / |^rootfs / " $O/snapshot.txt | tee -a $A; grep -E "writes=" $O/snapshot.txt | tee -a $A +grep -E "aurora-stage0" $O/snapshot.txt | tail -2 | tee -a $A +act "B12Q $V$K END" diff --git a/b12/b12q/build-b12q.sh b/b12/b12q/build-b12q.sh new file mode 100644 index 0000000..6afc81d --- /dev/null +++ b/b12/b12q/build-b12q.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# B12Q RAM images: B10 production kernel/DTB unchanged, B9C cmdline (+ test switches), initramfs = B10 (B9C) initramfs + overlay: +# /init = stage0 (switch_root into Alpine on p27, fallback -> /init.lfs), /init.lfs = exact B9C/B10 LFS init (rescue), +# aurora-modem (B12P relaxed disk-flag gate), aurora-alpine (manual p27 mount from rescue), e2fsck bundle. +# Images: aurora-b12q.img (normal), aurora-b12q-off.img (aurora.alpine=off), aurora-b12q-failtest.img (aurora.b12q.failtest=init). +set -e; cd "$(dirname "$0")"; O=out; F=../../b10/b10b3/out-v4; GEN=../../linux/out/usr/gen_init_cpio; TS=1790726400 +mkdir -p $O +echo "b0ea04666c4ab979067fe9c0015bda0e9c8ac742678321e57b18e841c9c69f95 $F/Image.gz-dtb" | sha256sum -c +echo "ac518e27e2957ad5e4515969b9a3808024e11ef8d1a79433a899a67dbc644558 $F/initramfs.cpio.gz" | sha256sum -c +echo "0fef41b262917c8ae308fa222da0017e2b3cb3ec5da1ca8ba18d0318476761a3 $F/cmdline.txt" | sha256sum -c +cmp rootfs/init.lfs ../../b9/b9c/rootfs/init || { echo "STOP: init.lfs != B9C init"; exit 1; } +cp $F/Image.gz-dtb $F/aurora-b10b3.dtb $O/ +{ echo "file /init rootfs/init 0755 0 0"; echo "file /init.lfs rootfs/init.lfs 0755 0 0"; echo "dir /newroot 0755 0 0" + echo "file /usr/sbin/aurora-modem ../b12p/rootfs/usr/sbin/aurora-modem 0755 0 0" + echo "file /usr/sbin/aurora-alpine ../b12p/rootfs/usr/sbin/aurora-alpine 0755 0 0" + echo "file /etc/init.d/aurora-alpine ../b12p/rootfs/etc/init.d/aurora-alpine 0755 0 0"; echo "dir /alpine 0755 0 0" + echo "dir /usr/libexec/aurora/e2fs 0755 0 0"; echo "dir /usr/libexec/aurora/e2fs/lib 0755 0 0"; echo "dir /usr/libexec/aurora/e2fs/sbin 0755 0 0" + for f in e2fs/lib/* e2fs/sbin/*; do echo "file /usr/libexec/aurora/$f $f 0755 0 0"; done; } > $O/overlay-b12q.list +$GEN -t $TS $O/overlay-b12q.list > $O/overlay-b12q.cpio; gzip -9 -n -c $O/overlay-b12q.cpio > $O/overlay-b12q.cpio.gz +cat $F/initramfs.cpio.gz $O/overlay-b12q.cpio.gz > $O/initramfs-b12q.cpio.gz +cp $F/cmdline.txt $O/cmdline.txt +printf '%s aurora.alpine=off' "$(cat $F/cmdline.txt)" > $O/cmdline-off.txt +printf '%s aurora.b12q.failtest=init' "$(cat $F/cmdline.txt)" > $O/cmdline-failtest.txt +for v in "" -off -failtest; do python3 ../../b6p/mkbootimg.py $O/Image.gz-dtb $O/initramfs-b12q.cpio.gz $O/cmdline$v.txt $O/aurora-b12q$v.img; done +(cd $O && sha256sum Image.gz-dtb aurora-b10b3.dtb cmdline*.txt overlay-b12q.cpio.gz initramfs-b12q.cpio.gz aurora-b12q*.img > SHA256SUMS; cat SHA256SUMS) diff --git a/b12/b12q/cache/extlinux-b12q.conf b/b12/b12q/cache/extlinux-b12q.conf new file mode 100644 index 0000000..c9d3edb --- /dev/null +++ b/b12/b12q/cache/extlinux-b12q.conf @@ -0,0 +1,6 @@ +default b12q +label b12q + linux /Image.gz-dtb + fdt /aurora-b10b3.dtb + initrd /initramfs-b12q.cpio.gz + append earlycon console=tty0 console=ttyMSM0,115200n8 ignore_loglevel loglevel=8 rdinit=/init fbcon=font:6x8 consoleblank=0 diff --git a/b12/b12q/cache/mkfs-cache-b12q.sh b/b12/b12q/cache/mkfs-cache-b12q.sh new file mode 100644 index 0000000..35a01cb --- /dev/null +++ b/b12/b12q/cache/mkfs-cache-b12q.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# B12Q cache CANDIDATE (Alpine primary root: stage0 switch_root, LFS rescue) (NOT for writing without a separate GO): exact recipe of b10/b10b3/cache-v4/mkfs-cache-b10b5.sh (c3732515); +# differs only in the initrd (B9C initramfs + B12P overlay: aurora-alpine autostart, modem safety gate, e2fsck bundle), file names, +# extlinux label, fs label and UUIDs. Kernel/DTB/append = B10 production unchanged. Runs on 480s (mke2fs 1.47.2). Offline only. +# The image contains the AP PSK (in the initramfs) - never publish it. +set -e +cd ~/doc/modem/jz08-aurora/b12/b12q/cache +OUT=${1:-cache-extlinux-b12q.img}; F=../out +echo "0fef41b262917c8ae308fa222da0017e2b3cb3ec5da1ca8ba18d0318476761a3 $F/cmdline.txt" | sha256sum -c +echo "b0ea04666c4ab979067fe9c0015bda0e9c8ac742678321e57b18e841c9c69f95 $F/Image.gz-dtb" | sha256sum -c +echo "e47762d1dcd0af6e2acb711fa47ecc13769cd6bcb81259ce50635e8b18b6514a $F/aurora-b10b3.dtb" | sha256sum -c +echo "9ade7cffb1f3f19e86acc8869565a88b960abba543385a2fddcebd27bd529e79 $F/initramfs-b12q.cpio.gz" | sha256sum -c +ST=$(mktemp -d); mkdir -p $ST/extlinux +cp $F/Image.gz-dtb $F/aurora-b10b3.dtb $F/initramfs-b12q.cpio.gz $ST/ +printf "default b12q\nlabel b12q\n\tlinux /Image.gz-dtb\n\tfdt /aurora-b10b3.dtb\n\tinitrd /initramfs-b12q.cpio.gz\n\tappend %s\n" "$(cat $F/cmdline.txt)" > $ST/extlinux/extlinux.conf +cp $ST/extlinux/extlinux.conf extlinux-b12q.conf +chmod 0644 $ST/*.* $ST/extlinux/extlinux.conf; chmod 0755 $ST $ST/extlinux +find $ST -exec touch -h -d @1790726400 {} + +rm -f $OUT; truncate -s 134217728 $OUT +E2FSPROGS_FAKE_TIME=1790726400 /sbin/mke2fs -F -q -t ext2 -O none,filetype,sparse_super \ + -b 4096 -I 128 -N 64 -m 0 -L aurora-b12q -U 4a5a3038-b6f0-4000-8000-0000b12b1300 \ + -E root_owner=0:0,hash_seed=4a5a3038-b6f0-4000-8000-0000b12b1301 -d $ST $OUT 32768 +rm -rf $ST +for f in / /lost+found /extlinux /extlinux/extlinux.conf /Image.gz-dtb /aurora-b10b3.dtb /initramfs-b12q.cpio.gz; do for t in atime ctime mtime; do echo "sif $f $t @1790726400"; done; done > /tmp/b12q-sif.$$ +/sbin/debugfs -w -f /tmp/b12q-sif.$$ $OUT >/dev/null 2>&1; rm -f /tmp/b12q-sif.$$ +/sbin/e2fsck -fn $OUT >/dev/null 2>&1 && echo "e2fsck -fn clean" +stat -c "%n %s" $OUT; sha256sum $OUT diff --git a/b12/b12q/install-p27.sh b/b12/b12q/install-p27.sh new file mode 100644 index 0000000..62be863 --- /dev/null +++ b/b12/b12q/install-p27.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# B12Q-2 (board, under B12P production with /alpine mounted): install the real-boot packages from a local repo (no network) +# and the Aurora overlay into p27. Writes only p27. Idempotent. +set -e; R=/alpine; T=/tmp/b12 +grep -q " $R ext4 rw" /proc/mounts || { echo "INSTALL FAIL: /alpine not mounted rw"; exit 1; } +rm -rf $R/var/cache/b12q; mkdir -p $R/var/cache/b12q; tar xzf $T/b12q-pkgs.tar.gz -C $R/var/cache/b12q; mkdir $R/var/cache/b12q/pkgs/aarch64; mv $R/var/cache/b12q/pkgs/*.apk $R/var/cache/b12q/pkgs/APKINDEX.tar.gz $R/var/cache/b12q/pkgs/aarch64/; ln -s aarch64 $R/var/cache/b12q/pkgs/noarch +N="openrc busybox-extras eudev dbus polkit modemmanager modemmanager-udev libqmi qmi-utils libqrtr-glib rmtfs hostapd iw mobile-broadband-provider-info" +chroot $R apk add --no-network --allow-untrusted --repositories-file /dev/null --repository /var/cache/b12q/pkgs $N > $T/apk-add.log 2>&1 || { tail -20 $T/apk-add.log; echo "INSTALL FAIL apk"; exit 1; } +tail -3 $T/apk-add.log +tar xzf $T/b12q-overlay.tar.gz -C $R +rm -rf $R/var/cache/b12q; echo 0 > $R/var/lib/aurora/boot-attempts; sync +echo "pkgs=$(chroot $R apk info | wc -l) openrc=$(chroot $R openrc --version | head -1) users=$(grep -cE '^(messagebus|polkitd):' $R/etc/passwd)" +ls $R/etc/runlevels/*; ls -l $R/sbin/init $R/sbin/openrc +echo "INSTALL OK" diff --git a/b12/b12r/B12R-rollback.sh b/b12/b12r/B12R-rollback.sh new file mode 100644 index 0000000..7ce332a --- /dev/null +++ b/b12/b12r/B12R-rollback.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# B12R rollback - restore the B12Q cache (59272cd3: Alpine primary root, kernel b10b5 without nf_tables). Needs 9008 via HW EDL. boot/aboot/userdata untouched +# (if aurora-router was added to the default runlevel it just logs "kernel has no nf_tables" under b10b5). Deeper: b12/b12q/B12Q-rollback.sh (B12P). +cd ~/doc/modem/jz08-aurora || exit 1 +L=firehose/007050e100000000_394a2e47cf830150_fhprg_peek.bin ; O=logs/b12/b12r/rollback ; mkdir -p $O +B=b12/b12q/cache/cache-extlinux-b12q.img +[ "$(sha256sum $B | cut -d' ' -f1)" = 59272cd3fb45a9388934110b345a0e320d62a3199b52aa0c47ec10eb79352aae ] || { echo "STOP: rollback image hash"; exit 1; } +lsusb | grep -q 05c6:9008 || { echo "STOP: no 9008"; exit 1; } +edl --loader=$L printgpt > $O/00-printgpt.log 2>&1 +edl --loader=$L w cache $B > $O/cache-w.log 2>&1 +edl --loader=$L r cache $O/cache-rb.bin > /dev/null 2>&1 ; cmp $B $O/cache-rb.bin && echo CACHE_RESTORED_B12Q +sha256sum $B $O/cache-rb.bin diff --git a/b12/b12r/B12R-write.sh b/b12/b12r/B12R-write.sh new file mode 100644 index 0000000..bb9f26f --- /dev/null +++ b/b12/b12r/B12R-write.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# B12R — the ONLY write: cache := B12R candidate (= B12Q + kernel 7.2.7-aurora-b12: b10b5 production config + nf_tables/NAT, same source tree). +# Run on 480s ONLY after explicit GO. Derived 1:1 from b12/b12q/B12Q-write.sh (B12Q_CACHE_WRITE_VERIFIED 2026-10-04); changed: image/hashes, +# rollback refs, log dir. The pre-write readback of the live cache (must be B12Q 59272cd3) is kept as backup. +# Entry: HW EDL (battery on, D+->GND, plug USB, release). Never `edl reset`. Stops at first failed gate. +cd ~/doc/modem/jz08-aurora || exit 1 +L=firehose/007050e100000000_394a2e47cf830150_fhprg_peek.bin +O=logs/b12/b12r/W ; A3=logs/b5a/A3 ; P=logs/b5a/A4pre ; mkdir -p $O +NEW=b12/b12r/cache/cache-extlinux-b12r.img ; NEW_SHA=e85d862e463aa4affd7ec1cea9b1f2c7f765bb08e2a6bdce4af9539278f80d33 +OLD_SHA=59272cd3fb45a9388934110b345a0e320d62a3199b52aa0c47ec10eb79352aae # current cache = B12Q extlinux +BOOT_SHA=e9579f33c7304cd47f487f2b61cd9226dc04fd3a63775d62b4b95fc15bd3ebff +ABOOT_SHA=15208dbb4feafccd72d2b06db5714cd746c216701f0ef8822c58bb47105d37c4 # aboot = lk1st-b9l (B9L-PERSIST) +die(){ echo "STOP: $*"; exit 1; } +sha(){ sha256sum "$1" | cut -d" " -f1; } +E(){ edl --loader=$L "$@"; } +echo "== G0 inputs" +[ "$(sha $L)" = 53f193500c03248f0d671ab57bfe9ca8a42967e97f28403294b4b3f854075aca ] || die loader +[ "$(sha $NEW)" = $NEW_SHA ] && [ $(stat -c %s $NEW) = 134217728 ] || die candidate +[ "$(sha b12/b12q/cache/cache-extlinux-b12q.img)" = $OLD_SHA ] || die rollback-image +[ "$(sha logs/b12/b12q/W/11-cache-readback.bin)" = $OLD_SHA ] || die b12q-readback-backup +lsusb | grep -q 05c6:9008 || die "no 9008" +echo "== G1 identity + geometry + baseline" +E printgpt > $O/00-printgpt.log 2>&1 || die printgpt +grep -aq "Serial: *0x" $O/00-printgpt.log || grep -aq "Mode detected: firehose" $O/00-printgpt.log || die serial +grep -a "^cache:" $O/00-printgpt.log | grep -q "Offset 0x000000004118c000, Length 0x0000000008000000" || die cache-geometry +E rs 0 34 $O/pre-gptp.bin >/dev/null 2>&1; cmp $O/pre-gptp.bin $A3/gpt-primary-34.bin || die gpt-changed +E r boot $O/pre-boot.bin >/dev/null 2>&1; [ "$(sha $O/pre-boot.bin)" = $BOOT_SHA ] || die boot-not-emmc1 +E r aboot $O/pre-aboot.bin >/dev/null 2>&1; [ "$(sha $O/pre-aboot.bin)" = $ABOOT_SHA ] || die aboot-not-b9l +E r cache $O/pre-cache.bin >/dev/null 2>&1; [ "$(sha $O/pre-cache.bin)" = $OLD_SHA ] || die cache-not-backup +echo "== W cache := candidate (LBA 2133088, 262144 sectors)" +E w cache $NEW > $O/10-cache-write.log 2>&1; echo "rc=$?"; grep -a "Wrote" $O/10-cache-write.log +E r cache $O/11-cache-readback.bin > $O/11-cache-readback.log 2>&1 || die readback-rc +sha256sum $NEW $O/11-cache-readback.bin +cmp $NEW $O/11-cache-readback.bin || die "CACHE READBACK MISMATCH — do NOT reboot; run b12/b12r/B12R-rollback.sh" +echo CACHE_MATCH +echo "== V everything else unchanged" +E rs 0 34 $O/post-gptp.bin >/dev/null 2>&1; cmp $O/post-gptp.bin $A3/gpt-primary-34.bin || die gptp +E rs 7634911 33 $O/post-gptb.bin >/dev/null 2>&1; cmp $O/post-gptb.bin $A3/gpt-backup-33.bin || die gptb +E rs 305184 1024 $O/post-tzbak.bin >/dev/null 2>&1; cmp $O/post-tzbak.bin $A3/tzbak-305184.bin || die tzbak +E r boot $O/post-boot.bin >/dev/null 2>&1; [ "$(sha $O/post-boot.bin)" = $BOOT_SHA ] || die boot +E r aboot $O/post-aboot.bin >/dev/null 2>&1; [ "$(sha $O/post-aboot.bin)" = $ABOOT_SHA ] || die aboot +for p in sbl1 rpm tz hyp recovery modemst1 modemst2 fsg fsc persist; do + E r $p $O/post-$p.bin >/dev/null 2>&1; cmp $O/post-$p.bin $A3/$p.bin || die "$p changed"; echo "$p == A3" +done +for p in modem system; do + E r $p $O/post-$p.bin >/dev/null 2>&1; cmp $O/post-$p.bin $P/$p.bin || die "$p changed"; echo "$p == A4pre"; rm -f $O/post-$p.bin +done +E rs 2428000 8 $O/post-userdata-sb.bin >/dev/null 2>&1 +[ "$(dd if=$O/post-userdata-sb.bin bs=1 skip=1080 count=2 2>/dev/null | od -An -tx1 | tr -d ' \n')" = 53ef ] && [ "$(dd if=$O/post-userdata-sb.bin bs=1 skip=1144 count=16 2>/dev/null | tr -d '\000')" = aurora-alpine ] || die "userdata superblock is not ext4 aurora-alpine" +echo "userdata = ext4 aurora-alpine (superblock)" +(cd $O && sha256sum *.bin > SHA256SUMS) +echo "B12R_CACHE_WRITE_VERIFIED — power off fully (USB, then battery) before first boot" diff --git a/b12/b12r/b12r-boot.sh b/b12/b12r/b12r-boot.sh new file mode 100644 index 0000000..6bbc10c --- /dev/null +++ b/b12/b12r/b12r-boot.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# 480s: B12R RAM test. b12q-boot.sh K normal|off|failtest — waits for lk1st fastboot, `fastboot boot` the variant, then checks over telnet. +# Nothing is flashed. normal: expect Alpine PID1 on p27; off/failtest: expect LFS rescue (stage0 fallback) with p27 untouched/relocked. +K=$1; V=$2; case "$V" in normal) IMG=aurora-b12r.img;; off) IMG=aurora-b12q-off.img;; failtest) IMG=aurora-b12q-failtest.img;; cold) IMG=;; *) echo "usage: $0 K normal|off|failtest"; exit 2;; esac +cd ~/doc/modem/jz08-aurora; O=logs/b12/b12r/$V$K; mkdir -p $O; [ -n "$IMG" ] && IMG=b12/b12r/out/$IMG +A=$O/host-actions.txt; act() { echo "$(date -u +%H:%M:%S) $*" | tee -a $A; } +tn() { { sleep 1; sed "s/\$/\r/" "$1"; sleep $2; } | ncat 172.16.42.1 23 | tr -d "\000\r"; } +if [ $V != cold ]; then +grep " $(basename $IMG)\$" b12/b12r/out/SHA256SUMS | sed "s| | b12/b12r/out/|" | sha256sum -c || exit 1 +act "B12R $V$K: waiting for fastboot (RESET held at power-on)" +i=0; until fastboot devices 2>/dev/null | grep -q ; do i=$((i+1)); [ $i -ge 900 ] && { act "ABORT: no fastboot"; exit 1; }; sleep 2; done +act "fastboot present"; { fastboot boot $IMG 2>&1; echo "rc=$?"; } | tee -a $A +else +act "B12R cold$K: waiting for normal power-on WITHOUT RESET (cache B12R 59272cd3)" +i=0; until ping -c1 -W1 172.16.42.1 >/dev/null 2>&1; do fastboot devices 2>/dev/null | grep -q && { act "ABORT: board in fastboot - RESET held?"; exit 1; }; i=$((i+1)); [ $i -ge 1800 ] && { act "ABORT: no NCM"; exit 1; }; sleep 1; done +fi +i=0; until ping -c1 -W1 172.16.42.1 >/dev/null 2>&1; do i=$((i+1)); [ $i -ge 240 ] && { act "ABORT: NCM not up"; exit 1; }; sleep 1; done; act "NCM ping ok" +printf 'grep -E "START OK|FAIL in|rollback|SAFETY" /run/aurora-modem/lifecycle.log | tail -2\n' > $O/.w +i=0; until tn $O/.w 3 | grep -qE "^\[[0-9.]+\] .*(=== START OK|FAIL in|rollback)"; do i=$((i+1)); [ $i -ge 80 ] && { act "no START OK/FAIL after ~12 min"; break; }; sleep 6; done +act "modem: $(tn $O/.w 3 | grep -E '^\[[0-9.]+\]' | tail -2 | tr '\n' ' ')" +printf 'grep -E "AP ENABLED|FAIL" /run/aurora-wifi/wifi.log | tail -1\n' > $O/.a +i=0; until tn $O/.a 3 | grep -qE "^\[[0-9.]+\] (=== AP ENABLED|FAIL)"; do i=$((i+1)); [ $i -ge 30 ] && break; sleep 6; done +act "wifi: $(tn $O/.a 3 | grep -E '^\[[0-9.]+\]' | tail -1)" +cat > $O/.c <<'C' +echo SNAP-BEGIN; cat /proc/uptime; uname -a; cat /proc/cmdline +echo ---pid1; ls -l /proc/1/exe; cat /proc/1/cmdline | tr '\0' ' '; echo; ls -l /sbin/init; cat /etc/alpine-release 2>/dev/null; cat /etc/os-release 2>/dev/null | head -2 +echo ---root; grep -E " / |mmcblk0p27|/alpine" /proc/mounts; df -k / | tail -1; cat /proc/mounts +echo ---stage0; dmesg | grep -E "aurora-stage0|aurora-ro|aurora-bootok|stage0"; cat /var/lib/aurora/boot-attempts 2>/dev/null; cat /var/lib/aurora/stage0-fsck.txt 2>/dev/null | tail -1 +echo ---openrc; rc-status -a 2>/dev/null; rc-status --crashed 2>/dev/null +echo ---ro; for x in /dev/mmcblk0 /dev/mmcblk0p* /dev/mmcblk0boot0 /dev/mmcblk0boot1; do printf "%s=%s " ${x#/dev/} $(blockdev --getro $x); done; echo +echo ---modem; /usr/libexec/aurora/svc/aurora-modem status 2>/dev/null || /etc/init.d/aurora-modem status; grep -E "SAFETY|START OK" /run/aurora-modem/lifecycle.log +echo ---ping; ping -c4 -W3 -I wwan0 77.88.8.8 | tail -2 +echo ---wifi; iw dev; grep -E "AP ENABLED" /run/aurora-wifi/wifi.log; for r in /sys/class/remoteproc/*; do echo "RP $(cat $r/name)=$(cat $r/state)"; done +echo ---display; cat /run/aurora-display/display.log 2>/dev/null | tail -3; cat /sys/class/tty/tty0/active; cat /sys/class/backlight/backlight/bl_power; cat /sys/class/drm/card0-SPI-1/enabled +echo ---charger; grep -q " /sys/kernel/debug " /proc/mounts || mount -t debugfs debugfs /sys/kernel/debug; head -3 /sys/kernel/debug/pm8916_lbc/state +echo ---apk; apk --version 2>/dev/null && apk info 2>/dev/null | wc -l; cat /root/persist-test.txt 2>/dev/null +echo ---procs; ps -o pid,user,args 2>/dev/null | grep -vE "\[|ps -o" | head -40 +echo ---mem; free +echo ---emmc; cat /sys/block/mmcblk0/stat; for p in /sys/class/block/mmcblk0p* /sys/class/block/mmcblk0boot0 /sys/class/block/mmcblk0boot1; do s=$(awk '{print $5}' $p/stat); [ "$s" != 0 ] && echo "$(basename $p) writes=$s"; done +echo SNAP-END +C +tn $O/.c 30 > $O/snapshot.txt; act "snapshot: $(grep -c SNAP-END $O/snapshot.txt) end" +grep -A3 -- "---pid1" $O/snapshot.txt | tail -3 | tee -a $A; grep -E "^/dev/mmcblk0p27 / |^rootfs / " $O/snapshot.txt | tee -a $A; grep -E "writes=" $O/snapshot.txt | tee -a $A +grep -E "aurora-stage0" $O/snapshot.txt | tail -2 | tee -a $A +act "B12R $V$K END" diff --git a/b12/b12r/build-b12r.sh b/b12/b12r/build-b12r.sh new file mode 100644 index 0000000..a005c8e --- /dev/null +++ b/b12/b12r/build-b12r.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# B12R RAM image: kernel 7.2.7-aurora-b12 (= b10b5 production config + nf_tables/NAT, same tree src-b10b5) + prod DTB e47762d1 + B9C cmdline +# + B12Q initramfs (stage0 -> Alpine on p27) unchanged. fastboot boot only. +set -e; cd "$(dirname "$0")"; O=out; KB=/home/q/aurora-kbuild/out-b12; Q=../b12q/out; mkdir -p $O +echo "9ade7cffb1f3f19e86acc8869565a88b960abba543385a2fddcebd27bd529e79 $Q/initramfs-b12q.cpio.gz" | sha256sum -c +echo "e47762d1dcd0af6e2acb711fa47ecc13769cd6bcb81259ce50635e8b18b6514a $Q/aurora-b10b3.dtb" | sha256sum -c +echo "0fef41b262917c8ae308fa222da0017e2b3cb3ec5da1ca8ba18d0318476761a3 $Q/cmdline.txt" | sha256sum -c +for s in NF_TABLES=y NFT_NAT=y NFT_MASQ=y NF_CONNTRACK=y CHARGER_PM8916_LBC=y 'LOCALVERSION="-aurora-b12"'; do grep -q "^CONFIG_$s\$" $KB/.config || { echo "STOP $s"; exit 1; }; done +cp $KB/arch/arm64/boot/Image.gz $O/Image-b12.gz; cp $KB/.config $O/config-b12; cp $Q/aurora-b10b3.dtb $Q/initramfs-b12q.cpio.gz $Q/cmdline.txt $O/ +cat $O/Image-b12.gz $O/aurora-b10b3.dtb > $O/Image.gz-dtb +python3 ../../b6p/mkbootimg.py $O/Image.gz-dtb $O/initramfs-b12q.cpio.gz $O/cmdline.txt $O/aurora-b12r.img +(cd $O && sha256sum Image-b12.gz config-b12 aurora-b10b3.dtb Image.gz-dtb initramfs-b12q.cpio.gz cmdline.txt aurora-b12r.img > SHA256SUMS; cat SHA256SUMS) diff --git a/b12/b12r/cache/extlinux-b12r.conf b/b12/b12r/cache/extlinux-b12r.conf new file mode 100644 index 0000000..b528989 --- /dev/null +++ b/b12/b12r/cache/extlinux-b12r.conf @@ -0,0 +1,6 @@ +default b12r +label b12r + linux /Image.gz-dtb + fdt /aurora-b10b3.dtb + initrd /initramfs-b12q.cpio.gz + append earlycon console=tty0 console=ttyMSM0,115200n8 ignore_loglevel loglevel=8 rdinit=/init fbcon=font:6x8 consoleblank=0 diff --git a/b12/b12r/cache/mkfs-cache-b12r.sh b/b12/b12r/cache/mkfs-cache-b12r.sh new file mode 100644 index 0000000..76cbfd7 --- /dev/null +++ b/b12/b12r/cache/mkfs-cache-b12r.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# B12R cache CANDIDATE (= B12Q + kernel 7.2.7-aurora-b12 with nf_tables for the Alpine router) (NOT for writing without a separate GO): exact recipe of b10/b10b3/cache-v4/mkfs-cache-b10b5.sh (c3732515); +# differs only in the initrd (B9C initramfs + B12P overlay: aurora-alpine autostart, modem safety gate, e2fsck bundle), file names, +# extlinux label, fs label and UUIDs. Kernel/DTB/append = B10 production unchanged. Runs on 480s (mke2fs 1.47.2). Offline only. +# The image contains the AP PSK (in the initramfs) - never publish it. +set -e +cd ~/doc/modem/jz08-aurora/b12/b12r/cache +OUT=${1:-cache-extlinux-b12r.img}; F=../out +echo "0fef41b262917c8ae308fa222da0017e2b3cb3ec5da1ca8ba18d0318476761a3 $F/cmdline.txt" | sha256sum -c +echo "19a53b6f4f6689fe20da8f2fc87f1f683b5b8c086537ef94ed8818ed47fd7d05 $F/Image.gz-dtb" | sha256sum -c +echo "e47762d1dcd0af6e2acb711fa47ecc13769cd6bcb81259ce50635e8b18b6514a $F/aurora-b10b3.dtb" | sha256sum -c +echo "9ade7cffb1f3f19e86acc8869565a88b960abba543385a2fddcebd27bd529e79 $F/initramfs-b12q.cpio.gz" | sha256sum -c +ST=$(mktemp -d); mkdir -p $ST/extlinux +cp $F/Image.gz-dtb $F/aurora-b10b3.dtb $F/initramfs-b12q.cpio.gz $ST/ +printf "default b12r\nlabel b12r\n\tlinux /Image.gz-dtb\n\tfdt /aurora-b10b3.dtb\n\tinitrd /initramfs-b12q.cpio.gz\n\tappend %s\n" "$(cat $F/cmdline.txt)" > $ST/extlinux/extlinux.conf +cp $ST/extlinux/extlinux.conf extlinux-b12r.conf +chmod 0644 $ST/*.* $ST/extlinux/extlinux.conf; chmod 0755 $ST $ST/extlinux +find $ST -exec touch -h -d @1790726400 {} + +rm -f $OUT; truncate -s 134217728 $OUT +E2FSPROGS_FAKE_TIME=1790726400 /sbin/mke2fs -F -q -t ext2 -O none,filetype,sparse_super \ + -b 4096 -I 128 -N 64 -m 0 -L aurora-b12r -U 4a5a3038-b6f0-4000-8000-0000b12b1400 \ + -E root_owner=0:0,hash_seed=4a5a3038-b6f0-4000-8000-0000b12b1401 -d $ST $OUT 32768 +rm -rf $ST +for f in / /lost+found /extlinux /extlinux/extlinux.conf /Image.gz-dtb /aurora-b10b3.dtb /initramfs-b12q.cpio.gz; do for t in atime ctime mtime; do echo "sif $f $t @1790726400"; done; done > /tmp/b12r-sif.$$ +/sbin/debugfs -w -f /tmp/b12r-sif.$$ $OUT >/dev/null 2>&1; rm -f /tmp/b12r-sif.$$ +/sbin/e2fsck -fn $OUT >/dev/null 2>&1 && echo "e2fsck -fn clean" +stat -c "%n %s" $OUT; sha256sum $OUT diff --git a/b12/b12r/install-p27.sh b/b12/b12r/install-p27.sh new file mode 100644 index 0000000..ea7a922 --- /dev/null +++ b/b12/b12r/install-p27.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# B12R (board, Alpine primary root): install nftables + dnsmasq from a local repo (no network) + aurora-router. Writes only p27. Idempotent. +set -e; T=/tmp/b12r +rm -rf /var/cache/b12r; mkdir -p /var/cache/b12r; tar xzf $T/b12r-pkgs.tar.gz -C /var/cache/b12r +apk add --no-network --allow-untrusted --repositories-file /dev/null --repository /var/cache/b12r/pkgs nftables dnsmasq > $T/apk.log 2>&1 || { tail -15 $T/apk.log; echo "INSTALL FAIL"; exit 1; } +tail -2 $T/apk.log; tar xzf $T/b12r-overlay.tar.gz -C /; rm -rf /var/cache/b12r; sync +rc-update show default | grep -q aurora-router && echo "WARN: aurora-router is in a runlevel" +echo "nft=$(nft --version) dnsmasq=$(dnsmasq --version | head -1 | cut -d' ' -f3)"; echo "INSTALL OK" diff --git a/b12/b12r/overlay/etc/aurora/router.conf b/b12/b12r/overlay/etc/aurora/router.conf new file mode 100644 index 0000000..954fa4f --- /dev/null +++ b/b12/b12r/overlay/etc/aurora/router.conf @@ -0,0 +1,10 @@ +# Aurora router (B12R). Read by /usr/sbin/aurora-router. Wi-Fi LAN -> NAT -> LTE; USB NCM management stays isolated. +LAN_IF=wlan0 +LAN_ADDR=192.168.77.1 +LAN_PFX=24 +LAN_NET=192.168.77.0/24 +DHCP_RANGE=192.168.77.100,192.168.77.199,255.255.255.0,1h +WAN_IF=wwan0 +MGMT_IF=usb0 +MGMT_NET=172.16.42.0/24 +FALLBACK_DNS="77.88.8.8" # only if /run/aurora-modem/ipcfg has no bearer DNS diff --git a/b12/b12r/overlay/etc/init.d/aurora-router b/b12/b12r/overlay/etc/init.d/aurora-router new file mode 100644 index 0000000..6ac8230 --- /dev/null +++ b/b12/b12r/overlay/etc/init.d/aurora-router @@ -0,0 +1,13 @@ +#!/sbin/openrc-run +# B12R: Wi-Fi LAN router on top of aurora-modem (bearer) + aurora-wifi (AP). Waits for both in the background, then 'aurora-router up'. +# NOT in any runlevel until the cache kernel has nf_tables (7.2.7-aurora-b12); start manually: rc-service aurora-router start +description="Aurora Wi-Fi -> LTE router (nftables NAT + dnsmasq)" +depend() { need aurora-modem aurora-wifi; } +start() { + ebegin "aurora-router (waits for START OK + AP ENABLED)" + ( i=0; while [ $i -lt 600 ]; do grep -q "=== START OK" /run/aurora-modem/lifecycle.log 2>/dev/null && grep -q "=== AP ENABLED" /run/aurora-wifi/wifi.log 2>/dev/null && break; sleep 2; i=$((i+2)); done + /usr/sbin/aurora-router up >> /run/aurora-router.service.log 2>&1 ) & + eend 0 +} +stop() { ebegin "aurora-router down"; /usr/sbin/aurora-router down >> /run/aurora-router.service.log 2>&1; eend $?; } +status() { /usr/sbin/aurora-router status; } diff --git a/b12/b12r/overlay/usr/sbin/aurora-router b/b12/b12r/overlay/usr/sbin/aurora-router new file mode 100644 index 0000000..9842644 --- /dev/null +++ b/b12/b12r/overlay/usr/sbin/aurora-router @@ -0,0 +1,91 @@ +#!/bin/sh +# Aurora router (B12R, runtime state only in /run): aurora-router up | down | status +# up: wlan0 192.168.77.1/24, default route via wwan0 (only if none), nftables table inet aurora_router (NAT LAN->wwan0, default-deny forward, +# no LTE->LAN, Wi-Fi blocked from the USB mgmt subnet and from every board address except 192.168.77.1 DHCP/DNS/ping), ip_forward=1, +# dnsmasq bound to wlan0 only (upstream = bearer DNS). +# down: undo exactly that. Needs kernel nf_tables (7.2.7-aurora-b12). +. /etc/aurora/router.conf +S=/run/aurora-router; LOG=$S/router.log; mkdir -p $S +now() { cut -d' ' -f1 /proc/uptime; } +log() { m="[$(now)] $*"; echo "$m"; echo "$m" >> $LOG; echo "<5>[aurora-router] $*" > /dev/kmsg 2>/dev/null; } +fail() { log "FAIL: $*"; exit 1; } +dpid() { [ -f $S/dnsmasq.pid ] && kill -0 "$(cat $S/dnsmasq.pid)" 2>/dev/null && cat $S/dnsmasq.pid; } +case "$1" in +up) + log "=== ROUTER UP" + nft list tables >/dev/null 2>&1 || fail "kernel has no nf_tables ($(uname -r))" + [ -e /sys/class/net/$LAN_IF ] || fail "$LAN_IF absent"; [ -e /sys/class/net/$WAN_IF ] || fail "$WAN_IF absent" + DNS=$(sed -n 's/^DNS=//p' /run/aurora-modem/ipcfg 2>/dev/null | tr ',' ' '); [ -n "$DNS" ] || DNS=$FALLBACK_DNS + [ -f $S/ip_forward.saved ] || cat /proc/sys/net/ipv4/ip_forward > $S/ip_forward.saved + ip -4 addr show dev $LAN_IF | grep -q "inet $LAN_ADDR/" || ip addr add $LAN_ADDR/$LAN_PFX dev $LAN_IF || fail "addr $LAN_IF" + if ! ip route | grep -q "^default"; then ip route add default dev $WAN_IF && echo $WAN_IF > $S/default.added || fail "default route"; fi + cat > $S/ruleset.nft < USB mgmt blocked" + iifname "$MGMT_IF" oifname "$LAN_IF" counter drop + counter drop comment "default deny (incl. LTE -> LAN)" + } + chain postrouting { + type nat hook postrouting priority srcnat; policy accept; + oifname "$WAN_IF" ip saddr $LAN_NET counter masquerade + } +} +N + nft -f $S/ruleset.nft || fail "nft ruleset" + echo 1 > /proc/sys/net/ipv4/ip_forward + if [ -z "$(dpid)" ]; then + { echo "interface=$LAN_IF"; echo "except-interface=lo"; echo "bind-interfaces"; echo "no-resolv"; echo "no-hosts" + for d in $DNS; do echo "server=$d"; done + echo "dhcp-range=$DHCP_RANGE"; echo "dhcp-option=option:router,$LAN_ADDR"; echo "dhcp-option=option:dns-server,$LAN_ADDR" + echo "dhcp-leasefile=$S/dnsmasq.leases"; echo "dhcp-authoritative"; echo "cache-size=300"; echo "domain-needed"; echo "bogus-priv" + echo "pid-file=$S/dnsmasq.pid"; echo "log-facility=$S/dnsmasq.log"; echo "log-dhcp"; echo "user=root"; } > $S/dnsmasq.conf + dnsmasq --conf-file=$S/dnsmasq.conf || fail "dnsmasq"; sleep 1 + fi + [ -n "$(dpid)" ] || fail "dnsmasq not running" + log "=== ROUTER UP OK lan $LAN_IF $LAN_ADDR/$LAN_PFX dhcp $DHCP_RANGE dns [$DNS] wan $WAN_IF default=[$(ip route | grep "^default")] fwd=$(cat /proc/sys/net/ipv4/ip_forward) dnsmasq=$(dpid)";; +down) + log "=== ROUTER DOWN" + p=$(dpid); [ -n "$p" ] && { kill $p; sleep 1; }; rm -f $S/dnsmasq.pid + nft list table inet aurora_router >/dev/null 2>&1 && nft delete table inet aurora_router + [ -f $S/ip_forward.saved ] && { cat $S/ip_forward.saved > /proc/sys/net/ipv4/ip_forward; rm -f $S/ip_forward.saved; } + ip addr del $LAN_ADDR/$LAN_PFX dev $LAN_IF 2>/dev/null + [ -f $S/default.added ] && { ip route del default dev "$(cat $S/default.added)" 2>/dev/null; rm -f $S/default.added; } + log "=== ROUTER DOWN OK fwd=$(cat /proc/sys/net/ipv4/ip_forward) tables=[$(nft list tables 2>/dev/null | tr '\n' ' ')] default=[$(ip route | grep "^default")]";; +status) + echo "kernel $(uname -r) ip_forward=$(cat /proc/sys/net/ipv4/ip_forward) dnsmasq=$(dpid)" + ip -4 addr show dev $LAN_IF | grep inet; ip route | grep "^default" + echo "-- leases"; cat $S/dnsmasq.leases 2>/dev/null + echo "-- stations"; iw dev $LAN_IF station dump 2>/dev/null | grep -E "^Station|signal:|connected time" + echo "-- conntrack $(cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null)" + nft list table inet aurora_router 2>&1 | grep -E "counter|chain" + tail -3 $LOG 2>/dev/null;; +*) echo "usage: $0 up|down|status"; exit 2;; +esac diff --git a/b12/mk-alpine-rootfs.sh b/b12/mk-alpine-rootfs.sh new file mode 100644 index 0000000..f24ee70 --- /dev/null +++ b/b12/mk-alpine-rootfs.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# Build the B12/B13 Alpine 3.24.2 aarch64 chroot tarball (RAM-only use on the board: /tmp/alpine tmpfs). +# minirootfs (sha-checked) + apk add X stack + prune of mesa/LLVM (Xorg does not link them; fbdev/modesetting w/o glamor) + overlay. +# Ownership/modes are preserved through fakeroot state. -> out/alpine-b13.tar.gz +set -e; cd "$(dirname "$0")"; A=$PWD/dl/apkst/sbin/apk.static; C=$PWD/dl/apkcache; W=${TMPDIR:-/tmp}/b12-alp.$$; R=$W/root; FS=$W/fakeroot.state +PKGS="xorg-server xf86-video-fbdev x11vnc xterm twm font-misc-misc xsetroot xdpyinfo e2fsprogs" +echo "9bf70a7f18ea44094cbb5f70c58f9af129c8214745743db0e68e5502cc2ce773 dl/alpine-minirootfs-3.24.2-aarch64.tar.gz" | sha256sum -c +mkdir -p $R out; touch $FS +fakeroot -s $FS tar xzf dl/alpine-minirootfs-3.24.2-aarch64.tar.gz -C $R +fakeroot -i $FS -s $FS $A --root $R --arch aarch64 --keys-dir $R/etc/apk/keys --cache-dir $C --no-scripts add $PKGS > $W/apk.log +tail -1 $W/apk.log; grep Installing $W/apk.log | sed 's/.*Installing //' > out/alpine-b13.pkgs +# prune (documented: apk db still lists mesa/llvm/spirv; files removed to fit RAM) +P="usr/lib/libLLVM* usr/lib/libgallium-* usr/lib/libSPIRV-Tools* usr/lib/gbm usr/lib/dri usr/lib/xorg/modules/libglamoregl.so usr/lib/xorg/modules/extensions/libglx.so usr/share/hwdata" +(cd $R && for p in $P; do ls -d $p 2>/dev/null; done) > out/alpine-b13.pruned +(cd $R && fakeroot -i $FS -s $FS rm -rf $P) +fakeroot -i $FS -s $FS cp -a alpine/overlay/. $R/ +fakeroot -i $FS -s $FS sh -c "chown -R 0:0 $R/etc/X11/xorg.conf $R/etc/X11/xorg-modesetting.conf $R/usr/local/bin/aurora-x" +echo "aurora-b13-chroot" > $R/etc/hostname +# all NEEDED of Xorg, its modules and the X clients must still resolve (catch over-pruning) +for f in $R/usr/libexec/Xorg $R/usr/lib/xorg/modules/*.so $R/usr/lib/xorg/modules/*/*.so $R/usr/bin/x11vnc $R/usr/bin/xterm $R/usr/bin/twm; do aarch64-linux-gnu-readelf -d $f | sed -n 's/.*NEEDED.*\[\(.*\)\]/\1/p'; done | sort -u | while read n; do [ -e $R/usr/lib/$n ] || [ -e $R/lib/$n ] || { echo "UNRESOLVED $n"; exit 1; }; done +du -sm $R | awk '{print "rootfs MiB:", $1}' +(cd $R && fakeroot -i $FS tar --numeric-owner -czf - .) > out/alpine-b13.tar.gz; rm -rf $W +sha256sum out/alpine-b13.tar.gz; ls -l out/alpine-b13.tar.gz diff --git a/b12/mk-e2fs-bundle.sh b/b12/mk-e2fs-bundle.sh new file mode 100644 index 0000000..24f4192 --- /dev/null +++ b/b12/mk-e2fs-bundle.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Host-side e2fsprogs bundle (mke2fs/e2fsck/tune2fs/dumpe2fs from Alpine 3.24 aarch64, own musl loader) -> out/e2fs-bundle.tar.gz +# Board: /tmp/aurora-e2fs (provisioning) or initramfs /usr/libexec/aurora/e2fs (boot fsck). Run: $B/lib/ld-musl-aarch64.so.1 --library-path $B/lib $B/sbin/X +set -e; cd "$(dirname "$0")"; A=$PWD/dl/apkst/sbin/apk.static; W=${TMPDIR:-/tmp}/b12-e2.$$; R=$W/root; C=$PWD/dl/apkcache +mkdir -p $R/etc/apk out; tar xzf dl/alpine-minirootfs-3.24.2-aarch64.tar.gz -C $W ./etc/apk/keys ./etc/apk/repositories 2>/dev/null; cp -a $W/etc/apk/keys $W/etc/apk/repositories $R/etc/apk/ +$A --root $R --arch aarch64 --keys-dir $R/etc/apk/keys --cache-dir $C --usermode --initdb --no-scripts add e2fsprogs > $W/apk.log +grep Installing $W/apk.log | sed 's/.*Installing //' > out/e2fs-bundle.pkgs +T=$W/aurora-e2fs; mkdir -p $T/lib $T/sbin; cp -L $R/lib/ld-musl-aarch64.so.1 $T/lib/ +for b in mke2fs e2fsck; do cp -L $R/sbin/$b $T/sbin/ 2>/dev/null || cp -L $R/usr/sbin/$b $T/sbin/; done +need() { for f in "$@"; do aarch64-linux-gnu-readelf -d $f | sed -n 's/.*NEEDED.*\[\(.*\)\]/\1/p'; done | sort -u | grep -v '^libc.musl'; } +for i in 1 2 3; do for n in $(need $T/sbin/* $T/lib/*.so* 2>/dev/null); do [ -e $T/lib/$n ] || cp -L $(find $R/lib $R/usr/lib -name $n | head -1) $T/lib/; done; done +for n in $(need $T/sbin/* $T/lib/*.so*); do [ -e $T/lib/$n ] || { echo "UNRESOLVED $n"; exit 1; }; done +(cd $W && tar --owner=0 --group=0 --numeric-owner -czf - aurora-e2fs) > out/e2fs-bundle.tar.gz +ls $T/lib | tr '\n' ' '; echo; rm -rf $W; sha256sum out/e2fs-bundle.tar.gz diff --git a/b12/mk-router-bundle.sh b/b12/mk-router-bundle.sh new file mode 100644 index 0000000..de6526c --- /dev/null +++ b/b12/mk-router-bundle.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# Build the B12 host-side router bundle (nft + dnsmasq from Alpine 3.24 aarch64, own musl loader) -> out/router-bundle.tar.gz +# Board: extracted to /tmp/aurora-router; binaries run as $B/lib/ld-musl-aarch64.so.1 --library-path $B/lib $B/sbin/X (host musl untouched). +set -e; cd "$(dirname "$0")"; A=$PWD/dl/apkst/sbin/apk.static; W=${TMPDIR:-/tmp}/b12-rt.$$; R=$W/root; C=$PWD/dl/apkcache +mkdir -p $R/etc/apk $C out; tar xzf dl/alpine-minirootfs-3.24.2-aarch64.tar.gz -C $W ./etc/apk/keys ./etc/apk/repositories 2>/dev/null; cp -a $W/etc/apk/keys $W/etc/apk/repositories $R/etc/apk/ +$A --root $R --arch aarch64 --keys-dir $R/etc/apk/keys --cache-dir $C --usermode --initdb --no-scripts add nftables dnsmasq > $W/apk.log +grep -E "Installing" $W/apk.log | sed 's/.*Installing //' > out/router-bundle.pkgs +T=$W/aurora-router; mkdir -p $T/lib $T/sbin +cp -L $R/lib/ld-musl-aarch64.so.1 $T/lib/ +for l in libnftables.so.1 libnftnl.so.11 libmnl.so.0 libjansson.so.4 libgmp.so.10 libreadline.so.8 libncursesw.so.6; do f=$(find $R/usr/lib $R/lib -name "$l" | head -1); [ -n "$f" ] || { echo "missing $l"; exit 1; }; cp -L $f $T/lib/; done +cp $R/usr/sbin/nft $R/usr/sbin/dnsmasq $T/sbin/ +cp router/router.conf router/router-common.sh router/router-up.sh router/router-down.sh router/router-status.sh $T/ +# every NEEDED must be in lib/ +for f in $T/sbin/* $T/lib/*.so*; do aarch64-linux-gnu-readelf -d $f 2>/dev/null | sed -n 's/.*NEEDED.*\[\(.*\)\]/\1/p'; done | sort -u | while read n; do [ "$n" = libc.musl-aarch64.so.1 ] || [ -e $T/lib/$n ] || { echo "UNRESOLVED $n"; exit 1; }; done +(cd $W && tar --owner=0 --group=0 --numeric-owner -czf - aurora-router) > out/router-bundle.tar.gz +(cd $T && find . -type f | sort | xargs sha256sum) > out/router-bundle.files; rm -rf $W +sha256sum out/router-bundle.tar.gz; cat out/router-bundle.pkgs | tr '\n' ' '; echo diff --git a/b12/persist/alpine-provision.sh b/b12/persist/alpine-provision.sh new file mode 100644 index 0000000..0fd16b2 --- /dev/null +++ b/b12/persist/alpine-provision.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# B12P-1 (board, ONE-TIME, DESTRUCTIVE to userdata only): format eMMC userdata as ext4 'aurora-alpine' and install the Alpine rootfs. +# alpine-provision.sh /tmp/b12/alpine-b13.tar.gz YES-FORMAT-USERDATA +# Gates: PARTNAME=userdata, expected start/size (S2 GPT), only this partition is switched RW and back to RO at the end. +TAR=$1; [ "$2" = YES-FORMAT-USERDATA ] || { echo "usage: $0 TAR YES-FORMAT-USERDATA"; exit 2; } +E=/tmp/aurora-e2fs; M=/mnt/aurora-alpine; LABEL=aurora-alpine +e2() { _b=$1; shift; $E/lib/ld-musl-aarch64.so.1 --library-path $E/lib $E/sbin/$_b "$@"; } +log() { echo "[$(cut -d' ' -f1 /proc/uptime)] P1 $*"; } +relock() { [ -n "$D" ] && blockdev --setro $D; blockdev --setro /dev/mmcblk0; } +fail() { log "FAIL: $*"; grep -q " $M " /proc/mounts && umount $M; relock; echo "PROVISION FAIL"; exit 1; } +others_ro() { for x in /dev/mmcblk0p*; do [ $x = $D ] && continue; [ "$(blockdev --getro $x)" = 1 ] || return 1; done; for x in /dev/mmcblk0boot*; do [ "$(blockdev --getro $x)" = 1 ] || return 1; done; } +D=; for p in /sys/class/block/mmcblk0p*; do [ "$(sed -n 's/^PARTNAME=//p' $p/uevent)" = userdata ] && D=/dev/$(basename $p); done +[ "$D" = /dev/mmcblk0p27 ] || fail "userdata is '$D', expected /dev/mmcblk0p27" +P=/sys/class/block/mmcblk0p27; [ "$(cat $P/start)" = 2428000 ] && [ "$(cat $P/size)" = 5206911 ] || fail "p27 geometry $(cat $P/start)/$(cat $P/size) unexpected" +grep -q "^/dev/mmcblk0" /proc/mounts && grep "^/dev/mmcblk0" /proc/mounts | grep -qv "^/dev/mmcblk0p1 " && fail "unexpected eMMC mount: $(grep ^/dev/mmcblk0 /proc/mounts)" +[ -f "$TAR" ] && [ -x $E/sbin/mke2fs ] || fail "missing $TAR or $E" +log "pre: stat=$(cat /sys/block/mmcblk0/stat | awk '{print "w="$5" ws="$7}') ro: disk=$(blockdev --getro /dev/mmcblk0) p27=$(blockdev --getro $D) p25=$(blockdev --getro /dev/mmcblk0p25)" +# disk-level RO masks partition RW: lift it on the disk, every other partition keeps its own RO flag (checked), RW only on p27 +others_ro || fail "some other partition is not RO before start" +blockdev --setrw /dev/mmcblk0 && blockdev --setrw $D || fail "setrw" +[ "$(blockdev --getro $D)" = 0 ] || fail "p27 still RO" +others_ro || fail "other devices lost RO" +log "rw only on $D (disk flag lifted, p1..p26 + boot0/1 RO verified)" +e2 mke2fs -F -q -t ext4 -L $LABEL -m 1 -E nodiscard -O ^metadata_csum_seed $D || fail "mke2fs" +log "mke2fs ok" +mkdir -p $M; mount -t ext4 -o noatime $D $M || fail "mount" +tar xzf "$TAR" -C $M || fail "extract" +date -u +"provisioned %Y-%m-%dT%H:%M:%SZ from $(basename $TAR) $(sha256sum $TAR | cut -c1-8)" > $M/etc/aurora-alpine-release +sync; log "extracted: $(du -sk $M | cut -f1)k files=$(find $M -xdev | wc -l)" +umount $M || fail "umount" +e2 e2fsck -fn $D > /tmp/b12/p1-fsck.txt 2>&1; rc=$?; log "e2fsck -fn rc=$rc $(tail -1 /tmp/b12/p1-fsck.txt)" +relock; others_ro || log "WARN others_ro"; log "post: disk ro=$(blockdev --getro /dev/mmcblk0) p27 ro=$(blockdev --getro $D) stat=$(cat /sys/block/mmcblk0/stat | awk '{print "w="$5" ws="$7}')" +mount -t ext4 -o ro $D $M && { log "ro-mount ok: $(cat $M/etc/alpine-release) $(cat $M/etc/aurora-alpine-release)"; ls $M/usr/libexec/Xorg >/dev/null && log "Xorg present"; umount $M; } +[ $rc = 0 ] && echo "PROVISION OK" || echo "PROVISION FAIL fsck" diff --git a/b12/pull.sh b/b12/pull.sh new file mode 100644 index 0000000..2194165 --- /dev/null +++ b/b12/pull.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# 480s: fetch a small file from the board over NCM (port 9962). pull.sh BOARD_PATH LOCAL_PATH +cd "$(dirname "$0")"; ./tn.sh 2 "nc -l -p 9962 < $1 &" > /dev/null; sleep 1; ncat --recv-only 172.16.42.1 9962 > "$2"; ls -l "$2" diff --git a/b12/push.sh b/b12/push.sh new file mode 100644 index 0000000..0e1478d --- /dev/null +++ b/b12/push.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# 480s: push FILE to the board as DEST over NCM in 4 MiB chunks, each sha256-verified (retry up to 3x). Port 9943. +F=$1; DEST=$2; [ -f "$F" ] && [ -n "$DEST" ] || { echo "usage: $0 FILE DEST"; exit 2; } +cd "$(dirname "$0")"; W=$(mktemp -d); split -b 4M -d -a 3 "$F" $W/p. +./tn.sh 2 "rm -f $DEST.part.*" > /dev/null +for p in $W/p.*; do n=${p##*.}; h=$(sha256sum < $p | cut -c1-64); ok= + for t in 1 2 3; do + ./tn.sh 2 "nc -l -p 9943 > $DEST.part.$n &" > /dev/null; sleep 1 + timeout 60 ncat --send-only 172.16.42.1 9943 < $p + r=$(./tn.sh 3 "sha256sum $DEST.part.$n" | grep -o "^[0-9a-f]\{64\}") + [ "$r" = "$h" ] && { ok=1; break; }; echo "chunk $n try $t mismatch (${r:-none})"; ./tn.sh 2 "pkill -f 'nc -l -p 9943'" >/dev/null + done; [ -n "$ok" ] || { echo "PUSH FAIL chunk $n"; rm -rf $W; exit 1; } +done +H=$(sha256sum < "$F" | cut -c1-64) +r=$(./tn.sh 5 "cat $DEST.part.* > $DEST && rm -f $DEST.part.* && sha256sum $DEST" | grep -o "^[0-9a-f]\{64\}") +rm -rf $W; [ "$r" = "$H" ] && echo "PUSH OK $DEST $H" || { echo "PUSH FAIL final ($r)"; exit 1; } diff --git a/b12/router/router-common.sh b/b12/router/router-common.sh new file mode 100644 index 0000000..043c813 --- /dev/null +++ b/b12/router/router-common.sh @@ -0,0 +1,7 @@ +# shellcheck shell=sh +. "$(dirname "$0")/router.conf" +now() { cut -d' ' -f1 /proc/uptime; } +log() { m="[$(now)] $*"; echo "$m"; mkdir -p $S; echo "$m" >> $S/router.log; } +bx() { _b=$1; shift; "$B/lib/ld-musl-aarch64.so.1" --library-path "$B/lib" "$B/sbin/$_b" "$@"; } +nft() { bx nft "$@"; } +dnsmasq_pid() { [ -f $S/dnsmasq.pid ] && kill -0 "$(cat $S/dnsmasq.pid)" 2>/dev/null && cat $S/dnsmasq.pid; } diff --git a/b12/router/router-down.sh b/b12/router/router-down.sh new file mode 100644 index 0000000..a6c3d31 --- /dev/null +++ b/b12/router/router-down.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# B12 router-down: undo exactly what router-up did (dnsmasq, nft table, ip_forward, LAN addr, default route if we added it). +. "$(dirname "$0")/router-common.sh" +log "=== ROUTER DOWN" +p=$(dnsmasq_pid); [ -n "$p" ] && kill "$p" && sleep 1; [ -n "$(dnsmasq_pid)" ] && kill -9 "$(cat $S/dnsmasq.pid)"; rm -f $S/dnsmasq.pid +nft list table inet aurora_router >/dev/null 2>&1 && nft delete table inet aurora_router +F=0; [ -f $S/sysctl.saved ] && F=$(sed -n 's/^ip_forward=//p' $S/sysctl.saved); echo "${F:-0}" > /proc/sys/net/ipv4/ip_forward; rm -f $S/sysctl.saved +ip addr del $LAN_ADDR/$LAN_PFX dev $LAN_IF 2>/dev/null +[ -f $S/default.added ] && { ip route del default dev "$(cat $S/default.added)" 2>/dev/null; rm -f $S/default.added; } +log "down: fwd=$(cat /proc/sys/net/ipv4/ip_forward) tables=[$(nft list tables 2>/dev/null | tr '\n' ' ')] default=[$(ip route show default)]" +echo "ROUTER-DOWN OK" diff --git a/b12/router/router-status.sh b/b12/router/router-status.sh new file mode 100644 index 0000000..09f82f9 --- /dev/null +++ b/b12/router/router-status.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# B12 router-status: read-only. +. "$(dirname "$0")/router-common.sh" +echo "== kernel $(uname -r) ip_forward=$(cat /proc/sys/net/ipv4/ip_forward)" +echo "== addrs"; ip -4 addr show | grep -E "^[0-9]+:|inet " +echo "== routes"; ip route +echo "== dnsmasq pid=$(dnsmasq_pid)"; [ -f $S/dnsmasq.leases ] && cat $S/dnsmasq.leases +echo "== stations"; iw dev $LAN_IF station dump 2>/dev/null | grep -E "^Station|signal:|rx bytes|tx bytes|connected time" +echo "== conntrack $(cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null)/$(cat /proc/sys/net/netfilter/nf_conntrack_max 2>/dev/null)" +[ -r /proc/net/nf_conntrack ] && grep -c . /proc/net/nf_conntrack +echo "== nft"; nft list table inet aurora_router 2>&1 +echo "== log"; tail -5 $S/router.log 2>/dev/null diff --git a/b12/router/router-up.sh b/b12/router/router-up.sh new file mode 100644 index 0000000..6eecef4 --- /dev/null +++ b/b12/router/router-up.sh @@ -0,0 +1,77 @@ +#!/bin/sh +# B12 router-up (runtime-only, nothing persistent): Wi-Fi LAN -> DHCP/DNS (dnsmasq, LAN only) -> nftables NAT -> wwan0. +# USB NCM management (usb0) untouched; Wi-Fi clients cannot reach the board except DHCP/DNS/ping, nor the mgmt subnet. +. "$(dirname "$0")/router-common.sh" +set -u +fail() { log "FAIL: $*"; echo "ROUTER-UP FAIL"; exit 1; } +log "=== ROUTER UP" +[ -x $B/sbin/nft ] && [ -x $B/sbin/dnsmasq ] || fail "bundle $B missing" +nft list tables >/dev/null 2>&1 || fail "kernel has no nf_tables (needs 7.2.7-aurora-b12)" +[ -e /sys/class/net/$LAN_IF ] || fail "$LAN_IF absent" +[ -e /sys/class/net/$WAN_IF ] || fail "$WAN_IF absent" +DNS=; [ -f /run/aurora-modem/ipcfg ] && DNS=$(sed -n 's/^DNS=//p' /run/aurora-modem/ipcfg | tr ',' ' ') +[ -n "$DNS" ] || DNS=$FALLBACK_DNS +mkdir -p $S +# 1. sysctl (save originals once) +[ -f $S/sysctl.saved ] || { echo "ip_forward=$(cat /proc/sys/net/ipv4/ip_forward)" > $S/sysctl.saved; } +# 2. LAN address +ip -4 addr show dev $LAN_IF | grep -q "inet $LAN_ADDR/" || ip addr add $LAN_ADDR/$LAN_PFX dev $LAN_IF || fail "addr $LAN_IF" +# 3. default route via LTE (only if none); remember that we added it +if ! ip route show default | grep -q .; then ip route add default dev $WAN_IF && echo $WAN_IF > $S/default.added || fail "default route"; fi +# 4. nftables (own table only; flushed/recreated atomically) +cat > $S/ruleset.nft < USB mgmt blocked" + iifname "$MGMT_IF" oifname "$LAN_IF" counter drop + counter drop comment "default deny (incl. LTE -> LAN)" + } + chain postrouting { + type nat hook postrouting priority srcnat; policy accept; + oifname "$WAN_IF" ip saddr $LAN_NET counter masquerade + } +} +N +nft -f $S/ruleset.nft || fail "nft ruleset" +echo 1 > /proc/sys/net/ipv4/ip_forward +# 5. dnsmasq on LAN only +if [ -z "$(dnsmasq_pid)" ]; then + { echo "interface=$LAN_IF"; echo "except-interface=lo"; echo "bind-interfaces"; echo "no-resolv"; echo "no-hosts" + for d in $DNS; do echo "server=$d"; done + echo "dhcp-range=$DHCP_RANGE"; echo "dhcp-option=option:router,$LAN_ADDR"; echo "dhcp-option=option:dns-server,$LAN_ADDR" + echo "dhcp-leasefile=$S/dnsmasq.leases"; echo "dhcp-authoritative"; echo "cache-size=300"; echo "domain-needed"; echo "bogus-priv" + echo "pid-file=$S/dnsmasq.pid"; echo "log-facility=$S/dnsmasq.log"; echo "log-dhcp"; echo "user=root"; } > $S/dnsmasq.conf + bx dnsmasq --conf-file=$S/dnsmasq.conf || fail "dnsmasq start" + sleep 1 +fi +[ -n "$(dnsmasq_pid)" ] || fail "dnsmasq not running" +log "LAN $LAN_IF $LAN_ADDR/$LAN_PFX dhcp $DHCP_RANGE dns-up [$DNS] wan $WAN_IF fwd=$(cat /proc/sys/net/ipv4/ip_forward) dnsmasq=$(dnsmasq_pid)" +echo "ROUTER-UP OK" diff --git a/b12/router/router.conf b/b12/router/router.conf new file mode 100644 index 0000000..340cdf6 --- /dev/null +++ b/b12/router/router.conf @@ -0,0 +1,12 @@ +# B12 router (runtime-only). Sourced by router-*.sh on the board. +B=/tmp/aurora-router # bundle: lib/ld-musl + libs, sbin/nft, sbin/dnsmasq (Alpine 3.24) +S=/run/aurora-router # state: saved sysctls, pids, leases, log +LAN_IF=wlan0 +LAN_ADDR=192.168.77.1 +LAN_PFX=24 +LAN_NET=192.168.77.0/24 +DHCP_RANGE=192.168.77.100,192.168.77.199,255.255.255.0,1h +WAN_IF=wwan0 +MGMT_IF=usb0 +MGMT_NET=172.16.42.0/24 +FALLBACK_DNS="77.88.8.8" # used only if /run/aurora-modem/ipcfg has no DNS diff --git a/b12/tn.sh b/b12/tn.sh new file mode 100644 index 0000000..0fe1949 --- /dev/null +++ b/b12/tn.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# 480s: run board commands over telnet. tn.sh WAIT 'cmd'... (one command per arg) +W=$1; shift; { sleep 1; for c in "$@"; do printf '%s\r\n' "$c"; done; sleep $W; } | ncat 172.16.42.1 23 | tr -d "\000\r" | sed -n '/# /,$p' diff --git a/b12/vncshot.py b/b12/vncshot.py new file mode 100644 index 0000000..3044131 --- /dev/null +++ b/b12/vncshot.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# Minimal RFB 3.8 client (security None, Raw encoding, 32bpp) -> PPM. Usage: vncshot.py HOST PORT OUT.ppm +import socket, struct, sys +h, p, out = sys.argv[1], int(sys.argv[2]), sys.argv[3] +s = socket.create_connection((h, p), 10) +def rx(n): + b = b'' + while len(b) < n: + c = s.recv(n - len(b)) + if not c: raise SystemExit('eof') + b += c + return b +ver = rx(12); s.sendall(b'RFB 003.008\n') +n = rx(1)[0]; types = rx(n) +if 1 not in types: raise SystemExit('no None auth: %r' % types) +s.sendall(b'\x01'); assert struct.unpack('>I', rx(4))[0] == 0 +s.sendall(b'\x01') +w, hh = struct.unpack('>HH', rx(4)); rx(16); name = rx(struct.unpack('>I', rx(4))[0]) +s.sendall(struct.pack('>BxxxBBBBHHHBBBxxx', 0, 32, 24, 0, 1, 255, 255, 255, 16, 8, 0)) +s.sendall(struct.pack('>BxHi', 2, 1, 0)) +s.sendall(struct.pack('>BBHHHH', 3, 0, 0, 0, w, hh)) +img = bytearray(w * hh * 3) +got = 0 +while got < w * hh: + t = rx(1)[0] + if t != 0: raise SystemExit('msg %d' % t) + rx(1); nr = struct.unpack('>H', rx(2))[0] + for _ in range(nr): + x, y, rw, rh, enc = struct.unpack('>HHHHi', rx(12)) + px = rx(rw * rh * 4) + for j in range(rh): + for i in range(rw): + v = px[(j * rw + i) * 4:(j * rw + i) * 4 + 4] + o = ((y + j) * w + x + i) * 3 + img[o:o + 3] = bytes((v[2], v[1], v[0])) + got += rw * rh +open(out, 'wb').write(b'P6 %d %d 255\n' % (w, hh) + img) +print('server', ver.strip().decode(), 'name', name.decode(), 'size %dx%d' % (w, hh)) diff --git a/b13/b13w-vnc-sway.txt b/b13/b13w-vnc-sway.txt new file mode 100644 index 0000000..af62781 --- /dev/null +++ b/b13/b13w-vnc-sway.txt @@ -0,0 +1,9 @@ +tls TLSv1.3 TLS_AES_256_GCM_SHA384 cert sha256 d4469db93b0b5671 +AUTH FAIL Invalid username or password +security types: [19, 129, 5] None offered: False +vencrypt subtypes: [262] +tls TLSv1.3 TLS_AES_256_GCM_SHA384 cert sha256 d4469db93b0b5671 pinned-match +AUTH OK subtype 262 +desktop WayVNC 128 x 128 +saved sway-vnc.ppm +saved sway-vnc-typed.ppm diff --git a/b13/run/cage-down.sh b/b13/run/cage-down.sh new file mode 100644 index 0000000..a7d2677 --- /dev/null +++ b/b13/run/cage-down.sh @@ -0,0 +1,4 @@ +#!/bin/sh +. /usr/local/lib/b13/run/wl-common.sh +pidof cage >/dev/null && { kill $(pidof cage); i=0; while pidof cage >/dev/null && [ $i -lt 50 ]; do sleep 0.1; i=$((i+1)); done; pidof cage >/dev/null && kill -9 $(pidof cage); } +rm -f $S/cage.pid $S/wayland-display; log "CAGE DOWN vt=$(cat /sys/class/tty/tty0/active)" diff --git a/b13/run/cage-up.sh b/b13/run/cage-up.sh new file mode 100644 index 0000000..cf2ea0b --- /dev/null +++ b/b13/run/cage-up.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# B13W: Cage (wlroots 0.20, Pixman) fullscreen on the ST7735S with the test UI. STOP_X=1 stops X11 first (X11 itself untouched). +. /usr/local/lib/b13/run/wl-common.sh +x_guard; seatd_up; t0=$(now) +setsid cage -d -s -- foot -o font=DejaVu\ Sans\ Mono:pixelsize=9 -o pad=0x0 -o colors.background=000000 -o csd.preferred=none -a b13 sh /usr/local/lib/b13/run/pattern.sh CAGE > $S/cage.log 2>&1 < /dev/null & +echo $! > $S/cage.pid; W=$(wait_socket) || { log "FAIL: cage socket (see $S/cage.log)"; tail -20 $S/cage.log; exit 1; } +echo $W > $S/wayland-display; i=0; while ! pidof foot >/dev/null && [ $i -lt 100 ]; do sleep 0.1; i=$((i+1)); done +log "CAGE UP socket=$W pid=$(pidof cage) foot=$(pidof foot) start=$(awk -v a=$t0 -v b=$(now) 'BEGIN{printf "%.2f", b-a}')s" diff --git a/b13/run/pattern.sh b/b13/run/pattern.sh new file mode 100644 index 0000000..367e20b --- /dev/null +++ b/b13/run/pattern.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# B13W test UI for 128x128 (inside foot): coloured corners (TL red, TR green, BL blue, BR white) + centre label + 1-cell white frame, +# so orientation, crop and colour order are visible at a glance; a typed line (VNC keyboard test) is echoed in green. Arg 1 = label. +L=${1:-WAYLAND}; T= +draw() { + set -- $(stty size 2>/dev/null); R=${1:-16}; C=${2:-21} + printf '\033[?25l\033[0m\033[2J' + cell() { printf '\033[%d;%dH\033[%sm \033[0m' $1 $2 "$3"; } + for c in $(seq 1 $C); do printf '\033[1;%dH\033[47m \033[%d;%dH\033[47m \033[0m' $c $R $c; done + for r in $(seq 1 $R); do printf '\033[%d;1H\033[47m \033[%d;%dH\033[47m \033[0m' $r $r $C; done + cell 2 2 41; cell 2 $((C-2)) 42; cell $((R-1)) 2 44; cell $((R-1)) $((C-2)) 107 + printf '\033[%d;%dH\033[1;93m%s\033[0m' $((R/2-1)) $(( (C-${#L})/2 + 1 )) "$L" + printf '\033[%d;%dH\033[96m%sx%s\033[0m' $((R/2+1)) $(( (C-${#C}-${#R}-1)/2 + 1 )) "$C" "$R" + printf '\033[%d;3H\033[92m%s\033[0m' $((R-3)) "$T" +} +trap draw WINCH +s0=; for i in 1 2 3 4 5 6 7 8 9 10; do s=$(stty size 2>/dev/null); [ -n "$s" ] && [ "$s" = "$s0" ] && break; s0=$s; sleep 0.3; done +draw; stty -echo -icanon 2>/dev/null +while :; do c=$(dd bs=1 count=1 2>/dev/null) || continue; case "$c" in '') T=;; *) T="$T$c";; esac; R=$(stty size | cut -d' ' -f1); printf '\033[%d;3H\033[K\033[92m%s\033[0m' $((R-3)) "$T"; done diff --git a/b13/run/sway-down.sh b/b13/run/sway-down.sh new file mode 100644 index 0000000..13f2f95 --- /dev/null +++ b/b13/run/sway-down.sh @@ -0,0 +1,4 @@ +#!/bin/sh +. /usr/local/lib/b13/run/wl-common.sh +pidof sway >/dev/null && { WAYLAND_DISPLAY=$(cat $S/wayland-display 2>/dev/null) SWAYSOCK=$(ls /run/user/0/sway-ipc.* 2>/dev/null | head -1) swaymsg exit >/dev/null 2>&1; i=0; while pidof sway >/dev/null && [ $i -lt 50 ]; do sleep 0.1; i=$((i+1)); done; pidof sway >/dev/null && kill $(pidof sway); } +rm -f $S/sway.pid $S/wayland-display; log "SWAY DOWN vt=$(cat /sys/class/tty/tty0/active)" diff --git a/b13/run/sway-up.sh b/b13/run/sway-up.sh new file mode 100644 index 0000000..5f28c75 --- /dev/null +++ b/b13/run/sway-up.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# B13W: Sway (debug/remote compositor) with /usr/local/lib/b13/run/sway.conf. STOP_X=1 stops X11 first. +. /usr/local/lib/b13/run/wl-common.sh +x_guard; seatd_up; t0=$(now) +setsid sway -c /usr/local/lib/b13/run/sway.conf > $S/sway.log 2>&1 < /dev/null & +echo $! > $S/sway.pid; W=$(wait_socket) || { log "FAIL: sway socket (see $S/sway.log)"; tail -20 $S/sway.log; exit 1; } +echo $W > $S/wayland-display; i=0; while ! pidof foot >/dev/null && [ $i -lt 100 ]; do sleep 0.1; i=$((i+1)); done +log "SWAY UP socket=$W pid=$(pidof sway) foot=$(pidof foot) start=$(awk -v a=$t0 -v b=$(now) 'BEGIN{printf "%.2f", b-a}')s" diff --git a/b13/run/sway.conf b/b13/run/sway.conf new file mode 100644 index 0000000..219d81c --- /dev/null +++ b/b13/run/sway.conf @@ -0,0 +1,15 @@ +# B13W minimal sway for 128x128: no bar, no wallpaper (no swaybg), no borders/titles, one workspace, fullscreen test client. +xwayland disable +swaybg_command - +default_border none +default_floating_border none +titlebar_padding 1 +titlebar_border_thickness 0 +gaps inner 0 +focus_follows_mouse no +font pango:DejaVu Sans Mono 6 +output * scale 1 +seat * hide_cursor 5000 +workspace_auto_back_and_forth no +for_window [app_id="b13"] fullscreen enable +exec foot -o font=DejaVu\ Sans\ Mono:pixelsize=9 -o pad=0x0 -o colors.background=000000 -o csd.preferred=none -a b13 sh /usr/local/lib/b13/run/pattern.sh SWAY diff --git a/b13/run/wayvnc-down.sh b/b13/run/wayvnc-down.sh new file mode 100644 index 0000000..fa483a3 --- /dev/null +++ b/b13/run/wayvnc-down.sh @@ -0,0 +1,4 @@ +#!/bin/sh +. /usr/local/lib/b13/run/wl-common.sh +pidof wayvnc >/dev/null && { kill $(pidof wayvnc); sleep 1; pidof wayvnc >/dev/null && kill -9 $(pidof wayvnc); } +rm -f $S/wayvnc.pid; log "WAYVNC DOWN" diff --git a/b13/run/wayvnc-up.sh b/b13/run/wayvnc-up.sh new file mode 100644 index 0000000..ca8b659 --- /dev/null +++ b/b13/run/wayvnc-up.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# B13W: wayvnc on the running compositor's physical output (SPI-1). Bind 172.16.42.1 (USB NCM mgmt) only; auth = VeNCrypt TLS + user/password. +# Config /etc/aurora/wayvnc/config (root 600): address, port, enable_auth, username, password, private_key_file, certificate_file. +. /usr/local/lib/b13/run/wl-common.sh +W=$(cat $S/wayland-display 2>/dev/null); [ -n "$W" ] && [ -S $XDG_RUNTIME_DIR/$W ] || { log "FAIL: no compositor running"; exit 1; } +pidof wayvnc >/dev/null && { log "wayvnc already running"; exit 0; } +WAYLAND_DISPLAY=$W setsid wayvnc -C /etc/aurora/wayvnc/config -o SPI-1 -S $S/wayvnc.sock -v > $S/wayvnc.log 2>&1 < /dev/null & +echo $! > $S/wayvnc.pid; sleep 3 +pidof wayvnc >/dev/null || { log "FAIL: wayvnc exited"; tail -15 $S/wayvnc.log; exit 1; } +log "WAYVNC UP on $W pid=$(pidof wayvnc) listen=[$(netstat -ltn | awk '$4 ~ /:5900$/ {printf "%s ", $4}')]" diff --git a/b13/run/wl-common.sh b/b13/run/wl-common.sh new file mode 100644 index 0000000..ee49d9a --- /dev/null +++ b/b13/run/wl-common.sh @@ -0,0 +1,19 @@ +# B13W common environment (sourced). Software rendering only (wlroots Pixman), DRM backend on the ST7735S card0, seatd seat. +# X11 (aurora-x: Xorg fbdev + x11vnc) is never modified; it is only stopped while a compositor holds the display (STOP_X=1). +B13=/usr/local/lib/b13; S=/run/b13; mkdir -p $S +export XDG_RUNTIME_DIR=/run/user/0; mkdir -p -m 0700 $XDG_RUNTIME_DIR +export WLR_RENDERER=pixman WLR_BACKENDS=drm,libinput WLR_DRM_DEVICES=/dev/dri/card0 WLR_LIBINPUT_NO_DEVICES=1 LIBSEAT_BACKEND=seatd +now() { cut -d' ' -f1 /proc/uptime; } +log() { echo "[$(now)] $*" | tee -a $S/b13.log; } +x_guard() { + if pidof Xorg >/dev/null; then + [ "${STOP_X:-0}" = 1 ] || { log "FAIL: Xorg is running (holds the panel); rerun with STOP_X=1"; exit 1; } + log "stopping X11 (aurora-x stop) for the compositor test"; /usr/local/bin/aurora-x stop >> $S/b13.log 2>&1; echo 1 > $S/x-was-running + fi + for c in cage sway; do pidof $c >/dev/null && { log "FAIL: $c already running"; exit 1; }; done +} +seatd_up() { pidof seatd >/dev/null || { seatd -g video > $S/seatd.log 2>&1 & i=0; while [ ! -S /run/seatd.sock ] && [ $i -lt 50 ]; do sleep 0.1; i=$((i+1)); done; }; [ -S /run/seatd.sock ] || { log "FAIL: seatd"; exit 1; }; } +wait_socket() { i=0; while [ $i -lt 300 ]; do for w in $XDG_RUNTIME_DIR/wayland-*; do case $w in *.lock) ;; *) [ -S "$w" ] && { basename $w; return 0; };; esac; done; sleep 0.1; i=$((i+1)); done; return 1; } +# RSS kB and CPU% (utime+stime over N s) of a pid +measure() { p=$1; n=${2:-30}; hz=100; a=$(awk '{print $14+$15}' /proc/$p/stat); t0=$(now); sleep $n; b=$(awk '{print $14+$15}' /proc/$p/stat); t1=$(now) + echo "pid=$p rss_kB=$(awk '/VmRSS/{print $2}' /proc/$p/status) cpu%=$(awk -v a=$a -v b=$b -v t0=$t0 -v t1=$t1 -v hz=$hz 'BEGIN{printf "%.2f", (b-a)/hz/(t1-t0)*100}') over ${n}s"; } diff --git a/b13/vnc-vencrypt.py b/b13/vnc-vencrypt.py new file mode 100644 index 0000000..5b75029 --- /dev/null +++ b/b13/vnc-vencrypt.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# B13W test client: RFB 3.8 + VeNCrypt X509Plain/TLSPlain (wayvnc enable_auth), raw 32bpp screenshot -> PPM, optional key/pointer input. +# vnc-vencrypt.py HOST PORT USER PASSFILE OUT.ppm [TEXT_TO_TYPE] [CERT_SHA256] +import socket, ssl, struct, sys, time, hashlib +h, p, user, pwf, out = sys.argv[1], int(sys.argv[2]), sys.argv[3], sys.argv[4], sys.argv[5] +text = sys.argv[6] if len(sys.argv) > 6 else ''; fp = sys.argv[7].replace(':', '').lower() if len(sys.argv) > 7 else '' +pw = open(pwf).read().strip() +s = socket.create_connection((h, p), 10) +def rx(n, c=None): + c = c or s; b = b'' + while len(b) < n: + d = c.recv(n - len(b)) + if not d: raise SystemExit('EOF') + b += d + return b +ver = rx(12); s.sendall(b'RFB 003.008\n') +n = rx(1)[0] +if n == 0: raise SystemExit('server refused: ' + rx(struct.unpack('>I', rx(4))[0]).decode()) +types = list(rx(n)); print('security types:', types, 'None offered:', 1 in types) +assert 19 in types, 'no VeNCrypt' +s.sendall(b'\x13'); sv = rx(2); s.sendall(b'\x00\x02'); assert rx(1) == b'\x00' +m = rx(1)[0]; subs = [struct.unpack('>I', rx(4))[0] for _ in range(m)]; print('vencrypt subtypes:', subs) +st = 262 if 262 in subs else 259; s.sendall(struct.pack('>I', st)); assert rx(1) == b'\x01' +ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT); ctx.check_hostname = False; ctx.verify_mode = ssl.CERT_NONE +t = ctx.wrap_socket(s); cert = t.getpeercert(binary_form=True) +cfp = hashlib.sha256(cert).hexdigest() if cert else '-'; print('tls', t.version(), t.cipher()[0], 'cert sha256', cfp[:16], 'pinned-match' if fp and cfp == fp else '') +if fp: assert cfp == fp, 'certificate fingerprint mismatch' +s = t +s.sendall(struct.pack('>II', len(user), len(pw)) + user.encode() + pw.encode()) +r = struct.unpack('>I', rx(4))[0] +if r != 0: + try: reason = rx(struct.unpack('>I', rx(4))[0]).decode() + except SystemExit: reason = '' + print('AUTH FAIL', reason); sys.exit(3) +print('AUTH OK subtype', st) +s.sendall(b'\x01'); w, hh = struct.unpack('>HH', rx(4)); rx(16); name = rx(struct.unpack('>I', rx(4))[0]).decode(); print('desktop', name, w, 'x', hh) +s.sendall(struct.pack('>BxxxBBBBHHHBBBxxx', 0, 32, 24, 0, 1, 255, 255, 255, 16, 8, 0)); s.sendall(struct.pack('>BxHi', 2, 1, 0)) +def grab(path): + s.sendall(struct.pack('>BBHHHH', 3, 0, 0, 0, w, hh)); img = bytearray(w * hh * 3); got = 0 + while got < w * hh: + mt = rx(1)[0] + if mt == 2: continue + if mt == 1: rx(5); continue + if mt != 0: raise SystemExit('msg %d' % mt) + rx(1); nr = struct.unpack('>H', rx(2))[0] + for _ in range(nr): + x, y, rw, rh, enc = struct.unpack('>HHHHi', rx(12)) + if enc != 0: continue + px = rx(rw * rh * 4) + for j in range(rh): + for i in range(rw): + v = px[(j*rw+i)*4:(j*rw+i)*4+4]; o = ((y+j)*w + x+i)*3; img[o:o+3] = bytes((v[2], v[1], v[0])) + got += rw * rh + open(path, 'wb').write(b'P6 %d %d 255\n' % (w, hh) + img); print('saved', path) +grab(out) +if text: + s.sendall(struct.pack('>BBHH', 5, 0, 64, 64)); time.sleep(0.2) # pointer to the centre (virtual pointer) + for ch in text: + k = ord(ch); s.sendall(struct.pack('>BBxxI', 4, 1, k)); s.sendall(struct.pack('>BBxxI', 4, 0, k)); time.sleep(0.05) + time.sleep(1.5); grab(out.replace('.ppm', '-typed.ppm')) diff --git a/b14/README.md b/b14/README.md new file mode 100644 index 0000000..517f428 --- /dev/null +++ b/b14/README.md @@ -0,0 +1,19 @@ +# B14 — aurora-ui: 128x128 modem status screen (Cage + SDL2/Wayland) + +``` +DRM/KMS card0 (SPI-1, ST7735S) ← Cage (wlroots 0.20, Pixman) ← aurora-ui (SDL2 2.32.10, video driver wayland, + GL renderer = Mesa llvmpipe via EGL/wl_shm, no GPU) +``` + +- `src/aurora-ui.c` — ~350 lines of C, software-drawn 128x128 ARGB frame (8x16 cp437 font from Linux `lib/fonts/font_8x16.c`, GPL-2.0), + uploaded once per second or on change. Worker thread: modem controller state files every 2 s, `mmcli` + `iw` every 5 s (timeout 3 s). + Main thread: sysfs/procfs every 1 s (wwan0 counters → throughput, default route, B10 LBC supervisor state + VADC VBAT, USB online, + pm8916-thermal). Missing sources show `--`; no SOC is invented (no fuel gauge) — only VBAT. +- `src/Makefile` — build on the board: `apk add build-base sdl2-dev && make && make install` (→ /usr/local/bin/aurora-ui). +- `config/aurora-ui.conf` → /etc/aurora/aurora-ui.conf: `TZ` for the clock, `LIBGL_ALWAYS_SOFTWARE=1`. +- `run/ui-up.sh [--stop-gui]`, `run/ui-stop.sh`, `run/ui-status.sh` → /usr/local/lib/b14/run/. wayvnc: reuse `b13/run/wayvnc-up.sh` + (write the socket name to /run/b13/wayland-display first; 172.16.42.1:5900, TLS + password). +- `run/aurora-ui.openrc` → /etc/init.d/aurora-ui (not enabled). `run/soak.sh` — read-only 1/min observer. + +Runtime packages (Alpine 3.24): sdl2, wayland-libs-egl (SDL dlopens libwayland-egl; without it SDL2 segfaults in Wayland_CreateWindow), +mesa (libEGL/libgallium llvmpipe), cage, seatd. Build only: build-base, sdl2-dev. diff --git a/b14/config/aurora-ui.conf b/b14/config/aurora-ui.conf new file mode 100644 index 0000000..d76e9ae --- /dev/null +++ b/b14/config/aurora-ui.conf @@ -0,0 +1,3 @@ +# B14 aurora-ui launcher settings (sourced by ui-up.sh). +TZ=MSK-3 # POSIX TZ for the clock (UTC+3); system clock itself stays UTC +LIBGL_ALWAYS_SOFTWARE=1 # no GPU/render node: Mesa EGL via zink on lavapipe (mesa-vulkan-swrast) diff --git a/b14/run/aurora-ui.openrc b/b14/run/aurora-ui.openrc new file mode 100644 index 0000000..e32f8e5 --- /dev/null +++ b/b14/run/aurora-ui.openrc @@ -0,0 +1,14 @@ +#!/sbin/openrc-run +# B14: Cage + aurora-ui on the ST7735S (installed as /etc/init.d/aurora-ui; NOT in any runlevel until a separate GO). +# Starts early: needs only the display (fbcon/backlight) and NCM for diagnostics; the modem/Wi-Fi keep starting in parallel — +# aurora-ui itself shows "MODEM STARTING" until the modem controller reports START OK. Boot never depends on the GUI: +# a failing UI just leaves fbcon/getty on tty1. `rc-service aurora-ui stop` returns the console. +description="Aurora 128x128 status UI (Cage + SDL2 Wayland)" +depend() { need aurora-display; after aurora-ncm aurora-ro; } +start() { + ebegin "aurora-ui (Cage + SDL2)" + ( sh /usr/local/lib/b14/run/ui-up.sh --stop-gui >> /run/aurora-ui.service.log 2>&1 ) & + eend 0 +} +stop() { ebegin "aurora-ui stop"; sh /usr/local/lib/b14/run/ui-stop.sh >> /run/aurora-ui.service.log 2>&1; eend 0; } +status() { sh /usr/local/lib/b14/run/ui-status.sh; } diff --git a/b14/run/soak.sh b/b14/run/soak.sh new file mode 100644 index 0000000..db912d0 --- /dev/null +++ b/b14/run/soak.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# B14 soak observer (read-only): one line per minute for N minutes -> /run/aurora-ui/soak.log +N=${1:-15}; O=/run/aurora-ui/soak.log; hz=100 +dm0=$(dmesg | grep -ciE "error|fail|oops|bug:|warn") +cpu() { awk '{print $14+$15}' /proc/$1/stat 2>/dev/null; } +U=$(pidof aurora-ui); C=$(pidof cage); u0=$(cpu $U); c0=$(cpu $C); t0=$(cut -d' ' -f1 /proc/uptime) +echo "# t ui_rss cage_rss ui_cpu% cage_cpu% modem ping wifi_cl fwd_pkts chg vbat pmic dmesg_new mmc_writes(p27/other) mem_avail" > $O +for i in $(seq 1 $N); do + sleep 60 + U2=$(pidof aurora-ui); C2=$(pidof cage); t=$(cut -d' ' -f1 /proc/uptime) + u=$(cpu $U2); c=$(cpu $C2) + uc=$(awk -v a=$u0 -v b=$u -v d=$(awk -v x=$t0 -v y=$t 'BEGIN{print y-x}') -v hz=$hz 'BEGIN{printf "%.2f",(b-a)/hz/d*100}') + cc=$(awk -v a=$c0 -v b=$c -v d=$(awk -v x=$t0 -v y=$t 'BEGIN{print y-x}') -v hz=$hz 'BEGIN{printf "%.2f",(b-a)/hz/d*100}') + u0=$u; c0=$c; t0=$t + p=$(ping -c1 -W3 77.88.8.8 >/dev/null 2>&1 && echo ok || echo LOSS) + cl=$(iw dev wlan0 station dump 2>/dev/null | grep -c ^Station) + fw=$(nft list chain inet aurora_router forward 2>/dev/null | awk '/oifname "wwan0"/{for(i=1;i<=NF;i++) if($i=="packets") print $(i+1)}') + ch=$(awk '/^state/{print $2"/"$6}' /sys/kernel/debug/pm8916_lbc/state) + tz=$(for z in /sys/class/thermal/thermal_zone*; do [ "$(cat $z/type)" = pm8916-thermal ] && cat $z/temp; done) + dm=$(( $(dmesg | grep -ciE "error|fail|oops|bug:|warn") - dm0 )) + w27=$(awk '{print $5}' /sys/class/block/mmcblk0p27/stat); wo=0; for q in /sys/class/block/mmcblk0p* /sys/class/block/mmcblk0boot0 /sys/class/block/mmcblk0boot1; do [ "$q" = /sys/class/block/mmcblk0p27 ] && continue; wo=$((wo + $(awk '{print $5}' $q/stat))); done + echo "$t $(awk '/VmRSS/{print $2}' /proc/$U2/status) $(awk '/VmRSS/{print $2}' /proc/$C2/status) $uc $cc $(cat /run/aurora-modem/state) $p $cl ${fw:--} $ch $tz $dm $w27/$wo $(awk '/MemAvailable/{print $2}' /proc/meminfo) pids=$U2/$C2" >> $O +done +echo "# SOAK END" >> $O diff --git a/b14/run/ui-status.sh b/b14/run/ui-status.sh new file mode 100644 index 0000000..4af73ff --- /dev/null +++ b/b14/run/ui-status.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# B14: read-only status of Cage + aurora-ui (+ wayvnc). +uipid() { for p in /proc/[0-9]*; do [ "$(readlink $p/exe 2>/dev/null)" = /usr/local/bin/aurora-ui ] && echo ${p#/proc/}; done; } # not pidof: /etc/init.d/aurora-ui has the same comm +D=/run/aurora-ui +for p in cage aurora-ui wayvnc seatd; do if [ $p = aurora-ui ]; then q=$(uipid); else q=$(pidof $p); fi; [ -n "$q" ] && echo "$p pid=$q rss_kB=$(awk '/VmRSS/{print $2}' /proc/${q%% *}/status)" || echo "$p -"; done +echo "socket=$(cat $D/wayland-display 2>/dev/null) vt=$(cat /sys/class/tty/tty0/active)" +grep -E "video driver|renderer|first frame" $D/cage.log 2>/dev/null | sed 's/^/ /' +tail -3 $D/ui.log 2>/dev/null diff --git a/b14/run/ui-stop.sh b/b14/run/ui-stop.sh new file mode 100644 index 0000000..fd0b139 --- /dev/null +++ b/b14/run/ui-stop.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# B14: stop Cage + aurora-ui (and wayvnc if it was attached); fbcon/tty1 comes back when Cage releases the VT. +uipid() { for p in /proc/[0-9]*; do [ "$(readlink $p/exe 2>/dev/null)" = /usr/local/bin/aurora-ui ] && echo ${p#/proc/}; done; } # not pidof: /etc/init.d/aurora-ui has the same comm +D=/run/aurora-ui; L=$D/ui.log; mkdir -p $D +pidof wayvnc >/dev/null && sh /usr/local/lib/b13/run/wayvnc-down.sh >/dev/null 2>&1 +P=$(cat $D/cage.pid 2>/dev/null) +if [ -n "$P" ] && kill -0 $P 2>/dev/null; then kill $P; i=0; while kill -0 $P 2>/dev/null && [ $i -lt 50 ]; do sleep 0.1; i=$((i+1)); done; kill -0 $P 2>/dev/null && kill -9 $P; fi +P2=$(uipid); [ -n "$P2" ] && kill $P2 +rm -f $D/cage.pid $D/wayland-display +echo "[$(cut -d' ' -f1 /proc/uptime)] UI STOPPED vt=$(cat /sys/class/tty/tty0/active)" | tee -a $L diff --git a/b14/run/ui-up.sh b/b14/run/ui-up.sh new file mode 100644 index 0000000..9c8543d --- /dev/null +++ b/b14/run/ui-up.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# B14: Cage + aurora-ui (SDL2, Wayland) on the ST7735S. Usage: ui-up.sh [--stop-gui] +uipid() { for p in /proc/[0-9]*; do [ "$(readlink $p/exe 2>/dev/null)" = /usr/local/bin/aurora-ui ] && echo ${p#/proc/}; done; } # not pidof: /etc/init.d/aurora-ui has the same comm +# Refuses if Xorg / Cage / Sway already hold the panel, unless --stop-gui (then aurora-x / b13 cage / b13 sway are stopped cleanly). +# One instance only; state in /run/aurora-ui. Equivalent to `cage -- /usr/local/bin/aurora-ui` with the Pixman/seatd environment. +D=/run/aurora-ui; mkdir -p $D; L=$D/ui.log +now() { cut -d' ' -f1 /proc/uptime; } +log() { echo "[$(now)] $*" | tee -a $L; } +[ -f /etc/aurora/aurora-ui.conf ] && . /etc/aurora/aurora-ui.conf +export TZ LIBGL_ALWAYS_SOFTWARE SDL_VIDEODRIVER=wayland +export XDG_RUNTIME_DIR=/run/user/0; mkdir -p -m 0700 $XDG_RUNTIME_DIR +export WLR_RENDERER=pixman WLR_BACKENDS=drm,libinput WLR_DRM_DEVICES=/dev/dri/card0 WLR_LIBINPUT_NO_DEVICES=1 LIBSEAT_BACKEND=seatd +[ -f $D/cage.pid ] && kill -0 "$(cat $D/cage.pid)" 2>/dev/null && { log "aurora-ui already running (cage $(cat $D/cage.pid))"; exit 0; } +busy=$(for p in Xorg cage sway; do pidof $p >/dev/null && echo $p; done | tr '\n' ' ') +if [ -n "$busy" ]; then + [ "$1" = --stop-gui ] || { log "FAIL: display held by: $busy(use --stop-gui)"; exit 1; } + pidof wayvnc >/dev/null && sh /usr/local/lib/b13/run/wayvnc-down.sh >/dev/null 2>&1 + pidof Xorg >/dev/null && { log "stopping X11"; /usr/local/bin/aurora-x stop >> $L 2>&1; } + pidof sway >/dev/null && sh /usr/local/lib/b13/run/sway-down.sh >> $L 2>&1 + pidof cage >/dev/null && sh /usr/local/lib/b13/run/cage-down.sh >> $L 2>&1 + sleep 1; for p in Xorg cage sway; do pidof $p >/dev/null && { log "FAIL: $p still running"; exit 1; }; done +fi +[ -x /usr/local/bin/aurora-ui ] || { log "FAIL: /usr/local/bin/aurora-ui missing"; exit 1; } +pidof seatd >/dev/null || { seatd -g video > $D/seatd.log 2>&1 & i=0; while [ ! -S /run/seatd.sock ] && [ $i -lt 50 ]; do sleep 0.1; i=$((i+1)); done; } +t0=$(now) +setsid cage -d -- /usr/local/bin/aurora-ui > $D/cage.log 2>&1 < /dev/null & +echo $! > $D/cage.pid +i=0; while ! grep -q "aurora-ui: first frame" $D/cage.log 2>/dev/null && [ $i -lt 300 ]; do kill -0 $(cat $D/cage.pid) 2>/dev/null || break; sleep 0.1; i=$((i+1)); done +if grep -q "aurora-ui: first frame" $D/cage.log; then + for w in $XDG_RUNTIME_DIR/wayland-*; do case $w in *.lock) ;; *) [ -S $w ] && basename $w > $D/wayland-display;; esac; done + log "UI UP cage=$(cat $D/cage.pid) aurora-ui=$(uipid) socket=$(cat $D/wayland-display) start=$(awk -v a=$t0 -v b=$(now) 'BEGIN{printf "%.2f", b-a}')s $(grep -E 'video driver|renderer|first frame' $D/cage.log | sed 's/aurora-ui: //' | tr '\n' ';')" +else log "FAIL: aurora-ui did not draw (see $D/cage.log)"; tail -15 $D/cage.log; exit 1; fi diff --git a/b14/src/Makefile b/b14/src/Makefile new file mode 100644 index 0000000..e2a8fdc --- /dev/null +++ b/b14/src/Makefile @@ -0,0 +1,8 @@ +# B14 aurora-ui — build natively on the board (Alpine aarch64, gcc + sdl2-dev). +CFLAGS ?= -O2 -Wall -Wextra +aurora-ui: aurora-ui.c font8x16.h + $(CC) $(CFLAGS) -o $@ aurora-ui.c $$(pkg-config --cflags --libs sdl2) -lpthread +install: aurora-ui + install -m 0755 aurora-ui /usr/local/bin/aurora-ui +clean: + rm -f aurora-ui diff --git a/b14/src/aurora-ui.c b/b14/src/aurora-ui.c new file mode 100644 index 0000000..40110a5 --- /dev/null +++ b/b14/src/aurora-ui.c @@ -0,0 +1,379 @@ +/* + * aurora-ui — B14 128x128 modem status screen (SDL2 Wayland client under Cage). + * + * Rendering: one 128x128 ARGB buffer drawn in software (8x16 cp437 bitmap font), uploaded to a streaming + * texture once per second or when data changes. Max ~2 FPS, no animations. + * + * Data (all optional; a missing source shows "--" / "ERR", never crashes): + * main thread, 1 s : /sys/class/net/wwan0/statistics/{rx,tx}_bytes (throughput; bam-dmux keeps them at 0 -> wlan0 counters), + * /proc/net/route (default), + * /sys/kernel/debug/pm8916_lbc/state (B10 supervisor: state + VADC vbat), + * /sys/class/power_supply/pm8916-lbc-chgr/online, thermal zone "pm8916-thermal", clock + * worker, 2 s : /run/aurora-modem/state, lifecycle.log (START OK / FAIL), ipcfg (IPv4) + * worker, 5 s : `timeout 3 mmcli -m any --output-keyvalue` (operator, RAT, signal, state) — only while ModemManager runs, + * `timeout 3 iw dev wlan0 info` + `station dump` (AP up, client count) + * No SOC: the board has no fuel gauge, so only VBAT is shown. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "font8x16.h" + +#define W 128 +#define H 128 + +/* palette: 4 main colours + dim grey */ +#define C_BG 0xff000000u +#define C_TEXT 0xffe8e8e8u +#define C_OK 0xff40d040u +#define C_WARN 0xffffc020u +#define C_BAD 0xffff4040u +#define C_DIM 0xff606060u + +static uint32_t fb[W * H]; +static volatile sig_atomic_t quit; + +/* ---------- drawing ---------- */ +static void fill(int x, int y, int w, int h, uint32_t c) +{ + for (int j = y; j < y + h; j++) + for (int i = x; i < x + w; i++) + if (i >= 0 && i < W && j >= 0 && j < H) fb[j * W + i] = c; +} + +static void glyph(int x, int y, unsigned char ch, uint32_t c) +{ + const unsigned char *g = &font8x16[ch * 16]; + for (int r = 0; r < 16; r++) + for (int b = 0; b < 8; b++) + if (g[r] & (0x80 >> b)) fill(x + b, y + r, 1, 1, c); +} + +static void text(int x, int y, uint32_t c, const char *s) +{ + for (; *s && x < W; s++, x += 8) glyph(x, y, (unsigned char)*s, c); +} + +/* right-aligned to column 16 */ +static void textr(int y, uint32_t c, const char *s) +{ + text(W - 8 * (int)strlen(s), y, c, s); +} + +static void bars(int x, int y, int pct, uint32_t c) +{ + for (int i = 0; i < 5; i++) { + int h = 3 + i * 3, on = pct >= 0 && pct > i * 20; + fill(x + i * 6, y + 14 - h, 4, h, on ? c : C_DIM); + } +} + +/* ---------- small file helpers ---------- */ +static int readfile(const char *p, char *buf, size_t n) +{ + FILE *f = fopen(p, "r"); + if (!f) return -1; + size_t k = fread(buf, 1, n - 1, f); + fclose(f); + buf[k] = 0; + return (int)k; +} + +static long long readll(const char *p) +{ + char b[64]; + return readfile(p, b, sizeof b) > 0 ? atoll(b) : -1; +} + +static int pid_alive(const char *pidfile) +{ + char b[32], p[64]; + if (readfile(pidfile, b, sizeof b) <= 0) return 0; + snprintf(p, sizeof p, "/proc/%d", atoi(b)); + return access(p, F_OK) == 0; +} + +/* ---------- shared state ---------- */ +struct state { + /* worker */ + char mstate[24]; /* controller state, e.g. BEARER_CONNECTED */ + int mstart; /* 0 starting, 1 START OK, -1 FAIL */ + char ip[20]; + char oper[16], rat[8]; + int sig; /* % or -1 */ + int mm_ok; /* mmcli answered */ + int ap; /* 1 AP, 0 down, -1 unknown */ + int clients; + /* main */ + double rxr, txr; /* B/s */ + int wwan_ok, defroute; + char chg[12]; /* CHARGING/HOLD/FAULT/... */ + int vbat_mv, usb, tempc; +}; +static struct state st = { .sig = -1, .ap = -1, .clients = -1, .vbat_mv = -1, .tempc = -1000 }; +static pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER; +static int dirty = 1; + +static void kv(const char *line, const char *key, char *out, size_t n) +{ + const char *p = strstr(line, key); + if (!p) return; + p = strchr(p, ':'); + if (!p) return; + p++; + while (*p == ' ') p++; + size_t k = strcspn(p, "\n"); + if (k >= n) k = n - 1; + memcpy(out, p, k); + out[k] = 0; +} + +static void *worker(void *arg) +{ + (void)arg; + int tick = 0; + while (!quit) { + struct state t; + pthread_mutex_lock(&mu); t = st; pthread_mutex_unlock(&mu); + + char b[4096]; + if (readfile("/run/aurora-modem/state", b, sizeof b) > 0) { b[strcspn(b, "\n")] = 0; snprintf(t.mstate, sizeof t.mstate, "%s", b); } + else snprintf(t.mstate, sizeof t.mstate, "--"); + t.mstart = 0; + FILE *f = fopen("/run/aurora-modem/lifecycle.log", "r"); + if (f) { + char l[512]; + while (fgets(l, sizeof l, f)) { + if (strstr(l, "=== START OK")) t.mstart = 1; + else if (strstr(l, "FAIL in") || strstr(l, "rollback")) t.mstart = -1; + else if (strstr(l, "=== START (")) t.mstart = 0; + } + fclose(f); + } + t.ip[0] = 0; + if (readfile("/run/aurora-modem/ipcfg", b, sizeof b) > 0) { + char *a = strstr(b, "\nA="); + if (a) { a += 3; size_t k = strcspn(a, "\n"); if (k < sizeof t.ip) { memcpy(t.ip, a, k); t.ip[k] = 0; } } + } + + if (tick % 5 == 0) { + /* LTE details from ModemManager, only if it runs (avoid spawning mmcli into a stopped stack) */ + t.mm_ok = 0; + if (pid_alive("/run/aurora-modem/mm.pid") && (f = popen("timeout 3 mmcli -m any --output-keyvalue 2>/dev/null", "r"))) { + char l[512], v[64]; + t.sig = -1; t.oper[0] = 0; t.rat[0] = 0; + while (fgets(l, sizeof l, f)) { + v[0] = 0; + if (strstr(l, "modem.3gpp.operator-name ")) { kv(l, "operator-name", v, sizeof v); snprintf(t.oper, sizeof t.oper, "%s", strcmp(v, "--") ? v : ""); t.mm_ok = 1; } + else if (strstr(l, "access-technologies.value[1]")) { kv(l, "]", v, sizeof v); snprintf(t.rat, sizeof t.rat, "%s", v); } + else if (strstr(l, "signal-quality.value")) { kv(l, "value", v, sizeof v); t.sig = atoi(v); } + } + pclose(f); + for (char *p = t.oper; *p; p++) if (*p >= 'a' && *p <= 'z') *p -= 32; + for (char *p = t.rat; *p; p++) if (*p >= 'a' && *p <= 'z') *p -= 32; + } + /* Wi-Fi AP + stations (one short iw call pair, every 5 s) */ + t.ap = -1; t.clients = -1; + if ((f = popen("timeout 3 iw dev wlan0 info 2>/dev/null; echo ---; timeout 3 iw dev wlan0 station dump 2>/dev/null", "r"))) { + char l[256]; int sect = 0, n = 0, ap = 0, any = 0; + while (fgets(l, sizeof l, f)) { + if (!strncmp(l, "---", 3)) { sect = 1; continue; } + if (!sect) { any = 1; if (strstr(l, "type AP")) ap = 1; } + else if (!strncmp(l, "Station ", 8)) n++; + } + pclose(f); + if (any) { t.ap = ap; t.clients = ap ? n : 0; } + } + } + + pthread_mutex_lock(&mu); + if (memcmp(&t, &st, sizeof t)) { + /* keep the main-thread fields current */ + t.rxr = st.rxr; t.txr = st.txr; t.wwan_ok = st.wwan_ok; t.defroute = st.defroute; + memcpy(t.chg, st.chg, sizeof t.chg); t.vbat_mv = st.vbat_mv; t.usb = st.usb; t.tempc = st.tempc; + st = t; dirty = 1; + } + pthread_mutex_unlock(&mu); + for (int i = 0; i < 20 && !quit; i++) usleep(100000); + tick += 2; + } + return NULL; +} + +/* ---------- fast metrics (main thread, 1 s) ---------- */ +static long long prx = -1, ptx = -1; +static double pt; + +static void fast(void) +{ + char b[512]; + struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); + double now = ts.tv_sec + ts.tv_nsec / 1e9; + long long rx = readll("/sys/class/net/wwan0/statistics/rx_bytes"), tx = readll("/sys/class/net/wwan0/statistics/tx_bytes"); + /* the bam-dmux wwan0 driver keeps its netdev counters at 0 -> fall back to the Wi-Fi LAN side (traffic to/from clients): + download = wlan0 tx, upload = wlan0 rx */ + if (rx == 0 && tx == 0) { + long long lrx = readll("/sys/class/net/wlan0/statistics/rx_bytes"), ltx = readll("/sys/class/net/wlan0/statistics/tx_bytes"); + if (lrx >= 0 && ltx >= 0) { rx = ltx; tx = lrx; } + } + struct state t; pthread_mutex_lock(&mu); t = st; pthread_mutex_unlock(&mu); + t.wwan_ok = rx >= 0; + if (rx >= 0 && prx >= 0 && now > pt) { t.rxr = (rx - prx) / (now - pt); t.txr = (tx - ptx) / (now - pt); } + else { t.rxr = t.txr = -1; } + prx = rx; ptx = tx; pt = now; + + t.defroute = 0; + FILE *f = fopen("/proc/net/route", "r"); + if (f) { char l[256], ifn[32]; unsigned d; while (fgets(l, sizeof l, f)) if (sscanf(l, "%31s %x", ifn, &d) == 2 && d == 0) t.defroute = 1; fclose(f); } + + t.chg[0] = 0; t.vbat_mv = -1; + if (readfile("/sys/kernel/debug/pm8916_lbc/state", b, sizeof b) > 0) { + char s[16]; long uv; + char *p = strstr(b, "state "); + if (p && sscanf(p, "state %15s", s) == 1) snprintf(t.chg, sizeof t.chg, "%s", s); + if ((p = strstr(b, "vbat_uv ")) && sscanf(p, "vbat_uv %ld", &uv) == 1) t.vbat_mv = (int)(uv / 1000); + } + t.usb = (int)readll("/sys/class/power_supply/pm8916-lbc-chgr/online"); + + t.tempc = -1000; + DIR *d = opendir("/sys/class/thermal"); + if (d) { + struct dirent *e; char p[300]; + while ((e = readdir(d))) { + if (strncmp(e->d_name, "thermal_zone", 12)) continue; + snprintf(p, sizeof p, "/sys/class/thermal/%s/type", e->d_name); + if (readfile(p, b, sizeof b) > 0 && !strncmp(b, "pm8916-thermal", 14)) { + snprintf(p, sizeof p, "/sys/class/thermal/%s/temp", e->d_name); + long long v = readll(p); if (v > -100000) t.tempc = (int)(v / 1000); + } + } + closedir(d); + } + pthread_mutex_lock(&mu); + st.rxr = t.rxr; st.txr = t.txr; st.wwan_ok = t.wwan_ok; st.defroute = t.defroute; + memcpy(st.chg, t.chg, sizeof st.chg); st.vbat_mv = t.vbat_mv; st.usb = t.usb; st.tempc = t.tempc; + pthread_mutex_unlock(&mu); +} + +static void rate(char *o, size_t n, double r) +{ + if (r < 0) snprintf(o, n, "--"); + else if (r < 1000) snprintf(o, n, "%dB", (int)r); + else if (r < 1e6) snprintf(o, n, "%.*fK", r < 1e4 ? 1 : 0, r / 1e3); + else snprintf(o, n, "%.1fM", r / 1e6); +} + +/* ---------- screen ---------- */ +static void render(void) +{ + struct state t; pthread_mutex_lock(&mu); t = st; pthread_mutex_unlock(&mu); + char s[40], a[16], b[16]; + fill(0, 0, W, H, C_BG); + + /* row 0-1: LTE */ + int online = t.mstart == 1 && !strcmp(t.mstate, "BEARER_CONNECTED"); + if (t.mstart == 0 && strcmp(t.mstate, "BEARER_CONNECTED")) { + text(0, 0, C_WARN, "MODEM STARTING"); + snprintf(s, sizeof s, "%.16s", t.mstate); text(0, 16, C_DIM, s); + } else if (t.mstart < 0) { + text(0, 0, C_BAD, "MODEM FAIL"); + snprintf(s, sizeof s, "%.16s", t.mstate); text(0, 16, C_DIM, s); + } else { + snprintf(s, sizeof s, "%.11s", t.oper[0] ? t.oper : "LTE --"); text(0, 0, t.mm_ok ? C_TEXT : C_DIM, s); + textr(0, online ? C_OK : C_WARN, t.rat[0] ? t.rat : "--"); + bars(0, 16, t.mm_ok ? t.sig : -1, online ? C_OK : C_WARN); + if (t.mm_ok && t.sig >= 0) { snprintf(s, sizeof s, "%d%%", t.sig); text(36, 16, C_TEXT, s); } + else text(36, 16, C_DIM, "--"); + } + /* row 2: IPv4 */ + text(0, 32, t.ip[0] ? C_TEXT : C_DIM, t.ip[0] ? t.ip : "IP --"); + + /* row 3: Wi-Fi */ + if (t.ap < 0) text(0, 48, C_DIM, "WiFi --"); + else if (!t.ap) text(0, 48, C_BAD, "WiFi DOWN"); + else { snprintf(s, sizeof s, "WiFi %d cl", t.clients); text(0, 48, C_OK, s); } + + /* row 4: throughput (cp437 0x19 down, 0x18 up) */ + rate(a, sizeof a, t.rxr); rate(b, sizeof b, t.txr); + snprintf(s, sizeof s, "\x19%-6s \x18%s", a, b); text(0, 64, t.wwan_ok ? C_TEXT : C_DIM, s); + + /* row 5: battery (VADC VBAT from the B10 supervisor; no SOC) */ + if (t.vbat_mv > 0) snprintf(s, sizeof s, "BAT %d.%02dV", t.vbat_mv / 1000, (t.vbat_mv % 1000) / 10); + else snprintf(s, sizeof s, "BAT --"); + text(0, 80, t.vbat_mv > 0 ? C_TEXT : C_DIM, s); + const char *cs = t.chg[0] ? t.chg : (t.usb == 1 ? "USB" : "--"); + uint32_t cc = !strcmp(cs, "FAULT") ? C_BAD : (!strcmp(cs, "CHARGING") ? C_WARN : C_OK); + if (!strcmp(cs, "CHARGING")) cs = "CHG"; + snprintf(s, sizeof s, "%.5s", cs); textr(80, cc, s); + + /* row 6: PMIC temperature + router (default route) */ + if (t.tempc > -1000) { snprintf(s, sizeof s, "PMIC %d\xf8""C", t.tempc); text(0, 96, t.tempc >= 60 ? C_BAD : C_TEXT, s); } + else text(0, 96, C_DIM, "PMIC --"); + textr(96, t.defroute ? C_OK : C_DIM, t.defroute ? "NAT" : "---"); + + /* row 7: clock + overall */ + time_t now = time(NULL); struct tm tm; localtime_r(&now, &tm); + if (tm.tm_year + 1900 >= 2026) { snprintf(s, sizeof s, "%02d%c%02d", tm.tm_hour, (now & 1) ? ':' : ' ', tm.tm_min); text(0, 112, C_TEXT, s); } + else text(0, 112, C_DIM, "--:--"); + textr(112, online ? C_OK : (t.mstart < 0 ? C_BAD : C_WARN), online ? "ONLINE" : (t.mstart < 0 ? "FAIL" : "WAIT")); +} + +static void on_sig(int s) { (void)s; quit = 1; } + +int main(void) +{ + setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stderr, NULL, _IONBF, 0); + struct timespec t0; clock_gettime(CLOCK_MONOTONIC, &t0); + signal(SIGTERM, on_sig); signal(SIGINT, on_sig); signal(SIGPIPE, SIG_IGN); + + if (SDL_Init(SDL_INIT_VIDEO)) { fprintf(stderr, "aurora-ui: SDL_Init: %s\n", SDL_GetError()); return 1; } + const char *drv = SDL_GetCurrentVideoDriver(); + printf("aurora-ui: SDL %d.%d.%d video driver %s\n", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL, drv ? drv : "?"); + if (!drv || strcmp(drv, "wayland")) { fprintf(stderr, "aurora-ui: refusing to run on video driver '%s' (Wayland required)\n", drv ? drv : "?"); SDL_Quit(); return 2; } + + SDL_Window *win = SDL_CreateWindow("aurora-ui", 0, 0, W, H, SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_BORDERLESS); + if (!win) { fprintf(stderr, "aurora-ui: window: %s\n", SDL_GetError()); SDL_Quit(); return 1; } + SDL_ShowCursor(SDL_DISABLE); + SDL_Renderer *r = SDL_CreateRenderer(win, -1, 0); + if (!r) { fprintf(stderr, "aurora-ui: renderer: %s\n", SDL_GetError()); SDL_Quit(); return 1; } + SDL_RendererInfo ri; SDL_GetRendererInfo(r, &ri); + SDL_Texture *tex = SDL_CreateTexture(r, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, W, H); + int ww, wh; SDL_GetWindowSize(win, &ww, &wh); + printf("aurora-ui: renderer %s, window %dx%d\n", ri.name, ww, wh); + + pthread_t th; pthread_create(&th, NULL, worker, NULL); + Uint32 last = 0; int first = 1; + while (!quit) { + SDL_Event e; + if (SDL_WaitEventTimeout(&e, 250)) { + if (e.type == SDL_QUIT) quit = 1; + if (e.type == SDL_WINDOWEVENT) dirty = 1; + } + Uint32 now = SDL_GetTicks(); + if (now - last >= 1000) { last = now; fast(); dirty = 1; } + if (dirty) { + dirty = 0; + render(); + SDL_UpdateTexture(tex, NULL, fb, W * 4); + SDL_RenderClear(r); + SDL_RenderCopy(r, tex, NULL, NULL); + SDL_RenderPresent(r); + if (first) { + struct timespec t1; clock_gettime(CLOCK_MONOTONIC, &t1); + printf("aurora-ui: first frame after %.3f s\n", (t1.tv_sec - t0.tv_sec) + (t1.tv_nsec - t0.tv_nsec) / 1e9); + first = 0; + } + } + } + pthread_join(th, NULL); + SDL_DestroyTexture(tex); SDL_DestroyRenderer(r); SDL_DestroyWindow(win); SDL_Quit(); + printf("aurora-ui: exit\n"); + return 0; +} diff --git a/b14/src/font8x16.h b/b14/src/font8x16.h new file mode 100644 index 0000000..236e49b --- /dev/null +++ b/b14/src/font8x16.h @@ -0,0 +1,259 @@ +/* 8x16 cp437 bitmap font extracted from Linux lib/fonts/font_8x16.c (GPL-2.0), 256 glyphs x 16 rows, MSB = left pixel. */ +static const unsigned char font8x16[4096] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xd6, 0xd6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0xee, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x7c, 0x38, 0x38, 0x7c, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x32, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x18, 0x70, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x70, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x10, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x18, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x36, 0x36, 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0xe0, 0x62, 0x66, 0x6c, 0x18, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00, + 0x00, 0x60, 0xe0, 0x62, 0x66, 0x6c, 0x18, 0x30, 0x66, 0xce, 0x9a, 0x3f, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6c, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x66, 0x0c, 0x18, 0x32, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/b14/src/sdlprobe.c b/b14/src/sdlprobe.c new file mode 100644 index 0000000..7244dce --- /dev/null +++ b/b14/src/sdlprobe.c @@ -0,0 +1,19 @@ +/* B14: SDL2 probe — which video drivers are compiled in, which one initialises, which renderer we get. */ +#include +#include +int main(void) { + setvbuf(stdout, NULL, _IONBF, 0); + SDL_version v; SDL_GetVersion(&v); printf("SDL %d.%d.%d\n", v.major, v.minor, v.patch); + printf("compiled video drivers:"); for (int i = 0; i < SDL_GetNumVideoDrivers(); i++) printf(" %s", SDL_GetVideoDriver(i)); printf("\n"); + if (SDL_Init(SDL_INIT_VIDEO)) { printf("SDL_Init failed: %s\n", SDL_GetError()); return 1; } + printf("current video driver: %s\n", SDL_GetCurrentVideoDriver()); + SDL_DisplayMode m; if (!SDL_GetCurrentDisplayMode(0, &m)) printf("display 0: %dx%d@%d\n", m.w, m.h, m.refresh_rate); + SDL_Window *w = SDL_CreateWindow("probe", 0, 0, 128, 128, SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_BORDERLESS); + if (!w) { printf("window: %s\n", SDL_GetError()); return 1; } + printf("render drivers:"); for (int i = 0; i < SDL_GetNumRenderDrivers(); i++) { SDL_RendererInfo ri; SDL_GetRenderDriverInfo(i, &ri); printf(" %s", ri.name); } printf("\n"); + SDL_Renderer *r = SDL_CreateRenderer(w, -1, 0); + if (!r) { printf("renderer: %s\n", SDL_GetError()); } else { SDL_RendererInfo ri; SDL_GetRendererInfo(r, &ri); printf("renderer: %s flags=0x%x\n", ri.name, ri.flags); } + int ww, wh; SDL_GetWindowSize(w, &ww, &wh); printf("window %dx%d\n", ww, wh); + if (r) { SDL_SetRenderDrawColor(r, 0, 0, 255, 255); SDL_RenderClear(r); SDL_RenderPresent(r); SDL_Delay(1500); } + SDL_Quit(); return 0; +} diff --git a/b15/B15-rollback.sh b/b15/B15-rollback.sh new file mode 100644 index 0000000..9b3772e --- /dev/null +++ b/b15/B15-rollback.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# B15 rollback - restore the B12R cache (e85d862e: Alpine primary root + router, kernel b12 without WireGuard). Needs 9008 via HW EDL. +# boot/aboot/userdata untouched (aurora-vpn just logs "kernel has no WireGuard" under b12). Deeper: b12/b12r/B12R-rollback.sh (B12Q). +cd ~/doc/modem/jz08-aurora || exit 1 +L=firehose/007050e100000000_394a2e47cf830150_fhprg_peek.bin ; O=logs/b15/rollback ; mkdir -p $O +B=b12/b12r/cache/cache-extlinux-b12r.img +[ "$(sha256sum $B | cut -d' ' -f1)" = e85d862e463aa4affd7ec1cea9b1f2c7f765bb08e2a6bdce4af9539278f80d33 ] || { echo "STOP: rollback image hash"; exit 1; } +lsusb | grep -q 05c6:9008 || { echo "STOP: no 9008"; exit 1; } +edl --loader=$L printgpt > $O/00-printgpt.log 2>&1 +edl --loader=$L w cache $B > $O/cache-w.log 2>&1 +edl --loader=$L r cache $O/cache-rb.bin > /dev/null 2>&1 ; cmp $B $O/cache-rb.bin && echo CACHE_RESTORED_B12R +sha256sum $B $O/cache-rb.bin diff --git a/b15/B15-write.sh b/b15/B15-write.sh new file mode 100644 index 0000000..3479f95 --- /dev/null +++ b/b15/B15-write.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# B15 — the ONLY write: cache := B15 candidate (= B12Q initramfs + kernel 7.2.7-aurora-b15: b12 + WireGuard + IP_MULTIPLE_TABLES policy routing). +# Run on 480s ONLY after explicit GO (operator chose to flash without a RAM test). Derived 1:1 from b12/b12r/B12R-write.sh; changed: image/hashes, +# rollback refs, log dir. The pre-write readback of the live cache (must be B12R e85d862e) is kept as backup. +# Entry: HW EDL (battery on, D+->GND, plug USB, release). Never `edl reset`. Stops at first failed gate. +cd ~/doc/modem/jz08-aurora || exit 1 +L=firehose/007050e100000000_394a2e47cf830150_fhprg_peek.bin +O=logs/b15/W ; A3=logs/b5a/A3 ; P=logs/b5a/A4pre ; mkdir -p $O +NEW=b15/cache/cache-extlinux-b15.img ; NEW_SHA=212e0ca903911dad19fca00c013610cb6d5a773821c36410d8c277e34139856f +OLD_SHA=e85d862e463aa4affd7ec1cea9b1f2c7f765bb08e2a6bdce4af9539278f80d33 # current cache = B12R extlinux +BOOT_SHA=e9579f33c7304cd47f487f2b61cd9226dc04fd3a63775d62b4b95fc15bd3ebff +ABOOT_SHA=15208dbb4feafccd72d2b06db5714cd746c216701f0ef8822c58bb47105d37c4 # aboot = lk1st-b9l (B9L-PERSIST) +die(){ echo "STOP: $*"; exit 1; } +sha(){ sha256sum "$1" | cut -d" " -f1; } +E(){ edl --loader=$L "$@"; } +echo "== G0 inputs" +[ "$(sha $L)" = 53f193500c03248f0d671ab57bfe9ca8a42967e97f28403294b4b3f854075aca ] || die loader +[ "$(sha $NEW)" = $NEW_SHA ] && [ $(stat -c %s $NEW) = 134217728 ] || die candidate +[ "$(sha b12/b12r/cache/cache-extlinux-b12r.img)" = $OLD_SHA ] || die rollback-image +[ "$(sha logs/b12/b12r/W/11-cache-readback.bin)" = $OLD_SHA ] || die b12r-readback-backup +lsusb | grep -q 05c6:9008 || die "no 9008" +echo "== G1 identity + geometry + baseline" +E printgpt > $O/00-printgpt.log 2>&1 || die printgpt +grep -aq "Serial: *0x" $O/00-printgpt.log || grep -aq "Mode detected: firehose" $O/00-printgpt.log || die serial +grep -a "^cache:" $O/00-printgpt.log | grep -q "Offset 0x000000004118c000, Length 0x0000000008000000" || die cache-geometry +E rs 0 34 $O/pre-gptp.bin >/dev/null 2>&1; cmp $O/pre-gptp.bin $A3/gpt-primary-34.bin || die gpt-changed +E r boot $O/pre-boot.bin >/dev/null 2>&1; [ "$(sha $O/pre-boot.bin)" = $BOOT_SHA ] || die boot-not-emmc1 +E r aboot $O/pre-aboot.bin >/dev/null 2>&1; [ "$(sha $O/pre-aboot.bin)" = $ABOOT_SHA ] || die aboot-not-b9l +E r cache $O/pre-cache.bin >/dev/null 2>&1; [ "$(sha $O/pre-cache.bin)" = $OLD_SHA ] || die cache-not-backup +echo "== W cache := candidate (LBA 2133088, 262144 sectors)" +E w cache $NEW > $O/10-cache-write.log 2>&1; echo "rc=$?"; grep -a "Wrote" $O/10-cache-write.log +E r cache $O/11-cache-readback.bin > $O/11-cache-readback.log 2>&1 || die readback-rc +sha256sum $NEW $O/11-cache-readback.bin +cmp $NEW $O/11-cache-readback.bin || die "CACHE READBACK MISMATCH — do NOT reboot; run b15/B15-rollback.sh" +echo CACHE_MATCH +echo "== V everything else unchanged" +E rs 0 34 $O/post-gptp.bin >/dev/null 2>&1; cmp $O/post-gptp.bin $A3/gpt-primary-34.bin || die gptp +E rs 7634911 33 $O/post-gptb.bin >/dev/null 2>&1; cmp $O/post-gptb.bin $A3/gpt-backup-33.bin || die gptb +E rs 305184 1024 $O/post-tzbak.bin >/dev/null 2>&1; cmp $O/post-tzbak.bin $A3/tzbak-305184.bin || die tzbak +E r boot $O/post-boot.bin >/dev/null 2>&1; [ "$(sha $O/post-boot.bin)" = $BOOT_SHA ] || die boot +E r aboot $O/post-aboot.bin >/dev/null 2>&1; [ "$(sha $O/post-aboot.bin)" = $ABOOT_SHA ] || die aboot +for p in sbl1 rpm tz hyp recovery modemst1 modemst2 fsg fsc persist; do + E r $p $O/post-$p.bin >/dev/null 2>&1; cmp $O/post-$p.bin $A3/$p.bin || die "$p changed"; echo "$p == A3" +done +for p in modem system; do + E r $p $O/post-$p.bin >/dev/null 2>&1; cmp $O/post-$p.bin $P/$p.bin || die "$p changed"; echo "$p == A4pre"; rm -f $O/post-$p.bin +done +E rs 2428000 8 $O/post-userdata-sb.bin >/dev/null 2>&1 +[ "$(dd if=$O/post-userdata-sb.bin bs=1 skip=1080 count=2 2>/dev/null | od -An -tx1 | tr -d ' \n')" = 53ef ] && [ "$(dd if=$O/post-userdata-sb.bin bs=1 skip=1144 count=16 2>/dev/null | tr -d '\000')" = aurora-alpine ] || die "userdata superblock is not ext4 aurora-alpine" +echo "userdata = ext4 aurora-alpine (superblock)" +(cd $O && sha256sum *.bin > SHA256SUMS) +echo "B15_CACHE_WRITE_VERIFIED — power off fully (USB, then battery) before first boot" diff --git a/b15/b15-boot.sh b/b15/b15-boot.sh new file mode 100644 index 0000000..f9e3176 --- /dev/null +++ b/b15/b15-boot.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# 480s: B15 RAM test. Waits for lk1st fastboot (RESET held at power-on), `fastboot boot` the b15 image (WireGuard kernel), then waits +# for NCM + router. Nothing flashed. After it prints READY, bring the tunnel up by hand: aurora-vpn up. +cd ~/doc/modem/jz08-aurora; O=logs/b15/ram${1:-1}; mkdir -p $O; IMG=b15/out/aurora-b15.img +A=$O/host-actions.txt; act() { echo "$(date -u +%H:%M:%S) $*" | tee -a $A; } +tn() { { sleep 1; sed "s/\$/\r/" "$1"; sleep $2; } | ncat 172.16.42.1 23 | tr -d "\000\r"; } +grep " aurora-b15.img\$" b15/out/SHA256SUMS | sed "s| | b15/out/|" | sha256sum -c || exit 1 +act "B15 ram: waiting for fastboot (RESET held at power-on)" +i=0; until fastboot devices 2>/dev/null | grep -q ; do i=$((i+1)); [ $i -ge 900 ] && { act ABORT; exit 1; }; sleep 2; done +act "fastboot present"; { fastboot boot $IMG 2>&1; echo rc=$?; } | tee -a $A +i=0; until ping -c1 -W1 172.16.42.1 >/dev/null 2>&1; do i=$((i+1)); [ $i -ge 240 ] && { act "ABORT NCM"; exit 1; }; sleep 1; done; act "NCM up" +printf 'uname -r; grep -c WIREGUARD=y /proc/config.gz 2>/dev/null; ls -d /sys/module/wireguard 2>/dev/null; grep -E "ROUTER UP OK" /run/aurora-router/router.log | tail -1 | cut -c1-70\n' > $O/.w +i=0; until tn $O/.w 3 | grep -q "ROUTER UP OK"; do i=$((i+1)); [ $i -ge 60 ] && break; sleep 6; done +tn $O/.w 3 | grep -aE "aurora|ROUTER|/sys/module" | tee -a $A +act "READY — run: ssh q@480s \"cd doc/modem/jz08-aurora/b12; ./tn.sh 12 'aurora-vpn up'\"" diff --git a/b15/build-b15.sh b/b15/build-b15.sh new file mode 100644 index 0000000..bc72cc1 --- /dev/null +++ b/b15/build-b15.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# B15 RAM image: kernel 7.2.7-aurora-b15 (= b12 + WireGuard + IP_MULTIPLE_TABLES policy routing, tree src-b10b5) + prod DTB e47762d1 +# + B9C cmdline + B12Q initramfs (stage0 -> Alpine on p27) unchanged. fastboot boot only; nothing flashed. +set -e; cd "$(dirname "$0")"; O=out; KB=/home/q/aurora-kbuild/out-b15; Q=../b12/b12q/out; mkdir -p $O +echo "9ade7cffb1f3f19e86acc8869565a88b960abba543385a2fddcebd27bd529e79 $Q/initramfs-b12q.cpio.gz" | sha256sum -c +echo "e47762d1dcd0af6e2acb711fa47ecc13769cd6bcb81259ce50635e8b18b6514a $Q/aurora-b10b3.dtb" | sha256sum -c +for s in WIREGUARD=y IP_MULTIPLE_TABLES=y NF_TABLES=y CHARGER_PM8916_LBC=y 'LOCALVERSION="-aurora-b15"'; do grep -q "^CONFIG_$s\$" $KB/.config || { echo "STOP $s"; exit 1; }; done +cp $KB/arch/arm64/boot/Image.gz $O/Image-b15.gz; cp $KB/.config $O/config-b15; cp $Q/aurora-b10b3.dtb $Q/initramfs-b12q.cpio.gz $Q/cmdline.txt $O/ +cat $O/Image-b15.gz $O/aurora-b10b3.dtb > $O/Image.gz-dtb +python3 ../b6p/mkbootimg.py $O/Image.gz-dtb $O/initramfs-b12q.cpio.gz $O/cmdline.txt $O/aurora-b15.img +(cd $O && sha256sum Image-b15.gz config-b15 aurora-b10b3.dtb Image.gz-dtb initramfs-b12q.cpio.gz cmdline.txt aurora-b15.img > SHA256SUMS; grep -E "aurora-b15.img|Image-b15" SHA256SUMS) diff --git a/b15/cache/extlinux-b15.conf b/b15/cache/extlinux-b15.conf new file mode 100644 index 0000000..9997104 --- /dev/null +++ b/b15/cache/extlinux-b15.conf @@ -0,0 +1,6 @@ +default b15 +label b15 + linux /Image.gz-dtb + fdt /aurora-b10b3.dtb + initrd /initramfs-b12q.cpio.gz + append earlycon console=tty0 console=ttyMSM0,115200n8 ignore_loglevel loglevel=8 rdinit=/init fbcon=font:6x8 consoleblank=0 diff --git a/b15/cache/mkfs-cache-b15.sh b/b15/cache/mkfs-cache-b15.sh new file mode 100644 index 0000000..e986de9 --- /dev/null +++ b/b15/cache/mkfs-cache-b15.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# B15 cache CANDIDATE (= B12Q initramfs + kernel 7.2.7-aurora-b15: b12 + WireGuard + policy routing) (NOT for writing without a separate GO): exact recipe of b10/b10b3/cache-v4/mkfs-cache-b10b5.sh (c3732515); +# differs only in the initrd (B9C initramfs + B12P overlay: aurora-alpine autostart, modem safety gate, e2fsck bundle), file names, +# extlinux label, fs label and UUIDs. Kernel/DTB/append = B10 production unchanged. Runs on 480s (mke2fs 1.47.2). Offline only. +# The image contains the AP PSK (in the initramfs) - never publish it. +set -e +cd ~/doc/modem/jz08-aurora/b15/cache +OUT=${1:-cache-extlinux-b15.img}; F=../out +echo "0fef41b262917c8ae308fa222da0017e2b3cb3ec5da1ca8ba18d0318476761a3 $F/cmdline.txt" | sha256sum -c +echo "8ba2c2274d89ec7622c6dc3831f312c086aabce1784f497edc39fe4eec87c857 $F/Image.gz-dtb" | sha256sum -c +echo "e47762d1dcd0af6e2acb711fa47ecc13769cd6bcb81259ce50635e8b18b6514a $F/aurora-b10b3.dtb" | sha256sum -c +echo "9ade7cffb1f3f19e86acc8869565a88b960abba543385a2fddcebd27bd529e79 $F/initramfs-b12q.cpio.gz" | sha256sum -c +ST=$(mktemp -d); mkdir -p $ST/extlinux +cp $F/Image.gz-dtb $F/aurora-b10b3.dtb $F/initramfs-b12q.cpio.gz $ST/ +printf "default b15\nlabel b15\n\tlinux /Image.gz-dtb\n\tfdt /aurora-b10b3.dtb\n\tinitrd /initramfs-b12q.cpio.gz\n\tappend %s\n" "$(cat $F/cmdline.txt)" > $ST/extlinux/extlinux.conf +cp $ST/extlinux/extlinux.conf extlinux-b15.conf +chmod 0644 $ST/*.* $ST/extlinux/extlinux.conf; chmod 0755 $ST $ST/extlinux +find $ST -exec touch -h -d @1790726400 {} + +rm -f $OUT; truncate -s 134217728 $OUT +E2FSPROGS_FAKE_TIME=1790726400 /sbin/mke2fs -F -q -t ext2 -O none,filetype,sparse_super \ + -b 4096 -I 128 -N 64 -m 0 -L aurora-b15 -U 4a5a3038-b6f0-4000-8000-00000b151500 \ + -E root_owner=0:0,hash_seed=4a5a3038-b6f0-4000-8000-00000b151501 -d $ST $OUT 32768 +rm -rf $ST +for f in / /lost+found /extlinux /extlinux/extlinux.conf /Image.gz-dtb /aurora-b10b3.dtb /initramfs-b12q.cpio.gz; do for t in atime ctime mtime; do echo "sif $f $t @1790726400"; done; done > /tmp/b15-sif.$$ +/sbin/debugfs -w -f /tmp/b15-sif.$$ $OUT >/dev/null 2>&1; rm -f /tmp/b15-sif.$$ +/sbin/e2fsck -fn $OUT >/dev/null 2>&1 && echo "e2fsck -fn clean" +stat -c "%n %s" $OUT; sha256sum $OUT diff --git a/b15/run/aurora-router b/b15/run/aurora-router new file mode 100644 index 0000000..9b28809 --- /dev/null +++ b/b15/run/aurora-router @@ -0,0 +1,95 @@ +#!/bin/sh +# Aurora router (B12R, runtime state only in /run): aurora-router up | down | status +# up: wlan0 192.168.77.1/24, default route via wwan0 (only if none), nftables table inet aurora_router (NAT LAN->wwan0, default-deny forward, +# no LTE->LAN, Wi-Fi blocked from the USB mgmt subnet and from every board address except 192.168.77.1 DHCP/DNS/ping), ip_forward=1, +# dnsmasq bound to wlan0 only (upstream = bearer DNS). +# down: undo exactly that. Needs kernel nf_tables (7.2.7-aurora-b12). +. /etc/aurora/router.conf +S=/run/aurora-router; LOG=$S/router.log; mkdir -p $S +now() { cut -d' ' -f1 /proc/uptime; } +log() { m="[$(now)] $*"; echo "$m"; echo "$m" >> $LOG; echo "<5>[aurora-router] $*" > /dev/kmsg 2>/dev/null; } +fail() { log "FAIL: $*"; exit 1; } +dpid() { [ -f $S/dnsmasq.pid ] && kill -0 "$(cat $S/dnsmasq.pid)" 2>/dev/null && cat $S/dnsmasq.pid; } +case "$1" in +up) + log "=== ROUTER UP" + nft list tables >/dev/null 2>&1 || fail "kernel has no nf_tables ($(uname -r))" + [ -e /sys/class/net/$LAN_IF ] || fail "$LAN_IF absent"; [ -e /sys/class/net/$WAN_IF ] || fail "$WAN_IF absent" + DNS=$(sed -n 's/^DNS=//p' /run/aurora-modem/ipcfg 2>/dev/null | tr ',' ' '); [ -n "$DNS" ] || DNS=$FALLBACK_DNS + [ -f $S/ip_forward.saved ] || cat /proc/sys/net/ipv4/ip_forward > $S/ip_forward.saved + ip -4 addr show dev $LAN_IF | grep -q "inet $LAN_ADDR/" || ip addr add $LAN_ADDR/$LAN_PFX dev $LAN_IF || fail "addr $LAN_IF" + if ! ip route | grep -q "^default"; then ip route add default dev $WAN_IF && echo $WAN_IF > $S/default.added || fail "default route"; fi + cat > $S/ruleset.nft < USB mgmt blocked" + iifname "$MGMT_IF" oifname "$LAN_IF" counter drop + counter drop comment "default deny (incl. LTE -> LAN)" + } + chain postrouting { + type nat hook postrouting priority srcnat; policy accept; + oifname "$WAN_IF" ip saddr $LAN_NET counter masquerade + } +} +N + # B15: aurora-vpn owns the firewall while up (fail-closed LAN -> tunnel); a late router up (AP enabled after VPN) must not + # re-add aurora_router, whose forward chain drops LAN -> wg0/singtun0 (both forward hooks must accept). + if nft list table inet aurora_vpn >/dev/null 2>&1; then log "aurora_vpn table present: keep VPN firewall, skip aurora_router ruleset" + else nft -f $S/ruleset.nft || fail "nft ruleset"; fi + echo 1 > /proc/sys/net/ipv4/ip_forward + if [ -z "$(dpid)" ]; then + { echo "interface=$LAN_IF"; echo "except-interface=lo"; echo "bind-interfaces"; echo "no-resolv"; echo "no-hosts" + for d in $DNS; do echo "server=$d"; done + echo "dhcp-range=$DHCP_RANGE"; echo "dhcp-option=option:router,$LAN_ADDR"; echo "dhcp-option=option:dns-server,$LAN_ADDR" + echo "dhcp-leasefile=$S/dnsmasq.leases"; echo "dhcp-authoritative"; echo "cache-size=300"; echo "domain-needed"; echo "bogus-priv" + echo "pid-file=$S/dnsmasq.pid"; echo "log-facility=$S/dnsmasq.log"; echo "log-dhcp"; echo "user=root"; } > $S/dnsmasq.conf + dnsmasq --conf-file=$S/dnsmasq.conf || fail "dnsmasq"; sleep 1 + fi + [ -n "$(dpid)" ] || fail "dnsmasq not running" + log "=== ROUTER UP OK lan $LAN_IF $LAN_ADDR/$LAN_PFX dhcp $DHCP_RANGE dns [$DNS] wan $WAN_IF default=[$(ip route | grep "^default")] fwd=$(cat /proc/sys/net/ipv4/ip_forward) dnsmasq=$(dpid)";; +down) + log "=== ROUTER DOWN" + p=$(dpid); [ -n "$p" ] && { kill $p; sleep 1; }; rm -f $S/dnsmasq.pid + nft list table inet aurora_router >/dev/null 2>&1 && nft delete table inet aurora_router + [ -f $S/ip_forward.saved ] && { cat $S/ip_forward.saved > /proc/sys/net/ipv4/ip_forward; rm -f $S/ip_forward.saved; } + ip addr del $LAN_ADDR/$LAN_PFX dev $LAN_IF 2>/dev/null + [ -f $S/default.added ] && { ip route del default dev "$(cat $S/default.added)" 2>/dev/null; rm -f $S/default.added; } + log "=== ROUTER DOWN OK fwd=$(cat /proc/sys/net/ipv4/ip_forward) tables=[$(nft list tables 2>/dev/null | tr '\n' ' ')] default=[$(ip route | grep "^default")]";; +status) + echo "kernel $(uname -r) ip_forward=$(cat /proc/sys/net/ipv4/ip_forward) dnsmasq=$(dpid)" + ip -4 addr show dev $LAN_IF | grep inet; ip route | grep "^default" + echo "-- leases"; cat $S/dnsmasq.leases 2>/dev/null + echo "-- stations"; iw dev $LAN_IF station dump 2>/dev/null | grep -E "^Station|signal:|connected time" + echo "-- conntrack $(cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null)" + nft list table inet aurora_router 2>&1 | grep -E "counter|chain" + tail -3 $LOG 2>/dev/null;; +*) echo "usage: $0 up|down|status"; exit 2;; +esac diff --git a/b15/run/aurora-vpn b/b15/run/aurora-vpn new file mode 100644 index 0000000..ad88626 --- /dev/null +++ b/b15/run/aurora-vpn @@ -0,0 +1,130 @@ +#!/bin/sh +# aurora-vpn (B15): full-tunnel VPN for the Wi-Fi LAN. Two modes (/etc/aurora/vpn.conf VPN_MODE): +# vless = sing-box VLESS-over-WS-over-TLS via CDN, TUN singtun0 (config /etc/sing-box/config.json) +# wg = kernel WireGuard wg0 (config /etc/wireguard/wg0.conf) +# LAN clients are policy-routed into the tunnel; the board itself, USB NCM management and the tunnel's own +# outbound (to the VPN server/CDN) stay DIRECT on wwan0 (source-based rule -> no loop). +# Fail-closed: while up, LAN may leave ONLY via the tunnel; if it is down, LAN has no internet (never LTE). +# aurora-vpn up | down | status. Needs kernel 7.2.7-aurora-b15 (TUN; WireGuard for wg mode). +TBL=51820; RULE_PRI=100; S=/run/aurora-vpn; mkdir -p $S; LOG=$S/vpn.log +[ -f /etc/aurora/router.conf ] && . /etc/aurora/router.conf +[ -f /etc/aurora/vpn.conf ] && . /etc/aurora/vpn.conf +VPN_MODE=${VPN_MODE:-wg}; LAN_IF=${LAN_IF:-wlan0}; LAN_NET=${LAN_NET:-192.168.77.0/24}; LAN_ADDR=${LAN_ADDR:-192.168.77.1} +WAN_IF=${WAN_IF:-wwan0}; MGMT_IF=${MGMT_IF:-usb0}; TUN_IF=${TUN_IF:-singtun0}; TUN_MTU=${TUN_MTU:-1400} +SB=/etc/sing-box/config.json; WGC=/etc/wireguard/wg0.conf +now() { cut -d' ' -f1 /proc/uptime; } +log() { m="[$(now)] $*"; echo "$m"; echo "$m" >> $LOG; echo "<5>[aurora-vpn] $*" > /dev/kmsg 2>/dev/null; } +fail() { log "FAIL: $*"; exit 1; } +tif() { [ "$VPN_MODE" = wg ] && echo wg0 || echo "$TUN_IF"; } + +firewall() { + T=$(tif); MSS=$(( ${1:-$TUN_MTU} - 40 )) + nft list table inet aurora_router >/dev/null 2>&1 && nft delete table inet aurora_router + nft -f - < /proc/sys/net/ipv4/ip_forward +} +unroute() { + while ip rule del from "$LAN_NET" lookup $TBL 2>/dev/null; do :; done + while ip rule del from "$LAN_NET" to "$LAN_NET" lookup main 2>/dev/null; do :; done + ip route flush table $TBL 2>/dev/null +} +case "$1" in +up) + log "=== VPN UP ($VPN_MODE)" + if [ "$VPN_MODE" = vless ]; then + [ -f $SB ] || fail "$SB missing" + command -v sing-box >/dev/null || fail "sing-box not installed" + pidof sing-box >/dev/null && { log "sing-box already running"; } || { + sing-box check -c $SB 2>>$LOG || fail "sing-box config invalid" + setsid sh -c "trap '' HUP; exec sing-box run -c $SB" >> $S/sing-box.log 2>&1 < /dev/null & + echo $! > $S/sing-box.pid + } + i=0; while [ ! -e /sys/class/net/$TUN_IF ] && [ $i -lt 100 ]; do sleep 0.1; i=$((i+1)); done + [ -e /sys/class/net/$TUN_IF ] || { tail -8 $S/sing-box.log; fail "$TUN_IF did not appear"; } + ip link set $TUN_IF mtu $TUN_MTU 2>/dev/null + else + ip link add dev wg0 type wireguard 2>/dev/null || ip link show wg0 >/dev/null 2>&1 || fail "no WireGuard" + [ -f $WGC ] || fail "$WGC missing" + A=$(sed -n 's/^Address[ ]*=[ ]*//p' $WGC | head -1); M=$(sed -n 's/^MTU[ ]*=[ ]*//p' $WGC | head -1); EP=$(sed -n 's/^Endpoint[ ]*=[ ]*//p' $WGC | head -1 | sed 's/:[0-9]*$//') + wg-quick strip wg0 2>/dev/null | wg setconf wg0 /dev/stdin || { ip link del wg0; fail "wg setconf"; } + ip addr flush dev wg0; ip addr add "$A" dev wg0; ip link set wg0 mtu "${M:-$TUN_MTU}" up + # loopback endpoint = local relay (free-turn-client on 127.0.0.1): its own uplink is board traffic -> main -> wwan0 + case "$EP" in 127.*) log "endpoint $EP is local relay: no direct route";; + *) GW=$(ip route show default dev $WAN_IF | awk '{print $3; exit}'); ip route replace "$EP/32" dev $WAN_IF ${GW:+via $GW}; echo "$EP" > $S/endpoint;; esac + fi + route_lan; firewall $M + log "=== VPN UP OK mode=$VPN_MODE tun=$(tif) LAN $LAN_NET -> $(tif) (table $TBL); fail-closed on" + ;; +down) + log "=== VPN DOWN" + [ "$VPN_MODE" = vless ] && { pidof sing-box >/dev/null && kill $(pidof sing-box); sleep 1; pidof sing-box >/dev/null && kill -9 $(pidof sing-box); rm -f $S/sing-box.pid; } + [ "$VPN_MODE" = wg ] && ip link del wg0 2>/dev/null + unroute + [ -f $S/endpoint ] && { ip route del "$(cat $S/endpoint)/32" dev $WAN_IF 2>/dev/null; rm -f $S/endpoint; } + nft list table inet aurora_vpn >/dev/null 2>&1 && nft delete table inet aurora_vpn + /usr/sbin/aurora-router up >/dev/null 2>&1 || log "note: run 'aurora-router up' to restore LAN->LTE" + log "=== VPN DOWN OK (router restored: $(nft list tables 2>/dev/null | tr '\n' ' '))" + ;; +status) + T=$(tif); echo "kernel $(uname -r) mode $VPN_MODE" + if [ -e /sys/class/net/$T ]; then + echo "$T UP mtu=$(cat /sys/class/net/$T/mtu)" + if [ "$VPN_MODE" = vless ]; then + echo "sing-box pid=$(pidof sing-box) rss_kB=$(awk '/VmRSS/{print $2}' /proc/$(pidof sing-box 2>/dev/null)/status 2>/dev/null)" + tail -3 $S/sing-box.log 2>/dev/null | sed 's/^/ /' + else + HS=$(wg show wg0 latest-handshakes 2>/dev/null | awk '{print $2}' | head -1) + echo "handshake_age=$([ -n "$HS" ] && [ "$HS" != 0 ] && echo $(( $(date +%s)-HS ))s || echo never) transfer=$(wg show wg0 transfer 2>/dev/null | awk '{print $2"/"$3}')" + fi + echo "rules:"; ip rule show | grep -E "lookup $TBL|$LAN_NET" + echo "table $TBL:"; ip route show table $TBL + else echo "$T DOWN"; fi + nft list table inet aurora_vpn 2>/dev/null | grep -E "oifname|masquerade|drop" | sed 's/^[ \t]*//' | head + ;; +*) echo "usage: $0 up|down|status"; exit 2;; +esac diff --git a/b15/run/aurora-vpn-diag b/b15/run/aurora-vpn-diag new file mode 100644 index 0000000..e576232 --- /dev/null +++ b/b15/run/aurora-vpn-diag @@ -0,0 +1,16 @@ +#!/bin/sh +# aurora-vpn-diag (B15): read-only VPN/leak diagnostics, no secrets (no keys, no endpoint port). +. /etc/aurora/router.conf 2>/dev/null; LAN_NET=${LAN_NET:-192.168.77.0/24}; WAN_IF=${WAN_IF:-wwan0}; WG=wg0; TBL=51820 +echo "== kernel"; uname -r +echo "== $WAN_IF"; ip -4 -o addr show $WAN_IF | awk '{print $4}'; ip route show default +echo "== $WG"; if ip link show $WG >/dev/null 2>&1; then ip -4 -o addr show $WG | awk '{print $2,$4,"mtu",$0}' | sed 's/.*mtu/mtu/;s/\\\\/ /'; wg show $WG | grep -vE "private|listening"; else echo DOWN; fi +echo "== handshake"; hs=$(wg show $WG latest-handshakes 2>/dev/null | awk '{print $2}' | head -1); [ -n "$hs" ] && [ "$hs" != 0 ] && echo "age $(( $(date +%s) - hs )) s" || echo "none" +echo "== rules"; ip rule show | grep -E "lookup $TBL|$LAN_NET" || echo none +echo "== table $TBL"; ip route show table $TBL || echo empty +echo "== endpoint path (must be $WAN_IF, not $WG):"; ep=$(cat /run/aurora-vpn/endpoint 2>/dev/null); [ -n "$ep" ] && ip route get "$ep" | head -1 || echo "no endpoint recorded" +echo "== nft forward/nat counters"; nft list table inet aurora_vpn 2>/dev/null | grep -E "oifname|masquerade|drop" | sed 's/^[ \t]*//' +echo "== wg transfer"; wg show $WG transfer 2>/dev/null | sed 's/\\t/ /g' || echo none +echo "== LEAK TEST (LAN source route): a LAN client packet must exit via $WG, NOT $WAN_IF" +echo " ip route get 1.1.1.1 from ${LAN_NET%/*}.100:"; ip route get 1.1.1.1 from "$(echo $LAN_NET | sed 's#0/24#100#')" 2>/dev/null | head -1 +verdict=$(ip route get 1.1.1.1 from "$(echo $LAN_NET | sed 's#0/24#100#')" 2>/dev/null | grep -oE "dev [a-z0-9]+" | awk '{print $2}' | head -1) +if ip link show $WG >/dev/null 2>&1; then [ "$verdict" = "$WG" ] && echo " LEAK-GUARD: OK (LAN -> $WG)" || echo " LEAK-GUARD: WARN (LAN -> ${verdict:-?})"; else echo " LEAK-GUARD: VPN down -> LAN forward dropped (fail-closed)"; fi diff --git a/b15/run/aurora-vpn.openrc b/b15/run/aurora-vpn.openrc new file mode 100644 index 0000000..dfcdd30 --- /dev/null +++ b/b15/run/aurora-vpn.openrc @@ -0,0 +1,7 @@ +#!/sbin/openrc-run +# B15: full-tunnel WireGuard for the Wi-Fi LAN (installed as /etc/init.d/aurora-vpn; default runlevel since 2026-10-05 (free-turn wg mode)). +description="Aurora WireGuard full tunnel (Wi-Fi LAN -> wg0)" +depend() { need aurora-router; use free-turn; after aurora-modem aurora-wifi free-turn; } +start() { ebegin "aurora-vpn up"; /usr/local/bin/aurora-vpn up >> /run/aurora-vpn.service.log 2>&1; eend $?; } +stop() { ebegin "aurora-vpn down"; /usr/local/bin/aurora-vpn down >> /run/aurora-vpn.service.log 2>&1; eend $?; } +status() { /usr/local/bin/aurora-vpn status; } diff --git a/b15/run/free-turn.openrc b/b15/run/free-turn.openrc new file mode 100644 index 0000000..d8ce2a6 --- /dev/null +++ b/b15/run/free-turn.openrc @@ -0,0 +1,11 @@ +#!/sbin/openrc-run +# B15: free-turn-proxy client (VK TURN relay) -> local UDP 127.0.0.1:9000 for wg0 (installed as /etc/init.d/free-turn). +# Config /etc/free-turn/ftc.env via /etc/free-turn/ftc-run.sh. Uplink is board traffic -> main -> wwan0 (not policy-routed). +description="free-turn-proxy client (WireGuard relay on 127.0.0.1:9000)" +supervisor=supervise-daemon +command=/etc/free-turn/ftc-run.sh +output_log=/run/free-turn.log +error_log=/run/free-turn.log +respawn_delay=10 +respawn_max=0 +depend() { need localmount; after aurora-modem aurora-router; before aurora-vpn; } diff --git a/b15/run/vpn.conf b/b15/run/vpn.conf new file mode 100644 index 0000000..a1369ba --- /dev/null +++ b/b15/run/vpn.conf @@ -0,0 +1,4 @@ +# aurora-vpn mode (sourced). vless (sing-box, TUN) or wg (WireGuard wg0). +VPN_MODE=vless +TUN_IF=singtun0 +TUN_MTU=1400 diff --git a/b16/README.md b/b16/README.md new file mode 100644 index 0000000..da50671 --- /dev/null +++ b/b16/README.md @@ -0,0 +1,13 @@ +# B16 — aurora-ui: VPN status, POWER-key pages, auto blank (on top of B14) + +Source: `src/aurora-ui.c` (+ `font8x16.h`, `font6x8.h` from Linux lib/fonts, GPL-2.0). Build on the board: +`cd /root/b16/src && make && install -m 0755 aurora-ui /usr/local/bin/aurora-ui && rc-service aurora-ui restart`. +Launch scripts/OpenRC unchanged from B14 (`b14/run`). Previous binary on the board: `/usr/local/bin/aurora-ui.b14`. + +- POWER (pm8941_pwrkey, evdev /dev/input/event0, read directly): screen off → backlight on + STATUS; screen on → next page + STATUS → CLIENTS → FREE-TURN log → WG → STATUS. No press for `AURORA_UI_BLANK_S` s (default 180) → backlight 0, page 0, no drawing. +- STATUS: row 6 = `WG ` (green ≤180 s) + `FT` (free-turn-client alive) / `VPN VLESS` / `VPN OFF`; PMIC temp moved to the clock row. +- CLIENTS (6x8 font): per station from `iw station dump` (rate = byte delta / 2 s, ↓ = to client) + name/IP from dnsmasq leases; up to 7. +- FREE-TURN: pid + tail of /run/free-turn.log (date/session stripped, Cyrillic transliterated, WARN yellow/ERROR red). +- WG: handshake age, rates, totals, endpoint, MTU, FT state + tail of /run/aurora-vpn/vpn.log. No keys are read/stored. +Holding POWER long may trigger the PMIC S2 hard reset — short presses only. diff --git a/b16/src/Makefile b/b16/src/Makefile new file mode 100644 index 0000000..e5ddd7b --- /dev/null +++ b/b16/src/Makefile @@ -0,0 +1,8 @@ +# B16 aurora-ui — build natively on the board (Alpine aarch64, gcc + sdl2-dev). +CFLAGS ?= -O2 -Wall -Wextra +aurora-ui: aurora-ui.c font8x16.h font6x8.h + $(CC) $(CFLAGS) -o $@ aurora-ui.c $$(pkg-config --cflags --libs sdl2) -lpthread +install: aurora-ui + install -m 0755 aurora-ui /usr/local/bin/aurora-ui +clean: + rm -f aurora-ui diff --git a/b16/src/aurora-ui.c b/b16/src/aurora-ui.c new file mode 100644 index 0000000..22a3494 --- /dev/null +++ b/b16/src/aurora-ui.c @@ -0,0 +1,1016 @@ +/* + * aurora-ui — B16 128x128 modem status screen (SDL2 Wayland client under Cage). B14 + VPN status, POWER-key pages, auto blank. + * + * Rendering: one 128x128 ARGB buffer drawn in software (8x16 cp437 font for the status page, 6x8 for the dense pages), + * uploaded to a streaming texture once per second or when data changes. Max ~2 FPS, no animations. Nothing is drawn + * while the backlight is off. + * + * POWER key (pm8941_pwrkey, read directly from evdev — Cage/libinput never grab it): + * screen off -> backlight on, status page; screen on -> next page: STATUS -> CLIENTS -> FREE-TURN log -> WG -> STATUS. + * No press for AURORA_UI_BLANK_S seconds (default 180) -> backlight off, back to the status page. + * Page 5 = STACK (B21): one-button tower game. On its title screen a single press goes on to STATUS (like every page), a double + * press (< 400 ms) starts a game; in a game every press drops the sliding block (~30 FPS while playing). Best score in + * /var/lib/aurora/stack-best. + * + * Data (all optional; a missing source shows "--" / "ERR", never crashes): + * main thread, 1 s : /sys/class/net/wwan0/statistics/{rx,tx}_bytes (throughput; bam-dmux keeps them at 0 -> wlan0 counters), + * /proc/net/route (default), + * /sys/kernel/debug/pm8916_lbc/state (B10 supervisor: state + VADC vbat), + * /sys/class/power_supply/pm8916-lbc-chgr/online, thermal zone "pm8916-thermal", clock; + * log pages: tail of /run/free-turn.log, /run/aurora-vpn/vpn.log + * worker, 2 s : /run/aurora-modem/state, lifecycle.log (START OK / FAIL), ipcfg (IPv4), + * wg0 / singtun0 presence, free-turn-client process, `wg show wg0` handshake/transfer/endpoint, + * CLIENTS page only: `iw dev wlan0 station dump` (per-station bytes -> rate, signal) + dnsmasq leases + * worker, 5 s : `timeout 3 mmcli -m any --output-keyvalue` (operator, RAT, signal, state) — only while ModemManager runs, + * `timeout 3 iw dev wlan0 info` + `station dump` (AP up, client count) + * No SOC: the board has no fuel gauge, so only VBAT is shown. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "font8x16.h" +#include "font6x8.h" + +#define W 128 +#define H 128 +#define PAGES 5 + +/* palette: 4 main colours + dim grey */ +#define C_BG 0xff000000u +#define C_TEXT 0xffe8e8e8u +#define C_OK 0xff40d040u +#define C_WARN 0xffffc020u +#define C_BAD 0xffff4040u +#define C_DIM 0xff606060u + +#define FT_EXE "/usr/local/bin/free-turn-client" +#define FT_LOG "/run/free-turn.log" +#define VPN_LOG "/run/aurora-vpn/vpn.log" +#define LEASES "/run/aurora-router/dnsmasq.leases" + +static uint32_t fb[W * H]; +static volatile sig_atomic_t quit; +static volatile int page; /* 0 status, 1 clients, 2 free-turn log, 3 wg */ +static volatile int screen_on = 1; + +/* ---------- drawing ---------- */ +static void fill(int x, int y, int w, int h, uint32_t c) +{ + for (int j = y; j < y + h; j++) + for (int i = x; i < x + w; i++) + if (i >= 0 && i < W && j >= 0 && j < H) fb[j * W + i] = c; +} + +static void glyph(int x, int y, unsigned char ch, uint32_t c) +{ + const unsigned char *g = &font8x16[ch * 16]; + for (int r = 0; r < 16; r++) + for (int b = 0; b < 8; b++) + if (g[r] & (0x80 >> b)) fill(x + b, y + r, 1, 1, c); +} + +static void text(int x, int y, uint32_t c, const char *s) +{ + for (; *s && x < W; s++, x += 8) glyph(x, y, (unsigned char)*s, c); +} + +/* right-aligned to column 16 */ +static void textr(int y, uint32_t c, const char *s) +{ + text(W - 8 * (int)strlen(s), y, c, s); +} + +/* 6x8 font: 21 columns x 16 rows; row/col in character cells (x offset 1 px to centre 126 px) */ +#define SC 21 +#define SR 16 +static void stext(int col, int row, uint32_t c, const char *s) +{ + if (col < 0) col = 0; + if (row < 0 || row >= SR) return; + for (int x = 1 + col * 6; *s && x + 6 <= W; s++, x += 6) { + const unsigned char *g = &font6x8[(unsigned char)*s * 8]; + for (int r = 0; r < 8; r++) + for (int b = 0; b < 6; b++) + if (g[r] & (0x80 >> b)) fb[(row * 8 + r) * W + x + b] = c; + } +} + +static void stextr(int row, uint32_t c, const char *s) +{ + stext(SC - (int)strlen(s), row, c, s); +} + +static void bars(int x, int y, int pct, uint32_t c) +{ + for (int i = 0; i < 5; i++) { + int h = 3 + i * 3, on = pct >= 0 && pct > i * 20; + fill(x + i * 6, y + 14 - h, 4, h, on ? c : C_DIM); + } +} + +/* ---------- small file helpers ---------- */ +static int readfile(const char *p, char *buf, size_t n) +{ + FILE *f = fopen(p, "r"); + if (!f) return -1; + size_t k = fread(buf, 1, n - 1, f); + fclose(f); + buf[k] = 0; + return (int)k; +} + +static long long readll(const char *p) +{ + char b[64]; + return readfile(p, b, sizeof b) > 0 ? atoll(b) : -1; +} + +static int pid_alive(const char *pidfile) +{ + char b[32], p[64]; + if (readfile(pidfile, b, sizeof b) <= 0) return 0; + snprintf(p, sizeof p, "/proc/%d", atoi(b)); + return access(p, F_OK) == 0; +} + +/* pid of the first process whose /proc/PID/exe is exe (not pidof: the OpenRC script may share the comm), 0 if none */ +static int exe_pid(const char *exe) +{ + DIR *d = opendir("/proc"); + if (!d) return 0; + struct dirent *e; char p[64], l[256]; int pid = 0; + while (!pid && (e = readdir(d))) { + if (e->d_name[0] < '1' || e->d_name[0] > '9') continue; + snprintf(p, sizeof p, "/proc/%s/exe", e->d_name); + ssize_t k = readlink(p, l, sizeof l - 1); + if (k > 0) { l[k] = 0; if (!strcmp(l, exe)) pid = atoi(e->d_name); } + } + closedir(d); + return pid; +} + +static double mono(void) +{ + struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec / 1e9; +} + +/* ---------- shared state ---------- */ +#define MAXCL 7 +struct client { + char mac[18], ip[16], name[24]; + int sig; + double rxr, txr; /* B/s seen by the AP: rx = client upload, tx = client download */ +}; +struct state { + /* worker */ + char mstate[24]; /* controller state, e.g. BEARER_CONNECTED */ + int mstart; /* 0 starting, 1 START OK, -1 FAIL */ + char ip[20]; + char oper[16], rat[8]; + int sig; /* % or -1 */ + int mm_ok; /* mmcli answered */ + int ap; /* 1 AP, 0 down, -1 unknown */ + int clients; + int wg, vless; /* active WireGuard tunnel / singtun0 present */ + int direct; /* B22: LAN via wg1 (direct WireGuard), else via wg0 (free-turn relay) */ + long long hs0, hs1; /* B22: latest handshake (unix s, 0 never) of wg0 (relay) and wg1 (direct) */ + int wl; /* B22 operator whitelist probe: 0 unknown, 1 on, 2 off, 3 no LTE data */ + long long wlts; /* probe time (unix s) */ + int ft; /* free-turn-client pid or 0 */ + long long hs; /* wg latest handshake (unix s), 0 never */ + long long wrx, wtx; /* wg transfer totals, -1 unknown */ + double wrxr, wtxr; /* wg rates B/s, -1 unknown */ + char wep[28]; + int wmtu; + int ncl; /* CLIENTS page: stations listed in cl[] (-1 = not sampled yet) */ + struct client cl[MAXCL]; + /* main */ + double rxr, txr; /* B/s */ + int wwan_ok, defroute; + char chg[12]; /* CHARGING/HOLD/FAULT/... */ + int vbat_mv, usb, tempc; +}; +static struct state st = { .sig = -1, .ap = -1, .clients = -1, .vbat_mv = -1, .tempc = -1000, .wrx = -1, .wtx = -1, + .wrxr = -1, .wtxr = -1, .ncl = -1 }; +static pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER; +static int dirty = 1; + +static void kv(const char *line, const char *key, char *out, size_t n) +{ + const char *p = strstr(line, key); + if (!p) return; + p = strchr(p, ':'); + if (!p) return; + p++; + while (*p == ' ') p++; + size_t k = strcspn(p, "\n"); + if (k >= n) k = n - 1; + memcpy(out, p, k); + out[k] = 0; +} + +/* active tunnel (wg1 direct if aurora-vpnsel says so, else wg0 relay): handshake / transfer / endpoint (no keys are kept) */ +static void wg_poll(struct state *t) +{ + static double pt; static long long prx = -1, ptx = -1; static int pdirect = -1; + char m[16] = ""; + char w[48] = "", wv[16] = ""; + t->wl = 0; t->wlts = 0; + if (readfile("/run/aurora-vpnsel/wl", w, sizeof w) > 0 && sscanf(w, "%15s %lld", wv, &t->wlts) >= 1) + t->wl = !strcmp(wv, "on") ? 1 : !strcmp(wv, "off") ? 2 : !strcmp(wv, "nodata") ? 3 : 0; + t->direct = readfile("/run/aurora-vpnsel/mode", m, sizeof m) > 0 && !strncmp(m, "direct", 6) && access("/sys/class/net/wg1", F_OK) == 0; + if (t->direct != pdirect) { prx = -1; pdirect = t->direct; } /* new interface: restart the rate */ + const char *ifn = t->direct ? "wg1" : "wg0"; + char p[64]; snprintf(p, sizeof p, "/sys/class/net/%s", ifn); + t->wg = access(p, F_OK) == 0; + t->vless = access("/sys/class/net/singtun0", F_OK) == 0; + if (!t->wg) { t->hs = 0; t->wrx = t->wtx = -1; t->wrxr = t->wtxr = -1; t->wep[0] = 0; t->wmtu = 0; prx = -1; return; } + snprintf(p, sizeof p, "/sys/class/net/%s/mtu", ifn); t->wmtu = (int)readll(p); + char cmd[200]; + snprintf(cmd, sizeof cmd, "timeout 2 sh -c 'wg show %s latest-handshakes; echo @; wg show %s transfer; echo @; wg show %s endpoints; " + "echo @; wg show wg0 latest-handshakes; echo @; wg show wg1 latest-handshakes' 2>/dev/null", ifn, ifn, ifn); + t->hs0 = t->hs1 = 0; + FILE *f = popen(cmd, "r"); + if (!f) return; + char l[256], a[64], b[64], c[64]; int sect = 0; + while (fgets(l, sizeof l, f)) { + if (l[0] == '@') { sect++; continue; } + if (sect == 0 && sscanf(l, "%63s %63s", a, b) == 2) t->hs = atoll(b); + else if (sect == 1 && sscanf(l, "%63s %63s %63s", a, b, c) == 3) { t->wrx = atoll(b); t->wtx = atoll(c); } + else if (sect == 2 && sscanf(l, "%63s %63s", a, b) == 2) snprintf(t->wep, sizeof t->wep, "%s", b); + else if (sect == 3 && sscanf(l, "%63s %63s", a, b) == 2) t->hs0 = atoll(b); + else if (sect == 4 && sscanf(l, "%63s %63s", a, b) == 2) t->hs1 = atoll(b); + } + pclose(f); + double now = mono(); + if (prx >= 0 && t->wrx >= prx && now > pt) { t->wrxr = (t->wrx - prx) / (now - pt); t->wtxr = (t->wtx - ptx) / (now - pt); } + prx = t->wrx; ptx = t->wtx; pt = now; +} + +/* CLIENTS page: per-station byte counters from the AP + names/IPs from dnsmasq leases */ +static void clients_poll(struct state *t) +{ + static struct { char mac[18]; long long rx, tx; } prev[16]; + static int nprev; static double pt; + struct { char mac[18]; long long rx, tx; int sig; } cur[16]; + int n = 0; + FILE *f = popen("timeout 3 iw dev wlan0 station dump 2>/dev/null", "r"); + if (!f) return; + char l[256]; + while (fgets(l, sizeof l, f)) { + if (!strncmp(l, "Station ", 8) && n < 16) { + n++; sscanf(l + 8, "%17s", cur[n - 1].mac); cur[n - 1].rx = cur[n - 1].tx = -1; cur[n - 1].sig = 0; + } else if (n) { + char *p; + if ((p = strstr(l, "rx bytes:"))) cur[n - 1].rx = atoll(p + 9); + else if ((p = strstr(l, "tx bytes:"))) cur[n - 1].tx = atoll(p + 9); + else if ((p = strstr(l, "signal:")) && !strstr(l, "avg")) cur[n - 1].sig = atoi(p + 7); + } + } + pclose(f); + double now = mono(), dt = now - pt; + char leases[4096]; int hl = readfile(LEASES, leases, sizeof leases) > 0; + t->ncl = 0; + for (int i = 0; i < n && t->ncl < MAXCL; i++) { + struct client *c = &t->cl[t->ncl++]; + memset(c, 0, sizeof *c); + snprintf(c->mac, sizeof c->mac, "%s", cur[i].mac); + c->sig = cur[i].sig; c->rxr = c->txr = -1; + for (int j = 0; j < nprev; j++) + if (!strcmp(prev[j].mac, cur[i].mac) && dt > 0.2 && dt < 6 && cur[i].rx >= prev[j].rx && cur[i].tx >= prev[j].tx) { + c->rxr = (cur[i].rx - prev[j].rx) / dt; c->txr = (cur[i].tx - prev[j].tx) / dt; + } + /* lease line: expiry mac ip hostname client-id */ + if (hl) for (char *ln = leases; ln && *ln; ln = strchr(ln, '\n') ? strchr(ln, '\n') + 1 : NULL) { + char mac[18], ip[16], name[64]; + if (sscanf(ln, "%*s %17s %15s %63s", mac, ip, name) == 3 && !strcasecmp(mac, c->mac)) { + snprintf(c->ip, sizeof c->ip, "%s", ip); + snprintf(c->name, sizeof c->name, "%s", strcmp(name, "*") ? name : ""); + } + } + } + for (int i = 0; i < n; i++) { snprintf(prev[i].mac, sizeof prev[i].mac, "%s", cur[i].mac); prev[i].rx = cur[i].rx; prev[i].tx = cur[i].tx; } + nprev = n; pt = now; +} + +static void *worker(void *arg) +{ + (void)arg; + int tick = 0; + while (!quit) { + struct state t; + pthread_mutex_lock(&mu); t = st; pthread_mutex_unlock(&mu); + int pg = page; + + char b[4096]; + if (readfile("/run/aurora-modem/state", b, sizeof b) > 0) { b[strcspn(b, "\n")] = 0; snprintf(t.mstate, sizeof t.mstate, "%s", b); } + else snprintf(t.mstate, sizeof t.mstate, "--"); + t.mstart = 0; + FILE *f = fopen("/run/aurora-modem/lifecycle.log", "r"); + if (f) { + char l[512]; + while (fgets(l, sizeof l, f)) { + if (strstr(l, "=== START OK")) t.mstart = 1; + else if (strstr(l, "FAIL in") || strstr(l, "rollback")) t.mstart = -1; + else if (strstr(l, "=== START (")) t.mstart = 0; + } + fclose(f); + } + t.ip[0] = 0; + if (readfile("/run/aurora-modem/ipcfg", b, sizeof b) > 0) { + char *a = strstr(b, "\nA="); + if (a) { a += 3; size_t k = strcspn(a, "\n"); if (k < sizeof t.ip) { memcpy(t.ip, a, k); t.ip[k] = 0; } } + } + + t.ft = exe_pid(FT_EXE); + wg_poll(&t); + if (screen_on && pg == 1) clients_poll(&t); + else t.ncl = -1; + + if (tick % 5 == 0) { + /* LTE details from ModemManager, only if it runs (avoid spawning mmcli into a stopped stack) */ + t.mm_ok = 0; + if (pid_alive("/run/aurora-modem/mm.pid") && (f = popen("timeout 3 mmcli -m any --output-keyvalue 2>/dev/null", "r"))) { + char l[512], v[64]; + t.sig = -1; t.oper[0] = 0; t.rat[0] = 0; + while (fgets(l, sizeof l, f)) { + v[0] = 0; + if (strstr(l, "modem.3gpp.operator-name ")) { kv(l, "operator-name", v, sizeof v); snprintf(t.oper, sizeof t.oper, "%s", strcmp(v, "--") ? v : ""); t.mm_ok = 1; } + else if (strstr(l, "access-technologies.value[1]")) { kv(l, "]", v, sizeof v); snprintf(t.rat, sizeof t.rat, "%s", v); } + else if (strstr(l, "signal-quality.value")) { kv(l, "value", v, sizeof v); t.sig = atoi(v); } + } + pclose(f); + for (char *p = t.oper; *p; p++) if (*p >= 'a' && *p <= 'z') *p -= 32; + for (char *p = t.rat; *p; p++) if (*p >= 'a' && *p <= 'z') *p -= 32; + } + /* Wi-Fi AP + stations (one short iw call pair, every 5 s) */ + t.ap = -1; t.clients = -1; + if ((f = popen("timeout 3 iw dev wlan0 info 2>/dev/null; echo ---; timeout 3 iw dev wlan0 station dump 2>/dev/null", "r"))) { + char l[256]; int sect = 0, n = 0, ap = 0, any = 0; + while (fgets(l, sizeof l, f)) { + if (!strncmp(l, "---", 3)) { sect = 1; continue; } + if (!sect) { any = 1; if (strstr(l, "type AP")) ap = 1; } + else if (!strncmp(l, "Station ", 8)) n++; + } + pclose(f); + if (any) { t.ap = ap; t.clients = ap ? n : 0; } + } + } + + pthread_mutex_lock(&mu); + if (memcmp(&t, &st, sizeof t)) { + /* keep the main-thread fields current */ + t.rxr = st.rxr; t.txr = st.txr; t.wwan_ok = st.wwan_ok; t.defroute = st.defroute; + memcpy(t.chg, st.chg, sizeof t.chg); t.vbat_mv = st.vbat_mv; t.usb = st.usb; t.tempc = st.tempc; + st = t; dirty = 1; + } + pthread_mutex_unlock(&mu); + /* 2 s, but react at once to a page change (CLIENTS needs a first sample) */ + for (int i = 0; i < 20 && !quit && page == pg; i++) usleep(100000); + tick += 2; + } + return NULL; +} + +/* ---------- POWER key (evdev) ---------- */ +static Uint32 ev_key; + +static int open_pwrkey(void) +{ + char p[32], name[64]; + for (int i = 0; i < 32; i++) { + snprintf(p, sizeof p, "/dev/input/event%d", i); + int fd = open(p, O_RDONLY | O_CLOEXEC); + if (fd < 0) continue; + name[0] = 0; + if (ioctl(fd, EVIOCGNAME(sizeof name), name) >= 0 && strstr(name, "pwrkey")) { + printf("aurora-ui: power key %s (%s)\n", p, name); + return fd; + } + close(fd); + } + return -1; +} + +static void *keys(void *arg) +{ + (void)arg; + int fd = -1, warned = 0; + while (!quit) { + if (fd < 0 && (fd = open_pwrkey()) < 0) { + if (!warned++) fprintf(stderr, "aurora-ui: no pwrkey input device (retrying)\n"); + for (int i = 0; i < 50 && !quit; i++) usleep(100000); + continue; + } + struct pollfd pf = { .fd = fd, .events = POLLIN }; + if (poll(&pf, 1, 500) <= 0) continue; + struct input_event ev[16]; + ssize_t k = read(fd, ev, sizeof ev); + if (k <= 0) { close(fd); fd = -1; continue; } + for (int i = 0; i < (int)(k / sizeof ev[0]); i++) + if (ev[i].type == EV_KEY && ev[i].code == KEY_POWER && ev[i].value == 1) { + SDL_Event e; SDL_zero(e); e.type = ev_key; SDL_PushEvent(&e); + } + } + if (fd >= 0) close(fd); + return NULL; +} + +/* ---------- backlight (MPP4: max_brightness 1) ---------- */ +static char bl_path[300]; +static int bl_max = 1; + +static void bl_init(void) +{ + DIR *d = opendir("/sys/class/backlight"); + if (!d) return; + struct dirent *e; + while ((e = readdir(d))) { + if (e->d_name[0] == '.') continue; + char p[300]; + snprintf(p, sizeof p, "/sys/class/backlight/%s/max_brightness", e->d_name); + long long m = readll(p); + if (m > 0) bl_max = (int)m; + snprintf(bl_path, sizeof bl_path, "/sys/class/backlight/%s/brightness", e->d_name); + break; + } + closedir(d); +} + +static void bl_set(int on) +{ + if (!bl_path[0]) return; + FILE *f = fopen(bl_path, "w"); + if (!f) return; + fprintf(f, "%d\n", on ? bl_max : 0); + fclose(f); +} + +/* ---------- fast metrics (main thread, 1 s) ---------- */ +static long long prx = -1, ptx = -1; +static double pt; + +static void fast(void) +{ + char b[512]; + double now = mono(); + long long rx = readll("/sys/class/net/wwan0/statistics/rx_bytes"), tx = readll("/sys/class/net/wwan0/statistics/tx_bytes"); + /* the bam-dmux wwan0 driver keeps its netdev counters at 0 -> fall back to the Wi-Fi LAN side (traffic to/from clients): + download = wlan0 tx, upload = wlan0 rx */ + if (rx == 0 && tx == 0) { + long long lrx = readll("/sys/class/net/wlan0/statistics/rx_bytes"), ltx = readll("/sys/class/net/wlan0/statistics/tx_bytes"); + if (lrx >= 0 && ltx >= 0) { rx = ltx; tx = lrx; } + } + struct state t; pthread_mutex_lock(&mu); t = st; pthread_mutex_unlock(&mu); + t.wwan_ok = rx >= 0; + if (rx >= 0 && prx >= 0 && now > pt) { t.rxr = (rx - prx) / (now - pt); t.txr = (tx - ptx) / (now - pt); } + else { t.rxr = t.txr = -1; } + prx = rx; ptx = tx; pt = now; + + t.defroute = 0; + FILE *f = fopen("/proc/net/route", "r"); + if (f) { char l[256], ifn[32]; unsigned d; while (fgets(l, sizeof l, f)) if (sscanf(l, "%31s %x", ifn, &d) == 2 && d == 0) t.defroute = 1; fclose(f); } + + t.chg[0] = 0; t.vbat_mv = -1; + if (readfile("/sys/kernel/debug/pm8916_lbc/state", b, sizeof b) > 0) { + char s[16]; long uv; + char *p = strstr(b, "state "); + if (p && sscanf(p, "state %15s", s) == 1) snprintf(t.chg, sizeof t.chg, "%s", s); + if ((p = strstr(b, "vbat_uv ")) && sscanf(p, "vbat_uv %ld", &uv) == 1) t.vbat_mv = (int)(uv / 1000); + } + t.usb = (int)readll("/sys/class/power_supply/pm8916-lbc-chgr/online"); + + t.tempc = -1000; + DIR *d = opendir("/sys/class/thermal"); + if (d) { + struct dirent *e; char p[300]; + while ((e = readdir(d))) { + if (strncmp(e->d_name, "thermal_zone", 12)) continue; + snprintf(p, sizeof p, "/sys/class/thermal/%s/type", e->d_name); + if (readfile(p, b, sizeof b) > 0 && !strncmp(b, "pm8916-thermal", 14)) { + snprintf(p, sizeof p, "/sys/class/thermal/%s/temp", e->d_name); + long long v = readll(p); if (v > -100000) t.tempc = (int)(v / 1000); + } + } + closedir(d); + } + pthread_mutex_lock(&mu); + st.rxr = t.rxr; st.txr = t.txr; st.wwan_ok = t.wwan_ok; st.defroute = t.defroute; + memcpy(st.chg, t.chg, sizeof st.chg); st.vbat_mv = t.vbat_mv; st.usb = t.usb; st.tempc = t.tempc; + pthread_mutex_unlock(&mu); +} + +static void rate(char *o, size_t n, double r) +{ + if (r < 0) snprintf(o, n, "--"); + else if (r < 1000) snprintf(o, n, "%dB", (int)r); + else if (r < 1e6) snprintf(o, n, "%.*fK", r < 1e4 ? 1 : 0, r / 1e3); + else snprintf(o, n, "%.1fM", r / 1e6); +} + +static void age(char *o, size_t n, long long s) +{ + if (s < 100) snprintf(o, n, "%llds", s); + else if (s < 6000) snprintf(o, n, "%lldm", s / 60); + else snprintf(o, n, "%lldh", s / 3600); +} + +static void page_tag(void) +{ + char s[8]; snprintf(s, sizeof s, "%d/%d", page + 1, PAGES); stextr(0, C_DIM, s); +} + +/* ---------- log tail (6x8, wrapped to 21 columns) ---------- */ +/* UTF-8 -> ASCII: Cyrillic transliterated (the client logs Russian), everything else non-ASCII -> '?' */ +static void ascii(const char *in, char *out, size_t n) +{ + static const char *cyr[32] = { "a", "b", "v", "g", "d", "e", "zh", "z", "i", "y", "k", "l", "m", "n", "o", "p", + "r", "s", "t", "u", "f", "h", "c", "ch", "sh", "sch", "", "y", "", "e", "yu", "ya" }; + size_t k = 0; + for (const unsigned char *p = (const unsigned char *)in; *p && k + 4 < n; ) { + if (*p < 0x80) { if (*p >= 0x20 || *p == '\t') out[k++] = *p == '\t' ? ' ' : (char)*p; p++; continue; } + int len = (*p & 0xe0) == 0xc0 ? 2 : (*p & 0xf0) == 0xe0 ? 3 : (*p & 0xf8) == 0xf0 ? 4 : 1; + unsigned cp = len == 2 && p[1] ? ((p[0] & 0x1fu) << 6) | (p[1] & 0x3fu) : 0; + const char *r = "?"; + if (cp >= 0x410 && cp <= 0x44f) r = cyr[(cp - 0x410) % 32]; + else if (cp == 0x401 || cp == 0x451) r = "e"; + while (*r && k + 1 < n) out[k++] = *r++; + for (int i = 0; i < len && *p; i++) p++; + } + out[k] = 0; +} + +/* remove every "[...] " group, e.g. "[session=" */ +static void cut_group(char *s, const char *tag) +{ + char *p; + while ((p = strstr(s, tag))) { + char *e = strchr(p, ']'); + if (!e) break; + e++; if (*e == ' ') e++; + memmove(p, e, strlen(e) + 1); + } +} + +static void subst(char *s, size_t n, const char *from, const char *to) +{ + char *p = strstr(s, from); + if (!p || strlen(s) - strlen(from) + strlen(to) >= n) return; + memmove(p + strlen(to), p + strlen(from), strlen(p + strlen(from)) + 1); + memcpy(p, to, strlen(to)); +} + +typedef uint32_t (*clean_fn)(char *s, size_t n); + +/* free-turn: "2026/10/05 17:54:10 [INFO] [session=..] [STREAM 11] TURN allocation up: ..." -> "17:54:10 S11 TURN allocation up: ..." */ +static uint32_t clean_ft(char *s, size_t n) +{ + uint32_t c = C_TEXT; + if (strlen(s) > 11 && s[4] == '/' && s[7] == '/' && s[10] == ' ') memmove(s, s + 11, strlen(s + 11) + 1); + cut_group(s, "[session="); + if (strstr(s, "[ERROR]") || strstr(s, "[FATAL]")) c = C_BAD; + else if (strstr(s, "[WARN]")) c = C_WARN; + else if (strstr(s, "allocation up") || strstr(s, "connected")) c = C_OK; + subst(s, n, "TURN allocation up: relayed=", "TURN up "); + char *sv = strstr(s, " server="); if (sv && strstr(s, "TURN up ")) *sv = 0; + subst(s, n, "[INFO] ", ""); subst(s, n, "[WARN] ", "W "); subst(s, n, "[ERROR] ", "E "); subst(s, n, "[FATAL] ", "F "); + char *p = strstr(s, "[STREAM "); + if (p) { char *e = strchr(p, ']'); if (e) { *p = 'S'; memmove(p + 1, p + 8, e - (p + 8)); memmove(p + 1 + (e - (p + 8)), e + 1, strlen(e + 1) + 1); } } + return c; +} + +/* aurora-vpn: "[33044.31] === VPN UP OK mode=wg ..." */ +static uint32_t clean_vpn(char *s, size_t n) +{ + subst(s, n, "=== ", ""); + if (strstr(s, "FAIL")) return C_BAD; + if (strstr(s, "UP OK")) return C_OK; + if (strstr(s, "DOWN")) return C_WARN; + return C_DIM; +} + +/* draw the newest lines of path into rows [r0, SR) */ +static void log_tail(const char *path, int r0, clean_fn fn) +{ + static char buf[8192]; + FILE *f = fopen(path, "r"); + if (!f) { stext(0, r0, C_DIM, "(no log)"); return; } + fseek(f, 0, SEEK_END); + long sz = ftell(f), off = sz > (long)sizeof buf - 1 ? sz - (long)sizeof buf + 1 : 0; + fseek(f, off, SEEK_SET); + size_t k = fread(buf, 1, sizeof buf - 1, f); + fclose(f); + buf[k] = 0; + char *start = buf; + if (off > 0 && (start = strchr(buf, '\n'))) start++; + if (!start) return; + + /* collect lines, newest last */ + char *lines[128]; int nl = 0; + for (char *p = start; *p && nl < 128; ) { + lines[nl++] = p; + char *e = strchr(p, '\n'); + if (!e) break; + *e = 0; p = e + 1; + } + /* wrap from the newest line upwards until the area is full */ + int row = SR; + for (int i = nl - 1; i >= 0 && row > r0; i--) { + char a[512]; + ascii(lines[i], a, sizeof a); + if (!a[0]) continue; + uint32_t c = fn(a, sizeof a); + int len = (int)strlen(a), nr = (len + SC - 1) / SC; + for (int j = nr - 1; j >= 0 && row > r0; j--) { + char seg[SC + 1]; + snprintf(seg, sizeof seg, "%.*s", SC, a + j * SC); + stext(0, --row, c, seg); + } + } + if (row == SR) stext(0, r0, C_DIM, "(empty)"); +} + +/* ---------- screens ---------- */ +static void render_status(const struct state *t) +{ + char s[40], a[16], b[16]; + + /* row 0-1: LTE */ + int online = t->mstart == 1 && !strcmp(t->mstate, "BEARER_CONNECTED"); + if (t->mstart == 0 && strcmp(t->mstate, "BEARER_CONNECTED")) { + text(0, 0, C_WARN, "MODEM STARTING"); + snprintf(s, sizeof s, "%.16s", t->mstate); text(0, 16, C_DIM, s); + } else if (t->mstart < 0) { + text(0, 0, C_BAD, "MODEM FAIL"); + snprintf(s, sizeof s, "%.16s", t->mstate); text(0, 16, C_DIM, s); + } else { + snprintf(s, sizeof s, "%.11s", t->oper[0] ? t->oper : "LTE --"); text(0, 0, t->mm_ok ? C_TEXT : C_DIM, s); + textr(0, online ? C_OK : C_WARN, t->rat[0] ? t->rat : "--"); + bars(0, 16, t->mm_ok ? t->sig : -1, online ? C_OK : C_WARN); + if (t->mm_ok && t->sig >= 0) { snprintf(s, sizeof s, "%d%%", t->sig); text(36, 16, C_TEXT, s); } + else text(36, 16, C_DIM, "--"); + } + /* row 2: IPv4 */ + text(0, 32, t->ip[0] ? C_TEXT : C_DIM, t->ip[0] ? t->ip : "IP --"); + + /* row 3: Wi-Fi */ + if (t->ap < 0) text(0, 48, C_DIM, "WiFi --"); + else if (!t->ap) text(0, 48, C_BAD, "WiFi DOWN"); + else { snprintf(s, sizeof s, "WiFi %d cl", t->clients); text(0, 48, C_OK, s); } + + /* row 4: throughput (cp437 0x19 down, 0x18 up) */ + rate(a, sizeof a, t->rxr); rate(b, sizeof b, t->txr); + snprintf(s, sizeof s, "\x19%-6s \x18%s", a, b); text(0, 64, t->wwan_ok ? C_TEXT : C_DIM, s); + + /* row 5: battery (VADC VBAT from the B10 supervisor; no SOC) */ + if (t->vbat_mv > 0) snprintf(s, sizeof s, "BAT %d.%02dV", t->vbat_mv / 1000, (t->vbat_mv % 1000) / 10); + else snprintf(s, sizeof s, "BAT --"); + text(0, 80, t->vbat_mv > 0 ? C_TEXT : C_DIM, s); + const char *cs = t->chg[0] ? t->chg : (t->usb == 1 ? "USB" : "--"); + uint32_t cc = !strcmp(cs, "FAULT") ? C_BAD : (!strcmp(cs, "CHARGING") ? C_WARN : C_OK); + if (!strcmp(cs, "CHARGING")) cs = "CHG"; + snprintf(s, sizeof s, "%.5s", cs); textr(80, cc, s); + + /* row 6: VPN path + handshake age (DIR = direct wg1, FT = wg0 via free-turn) | operator whitelist (WL on, NET open, LTE! no data) */ + time_t now = time(NULL); + if (t->wg) { + long long hs = t->direct ? t->hs1 : t->hs0, ag = hs > 0 ? (long long)now - hs : -1; + int alive = ag >= 0 && ag <= (t->direct ? 150 : 180); + if (!t->direct && !t->ft) text(0, 96, C_BAD, "FT DOWN"); + else if (!alive) text(0, 96, C_BAD, "VPN DOWN"); + else { age(a, sizeof a, ag); snprintf(s, sizeof s, "%s %s", t->direct ? "DIR" : "FT", a); text(0, 96, C_OK, s); } + } else if (t->vless) text(0, 96, C_OK, "VPN VLESS"); + else text(0, 96, C_DIM, "VPN OFF"); + switch (t->wl) { + case 1: textr(96, C_WARN, "WL"); break; + case 2: textr(96, C_OK, "NET"); break; + case 3: textr(96, C_BAD, "LTE!"); break; + default: textr(96, C_DIM, "--"); + } + + /* row 7: clock + PMIC temperature + overall */ + struct tm tm; localtime_r(&now, &tm); + if (tm.tm_year + 1900 >= 2026) { snprintf(s, sizeof s, "%02d%c%02d", tm.tm_hour, (now & 1) ? ':' : ' ', tm.tm_min); text(0, 112, C_TEXT, s); } + else text(0, 112, C_DIM, "--:--"); + if (t->tempc > -1000) { snprintf(s, sizeof s, "%d\xf8", t->tempc); text(48, 112, t->tempc >= 60 ? C_BAD : C_DIM, s); } + textr(112, online ? C_OK : (t->mstart < 0 ? C_BAD : C_WARN), online ? "ONLINE" : (t->mstart < 0 ? "FAIL" : "WAIT")); +} + +static void render_clients(const struct state *t) +{ + char s[40], a[16], b[16]; + if (t->ncl < 0) { stext(0, 0, C_TEXT, "CLIENTS"); page_tag(); stext(0, 2, C_DIM, "sampling..."); return; } + snprintf(s, sizeof s, "CLIENTS %d", t->ncl); stext(0, 0, t->ncl ? C_OK : C_TEXT, s); page_tag(); + if (!t->ncl) { stext(0, 2, C_DIM, t->ap == 0 ? "Wi-Fi AP down" : "no stations"); return; } + double sr = 0, sx = 0; int known = 0; + for (int i = 0; i < t->ncl; i++) { + const struct client *c = &t->cl[i]; + const char *dot = strrchr(c->ip, '.'); + snprintf(s, sizeof s, "%-15.15s %s", c->name[0] ? c->name : (c->ip[0] ? c->ip : c->mac + 6), dot && c->name[0] ? dot : ""); + stext(0, 1 + i * 2, C_TEXT, s); + rate(a, sizeof a, c->txr); rate(b, sizeof b, c->rxr); + snprintf(s, sizeof s, " \x19%-6s\x18%-6s%4d", a, b, c->sig); + stext(0, 2 + i * 2, c->txr < 0 ? C_DIM : (c->txr + c->rxr >= 1e5 ? C_WARN : C_OK), s); + if (c->txr >= 0) { sr += c->txr; sx += c->rxr; known = 1; } + } + rate(a, sizeof a, known ? sr : -1); rate(b, sizeof b, known ? sx : -1); + snprintf(s, sizeof s, "SUM\x19%-6s\x18%-6s dBm", a, b); stext(0, SR - 1, C_DIM, s); +} + +static void render_ft(const struct state *t) +{ + char s[40]; + stext(0, 0, C_TEXT, "FREE-TURN"); page_tag(); + if (t->ft) snprintf(s, sizeof s, "running pid %d", t->ft); else snprintf(s, sizeof s, "NOT RUNNING"); + stext(0, 1, t->ft ? C_OK : C_BAD, s); + fill(0, 2 * 8 + 3, W, 1, C_DIM); + log_tail(FT_LOG, 3, clean_ft); +} + +/* aurora-vpnsel log: "[up] LAN -> wg1 (direct): ..." / "whitelist on -> off ..." */ +static uint32_t clean_sel(char *s, size_t n) +{ + subst(s, n, "LAN -> ", ""); subst(s, n, "=== ", ""); + if (strstr(s, "(direct)") || strstr(s, "-> off")) return C_OK; + if (strstr(s, "(relay)") || strstr(s, "-> on")) return C_WARN; + if (strstr(s, "nodata") || strstr(s, "FAIL")) return C_BAD; + return C_DIM; +} + +static void render_wg(const struct state *t) +{ + char s[48], a[16], b[16]; + time_t now = time(NULL); + stext(0, 0, C_TEXT, t->direct ? "VPN DIRECT" : "VPN RELAY"); page_tag(); + if (!t->wg) stext(0, 1, t->vless ? C_OK : C_BAD, t->vless ? "wg off, VLESS mode" : "tunnel DOWN"); + else { + long long a0 = t->hs0 > 0 ? (long long)now - t->hs0 : -1, a1 = t->hs1 > 0 ? (long long)now - t->hs1 : -1; + int ok1 = a1 >= 0 && a1 <= 150, ok0 = a0 >= 0 && a0 <= 180 && t->ft; + /* situation */ + if (t->direct) stext(0, 1, ok1 ? C_OK : C_BAD, ok1 ? "direct WG ok" : "direct WG lost"); + else stext(0, 1, ok0 ? C_WARN : C_BAD, ok0 ? "fallback: free-turn" : (t->ft ? "relay: no handshake" : "relay: FT not running")); + /* both tunnels */ + if (a1 >= 0) age(a, sizeof a, a1); else snprintf(a, sizeof a, "never"); + snprintf(s, sizeof s, "wg1 direct hs %s", a); stext(0, 2, ok1 ? C_OK : C_DIM, s); + if (a0 >= 0) age(a, sizeof a, a0); else snprintf(a, sizeof a, "never"); + snprintf(s, sizeof s, "wg0 relay hs %s", a); stext(0, 3, ok0 ? C_OK : C_DIM, s); + rate(a, sizeof a, t->wrxr); rate(b, sizeof b, t->wtxr); + snprintf(s, sizeof s, "\x19%-7s \x18%-7s %s", a, b, t->ft ? "FT on" : "FT off"); stext(0, 4, C_TEXT, s); + } + /* operator whitelist */ + long long wa = t->wlts > 0 ? (long long)now - t->wlts : -1; + if (wa >= 0) age(a, sizeof a, wa); else snprintf(a, sizeof a, "-"); + switch (t->wl) { + case 1: snprintf(s, sizeof s, "whitelist ON (%s ago)", a); stext(0, 5, C_WARN, s); break; + case 2: snprintf(s, sizeof s, "open internet (%s)", a); stext(0, 5, C_OK, s); break; + case 3: snprintf(s, sizeof s, "LTE: no data (%s)", a); stext(0, 5, C_BAD, s); break; + default: stext(0, 5, C_DIM, "whitelist: unknown"); + } + fill(0, 6 * 8 + 3, W, 1, C_DIM); + if (access("/run/aurora-vpnsel/vpnsel.log", F_OK) == 0) log_tail("/run/aurora-vpnsel/vpnsel.log", 7, clean_sel); + else log_tail(VPN_LOG, 7, clean_vpn); +} + +/* ---------- STACK: one-button tower game (page 5) ---------- */ +#define SB_H 6 /* block height, px */ +#define SB_W0 64 /* base width */ +#define SB_GROUND (H - 2) /* y of the ground line */ +#define SB_MAX 1024 +#define SB_BEST "/var/lib/aurora/stack-best" +enum { G_TITLE, G_PLAY, G_OVER }; +static int gm = G_TITLE; +static struct { float x; int w; uint32_t c; } tw[SB_MAX]; +static int tn; /* blocks in the tower (incl. base); score = tn - 1 */ +static float cx, cdir, cam; /* sliding block x, direction, camera offset (px the tower is shifted down) */ +static int cw, perfect, best, newbest; +static struct { float x, y, vy; int w; uint32_t c; int on; } fall; +static Uint32 flash_t, over_t; + +static uint32_t hue(int i) +{ + int h = (i * 14) % 360, x = (h % 60) * 255 / 60, r, g, b; + switch (h / 60) { case 0: r = 255; g = x; b = 0; break; case 1: r = 255 - x; g = 255; b = 0; break; + case 2: r = 0; g = 255; b = x; break; case 3: r = 0; g = 255 - x; b = 255; break; + case 4: r = x; g = 0; b = 255; break; default: r = 255; g = 0; b = 255 - x; } + /* pastel: mix 35% white, keep it readable on the dark background */ + r = r * 65 / 100 + 89; g = g * 65 / 100 + 89; b = b * 65 / 100 + 89; + return 0xff000000u | (uint32_t)r << 16 | (uint32_t)g << 8 | (uint32_t)b; +} + +static void sb_next(void) +{ + cw = tw[tn - 1].w; cdir = (tn & 1) ? 1 : -1; cx = cdir > 0 ? 0 : W - cw; +} + +static void sb_start(void) +{ + tn = 1; tw[0].w = SB_W0; tw[0].x = (W - SB_W0) / 2; tw[0].c = hue(0); + perfect = 0; newbest = 0; cam = 0; fall.on = 0; flash_t = 0; gm = G_PLAY; sb_next(); +} + +static void sb_load(void) +{ + char b[32]; best = readfile(SB_BEST, b, sizeof b) > 0 ? atoi(b) : 0; +} + +static void sb_over(void) +{ + gm = G_OVER; over_t = SDL_GetTicks(); + if (tn - 1 > best) { + best = tn - 1; newbest = 1; + FILE *f = fopen(SB_BEST ".tmp", "w"); + if (f) { fprintf(f, "%d\n", best); fclose(f); rename(SB_BEST ".tmp", SB_BEST); } + } + printf("aurora-ui: stack over score %d best %d\n", tn - 1, best); +} + +static void sb_drop(void) +{ + float px = tw[tn - 1].x; int pw = tw[tn - 1].w; + float l = cx > px ? cx : px, r = (cx + cw < px + pw) ? cx + cw : px + pw; + if (r - l < 1) { /* missed completely: the whole block falls */ + fall.x = cx; fall.w = cw; fall.y = 0; fall.vy = 0; fall.c = hue(tn); fall.on = 1; + sb_over(); return; + } + if (tn >= SB_MAX - 1) { sb_over(); return; } + if (cx - px < 2.5f && px - cx < 2.5f) { /* perfect: snap; every 3rd perfect in a row grows the block a bit */ + l = px; r = px + pw; perfect++; flash_t = SDL_GetTicks(); + if (perfect >= 3 && pw < SB_W0) { l -= 2; r += 2; if (l < 0) l = 0; if (r > W) r = W; } + } else { + perfect = 0; + fall.on = 1; fall.y = 0; fall.vy = 0; fall.c = hue(tn); + if (cx < px) { fall.x = cx; fall.w = (int)(px - cx + 0.5f); } else { fall.x = px + pw; fall.w = (int)(cx + cw - (px + pw) + 0.5f); } + } + tw[tn].x = l; tw[tn].w = (int)(r - l + 0.5f); tw[tn].c = hue(tn); tn++; + sb_next(); +} + +/* advance the game by dt seconds */ +static void sb_step(float dt) +{ + if (dt > 0.1f) dt = 0.1f; + if (gm == G_PLAY) { + float v = 40 + (tn - 1) * 3; if (v > 150) v = 150; + cx += cdir * v * dt; + if (cx < 0) { cx = 0; cdir = 1; } + if (cx + cw > W) { cx = W - cw; cdir = -1; } + } + /* keep the top of the tower ~44 px below the screen top */ + float target = (tn + 1) * SB_H - (SB_GROUND - 44); if (target < 0) target = 0; + cam += (target - cam) * (dt * 6 > 1 ? 1 : dt * 6); + if (fall.on) { fall.vy += 400 * dt; fall.y += fall.vy * dt; if (fall.y > H) fall.on = 0; } +} + +static int sb_y(int i) { return SB_GROUND - (i + 1) * SB_H + (int)cam; } /* top y of tower block i */ + +static void sb_block(int x, int y, int w, uint32_t c) +{ + fill(x, y, w, SB_H - 1, c); + fill(x, y, w, 1, 0xffffffffu); /* top highlight */ + fill(x, y + SB_H - 2, w, 1, (c >> 1 & 0x7f7f7f) | 0xff000000u); /* bottom shade */ +} + +static void render_stack(void) +{ + char s[32]; + fill(0, 0, W, H, 0xff101826u); + if (gm == G_TITLE) { + for (int i = 0; i < 7; i++) sb_block(34 + (i % 2 ? 3 : -3) + i, 100 - i * SB_H, 60 - 2 * i, hue(i * 3)); + text((W - 5 * 8) / 2, 4, C_TEXT, "STACK"); page_tag(); + snprintf(s, sizeof s, "best %d", best); stext((SC - (int)strlen(s)) / 2, 3, C_WARN, s); + stext(0, 5, C_DIM, " 2x press: play"); + stext(0, 6, C_DIM, " 1x press: next page"); + fill(0, SB_GROUND, W, 2, C_DIM); + return; + } + fill(0, SB_GROUND + (int)cam, W, 2, C_DIM); + for (int i = 0; i < tn; i++) { + int y = sb_y(i); + if (y > H || y + SB_H < 16) continue; + sb_block((int)(tw[i].x + 0.5f), y, tw[i].w, tw[i].c); + } + if (gm == G_PLAY) { + int y = sb_y(tn); + sb_block((int)(cx + 0.5f), y, cw, hue(tn)); + if (flash_t && SDL_GetTicks() - flash_t < 250) { /* perfect: white outline on the block just placed */ + int py = sb_y(tn - 1), px = (int)(tw[tn - 1].x + 0.5f), pw = tw[tn - 1].w; + fill(px - 1, py - 1, pw + 2, 1, 0xffffffffu); fill(px - 1, py + SB_H - 1, pw + 2, 1, 0xffffffffu); + fill(px - 1, py - 1, 1, SB_H + 1, 0xffffffffu); fill(px + pw, py - 1, 1, SB_H + 1, 0xffffffffu); + } + } + if (fall.on) sb_block((int)(fall.x + 0.5f), sb_y(tn - (gm == G_OVER && fall.w == cw ? 0 : 1)) + (int)fall.y, fall.w, fall.c); + /* HUD */ + fill(0, 0, W, 16, 0xff101826u); + snprintf(s, sizeof s, "%d", tn - 1); text(2, 0, C_TEXT, s); + snprintf(s, sizeof s, "best %d", best > tn - 1 ? best : tn - 1); stextr(0, C_DIM, s); + if (perfect >= 2 && gm == G_PLAY) { snprintf(s, sizeof s, "perfect x%d", perfect); stextr(1, C_OK, s); } + if (gm == G_OVER) { + fill(14, 38, 100, 52, 0xe0000000u); fill(14, 38, 100, 1, C_DIM); fill(14, 89, 100, 1, C_DIM); + text((W - 9 * 8) / 2, 42, C_BAD, "GAME OVER"); + snprintf(s, sizeof s, "score %d", tn - 1); stext((SC - (int)strlen(s)) / 2, 8, C_TEXT, s); + stext((SC - 9) / 2, 9, newbest ? C_OK : C_DIM, newbest ? "NEW BEST!" : ""); + if (!newbest) { snprintf(s, sizeof s, "best %d", best); stext((SC - (int)strlen(s)) / 2, 9, C_DIM, s); } + } +} + +static void render(void) +{ + struct state t; pthread_mutex_lock(&mu); t = st; pthread_mutex_unlock(&mu); + fill(0, 0, W, H, C_BG); + switch (page) { + case 1: render_clients(&t); break; + case 2: render_ft(&t); break; + case 3: render_wg(&t); break; + case 4: render_stack(); break; + default: render_status(&t); + } +} + +static void on_sig(int s) { (void)s; quit = 1; } + +int main(void) +{ + setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stderr, NULL, _IONBF, 0); + struct timespec t0; clock_gettime(CLOCK_MONOTONIC, &t0); + signal(SIGTERM, on_sig); signal(SIGINT, on_sig); signal(SIGPIPE, SIG_IGN); + const char *bs = getenv("AURORA_UI_BLANK_S"); + Uint32 blank_ms = (bs && atoi(bs) > 0 ? (Uint32)atoi(bs) : 180) * 1000; + + if (SDL_Init(SDL_INIT_VIDEO)) { fprintf(stderr, "aurora-ui: SDL_Init: %s\n", SDL_GetError()); return 1; } + const char *drv = SDL_GetCurrentVideoDriver(); + printf("aurora-ui: SDL %d.%d.%d video driver %s\n", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL, drv ? drv : "?"); + if (!drv || strcmp(drv, "wayland")) { fprintf(stderr, "aurora-ui: refusing to run on video driver '%s' (Wayland required)\n", drv ? drv : "?"); SDL_Quit(); return 2; } + + SDL_Window *win = SDL_CreateWindow("aurora-ui", 0, 0, W, H, SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_BORDERLESS); + if (!win) { fprintf(stderr, "aurora-ui: window: %s\n", SDL_GetError()); SDL_Quit(); return 1; } + SDL_ShowCursor(SDL_DISABLE); + SDL_Renderer *r = SDL_CreateRenderer(win, -1, 0); + if (!r) { fprintf(stderr, "aurora-ui: renderer: %s\n", SDL_GetError()); SDL_Quit(); return 1; } + SDL_RendererInfo ri; SDL_GetRendererInfo(r, &ri); + SDL_Texture *tex = SDL_CreateTexture(r, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, W, H); + int ww, wh; SDL_GetWindowSize(win, &ww, &wh); + printf("aurora-ui: renderer %s, window %dx%d, blank after %u s\n", ri.name, ww, wh, blank_ms / 1000); + + bl_init(); bl_set(1); + ev_key = SDL_RegisterEvents(1); + pthread_t th, tk; pthread_create(&th, NULL, worker, NULL); pthread_create(&tk, NULL, keys, NULL); + Uint32 last = 0, act = SDL_GetTicks(), tap = 0, lastf = SDL_GetTicks(); int first = 1, pend = 0; + sb_load(); + while (!quit) { + SDL_Event e; + int game = screen_on && page == 4 && (gm != G_TITLE); + if (SDL_WaitEventTimeout(&e, game ? 8 : (pend ? 40 : 250))) { + if (e.type == SDL_QUIT) quit = 1; + if (e.type == SDL_WINDOWEVENT) dirty = 1; + if (e.type == ev_key) { + Uint32 k = SDL_GetTicks(); + if (!screen_on) { page = 0; screen_on = 1; bl_set(1); } + else if (page == 4 && gm == G_PLAY) sb_drop(); + else if (page == 4 && gm == G_OVER) { if (k - over_t > 700) gm = G_TITLE; } + else if (page == 4 && pend && k - tap <= 400) { pend = 0; sb_start(); lastf = k; } + else if (page == 4) { pend = 1; tap = k; } /* wait: a 2nd press within 400 ms starts a game */ + else page = (page + 1) % PAGES; + act = k; dirty = 1; + printf("aurora-ui: key -> page %d%s\n", page, page == 4 ? (gm == G_PLAY ? " play" : gm == G_OVER ? " over" : " title") : ""); + } + } + Uint32 now = SDL_GetTicks(); + if (pend && now - tap > 400) { pend = 0; page = 0; dirty = 1; printf("aurora-ui: key -> page 0\n"); } + if (game && now - lastf >= 33) { sb_step((now - lastf) / 1000.0f); lastf = now; dirty = 1; } + else if (!game) lastf = now; + if (gm == G_OVER && now - over_t > 15000) gm = G_TITLE; + if (screen_on && now - act >= blank_ms) { screen_on = 0; page = 0; gm = G_TITLE; pend = 0; bl_set(0); printf("aurora-ui: blank\n"); } + if (now - last >= 1000) { last = now; fast(); dirty = 1; } + if (dirty && screen_on) { + dirty = 0; + render(); + SDL_UpdateTexture(tex, NULL, fb, W * 4); + SDL_RenderClear(r); + SDL_RenderCopy(r, tex, NULL, NULL); + SDL_RenderPresent(r); + if (first) { + struct timespec t1; clock_gettime(CLOCK_MONOTONIC, &t1); + printf("aurora-ui: first frame after %.3f s\n", (t1.tv_sec - t0.tv_sec) + (t1.tv_nsec - t0.tv_nsec) / 1e9); + first = 0; + } + } + } + pthread_join(th, NULL); pthread_join(tk, NULL); + bl_set(1); /* leave the panel lit for fbcon/getty */ + SDL_DestroyTexture(tex); SDL_DestroyRenderer(r); SDL_DestroyWindow(win); SDL_Quit(); + printf("aurora-ui: exit\n"); + return 0; +} diff --git a/b16/src/font6x8.h b/b16/src/font6x8.h new file mode 100644 index 0000000..5485ff9 --- /dev/null +++ b/b16/src/font6x8.h @@ -0,0 +1,259 @@ +/* 6x8 cp437 bitmap font extracted from Linux lib/fonts/font_6x8.c (GPL-2.0), 256 glyphs x 8 rows, MSB = left pixel, 6 px wide. */ +static const unsigned char font6x8[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x84, 0xcc, 0x84, 0xcc, 0xb4, 0x78, 0x00, + 0x78, 0xfc, 0xb4, 0xfc, 0xb4, 0xcc, 0x78, 0x00, + 0x00, 0x28, 0x7c, 0x7c, 0x38, 0x10, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x7c, 0x38, 0x10, 0x00, 0x00, + 0x00, 0x38, 0x38, 0x6c, 0x6c, 0x10, 0x38, 0x00, + 0x00, 0x10, 0x38, 0x7c, 0x7c, 0x10, 0x38, 0x00, + 0x00, 0x00, 0x30, 0x78, 0x30, 0x00, 0x00, 0x00, + 0xfc, 0xfc, 0xcc, 0x84, 0xcc, 0xfc, 0xfc, 0xfc, + 0x00, 0x30, 0x48, 0x84, 0x48, 0x30, 0x00, 0x00, + 0xfc, 0xcc, 0xb4, 0x78, 0xb4, 0xcc, 0xfc, 0xfc, + 0x3c, 0x14, 0x20, 0x78, 0x44, 0x44, 0x38, 0x00, + 0x38, 0x44, 0x44, 0x38, 0x10, 0x38, 0x10, 0x00, + 0x18, 0x14, 0x14, 0x10, 0x10, 0x70, 0x60, 0x00, + 0x3c, 0x24, 0x3c, 0x24, 0x24, 0x6c, 0x6c, 0x00, + 0x10, 0x54, 0x38, 0x6c, 0x38, 0x54, 0x10, 0x00, + 0x40, 0x60, 0x70, 0x78, 0x70, 0x60, 0x40, 0x00, + 0x04, 0x0c, 0x1c, 0x3c, 0x1c, 0x0c, 0x04, 0x00, + 0x10, 0x38, 0x54, 0x10, 0x54, 0x38, 0x10, 0x00, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x00, 0x48, 0x00, + 0x3c, 0x54, 0x54, 0x3c, 0x14, 0x14, 0x14, 0x00, + 0x38, 0x44, 0x30, 0x28, 0x14, 0x0c, 0x44, 0x38, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, + 0x10, 0x38, 0x54, 0x10, 0x54, 0x38, 0x10, 0x7c, + 0x10, 0x38, 0x54, 0x10, 0x10, 0x10, 0x10, 0x00, + 0x10, 0x10, 0x10, 0x10, 0x54, 0x38, 0x10, 0x00, + 0x00, 0x10, 0x08, 0x7c, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x7c, 0x20, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x78, 0x00, + 0x00, 0x48, 0x84, 0xfc, 0x84, 0x48, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0x00, + 0x00, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, + 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x28, 0x7c, 0x28, 0x28, 0x7c, 0x28, 0x00, + 0x10, 0x38, 0x40, 0x30, 0x08, 0x70, 0x20, 0x00, + 0x64, 0x64, 0x08, 0x10, 0x20, 0x4c, 0x4c, 0x00, + 0x30, 0x48, 0x50, 0x20, 0x54, 0x48, 0x34, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, + 0x20, 0x10, 0x08, 0x08, 0x08, 0x10, 0x20, 0x00, + 0x10, 0x54, 0x38, 0x54, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x20, + 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x40, + 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38, 0x00, + 0x10, 0x30, 0x50, 0x10, 0x10, 0x10, 0x7c, 0x00, + 0x38, 0x44, 0x04, 0x08, 0x10, 0x20, 0x7c, 0x00, + 0x38, 0x44, 0x04, 0x18, 0x04, 0x44, 0x38, 0x00, + 0x08, 0x18, 0x28, 0x48, 0x7c, 0x08, 0x08, 0x00, + 0x7c, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00, + 0x18, 0x20, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00, + 0x7c, 0x04, 0x04, 0x08, 0x10, 0x10, 0x10, 0x00, + 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00, + 0x38, 0x44, 0x44, 0x3c, 0x04, 0x08, 0x30, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x20, + 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, + 0x00, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00, + 0x38, 0x44, 0x04, 0x08, 0x10, 0x00, 0x10, 0x00, + 0x38, 0x44, 0x5c, 0x54, 0x5c, 0x40, 0x38, 0x00, + 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00, + 0x78, 0x24, 0x24, 0x38, 0x24, 0x24, 0x78, 0x00, + 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00, + 0x78, 0x24, 0x24, 0x24, 0x24, 0x24, 0x78, 0x00, + 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00, + 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00, + 0x38, 0x44, 0x40, 0x5c, 0x44, 0x44, 0x38, 0x00, + 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00, + 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, + 0x1c, 0x08, 0x08, 0x08, 0x48, 0x48, 0x30, 0x00, + 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00, + 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00, + 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x44, 0x00, + 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00, + 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00, + 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00, + 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00, + 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00, + 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00, + 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00, + 0x44, 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x00, + 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00, + 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x18, 0x00, + 0x40, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, + 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x30, 0x00, + 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, + 0x40, 0x40, 0x58, 0x64, 0x44, 0x64, 0x58, 0x00, + 0x00, 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x00, + 0x04, 0x04, 0x34, 0x4c, 0x44, 0x4c, 0x34, 0x00, + 0x00, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, + 0x0c, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x00, + 0x00, 0x34, 0x4c, 0x44, 0x4c, 0x34, 0x04, 0x38, + 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00, + 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00, + 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x60, + 0x40, 0x40, 0x48, 0x50, 0x70, 0x48, 0x44, 0x00, + 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, + 0x00, 0x00, 0x68, 0x54, 0x54, 0x54, 0x54, 0x00, + 0x00, 0x00, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, + 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x00, 0x00, 0x78, 0x44, 0x64, 0x58, 0x40, 0x40, + 0x00, 0x00, 0x3c, 0x44, 0x4c, 0x34, 0x04, 0x04, + 0x00, 0x00, 0x58, 0x64, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x3c, 0x40, 0x38, 0x04, 0x78, 0x00, + 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x0c, 0x00, + 0x00, 0x00, 0x44, 0x44, 0x44, 0x4c, 0x34, 0x00, + 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00, + 0x00, 0x00, 0x54, 0x54, 0x54, 0x54, 0x28, 0x00, + 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, + 0x00, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x38, + 0x00, 0x00, 0x7c, 0x08, 0x10, 0x20, 0x7c, 0x00, + 0x08, 0x10, 0x10, 0x20, 0x10, 0x10, 0x08, 0x00, + 0x10, 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x00, + 0x20, 0x10, 0x10, 0x08, 0x10, 0x10, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x54, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x28, 0x44, 0x44, 0x7c, 0x00, + 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x10, 0x20, + 0x00, 0x28, 0x00, 0x44, 0x44, 0x4c, 0x34, 0x00, + 0x18, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, + 0x18, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, + 0x28, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, + 0x18, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, + 0x3c, 0x18, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, + 0x00, 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x10, + 0x18, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, + 0x28, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, + 0x18, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, + 0x28, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00, + 0x18, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00, + 0x18, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00, + 0x44, 0x10, 0x28, 0x44, 0x7c, 0x44, 0x44, 0x00, + 0x30, 0x48, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, + 0x10, 0x7c, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00, + 0x00, 0x00, 0x78, 0x14, 0x7c, 0x50, 0x3c, 0x00, + 0x3c, 0x50, 0x50, 0x78, 0x50, 0x50, 0x5c, 0x00, + 0x18, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x28, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x18, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x10, 0x28, 0x00, 0x44, 0x44, 0x4c, 0x34, 0x00, + 0x20, 0x10, 0x00, 0x44, 0x44, 0x4c, 0x34, 0x00, + 0x28, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x38, + 0x84, 0x38, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x88, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x10, 0x38, 0x54, 0x50, 0x54, 0x38, 0x10, 0x00, + 0x30, 0x48, 0x40, 0x70, 0x40, 0x44, 0x78, 0x00, + 0x44, 0x28, 0x7c, 0x10, 0x7c, 0x10, 0x10, 0x00, + 0x70, 0x48, 0x70, 0x48, 0x5c, 0x48, 0x44, 0x00, + 0x0c, 0x10, 0x10, 0x38, 0x10, 0x10, 0x60, 0x00, + 0x18, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, + 0x08, 0x10, 0x00, 0x30, 0x10, 0x10, 0x38, 0x00, + 0x08, 0x10, 0x00, 0x38, 0x44, 0x44, 0x38, 0x00, + 0x08, 0x10, 0x00, 0x44, 0x44, 0x4c, 0x34, 0x00, + 0x34, 0x58, 0x00, 0x58, 0x64, 0x44, 0x44, 0x00, + 0x58, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00, + 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x7c, 0x00, + 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x7c, 0x00, + 0x10, 0x00, 0x10, 0x20, 0x40, 0x44, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0x04, 0x04, 0x00, 0x00, + 0x20, 0x24, 0x28, 0x10, 0x28, 0x44, 0x08, 0x1c, + 0x20, 0x24, 0x28, 0x10, 0x28, 0x58, 0x3c, 0x08, + 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, + 0x00, 0x00, 0x24, 0x48, 0x90, 0x48, 0x24, 0x00, + 0x00, 0x00, 0x90, 0x48, 0x24, 0x48, 0x90, 0x00, + 0x10, 0x44, 0x10, 0x44, 0x10, 0x44, 0x10, 0x44, + 0xa8, 0x54, 0xa8, 0x54, 0xa8, 0x54, 0xa8, 0x54, + 0xdc, 0x74, 0xdc, 0x74, 0xdc, 0x74, 0xdc, 0x74, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0xf0, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0xf0, 0x10, 0xf0, 0x10, 0x10, 0x10, + 0x28, 0x28, 0x28, 0xe8, 0x28, 0x28, 0x28, 0x28, + 0x00, 0x00, 0x00, 0xf8, 0x28, 0x28, 0x28, 0x28, + 0x00, 0x00, 0xf0, 0x10, 0xf0, 0x10, 0x10, 0x10, + 0x28, 0x28, 0xe8, 0x08, 0xe8, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x00, 0x00, 0xf8, 0x08, 0xe8, 0x28, 0x28, 0x28, + 0x28, 0x28, 0xe8, 0x08, 0xf8, 0x00, 0x00, 0x00, + 0x28, 0x28, 0x28, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x10, 0xf0, 0x10, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf0, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x10, 0x10, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x1c, 0x10, 0x10, 0x10, 0x10, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x10, 0x10, 0xfc, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x1c, 0x10, 0x1c, 0x10, 0x10, 0x10, + 0x28, 0x28, 0x28, 0x2c, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x2c, 0x20, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x20, 0x2c, 0x28, 0x28, 0x28, + 0x28, 0x28, 0xec, 0x00, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x00, 0xec, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x2c, 0x20, 0x2c, 0x28, 0x28, 0x28, + 0x00, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, 0x00, + 0x28, 0x28, 0xec, 0x00, 0xec, 0x28, 0x28, 0x28, + 0x10, 0x10, 0xfc, 0x00, 0xfc, 0x00, 0x00, 0x00, + 0x28, 0x28, 0x28, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x00, 0xfc, 0x10, 0x10, 0x10, + 0x00, 0x00, 0x00, 0xfc, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x10, 0x1c, 0x10, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x10, 0x1c, 0x10, 0x10, 0x10, + 0x00, 0x00, 0x00, 0x3c, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0xfc, 0x28, 0x28, 0x28, 0x28, + 0x10, 0x10, 0xfc, 0x10, 0xfc, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x10, 0x10, 0x10, 0x10, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xfc, 0xfc, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0xfc, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x48, 0x48, 0x48, 0x34, 0x00, + 0x24, 0x44, 0x48, 0x48, 0x44, 0x44, 0x58, 0x40, + 0x7c, 0x44, 0x44, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x7c, 0x28, 0x28, 0x28, 0x28, 0x00, + 0x7c, 0x24, 0x10, 0x08, 0x10, 0x24, 0x7c, 0x00, + 0x00, 0x00, 0x3c, 0x48, 0x48, 0x48, 0x30, 0x00, + 0x00, 0x00, 0x48, 0x48, 0x48, 0x48, 0x74, 0x40, + 0x00, 0x00, 0x7c, 0x10, 0x10, 0x10, 0x0c, 0x00, + 0x10, 0x38, 0x44, 0x44, 0x38, 0x10, 0x38, 0x00, + 0x38, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x38, 0x00, + 0x38, 0x44, 0x44, 0x44, 0x44, 0x28, 0x6c, 0x00, + 0x18, 0x20, 0x18, 0x24, 0x24, 0x24, 0x18, 0x00, + 0x00, 0x00, 0x38, 0x54, 0x54, 0x54, 0x38, 0x00, + 0x00, 0x04, 0x38, 0x54, 0x54, 0x38, 0x40, 0x00, + 0x3c, 0x40, 0x40, 0x38, 0x40, 0x40, 0x3c, 0x00, + 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x00, + 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, + 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x7c, 0x00, + 0x20, 0x10, 0x08, 0x10, 0x20, 0x00, 0x38, 0x00, + 0x08, 0x10, 0x20, 0x10, 0x08, 0x00, 0x38, 0x00, + 0x0c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x60, + 0x00, 0x10, 0x00, 0x7c, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x20, 0x54, 0x08, 0x20, 0x54, 0x08, 0x00, + 0x30, 0x48, 0x48, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x08, 0x08, 0x50, 0x50, 0x20, 0x20, 0x00, + 0x60, 0x50, 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x10, 0x20, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/b16/src/font8x16.h b/b16/src/font8x16.h new file mode 100644 index 0000000..236e49b --- /dev/null +++ b/b16/src/font8x16.h @@ -0,0 +1,259 @@ +/* 8x16 cp437 bitmap font extracted from Linux lib/fonts/font_8x16.c (GPL-2.0), 256 glyphs x 16 rows, MSB = left pixel. */ +static const unsigned char font8x16[4096] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xd6, 0xd6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0xee, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x7c, 0x38, 0x38, 0x7c, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x32, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x18, 0x70, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x70, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x10, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x18, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x36, 0x36, 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0xe0, 0x62, 0x66, 0x6c, 0x18, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00, + 0x00, 0x60, 0xe0, 0x62, 0x66, 0x6c, 0x18, 0x30, 0x66, 0xce, 0x9a, 0x3f, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6c, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x66, 0x0c, 0x18, 0x32, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/b17/aurora-modem b/b17/aurora-modem new file mode 100644 index 0000000..456ee04 --- /dev/null +++ b/b17/aurora-modem @@ -0,0 +1,405 @@ +#!/bin/sh +# Aurora (JZ08AU, MSM8916) modem lifecycle controller - B6P production build of the B6N-proven controller (b6j/aurora-modem). +# aurora-modem start | disconnect | stop | status | addroute +# start: [rmtfs -r -P -s -v -> MSS -> wwan0qmi0 (+2 s port-race guard) -> SIM ready -> DMS online (one SET, retried only on DeviceNotReady) +# -> registration state machine (registered + PS attached, deadline REG_TIMEOUT, default 600 s)] (skipped when rmtfs+MSS already run) +# -> udevd/dbus/polkitd/ModemManager (reused if running) -> ONE simple-connect -> bearer IPv4 on wwanN + /32 routes only (no default route). +# disconnect: simple-disconnect, our routes, flush+down all wwan*, wait BAM-DMUX suspended. MSS, rmtfs, MM and radio stay up; 'start' reconnects. +# stop: disconnect -> stop MM (+qmi-proxy) -> BAM-DMUX suspended -> SIGTERM rmtfs (rmtfs writes 'stop' -> MSS offline) -> rmtfs exits last. +# Never writes eMMC/NV: rmtfs -r keeps EFS writes in RAM; refuses to start unless modemst1/2, fsc, fsg, persist, mmcblk0 are getro=1 and /firmware is ro. +# Does not need a valid wall clock (all timing from /proc/uptime). No automatic MSS/rmtfs retry. +# B7C: time bootstrap. At fixed points of start (QMI_READY, SIM_READY, RADIO_ONLINE, each REG transition, REGISTERED, BEARER_CONNECTED) logs QMI DMS +# + 3GPP NITZ. The first valid DMS (>= 2026, >= image build, plausible, |DMS-NITZ| < 2 s; NITZ required unless TIME_REQUIRE_NITZ=0) steps +# CLOCK_REALTIME once with date -s (never backwards, never RTC). After START OK, a background SNTP check (VNIIFTRI, query only) logs the residual +# and steps only if |offset| >= TIME_NTP_STEP s. No slew/discipline: STA_UNSYNC stays set, so the kernel RTC_SYSTOHC 11-min RTC write never engages. +# B17: the USB-management ping (480s 172.16.42.2) during registration is OFF unless MGMT_CHECK=1 in $CONF — the board must +# work standalone (Wi-Fi router on battery/charger, no host). The post-connect "mgmt route still via usb0" check runs only if usb0 has IPv4. +# Exit: 0 ok, 1 failure/timeout (start rolls back with a full stop unless MODEM_KEEP_ON_FAIL=1), 2 usage/config error. + +CONF=${MODEM_CONF:-/etc/aurora/modem.conf} +D=/run/aurora-modem; mkdir -p $D +LOG=$D/lifecycle.log; STATE=$D/state; ROUTES=$D/routes; BEARER=$D/bearer +RPNAME=4080000.remoteproc +BAM=/sys/bus/platform/devices/4080000.remoteproc:bam-dmux +Q="qmicli -p -d /dev/wwan0qmi0" + +# --- timeouts (s); override in $CONF +T_RMTFS=5 T_MSS=60 T_QMI_DEV=30 T_QMI=45 T_SIM=60 T_ONLINE=30 T_REG=180 T_MM_DETECT=150 T_MM_REG=60 T_CONNECT=150 +T_DISCONNECT=60 T_DISABLE=30 T_MM_EXIT=30 T_BAM_IDLE=15 T_MSS_STOP=30 T_RMTFS_EXIT=15 +APN= USER= PASSWORD= AUTH= IP_TYPE=ipv4 REG_TIMEOUT=600 ROUTES= KEEP=${MODEM_KEEP_ON_FAIL:-0} +MGMT_CHECK=0 TIME_BOOTSTRAP=1 TIME_REQUIRE_NITZ=1 TIME_MAX_DMS_NITZ=2 TIME_NTP="ntp1.vniiftri.ru ntp2.vniiftri.ru ntp3.vniiftri.ru" TIME_NTP_STEP=1 +[ -f "$CONF" ] && . "$CONF" +MODEM_APN=$APN MODEM_USER=$USER MODEM_PASSWORD=$PASSWORD MODEM_AUTH=$AUTH MODEM_IP_TYPE=${IP_TYPE:-ipv4} MODEM_TEST_ROUTES=$ROUTES T_REG=${REG_TIMEOUT:-600} +MODEM_KEEP_ON_FAIL=${MODEM_KEEP_ON_FAIL:-$KEEP} + +mmc() { mmcli "$@" 2>/dev/null | tr -d '\033' | sed 's/\[[0-9;]*m//g'; } # mmcli colours values even without a tty +now() { cut -d' ' -f1 /proc/uptime; } +log() { m="[$(now)] $*"; echo "$m" >> $LOG; echo "$m"; echo "<5>[aurora-modem] $*" > /dev/kmsg 2>/dev/null; } +setstate() { o=$(cat $STATE 2>/dev/null); echo "$1" > $STATE; log "STATE ${o:-OFF} -> $1"; } +die() { log "FAIL: $*"; exit 1; } +# wait_for TIMEOUT INTERVAL cmd... : poll a real condition, no blind sleeps +wait_for() { _to=$1; _iv=$2; shift 2; _t0=$(now) + while :; do "$@" && return 0 + [ "$(awk -v a="$(now)" -v b="$_t0" -v t="$_to" 'BEGIN{print (a-b>=t)}')" = 1 ] && return 1 + sleep $_iv; done; } +since() { awk -v a="$(now)" -v b="$1" 'BEGIN{printf "%.1f", a-b}'; } + +rp() { for r in /sys/class/remoteproc/remoteproc*; do [ "$(cat $r/name 2>/dev/null)" = $RPNAME ] && echo $r && return; done; } +mss_state() { r=$(rp); [ -n "$r" ] && cat $r/state || echo none; } +mss_is() { [ "$(mss_state)" = "$1" ]; } +rmtfs_pid() { pidof rmtfs; } +mm_pid() { pidof ModemManager; } +bam_pm() { cat $BAM/power/runtime_status 2>/dev/null || echo none; } +bam_suspended() { [ "$(bam_pm)" = suspended ]; } +qmi_dev() { [ -e /dev/wwan0qmi0 ] && [ -e /sys/class/net/wwan0 ]; } +link_up() { [ $(( $(cat /sys/class/net/$1/flags) & 1 )) -ne 0 ]; } +mm_idx() { mmc -L | grep -oE "Modem/[0-9]+" | head -1 | cut -d/ -f2; } +mm_has_modem() { [ -n "$(mm_idx)" ]; } +mm_state() { mmc -m "$(mm_idx)" | awk -F': ' '/\| +state: /{gsub(/ /,"",$2); print $2; exit}'; } +# MM initialized: proven session 22 pass 1 connected from 'disabled' (Simple.Connect enables the modem itself) +mm_usable() { case "$(mm_state)" in disabled|enabling|enabled|searching|registered|connected|connecting) return 0;; esac; return 1; } +qmi_ok() { timeout 20 $Q --dms-get-operating-mode > $D/qmi-mode.txt 2>&1 && grep -q "Mode:" $D/qmi-mode.txt; } +qmi_reg() { timeout 20 $Q --nas-get-serving-system > $D/qmi-ss.txt 2>&1 + grep -q "Registration state: 'registered'" $D/qmi-ss.txt && grep -q "PS: 'attached'" $D/qmi-ss.txt; } +sim_ready() { timeout 20 $Q --uim-get-card-status > $D/qmi-uim.txt 2>&1 + grep -q "Card state: 'present'" $D/qmi-uim.txt && grep -q "Application state: 'ready'" $D/qmi-uim.txt; } +set_online() { timeout 20 $Q --dms-set-operating-mode=online > $D/qmi-set.txt 2>&1; r=$?; cat $D/qmi-set.txt >> $LOG + [ $r = 0 ] && return 0; grep -q "DeviceNotReady" $D/qmi-set.txt || { echo hard > $D/qmi-set.err; return 0; }; return 1; } +# this mmcli has no --list-bearers ("no actions specified"); use 'Bearer | paths:' of mmcli -m, not the initial (attach) bearer +bearers() { mmc -m "$(mm_idx)" | grep -v "initial bearer" | grep -oE "Bearer/[0-9]+" | cut -d/ -f2; } +any_bearer_connected() { for b in $(bearers); do mmc -b $b | grep -q "connected: yes" && return 0; done; return 1; } +qv() { grep -m1 -oE "$1: *'[^']*'" $2 2>/dev/null | sed -E "s/.*'([^']*)'/\1/"; } +# B6M registration state machine. rc 0 = registered+PS attached, 1 = REGISTRATION_TIMEOUT, 2 = local failure (reason in $D/reg-fail) +reg_sm() { + T=$D/reg-trans.tsv; t0=$1; last=; n=0; nd=0; rm -f $D/reg-fail; D0=$(dmesg | wc -l) + printf 'uptime t_online reg ps status true_status domain plmn earfcn pci si_tac si_eci cl_tac cl_eci rsrp rsrq snr +' > $T + lf() { echo "$*" > $D/reg-fail; return 2; } + while :; do n=$((n+1)) + mss_is running || { lf "MSS state $(mss_state)"; return 2; } + [ -e /dev/wwan0qmi0 ] || { lf "wwan0qmi0 disappeared"; return 2; } + [ -n "$(rmtfs_pid)" ] || { lf "rmtfs died"; return 2; } + [ "$(bam_pm)" = error ] && { lf "BAM-DMUX runtime_status=error"; return 2; } + dmesg | tail -n +$((D0+1)) | grep -qE 'crash detected|fatal error|[Ww]atchdog|wdog|Oops|BUG:|Failed to resume' && { lf "dmesg: $(dmesg | tail -n +$((D0+1)) | grep -m1 -E 'crash detected|fatal error|[Ww]atchdog|wdog|Oops|BUG:|Failed to resume')"; return 2; } + if [ $((n % 5)) = 1 ]; then + sim_ready || { lf "SIM not ready: $(grep -E 'Card state|Application state' $D/qmi-uim.txt | head -2 | tr -s ' \t\n' ' ')"; return 2; } + if [ "$MGMT_CHECK" = 1 ]; then + ping -c1 -W2 172.16.42.2 >/dev/null 2>&1 || { sleep 1; ping -c1 -W2 172.16.42.2 >/dev/null 2>&1 || { lf "USB management (usb0 ping) lost"; return 2; }; } + fi + fi + timeout 20 $Q --nas-get-serving-system > $D/qmi-ss.txt 2>&1 + timeout 20 $Q --nas-get-system-info > $D/qmi-si-all.txt 2>&1; sed -n '/LTE service:/,/SIM reject/p' $D/qmi-si-all.txt > $D/qmi-si.txt + RG=$(qv 'Registration state' $D/qmi-ss.txt); PS=$(qv PS $D/qmi-ss.txt); ST=$(qv Status $D/qmi-si.txt); TS=$(qv 'True Status' $D/qmi-si.txt) + DM=$(qv Domain $D/qmi-si.txt); PL=$(qv MCC $D/qmi-si.txt)-$(qv MNC $D/qmi-si.txt); SIT=$(qv 'Tracking Area Code' $D/qmi-si.txt); SIE=$(qv 'Cell ID' $D/qmi-si.txt) + key="$RG|$PS|$ST|$TS|$DM|$PL|$SIT|$SIE" + if [ "$key" != "$last" ]; then + timeout 20 $Q --nas-get-cell-location-info > $D/qmi-cl.txt 2>&1; timeout 20 $Q --nas-get-signal-info > $D/qmi-sig.txt 2>&1 + [ "$RG" = registration-denied ] && [ "${last%%|*}" != registration-denied ] && nd=$((nd+1)) + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' $(now) $(since $t0) "$RG" "$PS" "$ST" "$TS" "$DM" "$PL" \ + "$(qv 'EUTRA Absolute RF Channel Number' $D/qmi-cl.txt)" "$(qv 'Serving Cell ID' $D/qmi-cl.txt)" "$SIT" "$SIE" \ + "$(qv 'Tracking Area Code' $D/qmi-cl.txt)" "$(qv 'Global Cell ID' $D/qmi-cl.txt)" "$(qv RSRP $D/qmi-sig.txt)" "$(qv RSRQ $D/qmi-sig.txt)" "$(qv SNR $D/qmi-sig.txt)" >> $T + log "REG t+$(since $t0)s: $RG / PS $PS / $ST ($TS, $DM) $PL pci=$(qv 'Serving Cell ID' $D/qmi-cl.txt) si=$SIT/$SIE cl=$(qv 'Tracking Area Code' $D/qmi-cl.txt)/$(qv 'Global Cell ID' $D/qmi-cl.txt)" + last=$key; tprobe "REG:$RG/$PS" + fi + echo $nd > $D/reg-denied-count + [ "$RG" = registered ] && [ "$PS" = attached ] && return 0 + [ "$(awk -v a="$(now)" -v b="$t0" -v t="$T_REG" 'BEGIN{print (a-b>=t)}')" = 1 ] && { lf "REGISTRATION_TIMEOUT after ${T_REG}s ($RG, PS $PS)"; return 1; } + sleep 3 + done +} +bearer_field() { awk -F': ' -v k="$1" '$1 ~ " "k"$" {gsub(/ /,"",$2); print $2; exit}' $BEARER.txt; } + +# --- B7C time bootstrap. Variables are _t-prefixed: tprobe runs inside reg_sm (n, T, last, t0 belong to it). +NTPBB=/usr/libexec/aurora/busybox; GPS_EPOCH=315964800; Y2026=1767225600 +time_floor() { _tf=$(cat /etc/aurora/build-epoch 2>/dev/null); case "$_tf" in ''|*[!0-9]*) _tf=$Y2026;; esac; [ $_tf -lt $Y2026 ] && _tf=$Y2026; echo $_tf; } +# tprobe PHASE: read DMS + NITZ (raw kept in $D/time-probe-N.*), validate, log; first VALID sample while the clock is still pre-build -> tset +tprobe() { + [ "$TIME_BOOTSTRAP" = 1 ] || return 0 + _tn=$(( $(cat $D/time-probes 2>/dev/null || echo 0) + 1 )); echo $_tn > $D/time-probes; _tp=$D/time-probe-$_tn + _u0=$(now); timeout 5 $Q --dms-get-time > $_tp.dms 2>&1; _u1=$(now); timeout 5 $Q --nas-get-network-time > $_tp.nitz 2>&1; _u2=$(now) + _ms=$(sed -n "s/.*System time: '\([0-9]*\).*/\1/p" $_tp.dms); _nd=$(qv '3GPP Date' $_tp.nitz); _nt=$(qv '3GPP Time' $_tp.nitz) + _ne=; [ -n "$_nd" ] && [ -n "$_nt" ] && _ne=$(date -u -d "$_nd $_nt" +%s 2>/dev/null); _clk=$(date -u +%s) + # -> "VERDICT dms_utc dms_uptime dms-nitz reason"; dms-nitz compares DMS projected to the NITZ read instant with whole-second NITZ + _v=$(awk -v ms="$_ms" -v g=$GPS_EPOCH -v u0=$_u0 -v u1=$_u1 -v u2=$_u2 -v ne="$_ne" -v fl=$(time_floor) -v y=$Y2026 -v rn=$TIME_REQUIRE_NITZ -v mx=$TIME_MAX_DMS_NITZ 'BEGIN{ + if (ms !~ /^[0-9]+$/) { print "INVALID - - - dms-unreadable"; exit } + dms=ms/1000+g; at=(u0+u1)/2; r="ok"; d="-" + if (dms < y) r="year<2026"; else if (dms < fl) r="before-build"; else if (dms > fl+5*365*86400) r="implausible" + if (ne ~ /^[0-9]+$/) { dd=dms+((u1+u2)/2-at)-ne; d=sprintf("%+.2f", dd); if (r=="ok" && (dd<-mx || dd>mx)) r="dms-nitz-mismatch" } + else if (r=="ok" && rn==1) r="no-nitz" + printf "%s %.3f %.2f %s %s\n", (r=="ok" ? "VALID" : "INVALID"), dms, at, d, r }') + set -- "$1" $_v + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' $_tn "$1" $_u0 "${_ms:--}" "$3" "${_nd:--}T${_nt:--}" "$5" $_clk "$2/$6" >> $D/time.tsv + log "TIME probe $_tn [$1]: dms_ms=${_ms:-none} dms_utc=$3 nitz=${_nd:-none} ${_nt} dms-nitz=$5 clock=$_clk -> $2 ($6)" + [ "$2" = VALID ] && [ ! -f $D/time-set ] && [ $_clk -lt $(time_floor) ] && tset "$1" $3 $4 $5 + return 0; } +# tset PHASE DMS_UTC DMS_UPTIME DIFF: step CLOCK_REALTIME once, landing on a whole UTC second (uptime-based wait, no fixed bias correction) +tset() { + _N=$(awk -v d=$2 -v a=$3 -v u=$(now) 'BEGIN{printf "%d", int(d+(u-a))+1}') + _w=$(awk -v N=$_N -v d=$2 -v a=$3 -v u=$(now) 'BEGIN{w=N-(d+(u-a)); printf "%.3f", (w>0 ? w : 0)}') + sleep $_w; _old=$(date -u +%s); [ $_N -gt $_old ] || { log "TIME SET skipped: target $_N not after clock $_old (never backwards)"; return 0; } + date -u -s @$_N > /dev/null 2>&1; _rc=$? + [ $_rc = 0 ] && echo "$_N $(now) $1 qmi-dms" > $D/time-set + log "TIME SET rc=$_rc source=qmi-dms phase=$1 old=$_old ($(date -u -d @$_old '+%F %T')) new=$_N ($(date -u '+%F %T')) uptime=$(now) dms-nitz=$4 validation=VALID rtc=untouched"; } +# after START OK, background: resolve VNIIFTRI via bearer DNS, /32 routes via addroute (removed by stop/disconnect), ntpd -w (query only) +time_sntp() { + [ "$TIME_BOOTSTRAP" = 1 ] && [ -x $NTPBB ] && [ -n "$TIME_NTP" ] || { log "SNTP: skipped (bootstrap=$TIME_BOOTSTRAP bin=$([ -x $NTPBB ] && echo yes || echo no))"; return 0; } + . $D/ipcfg 2>/dev/null || return 0; _dns=${DNS%%,*}; _ips= + for _h in $TIME_NTP; do _ip=$(nslookup $_h $_dns 2>/dev/null | awk '/^Name:/{f=1} f && /^Address/{print $2; exit}') + case "$_ip" in [0-9]*.[0-9]*.[0-9]*.[0-9]*) addroute $_ip && _ips="$_ips $_ip";; *) log "SNTP: $_h not resolved via $_dns";; esac; done + [ -n "$_ips" ] || return 0 + _a=; for _ip in $_ips; do _a="$_a -p $_ip"; done + timeout 15 $NTPBB ntpd -w -d $_a > $D/sntp.txt 2>&1 + _b=$(awk '/reply from/{o="";d=""; for(i=1;i<=NF;i++){ if($i~/^offset:/) o=substr($i,8); if($i~/^delay:/) d=substr($i,7) } if(o!="" && (bd=="" || d+0 0 = clock behind)" + [ -n "$_b" ] || return 0 + if [ "$(awk -v o=${_b%% *} -v s=$TIME_NTP_STEP 'BEGIN{print (o>=s || o<=-s)}')" = 1 ]; then + _old=$(date -u +%s); _N=$(awk -v c=$_old -v o=${_b%% *} 'BEGIN{printf "%d", c+o+0.5}'); date -u -s @$_N > /dev/null 2>&1 + log "SNTP STEP rc=$? old=$_old new=$_N (|offset| >= ${TIME_NTP_STEP}s) rtc=untouched" + else log "SNTP: |offset| < ${TIME_NTP_STEP}s - clock kept (no slew: STA_UNSYNC stays, RTC_SYSTOHC never engages)"; fi; } + +check_safety() { + for p in modemst1 modemst2 fsc fsg persist; do + d=/dev/disk/by-partlabel/$p; [ -e $d ] || return 1 + [ "$(blockdev --getro $(readlink -f $d))" = 1 ] || { log "SAFETY: $p not RO"; return 1; } + done + if [ "$(blockdev --getro /dev/mmcblk0)" != 1 ]; then # B12P: the disk flag may be lifted only for the Alpine userdata mount + _ud=$(readlink -f /dev/disk/by-partlabel/userdata 2>/dev/null) + for x in /dev/mmcblk0p* /dev/mmcblk0boot0 /dev/mmcblk0boot1; do [ "$x" = "$_ud" ] && continue + [ "$(blockdev --getro $x)" = 1 ] || { log "SAFETY: mmcblk0 not RO ($x rw)"; return 1; }; done + fi + grep -q " /firmware .* ro," /proc/mounts || { log "SAFETY: /firmware not mounted ro"; return 1; } + [ "$(cat /sys/module/firmware_class/parameters/path)" = /firmware/image ] || { log "SAFETY: firmware path"; return 1; } + return 0 +} +connect_args() { + for v in "$MODEM_APN" "$MODEM_USER" "$MODEM_PASSWORD" "$MODEM_AUTH" "$MODEM_IP_TYPE"; do + case "$v" in *[,\"\'=]*) echo "BAD"; return;; esac; done + a="apn=$MODEM_APN" + [ -n "$MODEM_USER" ] && a="$a,user=$MODEM_USER" + [ -n "$MODEM_PASSWORD" ] && a="$a,password=$MODEM_PASSWORD" + [ -n "$MODEM_AUTH" ] && a="$a,allowed-auth=$MODEM_AUTH" + echo "$a,ip-type=$MODEM_IP_TYPE" +} +addroute() { # /32 via the bearer interface, recorded so stop removes exactly what we added + ip=$1; . $D/ipcfg 2>/dev/null || { echo "no bearer ipcfg"; return 1; } + grep -qx "$ip" $ROUTES 2>/dev/null && return 0 + ip route add $ip/32 dev $IF src $A && echo $ip >> $ROUTES +} +diag() { # snapshot before rollback + { echo "### diag $(now) state=$(cat $STATE 2>/dev/null) mss=$(mss_state) rmtfs=$(rmtfs_pid) mm=$(mm_pid) bam=$(bam_pm)" + [ -n "$(mm_pid)" ] && { mmc -L; mmc -m "$(mm_idx)"; } + cat $D/qmi-mode.txt $D/qmi-ss.txt 2>/dev/null; dmesg | tail -40; } > $D/diag-$(now).txt 2>&1 +} +fail_start() { log "FAIL in $(cat $STATE 2>/dev/null): $*"; diag + if [ "$MODEM_KEEP_ON_FAIL" = 1 ]; then log "MODEM_KEEP_ON_FAIL=1: leaving stack as is" + else log "rollback: full stop"; do_stop; fi + exit 1; } + +do_start() { + T0=$(now); log "=== START (conf $CONF, apn '${MODEM_APN}', ip-type $MODEM_IP_TYPE)" + [ -n "$MODEM_APN" ] || { log "config: MODEM_APN empty - refusing (no APN guessing)"; exit 2; } + CA=$(connect_args); [ "$CA" = BAD ] && { log "config: forbidden character in APN/user/password/auth"; exit 2; } + check_safety || { log "FAIL: safety gate (RO) - nothing started"; exit 1; } + [ -n "$(rp)" ] || { log "FAIL: remoteproc $RPNAME not found"; exit 1; } + + # OFF -> RMTFS_READY -> MPSS_BOOTING -> MPSS_RUNNING + if [ -n "$(rmtfs_pid)" ] && mss_is running; then log "rmtfs $(rmtfs_pid) + MSS running already - reuse" + elif [ -n "$(rmtfs_pid)" ] || ! mss_is offline; then + log "FAIL: inconsistent (rmtfs='$(rmtfs_pid)' MSS=$(mss_state)); run stop first"; exit 1 + else + : > $ROUTES; rm -f $D/ipcfg + n0=$(grep -c 'registering services' $D/rmtfs.log 2>/dev/null) + LD_PRELOAD=/usr/lib/linebuf.so rmtfs -r -P -s -v >> $D/rmtfs.log 2>&1 & + echo $! > $D/rmtfs.pid + wait_for $T_RMTFS 0.2 sh -c "kill -0 $(cat $D/rmtfs.pid) 2>/dev/null && [ \$(grep -c 'registering services' $D/rmtfs.log) -gt ${n0:-0} ]" \ + || fail_start "rmtfs did not start (pid $(cat $D/rmtfs.pid))" + setstate RMTFS_READY + setstate MPSS_BOOTING + t=$(now); wait_for $T_MSS 0.2 mss_is running || fail_start "MSS not running after ${T_MSS}s (state $(mss_state))" + log "MSS running after $(since $t)s" + fi + setstate MPSS_RUNNING + + # -> QMI_READY + t=$(now); wait_for $T_QMI_DEV 0.2 qmi_dev || fail_start "wwan0qmi0/wwan0 missing after ${T_QMI_DEV}s" + log "wwan0qmi0 + wwan0 present after $(since $t)s; +2 s (JZ02 rpmsg_wwan_ctrl port-race guard, proven start_post)" + sleep 2 + [ "$(bam_pm)" = error ] && fail_start "BAM-DMUX runtime_status=error" + + # -> RADIO_ONLINE -> REGISTERED (Aurora-proven: qmicli online + NAS poll before ModemManager) + if [ -n "$(mm_pid)" ]; then + log "ModemManager already running - skip qmicli, use MM state" + setstate QMI_READY + else + t=$(now); wait_for $T_QMI 1 qmi_ok || fail_start "QMI DMS not answering after ${T_QMI}s" + setstate QMI_READY; log "DMS $(grep -o "Mode: '[^']*'" $D/qmi-mode.txt) after $(since $t)s"; tprobe QMI_READY + t=$(now); wait_for $T_SIM 1 sim_ready || fail_start "SIM/USIM not ready after ${T_SIM}s ($(grep -E "Card state|Application state" $D/qmi-uim.txt | head -2 | tr -s ' \t\n' ' '))" + setstate SIM_READY; log "UIM card present, USIM app ready after $(since $t)s"; tprobe SIM_READY + if ! grep -q "Mode: 'online'" $D/qmi-mode.txt; then + log "SET: $Q --dms-set-operating-mode=online (retried only on DeviceNotReady, <= ${T_ONLINE}s)" + rm -f $D/qmi-set.err; t=$(now); wait_for $T_ONLINE 1 set_online || fail_start "SET online: DeviceNotReady for ${T_ONLINE}s" + [ -f $D/qmi-set.err ] && fail_start "SET online failed: $(grep -i error $D/qmi-set.txt | head -1)" + qmi_ok && grep -q "Mode: 'online'" $D/qmi-mode.txt || fail_start "mode not online after SET" + log "DMS online after $(since $t)s" + fi + setstate RADIO_ONLINE; tprobe RADIO_ONLINE + t=$(now); reg_sm $t; rr=$? + [ $rr = 2 ] && fail_start "LOCAL FAILURE during registration: $(cat $D/reg-fail)" + [ $rr = 1 ] && fail_start "$(cat $D/reg-fail)" + + log "registered: $(grep -oE "(MCC|MNC|Description): '[^']*'" $D/qmi-ss.txt | tr '\n' ' ')after $(since $t)s" + setstate REGISTERED; tprobe REGISTERED + killall qmi-proxy 2>/dev/null; wait_for 5 0.2 sh -c '! pidof qmi-proxy >/dev/null' + fi + + # -> MM_READY (mm-up order: udevd, trigger, settle, dbus, polkitd, ModemManager) + pidof udevd >/dev/null || { udevd --daemon; udevadm trigger --action=add; udevadm settle --timeout=15; log "udevd started"; } + if ! pidof dbus-daemon >/dev/null; then + mkdir -p /run/dbus /var/lib/dbus /var/lib/polkit; rm -f /run/dbus/pid; dbus-uuidgen --ensure + dbus-daemon --system --fork || fail_start "dbus-daemon"; log "dbus started"; fi + pidof polkitd >/dev/null || { /usr/lib/polkit-1/polkitd --no-debug >> $D/polkitd.log 2>&1 & log "polkitd started"; } + if [ -z "$(mm_pid)" ]; then + N=$(ls $D/mm-*.log 2>/dev/null | wc -l) + ModemManager --debug > $D/mm-$N.log 2>&1 & + echo $! > $D/mm.pid; log "ModemManager started pid $! (log mm-$N.log)" + fi + t=$(now); wait_for $T_MM_DETECT 2 mm_has_modem || fail_start "MM: no modem after ${T_MM_DETECT}s" + log "MM modem $(mm_idx) detected after $(since $t)s" + mmc -m "$(mm_idx)" > $D/mm-modem.txt + grep -q "plugin: qcom-soc" $D/mm-modem.txt || fail_start "MM plugin is not qcom-soc" + grep -q "primary port: wwan0qmi0" $D/mm-modem.txt || fail_start "MM primary port is not wwan0qmi0" + setstate MM_READY + t=$(now); wait_for $T_MM_REG 2 mm_usable || fail_start "MM state '$(mm_state)' not usable after ${T_MM_REG}s" + log "MM state $(mm_state) after $(since $t)s" + + # -> BEARER_CONNECTED (exactly one simple-connect) + if any_bearer_connected; then log "bearer already connected - reuse" + else + log "mmcli -m $(mm_idx) --simple-connect=\"$(echo "$CA" | sed 's/password=[^,]*/password=***/')\"" + timeout $T_CONNECT mmcli -m "$(mm_idx)" --simple-connect="$CA" >> $LOG 2>&1 || fail_start "simple-connect failed/timeout" + fi + BR=; for b in $(bearers); do mmc -b $b | grep -q "connected: yes" && BR=$b; done + [ -n "$BR" ] || fail_start "no connected bearer after simple-connect" + mmc -m "$(mm_idx)" > $D/mm-modem.txt + grep -qE "registration: (home|roaming)" $D/mm-modem.txt || fail_start "MM registration not home/roaming after connect" + log "MM state $(mm_state), $(grep -oE "registration: [a-z]+" $D/mm-modem.txt)" + mmc -b $BR > $BEARER.txt + IF=$(bearer_field interface); A=$(bearer_field address); PF=$(bearer_field prefix); GW=$(bearer_field gateway) + DNS=$(bearer_field dns); MTU=$(bearer_field mtu) + echo "$IF" | grep -qE '^wwan[0-7]$' && [ -n "$A" ] && [ -n "$PF" ] || fail_start "bearer $BR bad ipv4 config IF=$IF A=$A PF=$PF" + printf 'BR=%s\nIF=%s\nA=%s\nPF=%s\nGW=%s\nDNS=%s\nMTU=%s\n' "$BR" "$IF" "$A" "$PF" "$GW" "$DNS" "$MTU" > $D/ipcfg + log "bearer $BR: $IF $A/$PF gw $GW dns $DNS mtu $MTU" + ip addr flush dev $IF; ip addr add $A/$PF dev $IF || fail_start "ip addr add" + [ -n "$MTU" ] && ip link set $IF mtu $MTU + ip link set $IF up || fail_start "ip link set $IF up (bam-dmux runtime PM?)" + link_up $IF || fail_start "$IF not UP" + for h in $(echo "$DNS" | tr ',' ' ') $MODEM_TEST_ROUTES; do addroute $h || fail_start "route $h"; done + if ip -4 addr show usb0 2>/dev/null | grep -q "inet "; then + ip route get 172.16.42.2 2>&1 | grep -q "dev usb0" || fail_start "management route no longer via usb0" + fi + ip route | grep -q "^default" && log "WARNING: a default route exists (not created by us)" + setstate BEARER_CONNECTED + log "=== START OK in $(since $T0)s ($IF $A/$PF, routes: $(tr '\n' ' ' < $ROUTES))" + tprobe BEARER_CONNECTED; ( time_sntp ) & +} + +do_stop() { + T0=$(now); log "=== STOP (from $(cat $STATE 2>/dev/null || echo OFF): mss=$(mss_state) rmtfs=$(rmtfs_pid) mm=$(mm_pid) bam=$(bam_pm))" + rc=0 + # 1. disconnect bearer(s) + if [ -n "$(mm_pid)" ] && mm_has_modem; then + timeout $T_DISCONNECT mmcli -m "$(mm_idx)" --simple-disconnect >> $LOG 2>&1; log "simple-disconnect rc=$? (bearers: $(bearers | tr '\n' ' '))" + wait_for 15 1 sh -c "! sh $0 _anyconn" && log "no bearer connected" || { log "WARNING: bearer still connected"; rc=1; } + fi + # 2. our /32 routes, 3. flush + DOWN every wwan* + for h in $(cat $ROUTES 2>/dev/null); do ip route del $h/32 2>/dev/null; done; : > $ROUTES + for d in /sys/class/net/wwan*; do [ -e "$d" ] || continue; w=${d##*/} + ip addr flush dev $w 2>/dev/null; ip link set $w down 2>/dev/null + link_up $w && { log "ERROR: $w still UP"; rc=1; }; done + rm -f $D/ipcfg + ip route | grep -q " dev wwan" && { log "ERROR: routes via wwan remain: $(ip route | grep ' dev wwan' | tr '\n' ';')"; rc=1; } + log "wwan links down, addresses/routes removed" + [ -n "$(cat $STATE 2>/dev/null)" ] && setstate WWAN_DOWN + # 4. stop ModemManager (+ its qmi-proxy) + if [ -n "$(mm_pid)" ]; then + kill -TERM $(mm_pid); wait_for $T_MM_EXIT 0.5 sh -c '! pidof ModemManager >/dev/null' \ + || { log "MM did not exit in ${T_MM_EXIT}s - SIGKILL"; kill -KILL $(mm_pid) 2>/dev/null; rc=1; } + log "ModemManager stopped" + fi + pidof qmi-proxy >/dev/null && { killall qmi-proxy; wait_for 5 0.2 sh -c '! pidof qmi-proxy >/dev/null'; log "qmi-proxy stopped"; } + setstate MM_STOPPED + # 5. BAM-DMUX idle + if [ -e $BAM ] && ! mss_is offline; then + t=$(now); if wait_for $T_BAM_IDLE 0.5 bam_suspended; then log "BAM-DMUX suspended after $(since $t)s" + else log "ERROR: BAM-DMUX '$(bam_pm)' not suspended after ${T_BAM_IDLE}s - NOT stopping MSS"; exit 1; fi + fi + # 6. MSS stop via rmtfs -s (SIGTERM -> pwrite "stop" to remoteproc state -> exit), 7. rmtfs exits last + P=$(rmtfs_pid) + if [ -n "$P" ]; then + t=$(now); kill -TERM $P; log "SIGTERM rmtfs $P (rmtfs stops MSS, then exits)" + wait_for $T_MSS_STOP 0.2 mss_is offline && log "MSS offline after $(since $t)s (rmtfs alive: $(kill -0 $P 2>/dev/null && echo yes || echo no))" \ + || { log "ERROR: MSS '$(mss_state)' after ${T_MSS_STOP}s"; rc=1; } + setstate MPSS_STOPPED + wait_for $T_RMTFS_EXIT 0.2 sh -c "! kill -0 $P 2>/dev/null" && log "rmtfs exited after $(since $t)s" \ + || { log "rmtfs still alive - second SIGTERM"; kill -TERM $P; wait_for 5 0.2 sh -c "! kill -0 $P 2>/dev/null" || { kill -KILL $P; rc=1; }; } + elif ! mss_is offline && [ -n "$(rp)" ]; then + log "MSS $(mss_state) without rmtfs - echo stop > state"; echo stop > $(rp)/state + wait_for $T_MSS_STOP 0.2 mss_is offline || rc=1 + fi + mss_is offline || mss_is none || { log "ERROR: MSS state $(mss_state)"; rc=1; } + setstate OFF + log "=== STOP done rc=$rc in $(since $T0)s (mss=$(mss_state) rmtfs='$(rmtfs_pid)' mm='$(mm_pid)' bam=$(bam_pm))" + return $rc +} + +do_disconnect() { # B6N: bearer down only; MSS, rmtfs, MM (and DMS radio) stay up. Next 'start' reuses them and only simple-connects. + T0=$(now); log "=== DISCONNECT (from $(cat $STATE 2>/dev/null || echo OFF): mss=$(mss_state) rmtfs=$(rmtfs_pid) mm=$(mm_pid) bam=$(bam_pm))"; rc=0 + [ -n "$(mm_pid)" ] && mm_has_modem || { log "DISCONNECT: no MM modem"; return 1; } + timeout $T_DISCONNECT mmcli -m "$(mm_idx)" --simple-disconnect >> $LOG 2>&1; log "simple-disconnect rc=$? (bearers: $(bearers | tr '\n' ' '))" + wait_for 15 1 sh -c "! sh $0 _anyconn" && log "no bearer connected" || { log "ERROR: bearer still connected"; rc=1; } + for h in $(cat $ROUTES 2>/dev/null); do ip route del $h/32 2>/dev/null; done; : > $ROUTES + for d in /sys/class/net/wwan*; do [ -e "$d" ] || continue; w=${d##*/} + ip addr flush dev $w 2>/dev/null; ip link set $w down 2>/dev/null + link_up $w && { log "ERROR: $w still UP"; rc=1; }; done + rm -f $D/ipcfg + ip route | grep -q " dev wwan" && { log "ERROR: routes via wwan remain: $(ip route | grep ' dev wwan' | tr '\n' ';')"; rc=1; } + t=$(now); wait_for $T_BAM_IDLE 0.5 bam_suspended && log "BAM-DMUX suspended after $(since $t)s" || { log "ERROR: BAM-DMUX '$(bam_pm)' not suspended"; rc=1; } + setstate MM_IDLE + log "=== DISCONNECT done rc=$rc in $(since $T0)s (mss=$(mss_state) rmtfs='$(rmtfs_pid)' mm='$(mm_pid)' mm_state=$(mm_state) bam=$(bam_pm))" + return $rc +} + +do_status() { + r=$(rp) + echo "controller: $(cat $STATE 2>/dev/null || echo OFF)" + echo "remoteproc: $RPNAME $(mss_state) ($r) fw=$(cat $r/firmware 2>/dev/null)" + echo "rmtfs: pid='$(rmtfs_pid)' writes_logged=$(grep -c 'write ' $D/rmtfs.log 2>/dev/null)" + echo "qmi dev: $(ls /dev/wwan0qmi0 2>/dev/null || echo absent) qmi-proxy='$(pidof qmi-proxy)'" + echo "daemons: udevd='$(pidof udevd)' dbus='$(pidof dbus-daemon)' polkitd='$(pidof polkitd)' ModemManager='$(mm_pid)'" + if [ -n "$(mm_pid)" ] && mm_has_modem; then + mmc -m "$(mm_idx)" | grep -E "\| +(state|power state|registration|operator name|access tech|signal quality|plugin|primary port): " \ + | sed -E 's/^[^|]*\| */ mm /' + for b in $(bearers); do echo " bearer $b: $(mmc -b $b | grep -E 'connected:|interface:|address:' | tr -s ' |' ' ' | tr '\n' ';')"; done + else echo " mm: n/a"; fi + for d in /sys/class/net/wwan*; do [ -e "$d" ] || continue; w=${d##*/} + link_up $w && echo "wwan: $w UP $(ip -4 addr show $w | awk '/inet /{print $2}')"; done + echo "wwan up: $(n=0; for d in /sys/class/net/wwan*; do [ -e $d ] && link_up ${d##*/} && n=$((n+1)); done; echo $n) of $(ls -d /sys/class/net/wwan* 2>/dev/null | wc -l)" + echo "routes: $(ip route | grep ' dev wwan' | tr '\n' ';')" + echo "bam-dmux: $(bam_pm) active_ms=$(cat $BAM/power/runtime_active_time 2>/dev/null) suspended_ms=$(cat $BAM/power/runtime_suspended_time 2>/dev/null) control=$(cat $BAM/power/control 2>/dev/null)" + echo "usb0: $(ip -4 addr show usb0 | awk '/inet /{print $2}') route-to-host: $(ip route get 172.16.42.2 2>&1 | head -1)" + echo "emmc: $(awk '{print "writes_completed="$5" sectors_written="$7}' /sys/block/mmcblk0/stat)" + echo "time: $(date -u '+%F %T') UTC; set: $(cat $D/time-set 2>/dev/null || echo no); probes: $(cat $D/time-probes 2>/dev/null || echo 0)" + echo "dmesg-errs: $(dmesg | grep -cE 'Failed to resume|Resource temporarily|crash detected|fatal error|[Ww]atchdog|Oops|Kernel panic|Unable to handle')" +} + +case "$1" in + start) do_start;; + stop) do_stop; exit $?;; + disconnect) do_disconnect; exit $?;; + status) do_status;; + addroute) [ -n "$2" ] || exit 2; addroute "$2";; + _anyconn) any_bearer_connected;; + _mmstate) mm_state;; + *) echo "usage: $0 start|stop|status|addroute "; exit 2;; +esac diff --git a/b17/aurora-ssh.openrc b/b17/aurora-ssh.openrc new file mode 100644 index 0000000..58999c4 --- /dev/null +++ b/b17/aurora-ssh.openrc @@ -0,0 +1,19 @@ +#!/sbin/openrc-run +# B17: dropbear SSH for standalone use (installed as /etc/init.d/aurora-ssh). Root login by key (/root/.ssh/authorized_keys) or password. +# Reachable from Wi-Fi (nft lan_in: tcp 22 to 192.168.77.1 only) and USB mgmt; LTE/wg0 inbound is dropped by aurora_router/aurora_vpn. +# Not Alpine's /etc/init.d/dropbear: that one needs "net" (-> networking without /etc/network/interfaces fails, and pulls a 2nd udevd). +description="Dropbear SSH (Wi-Fi/USB management)" +supervisor=supervise-daemon +command=/usr/sbin/dropbear +command_args="-F -E -p 22" +output_log=/run/aurora-ssh.log +error_log=/run/aurora-ssh.log +respawn_delay=5 +respawn_max=0 +depend() { need localmount; after aurora-router; } +start_pre() { + mkdir -p /etc/dropbear + [ -f /etc/dropbear/dropbear_ed25519_host_key ] || dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key >/dev/null 2>&1 + [ -f /etc/dropbear/dropbear_rsa_host_key ] || dropbearkey -t rsa -s 3072 -f /etc/dropbear/dropbear_rsa_host_key >/dev/null 2>&1 + return 0 +} diff --git a/b17/modem.conf.add b/b17/modem.conf.add new file mode 100644 index 0000000..8a0edf6 --- /dev/null +++ b/b17/modem.conf.add @@ -0,0 +1,3 @@ + +# B17: 1 = fail registration if the USB host (480s 172.16.42.2) stops answering ping. 0 = standalone (default). +MGMT_CHECK=0 diff --git a/b18/B18-rollback.sh b/b18/B18-rollback.sh new file mode 100644 index 0000000..15af624 --- /dev/null +++ b/b18/B18-rollback.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# B18 rollback - restore the B15 cache (212e0ca9: Alpine primary root + router + VPN, kernel b15 = WireGuard, no USB host). Needs 9008 via HW EDL. +# boot/aboot/userdata untouched (aurora-usb just reports "no usb_role switch" under b15). Deeper: b15/B15-rollback.sh (B12R). +cd ~/doc/modem/jz08-aurora || exit 1 +L=firehose/007050e100000000_394a2e47cf830150_fhprg_peek.bin ; O=logs/b18/rollback ; mkdir -p $O +B=b15/cache/cache-extlinux-b15.img +[ "$(sha256sum $B | cut -d' ' -f1)" = 212e0ca903911dad19fca00c013610cb6d5a773821c36410d8c277e34139856f ] || { echo "STOP: rollback image hash"; exit 1; } +lsusb | grep -q 05c6:9008 || { echo "STOP: no 9008"; exit 1; } +edl --loader=$L printgpt > $O/00-printgpt.log 2>&1 +edl --loader=$L w cache $B > $O/cache-w.log 2>&1 +edl --loader=$L r cache $O/cache-rb.bin > /dev/null 2>&1 ; cmp $B $O/cache-rb.bin && echo CACHE_RESTORED_B15 +sha256sum $B $O/cache-rb.bin diff --git a/b18/B18-write.sh b/b18/B18-write.sh new file mode 100644 index 0000000..f8a46c9 --- /dev/null +++ b/b18/B18-write.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# B18 — the ONLY write: cache := B18 candidate (= B12Q initramfs + kernel 7.2.7-aurora-b18: b15 + USB host; DTB b18 = b10b3 + &usb otg/usb-role-switch). +# Run on 480s ONLY after explicit GO (RAM test ram1 PASS 2026-10-06: NCM, modem, VPN, host role, kbd+mouse in X). Derived 1:1 from b15/B15-write.sh; changed: image/hashes, +# rollback refs, log dir. The pre-write readback of the live cache (must be B15 212e0ca9) is kept as backup. +# Entry: HW EDL (battery on, D+->GND, plug USB, release). Never `edl reset`. Stops at first failed gate. +cd ~/doc/modem/jz08-aurora || exit 1 +L=firehose/007050e100000000_394a2e47cf830150_fhprg_peek.bin +O=logs/b18/W ; A3=logs/b5a/A3 ; P=logs/b5a/A4pre ; mkdir -p $O +NEW=b18/cache/cache-extlinux-b18.img ; NEW_SHA=ac82811ad88d111c673bd3e14ff9cd635e16b87869ce0d0e0070b28b35947e01 +OLD_SHA=212e0ca903911dad19fca00c013610cb6d5a773821c36410d8c277e34139856f # current cache = B15 extlinux +BOOT_SHA=e9579f33c7304cd47f487f2b61cd9226dc04fd3a63775d62b4b95fc15bd3ebff +ABOOT_SHA=15208dbb4feafccd72d2b06db5714cd746c216701f0ef8822c58bb47105d37c4 # aboot = lk1st-b9l (B9L-PERSIST) +die(){ echo "STOP: $*"; exit 1; } +sha(){ sha256sum "$1" | cut -d" " -f1; } +E(){ edl --loader=$L "$@"; } +echo "== G0 inputs" +[ "$(sha $L)" = 53f193500c03248f0d671ab57bfe9ca8a42967e97f28403294b4b3f854075aca ] || die loader +[ "$(sha $NEW)" = $NEW_SHA ] && [ $(stat -c %s $NEW) = 134217728 ] || die candidate +[ "$(sha b15/cache/cache-extlinux-b15.img)" = $OLD_SHA ] || die rollback-image +[ "$(sha logs/b15/W/11-cache-readback.bin)" = $OLD_SHA ] || die b15-readback-backup +lsusb | grep -q 05c6:9008 || die "no 9008" +echo "== G1 identity + geometry + baseline" +E printgpt > $O/00-printgpt.log 2>&1 || die printgpt +grep -aq "Serial: *0x" $O/00-printgpt.log || grep -aq "Mode detected: firehose" $O/00-printgpt.log || die serial +grep -a "^cache:" $O/00-printgpt.log | grep -q "Offset 0x000000004118c000, Length 0x0000000008000000" || die cache-geometry +E rs 0 34 $O/pre-gptp.bin >/dev/null 2>&1; cmp $O/pre-gptp.bin $A3/gpt-primary-34.bin || die gpt-changed +E r boot $O/pre-boot.bin >/dev/null 2>&1; [ "$(sha $O/pre-boot.bin)" = $BOOT_SHA ] || die boot-not-emmc1 +E r aboot $O/pre-aboot.bin >/dev/null 2>&1; [ "$(sha $O/pre-aboot.bin)" = $ABOOT_SHA ] || die aboot-not-b9l +E r cache $O/pre-cache.bin >/dev/null 2>&1; [ "$(sha $O/pre-cache.bin)" = $OLD_SHA ] || die cache-not-backup +echo "== W cache := candidate (LBA 2133088, 262144 sectors)" +E w cache $NEW > $O/10-cache-write.log 2>&1; echo "rc=$?"; grep -a "Wrote" $O/10-cache-write.log +E r cache $O/11-cache-readback.bin > $O/11-cache-readback.log 2>&1 || die readback-rc +sha256sum $NEW $O/11-cache-readback.bin +cmp $NEW $O/11-cache-readback.bin || die "CACHE READBACK MISMATCH — do NOT reboot; run b18/B18-rollback.sh" +echo CACHE_MATCH +echo "== V everything else unchanged" +E rs 0 34 $O/post-gptp.bin >/dev/null 2>&1; cmp $O/post-gptp.bin $A3/gpt-primary-34.bin || die gptp +E rs 7634911 33 $O/post-gptb.bin >/dev/null 2>&1; cmp $O/post-gptb.bin $A3/gpt-backup-33.bin || die gptb +E rs 305184 1024 $O/post-tzbak.bin >/dev/null 2>&1; cmp $O/post-tzbak.bin $A3/tzbak-305184.bin || die tzbak +E r boot $O/post-boot.bin >/dev/null 2>&1; [ "$(sha $O/post-boot.bin)" = $BOOT_SHA ] || die boot +E r aboot $O/post-aboot.bin >/dev/null 2>&1; [ "$(sha $O/post-aboot.bin)" = $ABOOT_SHA ] || die aboot +for p in sbl1 rpm tz hyp recovery modemst1 modemst2 fsg fsc persist; do + E r $p $O/post-$p.bin >/dev/null 2>&1; cmp $O/post-$p.bin $A3/$p.bin || die "$p changed"; echo "$p == A3" +done +for p in modem system; do + E r $p $O/post-$p.bin >/dev/null 2>&1; cmp $O/post-$p.bin $P/$p.bin || die "$p changed"; echo "$p == A4pre"; rm -f $O/post-$p.bin +done +E rs 2428000 8 $O/post-userdata-sb.bin >/dev/null 2>&1 +[ "$(dd if=$O/post-userdata-sb.bin bs=1 skip=1080 count=2 2>/dev/null | od -An -tx1 | tr -d ' \n')" = 53ef ] && [ "$(dd if=$O/post-userdata-sb.bin bs=1 skip=1144 count=16 2>/dev/null | tr -d '\000')" = aurora-alpine ] || die "userdata superblock is not ext4 aurora-alpine" +echo "userdata = ext4 aurora-alpine (superblock)" +(cd $O && sha256sum *.bin > SHA256SUMS) +echo "B18_CACHE_WRITE_VERIFIED — power off fully (USB, then battery) before first boot" diff --git a/b18/aurora-usb b/b18/aurora-usb new file mode 100644 index 0000000..5134fa2 --- /dev/null +++ b/b18/aurora-usb @@ -0,0 +1,16 @@ +#!/bin/sh +# B18: USB role of the Type-C port (ChipIdea usb-role-switch). aurora-usb host | device | status +# host: port becomes USB host (powered hub needed: the board gives no VBUS); USB NCM 172.16.42.1 is gone -> use ssh root@192.168.77.1. +# device: back to gadget (NCM). Not persistent: every boot starts as device. +R=$(ls -d /sys/class/usb_role/*/ 2>/dev/null | head -1) +[ -n "$R" ] || { echo "no usb_role switch (kernel b18 + DTB b18 needed; running $(uname -r))"; exit 1; } +case "$1" in +host|device) echo "$1" > "${R}role" || exit 1; echo "<5>[aurora-usb] role -> $1" > /dev/kmsg; sleep 2; cat "${R}role";; +status|"") + echo "role: $(cat ${R}role)" + for d in /sys/bus/usb/devices/*-*; do [ -f $d/idVendor ] || continue + echo " $(basename $d) $(cat $d/idVendor):$(cat $d/idProduct) $(cat $d/manufacturer 2>/dev/null) $(cat $d/product 2>/dev/null) [$(cat $d/speed)M]"; done + for b in /sys/block/sd*; do [ -e $b ] && echo " disk $(basename $b) $(( $(cat $b/size) / 2048 )) MiB"; done + for k in /sys/class/input/event*; do n=$(cat $k/device/name); case "$n" in *pwrkey*) ;; *) echo " input $(basename $k): $n";; esac; done;; +*) echo "usage: $0 host|device|status"; exit 2;; +esac diff --git a/b18/b18-boot.sh b/b18/b18-boot.sh new file mode 100644 index 0000000..c61de69 --- /dev/null +++ b/b18/b18-boot.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# 480s: B18 RAM test. Waits for lk1st fastboot (RESET held at power-on, USB to 480s), `fastboot boot` the b18 image, waits for NCM, +# then reports kernel, USB role switch and gadget state. Nothing flashed. Role stays "device" until switched by hand: +# echo host > /sys/class/usb_role/*/role (NCM drops; use ssh root@192.168.77.1 over Wi-Fi) | echo device > ... to return. +cd ~/doc/modem/jz08-aurora; O=logs/b18/ram${1:-1}; mkdir -p $O; IMG=b18/out/aurora-b18.img +A=$O/host-actions.txt; act() { echo "$(date -u +%H:%M:%S) $*" | tee -a $A; } +tn() { { sleep 1; sed "s/\$/\r/" "$1"; sleep $2; } | ncat 172.16.42.1 23 | tr -d "\000\r"; } +grep " aurora-b18.img\$" b18/out/SHA256SUMS | sed "s| | b18/out/|" | sha256sum -c || exit 1 +act "B18 ram: waiting for fastboot (RESET held at power-on)" +i=0; until fastboot devices 2>/dev/null | grep -q ; do i=$((i+1)); [ $i -ge 900 ] && { act ABORT; exit 1; }; sleep 2; done +act "fastboot present"; { fastboot boot $IMG 2>&1; echo rc=$?; } | tee -a $A +i=0; until ping -c1 -W1 172.16.42.1 >/dev/null 2>&1; do i=$((i+1)); [ $i -ge 240 ] && { act "ABORT NCM (role came up as host? check over Wi-Fi)"; exit 1; }; sleep 1; done; act "NCM up" +printf 'uname -r; for r in /sys/class/usb_role/*; do echo "$r role=$(cat $r/role)"; done; dmesg | grep -iE "ci_hdrc|ehci|usbcore|role" | tail -8\n' > $O/.w +sleep 5; tn $O/.w 4 | grep -avE "^/ #" | tee -a $A +act "READY" diff --git a/b18/build-b18.sh b/b18/build-b18.sh new file mode 100644 index 0000000..0b129bb --- /dev/null +++ b/b18/build-b18.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# B18 RAM image: kernel 7.2.7-aurora-b18 (= b15 + USB host: CONFIG_USB/EHCI/CHIPIDEA_HOST, usb-storage/UAS, HID, exFAT/NLS) + DTB b18 +# (production b10b3 e47762d1 + &usb dr_mode "otg" + usb-role-switch) + B9C cmdline + B12Q initramfs (stage0 -> Alpine on p27) unchanged. +# fastboot boot only; nothing flashed. +set -e; cd "$(dirname "$0")"; O=out; KB=/home/q/aurora-kbuild/out-b18; Q=../b12/b12q/out; DT=../linux/dts/msm8916-jz08-aurora-b18.dtb; mkdir -p $O +echo "9ade7cffb1f3f19e86acc8869565a88b960abba543385a2fddcebd27bd529e79 $Q/initramfs-b12q.cpio.gz" | sha256sum -c +for s in WIREGUARD=y IP_MULTIPLE_TABLES=y NF_TABLES=y CHARGER_PM8916_LBC=y USB=y USB_CHIPIDEA_HOST=y USB_CHIPIDEA_UDC=y USB_STORAGE=y USB_HID=y 'LOCALVERSION="-aurora-b18"'; do + grep -q "^CONFIG_$s\$" $KB/.config || { echo "STOP $s"; exit 1; }; done +[ "$(fdtget $DT /soc@0/usb@78d9000 dr_mode)" = otg ] && fdtget -p $DT /soc@0/usb@78d9000 | grep -qx usb-role-switch || { echo "STOP dtb usb"; exit 1; } +cp $KB/arch/arm64/boot/Image.gz $O/Image-b18.gz; cp $KB/.config $O/config-b18; cp $DT $O/aurora-b18.dtb; cp $Q/initramfs-b12q.cpio.gz $Q/cmdline.txt $O/ +cat $O/Image-b18.gz $O/aurora-b18.dtb > $O/Image.gz-dtb +python3 ../b6p/mkbootimg.py $O/Image.gz-dtb $O/initramfs-b12q.cpio.gz $O/cmdline.txt $O/aurora-b18.img +(cd $O && sha256sum Image-b18.gz config-b18 aurora-b18.dtb Image.gz-dtb initramfs-b12q.cpio.gz cmdline.txt aurora-b18.img > SHA256SUMS; grep -E "aurora-b18.img|Image-b18|b18.dtb" SHA256SUMS) diff --git a/b18/cache/emu/run-emu-b18.sh b/b18/cache/emu/run-emu-b18.sh new file mode 100644 index 0000000..0093736 --- /dev/null +++ b/b18/cache/emu/run-emu-b18.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# lk2nd-23.1 extlinux emulator (b5b/emu): S15 current flash (B15 212e0ca9) regression + S18 B18 candidate + fault variants. Offline, 480s. +cd ~/doc/modem/jz08-aurora/b18/cache/emu; E=../../../b5b/emu; GPT=../../../logs/b5a/A3/gpt-primary-34.bin; C=/home/q/doc/modem/jz08-aurora/b18/cache/cache-extlinux-b18.img +run() { python3 $E/mkmap.py $2 > map-$1.txt; $E/lk2nd-scan-emu map-$1.txt $GPT $3 > $1.out 2>&1; echo "== $1 rc=$? :: $(grep -E "Trying to boot|Reverting|boot_linux reached|ramdisk sha256|Could not" $1.out | tr '\n' ' ' | cut -c1-260)"; } +run S15-b15-current /home/q/doc/modem/jz08-aurora/b15/cache/cache-extlinux-b15.img 25290760 +cmp <(grep HARNESS S15-b15-current.out) <(grep HARNESS ../../../b15/cache/emu/S15-b15-candidate.out) && echo " S15 HARNESS lines == b15 emu S15" +run S18-b18-candidate $C 26032136; grep HARNESS S18-b18-candidate.out +D=$(mktemp -d); v(){ cp $C $D/$1.img; chmod u+w $D/$1.img; } +v F3-no-initrd; /sbin/debugfs -w -R "rm /initramfs-b12q.cpio.gz" $D/F3-no-initrd.img >/dev/null 2>&1 +v F1-no-conf; /sbin/debugfs -w -R "rm /extlinux/extlinux.conf" $D/F1-no-conf.img >/dev/null 2>&1 +for f in $D/F*.img; do run $(basename $f .img) $f; done; rm -rf $D diff --git a/b18/cache/extlinux-b18.conf b/b18/cache/extlinux-b18.conf new file mode 100644 index 0000000..8cb2ee6 --- /dev/null +++ b/b18/cache/extlinux-b18.conf @@ -0,0 +1,6 @@ +default b18 +label b18 + linux /Image.gz-dtb + fdt /aurora-b18.dtb + initrd /initramfs-b12q.cpio.gz + append earlycon console=tty0 console=ttyMSM0,115200n8 ignore_loglevel loglevel=8 rdinit=/init fbcon=font:6x8 consoleblank=0 diff --git a/b18/cache/mkfs-cache-b18.sh b/b18/cache/mkfs-cache-b18.sh new file mode 100644 index 0000000..e1d17ba --- /dev/null +++ b/b18/cache/mkfs-cache-b18.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# B18 cache CANDIDATE (= B12Q initramfs + kernel 7.2.7-aurora-b18: b15 + USB host + DTB b18 otg/usb-role-switch) (NOT for writing without a separate GO): exact recipe of b10/b10b3/cache-v4/mkfs-cache-b10b5.sh (c3732515); +# differs only in the initrd (B9C initramfs + B12P overlay: aurora-alpine autostart, modem safety gate, e2fsck bundle), file names, +# extlinux label, fs label and UUIDs. Kernel/DTB/append = B10 production unchanged. Runs on 480s (mke2fs 1.47.2). Offline only. +# The image contains the AP PSK (in the initramfs) - never publish it. +set -e +cd ~/doc/modem/jz08-aurora/b18/cache +OUT=${1:-cache-extlinux-b18.img}; F=../out +echo "0fef41b262917c8ae308fa222da0017e2b3cb3ec5da1ca8ba18d0318476761a3 $F/cmdline.txt" | sha256sum -c +echo "2dd4a5957f5691b619f4670a78cffc9547948a210770f2cb4fed565265ae5946 $F/Image.gz-dtb" | sha256sum -c +echo "1504058061d31f84c17c4436874a6aa98ff4c2097431c0439a2d6dfb69f7e005 $F/aurora-b18.dtb" | sha256sum -c +echo "9ade7cffb1f3f19e86acc8869565a88b960abba543385a2fddcebd27bd529e79 $F/initramfs-b12q.cpio.gz" | sha256sum -c +ST=$(mktemp -d); mkdir -p $ST/extlinux +cp $F/Image.gz-dtb $F/aurora-b18.dtb $F/initramfs-b12q.cpio.gz $ST/ +printf "default b18\nlabel b18\n\tlinux /Image.gz-dtb\n\tfdt /aurora-b18.dtb\n\tinitrd /initramfs-b12q.cpio.gz\n\tappend %s\n" "$(cat $F/cmdline.txt)" > $ST/extlinux/extlinux.conf +cp $ST/extlinux/extlinux.conf extlinux-b18.conf +chmod 0644 $ST/*.* $ST/extlinux/extlinux.conf; chmod 0755 $ST $ST/extlinux +find $ST -exec touch -h -d @1790726400 {} + +rm -f $OUT; truncate -s 134217728 $OUT +E2FSPROGS_FAKE_TIME=1790726400 /sbin/mke2fs -F -q -t ext2 -O none,filetype,sparse_super \ + -b 4096 -I 128 -N 64 -m 0 -L aurora-b18 -U 4a5a3038-b6f0-4000-8000-00000b181800 \ + -E root_owner=0:0,hash_seed=4a5a3038-b6f0-4000-8000-00000b181801 -d $ST $OUT 32768 +rm -rf $ST +for f in / /lost+found /extlinux /extlinux/extlinux.conf /Image.gz-dtb /aurora-b18.dtb /initramfs-b12q.cpio.gz; do for t in atime ctime mtime; do echo "sif $f $t @1790726400"; done; done > /tmp/b18-sif.$$ +/sbin/debugfs -w -f /tmp/b18-sif.$$ $OUT >/dev/null 2>&1; rm -f /tmp/b18-sif.$$ +/sbin/e2fsck -fn $OUT >/dev/null 2>&1 && echo "e2fsck -fn clean" +stat -c "%n %s" $OUT; sha256sum $OUT diff --git a/b18/out/SHA256SUMS b/b18/out/SHA256SUMS new file mode 100644 index 0000000..4c9438a --- /dev/null +++ b/b18/out/SHA256SUMS @@ -0,0 +1,7 @@ +9e0ff9ec817364aab3a1796e44052509e5fea5d0f6d593992433a761bb5bf0fd Image-b18.gz +8531a00f5f348bf939e96872b84dfcec278a28626e1d5daa30dbd8198e3033f0 config-b18 +1504058061d31f84c17c4436874a6aa98ff4c2097431c0439a2d6dfb69f7e005 aurora-b18.dtb +2dd4a5957f5691b619f4670a78cffc9547948a210770f2cb4fed565265ae5946 Image.gz-dtb +9ade7cffb1f3f19e86acc8869565a88b960abba543385a2fddcebd27bd529e79 initramfs-b12q.cpio.gz +0fef41b262917c8ae308fa222da0017e2b3cb3ec5da1ca8ba18d0318476761a3 cmdline.txt +9f6d6f8d172b5325acbea315c0667776f625a8b4d4ebcda690f10e5eb01bbd7d aurora-b18.img diff --git a/b20/aurora-blackbox b/b20/aurora-blackbox new file mode 100644 index 0000000..e69463c --- /dev/null +++ b/b20/aurora-blackbox @@ -0,0 +1,48 @@ +#!/bin/sh +# aurora-blackbox (B20): logs that survive a reboot or a sudden power loss (everything else lives in /run = RAM). +# Runs under supervise-daemon (/etc/init.d/aurora-blackbox). Writes to /var/log/aurora/ on p27 (ext4, commit 5 s): +# status.log one line per $BB_PERIOD s: wall clock, uptime, VBAT/charger/USB, PMIC temp, load, MemAvailable, modem state, +# netwatch status, WG handshake age + bytes, free-turn pid, Wi-Fi stations. (~200 B/line, ~300 KB/day) +# kmsg.log the whole kernel log of this boot (replayed from boot, then followed), minus known spam, prefixed with boot number. +# Each file rotates to .1 at $BB_MAX bytes (one generation kept). Boot markers "=== BOOT n" separate boots; the last lines before a +# marker show what the board was doing when it died. +BB_PERIOD=60 BB_MAX=2097152 +[ -f /etc/aurora/blackbox.conf ] && . /etc/aurora/blackbox.conf +L=/var/log/aurora; mkdir -p $L /var/lib/aurora +ST=$L/status.log; KM=$L/kmsg.log +# boot number follows the kernel boot_id, so a service respawn within one boot neither counts a new boot nor re-logs the kernel log +BID=$(cat /proc/sys/kernel/random/boot_id); N=$(cat /var/lib/aurora/boot-count 2>/dev/null || echo 0); SKIP=-1 +if [ "$(cat /var/lib/aurora/boot-id 2>/dev/null)" = "$BID" ]; then + SKIP=$(tail -n 50 $KM 2>/dev/null | sed -n "s/^b$N #\([0-9]*\) .*/\1/p" | tail -1); SKIP=${SKIP:--1} +else N=$((N + 1)); echo $N > /var/lib/aurora/boot-count; echo "$BID" > /var/lib/aurora/boot-id; fi + +rot() { [ -f "$1" ] && [ $(stat -c %s "$1") -ge $BB_MAX ] && { cp "$1" "$1.1"; : > "$1"; }; } +kv() { sed -n "s/.*$1 \([^ ]*\).*/\1/p" /sys/kernel/debug/pm8916_lbc/state 2>/dev/null | head -1; } +temp() { for z in /sys/class/thermal/thermal_zone*; do [ "$(cat $z/type)" = pm8916-thermal ] && { echo $(( $(cat $z/temp) / 1000 )); return; }; done; echo -; } +wif() { [ "$(cat /run/aurora-vpnsel/mode 2>/dev/null)" = direct ] && echo wg1 || echo wg0; } +hs() { h=$(wg show $(wif) latest-handshakes 2>/dev/null | awk '{print $2; exit}'); [ -n "$h" ] && [ "$h" != 0 ] && echo $(( $(date +%s) - h )) || echo -; } +status() { + v=$(kv vbat_uv); [ -n "$v" ] && v=$(( v / 1000 )) + printf '%s up=%s vbat=%s chg=%s usb=%s t=%s load=%s memavail=%s modem=%s nw=%s vpn=%s wl=%s wg_hs=%s wg_rxtx=%s ft=%s sta=%s\n' \ + "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(cut -d' ' -f1 /proc/uptime)" "${v:--}" "$(kv state)" \ + "$(cat /sys/class/power_supply/pm8916-lbc-chgr/online 2>/dev/null || echo -)" "$(temp)" "$(cut -d' ' -f1 /proc/loadavg)" \ + "$(awk '/MemAvailable/{print int($2/1024)"M"}' /proc/meminfo)" "$(cat /run/aurora-modem/state 2>/dev/null || echo -)" \ + "$(cut -d' ' -f1,2 /run/aurora-netwatch/status 2>/dev/null | tr ' ' ':' || echo -)" "$(cat /run/aurora-vpnsel/mode 2>/dev/null || echo -)" "$(cut -d' ' -f1 /run/aurora-vpnsel/wl 2>/dev/null || echo -)" "$(hs)" \ + "$(wg show $(wif) transfer 2>/dev/null | awk '{print $2"/"$3; exit}')" "$(pgrep -f /usr/local/bin/free-turn-client | head -1)" \ + "$(timeout 3 iw dev wlan0 station dump 2>/dev/null | grep -c '^Station')" +} + +if [ "$SKIP" = -1 ]; then echo "=== BOOT $N kernel $(uname -r) at uptime $(cut -d' ' -f1 /proc/uptime) clock $(date -u +%Y-%m-%dT%H:%M:%SZ)" | tee -a $ST >> $KM +else echo "=== blackbox restart in boot $N at uptime $(cut -d' ' -f1 /proc/uptime) (kmsg resumes after #$SKIP)" | tee -a $ST >> $KM; fi +# kernel log: replay this boot from /dev/kmsg, then follow; "pri,seq,usec,-;msg" -> "b #seq [ sec.usec] msg" +( exec cat /dev/kmsg ) | awk -v b=$N -v skip=$SKIP ' + { i = index($0, ";"); split(substr($0, 1, i - 1), h, ","); if (h[2] + 0 <= skip) next } + /CMA: alloc failed|alloc_contig_range: .* busy|Spurious TX complete/ { next } + { printf "b%d #%d [%12.6f] %s\n", b, h[2], h[3] / 1000000, substr($0, i + 1); fflush() }' >> $KM & +KP=$! +trap 'kill $KP 2>/dev/null; echo "=== STOP boot $N at uptime $(cut -d" " -f1 /proc/uptime)" | tee -a $ST >> $KM; sync; exit 0' TERM INT +while :; do + status >> $ST + rot $ST; rot $KM + sleep $BB_PERIOD & wait $! +done diff --git a/b20/aurora-blackbox.openrc b/b20/aurora-blackbox.openrc new file mode 100644 index 0000000..541d167 --- /dev/null +++ b/b20/aurora-blackbox.openrc @@ -0,0 +1,10 @@ +#!/sbin/openrc-run +# B20: /usr/local/bin/aurora-blackbox under supervise-daemon (installed as /etc/init.d/aurora-blackbox). See the script header. +description="Aurora blackbox (B20)" +supervisor=supervise-daemon +command=/usr/local/bin/aurora-blackbox +output_log=/run/aurora-blackbox.service.log +error_log=/run/aurora-blackbox.service.log +respawn_delay=10 +respawn_max=0 +depend() { need localmount; before aurora-modem aurora-wifi aurora-vpn; } diff --git a/b20/aurora-netwatch b/b20/aurora-netwatch new file mode 100644 index 0000000..706d2c1 --- /dev/null +++ b/b20/aurora-netwatch @@ -0,0 +1,74 @@ +#!/bin/sh +# aurora-netwatch (B20): LTE data-path watchdog for standalone use. Runs under supervise-daemon (/etc/init.d/aurora-netwatch). +# Why: on 2026-10-06 Beeline detached the UE (bearer "cm error: emm-detached"); the modem re-attached, but nothing re-connected the bearer, +# wwan0 kept its dead address/routes and the board sat without internet (and without VPN) until a manual reconnect. +# Every $NW_PERIOD s while the controller reports BEARER_CONNECTED: +# healthy = MM bearer connected AND at least one of the probe hosts (/32 via wwan0) answers ping. +# $NW_FAILS consecutive unhealthy checks -> `aurora-modem disconnect` + `aurora-modem start` (radio/MSS/MM stay up, ~6 s), then +# restore the default route (aurora-router adds it once at boot; disconnect drops it with the link). +# Controller state OFF (a start failed and rolled back) -> `aurora-modem start` again, at most every $NW_OFF_RETRY s. +# Never runs while another aurora-modem start/stop/disconnect is in progress (manual or boot). Config: /etc/aurora/netwatch.conf (optional). +NW_PERIOD=30 NW_FAILS=3 NW_OFF_RETRY=300 NW_BOOT_GRACE=600 NW_PROBES="77.88.8.8" +[ -f /etc/aurora/netwatch.conf ] && . /etc/aurora/netwatch.conf +M=/usr/sbin/aurora-modem; D=/run/aurora-modem; S=/run/aurora-netwatch; mkdir -p $S; LOG=$S/netwatch.log +now() { cut -d' ' -f1 /proc/uptime; } +log() { m="[$(now)] $*"; echo "$m" >> $LOG; echo "<5>[aurora-netwatch] $*" > /dev/kmsg 2>/dev/null; } +busy() { for a in start stop disconnect; do pgrep -f "$M $a" >/dev/null && return 0; done; return 1; } +mstate() { cat $D/state 2>/dev/null || echo NONE; } +up_s() { cut -d. -f1 /proc/uptime; } + +bearer_ok() { + BR=$(sed -n 's/^BR=//p' $D/ipcfg 2>/dev/null) + [ -n "$BR" ] || return 1 + timeout 10 mmcli -b "$BR" --output-keyvalue 2>/dev/null | grep -q "bearer.status.connected *: yes" +} +ping_ok() { + . $D/ipcfg 2>/dev/null + for h in $NW_PROBES $(echo "$DNS" | tr ',' ' '); do ping -c1 -W3 "$h" >/dev/null 2>&1 && return 0; done + return 1 +} +defroute() { + IF=$(sed -n 's/^IF=//p' $D/ipcfg 2>/dev/null) + [ -n "$IF" ] && ! ip route | grep -q "^default" && ip route add default dev "$IF" 2>/dev/null && log "default route restored via $IF" +} +diag() { + timeout 10 mmcli -m any 2>/dev/null | sed 's/\x1b\[[0-9;]*m//g' | grep -E " state:|signal quality|access tech|registration|packet service" | tr -s ' ' | tr '\n' ';' + BR=$(sed -n 's/^BR=//p' $D/ipcfg 2>/dev/null) + [ -n "$BR" ] && timeout 10 mmcli -b "$BR" 2>/dev/null | sed 's/\x1b\[[0-9;]*m//g' | grep -E "connected:|error message|duration|bytes rx" | tr -s ' ' | tr '\n' ';' +} +recover() { + log "RECOVER ($1): $(diag)" + t0=$(up_s) + # timeouts above the controller's own deadlines (REG_TIMEOUT 600 + MM/connect) so it always finishes its own rollback + if [ "$(mstate)" = OFF ]; then timeout 1500 $M start >> $LOG 2>&1; rc=$? + else timeout 300 $M disconnect >> $LOG 2>&1; timeout 1500 $M start >> $LOG 2>&1; rc=$?; fi + defroute + if [ $rc = 0 ]; then log "RECOVER OK in $(( $(up_s) - t0 ))s: $(tail -1 $D/lifecycle.log | cut -c1-110)" + else log "RECOVER FAIL rc=$rc in $(( $(up_s) - t0 ))s, state $(mstate)"; fi + echo $(up_s) > $S/last-recover +} + +log "=== NETWATCH START period=${NW_PERIOD}s fails=$NW_FAILS probes=[$NW_PROBES + bearer DNS] off_retry=${NW_OFF_RETRY}s" +fails=0; last_off=0 +while :; do + sleep $NW_PERIOD + if busy; then fails=0; continue; fi + st=$(mstate) + case "$st" in + BEARER_CONNECTED) + defroute + if bearer_ok && ping_ok; then + [ $fails -gt 0 ] && log "healthy again after $fails failed check(s)"; fails=0; echo "ok $(up_s)" > $S/status + else + fails=$((fails+1)); echo "fail $fails $(up_s)" > $S/status + log "check failed ($fails/$NW_FAILS): bearer=$(bearer_ok && echo up || echo DOWN) ping=$(ping_ok && echo ok || echo FAIL)" + [ $fails -ge $NW_FAILS ] && { recover "bearer/data dead"; fails=0; } + fi;; + OFF) + # boot start still has its own deadline; only act after the grace period, then rate-limited + [ $(up_s) -lt $NW_BOOT_GRACE ] && continue + [ $(( $(up_s) - last_off )) -lt $NW_OFF_RETRY ] && continue + last_off=$(up_s); echo "off $(up_s)" > $S/status; recover "controller OFF";; + *) fails=0;; # starting / stopping: the controller owns the modem + esac +done diff --git a/b20/aurora-netwatch.openrc b/b20/aurora-netwatch.openrc new file mode 100644 index 0000000..4be7b54 --- /dev/null +++ b/b20/aurora-netwatch.openrc @@ -0,0 +1,10 @@ +#!/sbin/openrc-run +# B20: /usr/local/bin/aurora-netwatch under supervise-daemon (installed as /etc/init.d/aurora-netwatch). See the script header. +description="Aurora netwatch (B20)" +supervisor=supervise-daemon +command=/usr/local/bin/aurora-netwatch +output_log=/run/aurora-netwatch.service.log +error_log=/run/aurora-netwatch.service.log +respawn_delay=10 +respawn_max=0 +depend() { need localmount; after aurora-modem; } diff --git a/b22/aurora-vpn b/b22/aurora-vpn new file mode 100644 index 0000000..19f81db --- /dev/null +++ b/b22/aurora-vpn @@ -0,0 +1,146 @@ +#!/bin/sh +# aurora-vpn (B15): full-tunnel VPN for the Wi-Fi LAN. Two modes (/etc/aurora/vpn.conf VPN_MODE): +# vless = sing-box VLESS-over-WS-over-TLS via CDN, TUN singtun0 (config /etc/sing-box/config.json) +# wg = kernel WireGuard wg0 (config /etc/wireguard/wg0.conf) [+ B22: wg1 from /etc/wireguard/wg1.conf if present] +# B22: with wg1 present both tunnels are up; the LAN default route (table 51820) starts on wg0 and aurora-vpnsel moves it between +# wg1 (direct WireGuard over LTE, preferred) and wg0 (via the free-turn relay on 127.0.0.1). The firewall allows both. +# LAN clients are policy-routed into the tunnel; the board itself, USB NCM management and the tunnel's own +# outbound (to the VPN server/CDN) stay DIRECT on wwan0 (source-based rule -> no loop). +# Fail-closed: while up, LAN may leave ONLY via the tunnel; if it is down, LAN has no internet (never LTE). +# aurora-vpn up | down | status. Needs kernel 7.2.7-aurora-b15 (TUN; WireGuard for wg mode). +TBL=51820; RULE_PRI=100; S=/run/aurora-vpn; mkdir -p $S; LOG=$S/vpn.log +[ -f /etc/aurora/router.conf ] && . /etc/aurora/router.conf +[ -f /etc/aurora/vpn.conf ] && . /etc/aurora/vpn.conf +VPN_MODE=${VPN_MODE:-wg}; LAN_IF=${LAN_IF:-wlan0}; LAN_NET=${LAN_NET:-192.168.77.0/24}; LAN_ADDR=${LAN_ADDR:-192.168.77.1} +WAN_IF=${WAN_IF:-wwan0}; MGMT_IF=${MGMT_IF:-usb0}; TUN_IF=${TUN_IF:-singtun0}; TUN_MTU=${TUN_MTU:-1400} +SB=/etc/sing-box/config.json; WGC=/etc/wireguard/wg0.conf +now() { cut -d' ' -f1 /proc/uptime; } +log() { m="[$(now)] $*"; echo "$m"; echo "$m" >> $LOG; echo "<5>[aurora-vpn] $*" > /dev/kmsg 2>/dev/null; } +fail() { log "FAIL: $*"; exit 1; } +tif() { [ "$VPN_MODE" = wg ] && echo wg0 || echo "$TUN_IF"; } +# all tunnel interfaces the LAN may leave through, as an nft list body +tl() { if [ "$VPN_MODE" = wg ]; then [ -f /etc/wireguard/wg1.conf ] && echo '"wg0", "wg1"' || echo '"wg0"'; else echo "\"$TUN_IF\""; fi; } +# bring up one WireGuard interface from /etc/wireguard/.conf; non-loopback endpoint gets a /32 direct route via WAN +wg_up() { + i=$1; c=/etc/wireguard/$i.conf + ip link add dev $i type wireguard 2>/dev/null || ip link show $i >/dev/null 2>&1 || fail "no WireGuard" + [ -f $c ] || fail "$c missing" + A=$(sed -n 's/^Address[ ]*=[ ]*//p' $c | head -1); M=$(sed -n 's/^MTU[ ]*=[ ]*//p' $c | head -1); EP=$(sed -n 's/^Endpoint[ ]*=[ ]*//p' $c | head -1 | sed 's/:[0-9]*$//') + wg-quick strip $i 2>/dev/null | wg setconf $i /dev/stdin || { ip link del $i; fail "wg setconf $i"; } + ip addr flush dev $i; ip addr add "$A" dev $i; ip link set $i mtu "${M:-$TUN_MTU}" up + # loopback endpoint = local relay (free-turn-client on 127.0.0.1): its own uplink is board traffic -> main -> wwan0 + case "$EP" in 127.*) log "$i endpoint $EP is local relay: no direct route";; + *) GW=$(ip route show default dev $WAN_IF | sed -n 's/.* via \([^ ]*\).*/\1/p' | head -1) # point-to-point wwan0: usually no gateway + ip route replace "$EP/32" dev $WAN_IF ${GW:+via $GW}; echo "$EP" > $S/endpoint-$i + log "$i endpoint $EP direct via $WAN_IF";; esac +} + +firewall() { + T=$(tif); TL=$(tl); MSS=$(( ${1:-$TUN_MTU} - 40 )) + nft list table inet aurora_router >/dev/null 2>&1 && nft delete table inet aurora_router + nft -f - < /proc/sys/net/ipv4/ip_forward +} +unroute() { + while ip rule del from "$LAN_NET" lookup $TBL 2>/dev/null; do :; done + while ip rule del from "$LAN_NET" to "$LAN_NET" lookup main 2>/dev/null; do :; done + ip route flush table $TBL 2>/dev/null +} +case "$1" in +up) + log "=== VPN UP ($VPN_MODE)" + if [ "$VPN_MODE" = vless ]; then + [ -f $SB ] || fail "$SB missing" + command -v sing-box >/dev/null || fail "sing-box not installed" + pidof sing-box >/dev/null && { log "sing-box already running"; } || { + sing-box check -c $SB 2>>$LOG || fail "sing-box config invalid" + setsid sh -c "trap '' HUP; exec sing-box run -c $SB" >> $S/sing-box.log 2>&1 < /dev/null & + echo $! > $S/sing-box.pid + } + i=0; while [ ! -e /sys/class/net/$TUN_IF ] && [ $i -lt 100 ]; do sleep 0.1; i=$((i+1)); done + [ -e /sys/class/net/$TUN_IF ] || { tail -8 $S/sing-box.log; fail "$TUN_IF did not appear"; } + ip link set $TUN_IF mtu $TUN_MTU 2>/dev/null + else + wg_up wg0; M0=$M + [ -f /etc/wireguard/wg1.conf ] && wg_up wg1 + M=$M0 # MSS clamp from wg0 (the smaller MTU, 1280) covers both tunnels + fi + route_lan; firewall $M + log "=== VPN UP OK mode=$VPN_MODE tun=$(tif) LAN $LAN_NET -> $(tif) (table $TBL); fail-closed on" + ;; +down) + log "=== VPN DOWN" + [ "$VPN_MODE" = vless ] && { pidof sing-box >/dev/null && kill $(pidof sing-box); sleep 1; pidof sing-box >/dev/null && kill -9 $(pidof sing-box); rm -f $S/sing-box.pid; } + [ "$VPN_MODE" = wg ] && { ip link del wg0 2>/dev/null; ip link del wg1 2>/dev/null; } + unroute + for e in $S/endpoint $S/endpoint-*; do [ -f $e ] && { ip route del "$(cat $e)/32" dev $WAN_IF 2>/dev/null; rm -f $e; }; done + nft list table inet aurora_vpn >/dev/null 2>&1 && nft delete table inet aurora_vpn + /usr/sbin/aurora-router up >/dev/null 2>&1 || log "note: run 'aurora-router up' to restore LAN->LTE" + log "=== VPN DOWN OK (router restored: $(nft list tables 2>/dev/null | tr '\n' ' '))" + ;; +status) + T=$(tif); echo "kernel $(uname -r) mode $VPN_MODE" + if [ -e /sys/class/net/$T ]; then + echo "$T UP mtu=$(cat /sys/class/net/$T/mtu)" + if [ "$VPN_MODE" = vless ]; then + echo "sing-box pid=$(pidof sing-box) rss_kB=$(awk '/VmRSS/{print $2}' /proc/$(pidof sing-box 2>/dev/null)/status 2>/dev/null)" + tail -3 $S/sing-box.log 2>/dev/null | sed 's/^/ /' + else + for i in wg0 wg1; do [ -e /sys/class/net/$i ] || continue + HS=$(wg show $i latest-handshakes 2>/dev/null | awk '{print $2}' | head -1) + echo "$i handshake_age=$([ -n "$HS" ] && [ "$HS" != 0 ] && echo $(( $(date +%s)-HS ))s || echo never) transfer=$(wg show $i transfer 2>/dev/null | awk '{print $2"/"$3}')" + done + echo "active: $(ip route show table $TBL | awk '/^default/{print $3}') ($(cat /run/aurora-vpnsel/mode 2>/dev/null || echo no-selector))" + fi + echo "rules:"; ip rule show | grep -E "lookup $TBL|$LAN_NET" + echo "table $TBL:"; ip route show table $TBL + else echo "$T DOWN"; fi + nft list table inet aurora_vpn 2>/dev/null | grep -E "oifname|masquerade|drop" | sed 's/^[ \t]*//' | head + ;; +*) echo "usage: $0 up|down|status"; exit 2;; +esac diff --git a/b22/aurora-vpnsel b/b22/aurora-vpnsel new file mode 100644 index 0000000..021c9f3 --- /dev/null +++ b/b22/aurora-vpnsel @@ -0,0 +1,68 @@ +#!/bin/sh +# aurora-vpnsel (B22): chooses the LAN tunnel. Runs under supervise-daemon (/etc/init.d/aurora-vpnsel), after aurora-vpn (which brings up +# both wg0 = WireGuard via the free-turn relay on 127.0.0.1:9000 and wg1 = direct WireGuard to the server over LTE). +# wg1 handshake fresh (age <= VS_DEAD s) -> LAN default route (table 51820) via wg1, free-turn stopped (no VK/TURN traffic). +# wg1 handshake stale -> free-turn started, LAN via wg0; wg1 keeps its keepalive, so it re-handshakes by itself +# as soon as the direct path works again -> back to wg1 after VS_BACK fresh checks. +# A healthy wg1 re-handshakes every ~120 s (REKEY_AFTER_TIME, driven by the 25 s keepalive), so age > 150 s means the direct path is gone. +# State: /run/aurora-vpnsel/mode = direct | relay. Config: /etc/aurora/vpnsel.conf (optional). +# Operator whitelist probe (every VS_WL_PERIOD s, board traffic straight over LTE, TCP connect to :443, no data sent): +# a domestic host answers and no foreign one does -> on | foreign answers -> off | nothing answers -> nodata. +# /run/aurora-vpnsel/wl = " ". Beeline in whitelist mode drops/RSTs every non-whitelisted (foreign) IP, +# which also blocks the direct WireGuard server - so "on" explains a relay-only period. +VS_PERIOD=10 VS_DEAD=150 VS_BACK=2 VS_BOOT_WAIT=40 VS_WL_PERIOD=60 +VS_WL_DOM="ya.ru vk.com gosuslugi.ru" VS_WL_FOR="1.1.1.1 8.8.8.8 9.9.9.9" +[ -f /etc/aurora/vpnsel.conf ] && . /etc/aurora/vpnsel.conf +TBL=51820; S=/run/aurora-vpnsel; mkdir -p $S; LOG=$S/vpnsel.log +now() { cut -d' ' -f1 /proc/uptime; } +log() { m="[$(now)] $*"; echo "$m" >> $LOG; echo "<5>[aurora-vpnsel] $*" > /dev/kmsg 2>/dev/null; } +age() { h=$(wg show $1 latest-handshakes 2>/dev/null | awk '{print $2; exit}'); [ -n "$h" ] && [ "$h" != 0 ] && echo $(( $(date +%s) - h )) || echo 99999; } +active() { ip route show table $TBL | awk '/^default/{print $3; exit}'; } +ft_on() { rc-service free-turn status >/dev/null 2>&1 || { rc-service free-turn start >/dev/null 2>&1; log "free-turn started"; }; } +ft_off() { rc-service free-turn status >/dev/null 2>&1 && { rc-service free-turn stop >/dev/null 2>&1; log "free-turn stopped"; }; } +wl_probe() { + d=0; f=0 + for h in $VS_WL_DOM; do timeout 6 nc -z -w4 $h 443 >/dev/null 2>&1 && { d=1; break; }; done + for h in $VS_WL_FOR; do timeout 6 nc -z -w4 $h 443 >/dev/null 2>&1 && { f=1; break; }; done + if [ $f = 1 ]; then w=off; elif [ $d = 1 ]; then w=on; else w=nodata; fi + o=$(cut -d' ' -f1 $S/wl 2>/dev/null) + echo "$w $(date +%s)" > $S/wl + [ "$w" != "$o" ] && log "whitelist ${o:-?} -> $w (domestic=$d foreign=$f)" +} +use() { # use + [ -e /sys/class/net/$1 ] || return 1 + ip route replace default dev $1 table $TBL || return 1 + echo $2 > $S/mode; log "LAN -> $1 ($2): $3" +} + +log "=== VPNSEL START dead=${VS_DEAD}s back=${VS_BACK}x${VS_PERIOD}s whitelist probe every ${VS_WL_PERIOD}s" +wl_probe +# wait for aurora-vpn to create the tunnels (it may still be starting at boot) +i=0; while ! { [ -e /sys/class/net/wg0 ] && [ -e /sys/class/net/wg1 ]; } && [ $i -lt 120 ]; do sleep 2; i=$((i+1)); done +[ -e /sys/class/net/wg1 ] || { log "no wg1 (no /etc/wireguard/wg1.conf?): relay only"; echo relay > $S/mode; ft_on + while :; do sleep $VS_WL_PERIOD; wl_probe; done; } +# give the direct path a chance first: free-turn keeps running until wg1 proves itself +mode=$(cat $S/mode 2>/dev/null); fresh=0 +if [ -z "$mode" ]; then + i=0; while [ $(age wg1) -gt $VS_DEAD ] && [ $i -lt $VS_BOOT_WAIT ]; do sleep 1; i=$((i+1)); done + if [ $(age wg1) -le $VS_DEAD ]; then use wg1 direct "handshake after ${i}s"; mode=direct; ft_off + else use wg0 relay "no direct handshake in ${VS_BOOT_WAIT}s"; mode=relay; ft_on; fi +fi +n=0 +while :; do + sleep $VS_PERIOD + n=$((n+1)); [ $((n * VS_PERIOD % VS_WL_PERIOD)) = 0 ] && wl_probe + a1=$(age wg1) + case "$mode" in + direct) + [ "$(active)" = wg1 ] || use wg1 direct "route was $(active), restored" + if [ $a1 -gt $VS_DEAD ]; then + ft_on; use wg0 relay "wg1 handshake age ${a1}s > ${VS_DEAD}s"; mode=relay; fresh=0 + fi;; + *) + [ "$(active)" = wg0 ] || use wg0 relay "route was $(active), restored" + ft_on + if [ $a1 -le $VS_DEAD ]; then fresh=$((fresh+1)); else fresh=0; fi + if [ $fresh -ge $VS_BACK ]; then use wg1 direct "wg1 handshake age ${a1}s (fresh ${fresh}x)"; mode=direct; ft_off; fi;; + esac +done diff --git a/b22/aurora-vpnsel.openrc b/b22/aurora-vpnsel.openrc new file mode 100644 index 0000000..9df846c --- /dev/null +++ b/b22/aurora-vpnsel.openrc @@ -0,0 +1,10 @@ +#!/sbin/openrc-run +# B22: /usr/local/bin/aurora-vpnsel under supervise-daemon (installed as /etc/init.d/aurora-vpnsel): direct wg1 preferred, free-turn wg0 fallback. +description="Aurora VPN path selector (B22)" +supervisor=supervise-daemon +command=/usr/local/bin/aurora-vpnsel +output_log=/run/aurora-vpnsel.service.log +error_log=/run/aurora-vpnsel.service.log +respawn_delay=10 +respawn_max=0 +depend() { need localmount; after aurora-vpn free-turn; } diff --git a/linux/aurora-b12.config b/linux/aurora-b12.config new file mode 100644 index 0000000..d2164b0 --- /dev/null +++ b/linux/aurora-b12.config @@ -0,0 +1,30 @@ +# B12 router stack: b10b5 production + nftables NAT/firewall (RAM-boot kernel only) +CONFIG_LOCALVERSION="-aurora-b12" +CONFIG_NETFILTER=y +CONFIG_NETFILTER_ADVANCED=y +CONFIG_NETFILTER_INGRESS=y +CONFIG_NETFILTER_NETLINK=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NF_NAT=y +CONFIG_NF_NAT_MASQUERADE=y +CONFIG_NF_TABLES=y +CONFIG_NF_TABLES_INET=y +CONFIG_NF_TABLES_IPV4=y +CONFIG_NF_TABLES_IPV6=y +CONFIG_NFT_CT=y +CONFIG_NFT_COUNTER=y +CONFIG_NFT_LOG=y +CONFIG_NFT_LIMIT=y +CONFIG_NFT_MASQ=y +CONFIG_NFT_NAT=y +CONFIG_NFT_REJECT=y +CONFIG_NFT_REJECT_INET=y +CONFIG_NFT_FIB_INET=y +CONFIG_NFT_FIB_IPV4=y +CONFIG_NF_LOG_IPV4=y +CONFIG_NF_REJECT_IPV4=y +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_IP_NF_IPTABLES=n diff --git a/linux/aurora-b15.config b/linux/aurora-b15.config new file mode 100644 index 0000000..cfb3831 --- /dev/null +++ b/linux/aurora-b15.config @@ -0,0 +1,8 @@ +# B15: b12 + WireGuard + policy routing (ip rule / fwmark tables) for WireGuard-over-CSQTT. RAM-boot kernel. +CONFIG_LOCALVERSION="-aurora-b15" +CONFIG_WIREGUARD=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_NET_UDP_TUNNEL=y diff --git a/linux/aurora-b18.config b/linux/aurora-b18.config new file mode 100644 index 0000000..be89685 --- /dev/null +++ b/linux/aurora-b18.config @@ -0,0 +1,15 @@ +# B18: b15 + USB host (ChipIdea OTG role switch) for a powered hub: flash drives + keyboards. RAM-boot kernel. +CONFIG_LOCALVERSION="-aurora-b18" +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_UAS=y +CONFIG_HID=y +CONFIG_HID_GENERIC=y +CONFIG_USB_HID=y +CONFIG_EXFAT_FS=y +CONFIG_NLS_UTF8=y +CONFIG_NLS_CODEPAGE_866=y diff --git a/logs/b11/B11-LED-AUDIT.md b/logs/b11/B11-LED-AUDIT.md new file mode 100644 index 0000000..75e2863 --- /dev/null +++ b/logs/b11/B11-LED-AUDIT.md @@ -0,0 +1,82 @@ +# B11 — LED / button audit (read-only), 2026-10-04 + +Board: JZ08AU Aurora (MSM8916 + PM8916). Live system: eMMC cache B10B-5 c3732515, kernel 7.2.7-aurora-b10b5, uptime ≈ 53 min ("HB cold2"). +Nothing was written to eMMC, PMIC, or TLMM during the audit. All reads used debugfs (`/sys/kernel/debug/gpio`, `pinctrl/*/pinmux-pins`, +`regmap/0-00|0-01/registers`). + +Raw data: +- `logs/b11/live/live-debugfs-1.txt`: TLMM 122 pins (dir, level, func, drive, pull), PM8916 MPP/GPIO, pinmux owners, /proc/interrupts +- `logs/b11/live/live-pmic-ro-2.txt`: PM8916 PON 0x0800–0x084f; MPP1–4 and GPIO1–4 (sid0); LPG 0xbc00 and VIB 0xc000 (sid1); inputs, backlight, leds +- script: `b11/audit/b11-ro.sh` (read-only) + +## 1. Sources checked + +| Source | What was looked for | Result | +|---|---|---| +| Stock DT `dt/dtb_01.dts` (active 512MB DT) | `gpio-leds`, `qcom,leds-qpnp`, MPP/GPIO/PWM/LPG, pinctrl | `gpio-leds` node holds **only the SIM mux** (`sim1/2/3_switch_gpio`, `sim_hotdet_gpio` = GPIO22/23/1/20). These are not LEDs. `qcom,leds-qpnp` has only `lcd-bl` on MPP4. MPP1–3 and PM GPIO1–4 are `disabled`. `pwm@bc00` is declared with no consumer. `vibrator@c000` is `okay`, set to 3.1 V. | +| Stock Android runtime `android/leds.txt`, `android/gpio.txt` | `/sys/class/leds` | `lcd-backlight` (MDSS virtual), `lcd-bl` (MPP4), `mmc0::` (trigger only), 4× SIM switch. **No indicator LED.** | +| Stock /system (`partitions/system.bin`, ext4, read via debugfs) | `/sys/class/leds/*`, `/sys/class/gpio/*` users | `lights.msm8916.so` and `mmi_led.so` are generic Qualcomm (red/green/blue). `usbhub` (gpio56) and `init.qcom.post_boot.sh` (gpio253–259) are generic code for other targets. Nothing is Aurora-specific. | +| Stock init.rc | LED chown list | Generic red/green/blue/yellow(+_sec, 2, 2_sec) chowns. No matching DT nodes exist. | +| Stock MPSS (`firmware-fat/image/modem.b21`) | LED strings | `led_red/green/blue` appear only in the generic TLMM pin-name table (next to `lcd_rst_n`, `cam_flash_torch_en`, `kpsns0`…, MTP names). They give no pin mapping and no proof that the modem drives an LED. | +| Stock LK / lk1st-b9l | LED code | Only MPP4 (backlight) and the SPI panel are used. | +| Current production DTS (b9b → b10b3 chain) | LED / keys | MPP4 → `gpio-backlight` (`/sys/class/backlight/backlight`). No gpio-leds and no gpio-keys. Only `pm8941_pwrkey` is an input. | +| Live TLMM / PMIC state | outputs, current sinks | See §2 and §3 | +| JZ02 (reference only) | RGB GPIO6/7/8 | Different PCB. Stock Aurora DT dropped these LEDs, and Aurora GPIO6/7/8 are unclaimed inputs with pull-down. This is not evidence of an LED on Aurora. | + +## 2. TLMM (MSM8916 GPIO 0–121) — live state and classification + +| GPIO | live | consumer / function | class | +|---|---|---|---| +| 0, 2, 3 | in, pull-down | unclaimed (BLSP1 SPI/UART1 pins) | UNKNOWN | +| 1, 20, 22, 23 | out, 1 / 0 / 0 / 0 | SIM mux (MPSS pinctrl, stock "gpio-leds" hack) | **DO NOT TOUCH** (modem/SIM) | +| 4, 5 | func2 | BLSP1 UART2 console | **DO NOT TOUCH** (UART) | +| 6–11, 16–19, 21, 24–36, 38, 39, 45–56, 60–62, 69–98, 108–112, 114, 115, 117, 119–121 | in, pull-down, func0 | unclaimed; no stock consumer (stock DT references only TPIU debug, MTP cameras, codec, usb-id) | UNKNOWN | +| 12–15 | func1 | BLSP1 QUP4 SPI → ST7735S | **DO NOT TOUCH** (display SPI) | +| 37 | in, low, pull-down | `key_f2` (stock gpio-keys, active-high) | button: read-only | +| 40–44 | func1 | WCNSS 5-wire (Pronto/Iris) | **DO NOT TOUCH** (WCNSS) | +| 57–60 (57–59 out, func1) | UIM1 | SIM interface | **DO NOT TOUCH** (modem/SIM) | +| 63–68 | in | codec PDM (PM8916 audio) | **DO NOT TOUCH** | +| 99–102 | in, no pull | GSM TX phase (RF) | **DO NOT TOUCH** (modem RF) | +| 103, 104 | in, pull-down | SSBI WTR0 | **DO NOT TOUCH** (RF) | +| 105 | **out low**, func0, no pull | not claimed by Linux, driven by boot/MPSS firmware (ssbi_wtr1 pad as GPIO) | **DO NOT TOUCH** (modem-owned RF/GRFC, consumer unknown) | +| 106 | in, **pull-up**, high | ssbi_wtr1 pad; pull set by firmware | **DO NOT TOUCH** (RF/unknown) | +| 107 | in, high, pull-up | `key_f3` (stock gpio-keys, active-low) | button: read-only | +| 110 | in | usb-id pin (stock) | **DO NOT TOUCH** (USB) | +| 113 | in | cdc-us-euro (audio switch) | **DO NOT TOUCH** | +| 116, 118 | out high, 8 mA | panel D/C, RESET (spi0.0) | **DO NOT TOUCH** (display) | +| SDC1 / SDC2 / QDSD pads | — | eMMC / SD | **DO NOT TOUCH** | + +**No TLMM pin has evidence of an indicator LED.** No DT node, stock userspace, or live output state points to one. +Every unclaimed pin is a firmware-default input with pull-down, so no LED on those pins is lit now. +Because the topology is unknown, none of them may be driven (B11 rule). + +## 3. PM8916 peripherals + +| Block | live registers | meaning | class | +|---|---|---|---| +| **MPP4** (0xa300) | MODE 0x61, VIN 0, EN 0x80, SINK_CTL 0x07 | current sink 40 mA, ON = LCD backlight (B9A/B9B, stock `lcd-bl`) | **SAFE** (proven in B9B/B9C/B9L) | +| MPP3 (0xa200) | MODE 0x60 (sink, source = 0 → off), EN 0x80, SINK 0x00 (5 mA) | current sink configured but off. Same value on every boot since B9C baselines. No consumer in any stock layer. | UNKNOWN (likely LED-type driver, but the load is unproven) | +| MPP2 (0xa100) | MODE 0x11 (digital output, **HIGH**), VIN 1, EN 0x80 | set by boot firmware; consumer unknown (could be an enable or a reference) | **DO NOT TOUCH** | +| MPP1 (0xa000) | MODE 0x51 (analog output), VIN 2, EN 0x80 | analog/reference output set by firmware | **DO NOT TOUCH** | +| PM GPIO1–4 (0xc000–0xc300) | input, pull-down 10 µA, EN 0x80 | stock `disabled`; consumer unknown | UNKNOWN | +| LPG/PWM (sid1 0xbc00) | EN 0x00 (off) | no consumer in stock or current DT. **PWM routing to MPP4 is unproven.** | no PWM-capable LED confirmed | +| VIB_DRV (sid1 0xc000) | EN 0x00, VSET 0x16 | stock `vibrator` 3.1 V; the board has no known motor; the load is unknown | UNKNOWN | +| LBC charger | — | no charge-LED output (checked against downstream qpnp-linear-charger) | n/a | + +## 4. Buttons (read-only) + +| Button | line | idle level | notes | +|---|---|---|---| +| POWER | PM8916 KPDPWR (PON RT 0x0810 bit0); Linux `pm8941_pwrkey` → event0 | released | S2 reset enabled (0x0843 = 0x80, warm reset after long hold). **Short presses only.** | +| RESET | PM8916 RESIN (PON RT bit1) | released | no Linux input. Holding it too long → PMIC stage-3 reset (B10 note). KPDPWR+RESIN = hard reset (0x084b = 0x80). **Short presses only, never both at once.** | +| key_f2 | TLMM GPIO37, active-high | low (pull-down) | not bound in current DT; poll via debugfs | +| key_f3 | TLMM GPIO107, active-low | high (pull-up) | not bound in current DT; poll via debugfs | + +## 5. Conclusion of the audit + +- SAFE LED lines: **1**, PM8916 MPP4 (LCD backlight current sink). It is already controllable in the live production system through + `/sys/class/backlight/backlight` (gpio-backlight, 0/1). No PWM. +- UNKNOWN: MPP3 sink, PM GPIO1–4, VIB_DRV, and all unclaimed TLMM pins. None is switched. +- DO NOT TOUCH: MPP1, MPP2, GPIO1/20/22/23, 4/5, 12–15, 40–44, 57–60, 63–68, 99–106, 110, 113, 116, 118, SDC pads. +- The physical indicator LEDs and empty footprints the operator sees cannot be tied to any software-controlled line from the sources available. + They are likely hard-wired (power/VBUS/charge), backlight, or DNP options for another SKU. Proving that needs continuity checks on the board. diff --git a/logs/b11/B11-LED-RESULT.md b/logs/b11/B11-LED-RESULT.md new file mode 100644 index 0000000..dddad86 --- /dev/null +++ b/logs/b11/B11-LED-RESULT.md @@ -0,0 +1,73 @@ +# B11 — full LED audit and all-SAFE-LED run: RESULT (2026-10-04) + +**Verdict: PASS, with a scope note.** The audit found exactly **one** LED line whose control is proven safe: PM8916 MPP4, the LCD backlight current sink. +It was exercised in one automatic run with every stage and full monitoring. No other line was switched. + +- Board: live production boot, eMMC cache B10B-5 c3732515, kernel 7.2.7-aurora-b10b5, "HB cold2". +- **No eMMC/cache writes. No new image. No reboot. No raw PMIC/TLMM register writes.** The run used only the Linux backlight class. +- PMIC MPP/GPIO/PON/LPG/VIB and TLMM key state are **byte-identical before and after** (`live-pmic-ro-2.txt` vs `live-pmic-ro-3-post.txt`). + +## Why no separate RAM image was built +The only SAFE line is already a Linux class device in the production kernel (`/sys/class/backlight/backlight`, gpio-backlight on MPP4). +A RAM DTB that renames it to `/sys/class/leds/aurora-led-*` adds nothing to the test and would need a RESET-held fastboot boot. +The operator approved running on the live boot instead. The production kernel has `LEDS_GPIO=y`, so a DTB-only RAM image is possible later if new SAFE lines are proven. + +## Main table + +| LED | physical position | GPIO/MPP/LPG | active level | initial state | all-ON result | chase result | brightness | verdict | +|---|---|---|---|---|---|---|---|---| +| L1 LCD backlight | behind the 1.44" ST7735S panel | PM8916 **MPP4** current sink 40 mA (MODE 0x61 on / 0x60 off, SINK 0x07, EN 0x80) | sink ON = lit (gpio-backlight 1) | ON (bl=1, a340=61) | **lit**: operator-confirmed; a340 60→61 | **2/2 cycles**: operator-confirmed | not tested (no PWM; LPG 0xbc00 not routed/proven) | **PASS / SAFE** | +| MPP3 | unknown | PM8916 MPP3, current sink, source = off | — | off (a240=60) | not switched | — | — | UNKNOWN | +| PM GPIO1–4 | unknown | PM8916 GPIO 1–4, input with pull-down | — | input | not switched | — | — | UNKNOWN | +| VIB_DRV | unknown (no motor known) | PM8916 sid1 0xc000 | — | off | not switched | — | — | UNKNOWN | +| TLMM unclaimed pins (list in audit §2) | — | input with pull-down, func0 | — | input | not switched | — | — | UNKNOWN | + +## Run r1 (`logs/b11/live/b11-run-r1.log`, script `b11/run/b11-led-run.sh`) + +``` +3415.29 PRECHECK lte=ok mpss=ok wcnss=ok wifi=ok ncm=ok disp=ok chg=Charging pmic=40504 kerr=0 +3415.30 STAGE1 ALL-OFF a340=60 bl=0 +3417.31 STAGE2 ALL-ON 4s a340=61 bl=1 +3421.34 STAGE3 ALL-OFF a340=60 +3423.36 STAGE4 CHASE c1 L1 ON / 3424.07 OFF / 3424.77 c2 ON / 3425.48 OFF +3426.19 STAGE5 SKIPPED no PWM-confirmed line +3426.21 STAGE6 RESTORE a340=61 a341=00 a346=80 a34c=07 bl=1 (= PRE) +3431.24 POSTCHECK lte=ok mpss=ok wcnss=ok wifi=ok ncm=ok disp=ok chg=Charging pmic=41033 kerr=0 +3431.25 END OK +``` + +- A background monitor ran about every 2 s. It checked LTE ping via wwan0, MPSS, WCNSS, wlan0+hostapd, usb0 NCM, the panel driver bound plus fb0, charger status, PMIC temperature, and new kernel errors. + Any FAIL would have restored the line and aborted the run. There were 0 FAILs and 0 new kernel error lines. The heartbeat continued. +- Afterwards: LTE ping 3/3, both remoteprocs running, wlan0 up, backlight 1 (`live/post-health.txt`). +- Side observation: the `pm8916-thermal` reading drops by about 7 °C (40.5 → 33.2 °C) within 1 s of switching the 40 mA sink off, and comes back when it is switched on. + That is too fast to be real heating, so it is a measurement artefact of the temperature-alarm/ADC path under MPP4 load. Account for it in thermal logic. + +## Buttons (read-only, `b11/run/b11-btn-watch.sh`, logs `live/b11-btn-b1.log`, `b11-btn-b2.log`) + +No line was driven. The script polled `debugfs gpio` and PON RT 0x0810 every ~0.12 s. +The case has 3 buttons (operator): **POWER, MENU, RESET**. + +| Button | line | observed | notes | +|---|---|---|---| +| POWER | PM8916 KPDPWR (PON RT bit0), Linux `pm8941_pwrkey` event0 | bit0 pulses; pwrkey IRQ 2 → 36 | works. Nothing in userspace acts on KEY_POWER. S2 warm reset on long hold (0x0843 = 0x80). | +| MENU | **probably also KPDPWR** | the 2nd press series (≈3697 s, order POWER → MENU → RESET) is also bit0; GPIO37/107 never changed | not provable from software alone (MENU and POWER look identical). Needs a one-button-at-a-time recheck or continuity check. | +| RESET | PM8916 RESIN (PON RT bit1) | bit1 pulses (≈3705 s, ≈3761 s) | no Linux input bound. Long hold → PMIC stage-3 reset. KPDPWR+RESIN = hard reset. | +| key_f2 / key_f3 (stock DT GPIO37 / GPIO107) | TLMM | no change in either window | no button on the case drives these lines; stock gpio-keys entries are probably leftovers. | + +The first watcher window (b1) saw no presses because the operator had not pressed yet. Window b2 is the valid one. + +## Summary + +- Physical LEDs that lit: **1 LED function** (LCD backlight; operator-confirmed). The operator sees no other lit indicator LED in normal operation. +- SAFE lines found: **1** (MPP4). +- Empty LED footprints: **not mapped**. No DT, firmware, or live-state evidence ties them to a line. With no continuity data they are documented only and not switched. +- Still UNKNOWN: MPP3 (current sink, off; the most LED-like candidate), PM GPIO1–4, VIB_DRV, and all unclaimed TLMM inputs (GPIO 0, 2, 3, 6–11, 16–19, 21, 24–36, 38, 39, 45–56, 61, 62, 69–98, 108, 109, 111, 112, 114, 115, 117, 119–121). +- DO NOT TOUCH: MPP1 (analog out), MPP2 (digital out HIGH, set by firmware), GPIO1/20/22/23 (SIM mux), 4/5 (UART), 12–15/116/118 (display), 40–44 (WCNSS), + 57–60 (UIM), 63–68 (codec), 99–106 (modem RF/SSBI incl. 105 out-low and 106 pulled up), 110 (USB-ID), 113 (audio), SDC pads; PON KPDPWR/RESIN (read-only only). + +## Next steps (each needs a separate GO) +1. Continuity: trace the empty LED footprints, and their series-resistor pads if present, to the SoC/PMIC. A footprint that reaches MPP3 or a PM GPIO can then become SAFE. +2. MENU: press one button per window to confirm MENU = KPDPWR. If it is separate, find its line. +3. Optional: MPP4 brightness by sink current (5–40 mA, SINK_CTL 0–7). This is not PWM; it needs a test kernel with a register hook or an LED driver. + +Files: `logs/b11/B11-LED-AUDIT.md` (full audit), `logs/b11/live/*`, `b11/audit/b11-ro.sh`, `b11/run/b11-led-run.sh`, `b11/run/b11-btn-watch.sh`. diff --git a/logs/b11/b11c/B11C-TRACE-PLAN.md b/logs/b11/b11c/B11C-TRACE-PLAN.md new file mode 100644 index 0000000..42bec4e --- /dev/null +++ b/logs/b11/b11c/B11C-TRACE-PLAN.md @@ -0,0 +1,139 @@ +# B11C — unused LED footprints + MPP3: trace map and continuity plan (NO WRITES) + +Date: 2026-10-04. Inputs: B11-LED-AUDIT.md and B11-LED-RESULT.md (B11A/B11B closed). Nothing was switched or written in B11C. + +## 0. Limitation: there are no board photos + +The workspace (local mirror and 480s `~/doc/modem`) contains **no photos of the PCB**. The only images are the splash, the LK logo and recovery icons. +Step 1 (find all LEDs and DNP LED footprints from photos) and step 2 (read polarity, resistor values and traces from photos) therefore **cannot be done yet**. +Nothing below assigns a footprint to a pin. That would be invented. + +What is needed (put the files in `logs/b11/b11c/photos/`): +- top and bottom of the bare PCB, shot straight down, sharp at full resolution, with the shield cans as they are (do not remove them); +- a close-up of every LED and every empty 2-pad LED-like footprint, together with the 1–2 small resistor pads next to it; +- the silkscreen designators (D?, LED?, R?) if any are printed; +- one photo with a ruler or a coin for scale. +From these I can fill §2: polarity marks, R markings, which pad visibly goes to a ground pour, a VBAT/VPH plane, or a via toward the PMIC. + +## 1. What is known about the candidate controllers (from audit and PMIC driver facts) + +| Controller | live state (read-only) | what an LED on it would look like | can it light an LED by itself | +|---|---|---|---| +| PM8916 **MPP3** | current-sink mode, source = off (0xa240 = 0x60), EN 0x80, I = 5 mA setting | **sink topology**: rail (VPH/VBAT) → LED anode, cathode → (R or none) → MPP3. Same as the MPP4 backlight. | yes (5–40 mA regulated sink, so no resistor is required) | +| PM8916 GPIO1–4 | input, pull-down 10 µA | either sink (rail → LED → R → GPIO, open-drain low) or source (GPIO → R → LED → GND, needs a VPH-powered GPIO) | yes, with a series R | +| PM8916 VIB_DRV | off, VSET 0x16 | motor-driver output, 1.2–3.1 V (pm8xxx-vibrator, PM8916 regs 0x41/0x46); polarity on this PCB is unknown | possibly (it can drive a LED+R like a small load), but this is not proven | +| PM8916 MPP4 | backlight sink, ON | already used; a footprint on this net would only add an LED to the backlight | n/a | +| SoC TLMM (unclaimed pins) | inputs, pull-down, 1.8 V I/O | GPIO → R → LED → GND | weak: 1.8 V I/O cannot forward-bias most LEDs, at best a red LED with a tiny current. Low prior. | +| PM8916 LPG/PWM | off | only drives a pin through MPP/GPIO DTEST routing | only if a PM GPIO/MPP is the wired pin | + +A meter alone cannot tell **which** PMIC pin a trace reaches: MPP3, GPIO1–4 and VIB_DRV all show a similar ESD-diode signature. +Continuity tells the **topology** (GND side / rail side / IC side, and whether the resistor is populated). The pin itself is identified in a later, separate +**one-line identification step** (§5) or by the powered voltage map (§3, phase V), which is read-only for the board. + +## 2. Footprint map (to fill in after photos and measurements) + +| ID | position | type (LED / DNP LED) | pad A | pad B | series R (ID, marking, Ω in-circuit) | A→ | B→ | far node diode V (fwd / rev) | far node 0 Ω to known net? | V(on) far node | topology | controller/pin | verdict | +|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +| D? | | | | | | | | | | | | | needs continuity measurement | + +Verdict categories: **SAFE candidate** / **needs continuity measurement** / **not LED / unrelated** / **do not touch**. + +## 3. Measurement instructions for the operator + +### Safety and preparation (mandatory) +1. **Fully de-energise the board.** Pull the USB cable, **take the battery out** (the board cannot power off by software and the PMIC stays alive on battery), + and **disconnect the CH340 UART wires** (TX backfeed keeps the PMIC alive, see T3C). Wait ≥ 60 s. + Reason: the 2026-10-03 probe slip shorted VBAT to GND (UVLO). Continuity and diode tests on a powered board give false readings and can short rails. +2. You can keep the DMM leads already soldered to VBAT/GND, as long as the battery is out. +3. Use thin, sharp probes, one pad at a time. Never bridge two neighbouring pads. + +### Reference nodes (measure these first and write the values down) +| Ref | where | why | +|---|---|---| +| GND | USB connector shell, or battery − contact | ground | +| VBAT | battery + contact (or your soldered VBAT lead) | LED anode rail candidate (VPH ≈ VBAT on this board, but through the LBC FET, so it may **not** read 0 Ω) | +| VBUS | USB connector pin 1 (5 V) | some designs hang a "charge/power" LED on VBUS | +| BL-K | the backlight cathode pin on the display flex/connector (LED-K / LEDK), = **MPP4** node | a known PMIC MPP signature, and a check for "shares the backlight net" | +| BL-A | the backlight anode pin (LED-A) | it reveals the rail the backlight uses (VPH/VBAT?) | +| UART-RX / UART-TX | the pads where the CH340 was soldered (SoC GPIO5 / GPIO4) | a known SoC 1.8 V GPIO signature | + +For each Ref, record the **diode-mode** reading with the red probe on GND and the black probe on Ref, then reversed. Also record **resistance** Ref→GND. + +### Per footprint (repeat for every LED / DNP LED, priority order in §4) +Label the footprint pads A and B. If there is a polarity mark, A = the cathode side. Label the resistor pads R1 (toward the LED) and R2 (far side). + +| Step | DMM mode | probes | record | +|---|---|---|---| +| M1 | continuity / 200 Ω | A ↔ GND, B ↔ GND | Ω (0 Ω means that pad is ground) | +| M2 | 200 Ω (wait 2–3 s, rail capacitors charge) | A ↔ VBAT, B ↔ VBAT, A ↔ BL-A, B ↔ BL-A, A ↔ VBUS, B ↔ VBUS | Ω | +| M3 | continuity | A ↔ R1, B ↔ R1 (find which LED pad the resistor belongs to) | 0 Ω / OL | +| M4 | 2 kΩ / 20 kΩ | R1 ↔ R2 | in-circuit Ω (with R populated, about its value; with R DNP, OL), plus the marking (e.g. "102" = 1 kΩ, "471" = 470 Ω) | +| M5 | diode | **far node** = R2, or the LED pad that is neither GND nor rail when no R is present: red on GND, black on far node; then reversed | V fwd / V rev (an IC pin with ESD is typically ~0.4–0.7 V; OL both ways means nothing is connected) | +| M6 | continuity | far node ↔ BL-K, far node ↔ UART-RX, far node ↔ UART-TX | 0 Ω / OL (exclusion checks) | +| M7 | diode | across the LED pads (populated LEDs only): red on A, black on B, then reversed | the forward reading or a faint glow gives polarity | + +Write down: footprint ID, position (e.g. "top side, left of SIM slot"), the photo filename, and every value above. + +### Phase V (optional, read-only for the board, separate operator decision): powered voltage map +Only if you are comfortable doing it. Clip the black lead to the USB shell first. The board runs normally on USB with the battery in. +Measure **DC volts** at each **far node** with one sharp red probe. +Software state is known from the audit, so the voltage separates the candidates without any write: +| expected far-node voltage (sink topology, LED anode on rail) | means | +|---|---| +| ≈ rail (≈ VBAT, LED dark) | high-Z pin: **MPP3** (sink off) or VIB_DRV off. Candidate MPP3. | +| ≈ rail − 1.6…2.5 V, LED dark | 10 µA pull-down: **PM GPIO1–4** | +| ≤ ~2.3 V, clamped | SoC 1.8 V pad (TLMM). Low priority. | +| ≈ 0 V | the node is driven low or tied to GND | +For source topology (LED to GND), every candidate pin is low or high-Z, so all read ≈ 0 V and phase V does not separate them. + +## 4. Priority order +1. Footprints whose far node is on the **rail side + IC side** (sink topology) → **MPP3** first (the only PMIC output already configured as a sink). +2. Then sink/source footprints that fit **PM GPIO1–4**. +3. Then **VIB_DRV** (expect a single pin near the PMIC; on many boards there is a motor pad pair). +4. SoC GPIO only if a trace visibly runs to an unclaimed TLMM pin, and is **never** any pin from the DO-NOT-TOUCH list: + reset/PON/EDL (KPDPWR, RESIN, USB D+), SIM/UIM (1, 20, 22, 23, 57–60), UART (4, 5), SPI/display (12–15, 116, 118), WCNSS (40–44), + RF/SSBI/GRFC (99–106), USB-ID (110), audio (63–68, 113), eMMC/SD pads, MPP1, MPP2. + +## 5. Decision rules after measurement +| result | verdict | +|---|---| +| R populated (or a sink-only design with no R) + sink topology + far node = IC pin + phase V says high-Z | **SAFE candidate → MPP3** (confirm in the RAM ID step) | +| R populated + topology proven + far node = IC pin, but which PMIC pin is unknown | **needs continuity measurement** (or the ID step below) | +| R DNP, or far node OL (no IC), or topology unproven | document only, **do not switch** | +| far node 0 Ω to BL-K | part of the backlight (MPP4). **not a separate LED.** | +| far node 0 Ω to UART/any DO-NOT-TOUCH net, or the pad is a rail/GND-only test point | **not LED / do not touch** | + +## 6. One common RAM-only plan (prepared only, needs a separate GO; nothing built) + +Base = the production kernel **7.2.7-aurora-b10b5** unchanged. Its config already has `LEDS_GPIO=y`, `LEDS_QCOM_LPG=y`, `PWM=y`, +`PINCTRL_QCOM_SPMI_PMIC=y`, `INPUT_PM8XXX_VIBRATOR=y` and `LEDS_TRIGGER_TIMER=y`, so the plan is a **DTB-only change** on top of prod DTB e47762d1. +Boot it with RESET-held power-on, then `fastboot boot`. **No cache/eMMC write.** + +1. **ID step (one boot, before the LED image; separate GO):** with the operator watching the footprints, one 0.5 s pulse per candidate, + each preceded by a register snapshot and followed by a restore and readback: + MPP3 sink 5 mA ON → OFF; then each PM GPIO that §5 left as a candidate (open-drain low for sink topology, push-pull high for source topology; never both); + then VIB at minimum level 1.2 V for 0.5 s (only if a footprint was traced to the vibrator pads). + Only candidates that passed continuity are pulsed. The rest stay untouched. The same monitor and abort logic as `b11/run/b11-led-run.sh`. +2. **LED image (the same boot, or a second DTB):** every confirmed line goes into one `gpio-leds` node, `aurora-led-`: + - MPP3: the same recipe as B9B MPP4: `&pm8916_mpps` pinctrl `pins = "mpp3"; function = "sink"; qcom,drive-strength = ` (sink level, + the lowest that is visibly lit, starting at 0 = 5 mA), output-low at probe, plus a gpio-leds entry `gpios = <&pm8916_mpps 3 GPIO_ACTIVE_HIGH>`, `default-state = "off"`. + - PM GPIO n: `&pm8916_gpios` pinctrl (`function = "normal"`, `power-source` matching the rail, `drive-open-drain` for sink topology), + plus a gpio-leds entry with the measured active level. + - Backlight MPP4 stays on gpio-backlight (unchanged). + - PWM/brightness: only if a confirmed line is a PM GPIO/MPP that the LPG can be routed to (`qcom,pm8916-pwm` + `leds-pwm`). Otherwise there is no brightness test. +3. **Run:** the existing `b11/run/b11-led-run.sh`, with `LEDS="L1:…backlight… L2:/sys/class/leds/aurora-led-x/brightness:1:0 …"`. + Stages: all OFF → all ON 4 s → OFF → chase 2× → (PWM) → restore. The same monitor (LTE, Wi-Fi/WCNSS, NCM, display, charger, PMIC temp, kernel errors), + pre/post PMIC dump diff with `b11/audit/b11-ro.sh`. + +## 7. Current verdicts (before measurement) + +| footprint | resistor | controller/pin | verdict | +|---|---|---|---| +| LCD backlight (populated, inside the panel) | none (current sink) | PM8916 MPP4 | SAFE (done in B11B) | +| all other LED / DNP LED footprints | unknown (no photos) | unknown | **needs continuity measurement** | +| MPP3 | — | PM8916 MPP3 (sink, off) | SAFE candidate *only after* a footprint is traced to it, otherwise UNKNOWN | +| PM GPIO1–4 | — | PM8916 GPIO1–4 | needs continuity measurement | +| VIB_DRV | — | PM8916 vibrator | needs continuity measurement | +| MPP1, MPP2, SIM/UART/SPI/WCNSS/UIM/RF/USB/eMMC/PON lines | — | — | do not touch | + +STOP. Nothing is switched without a separate GO. diff --git a/logs/b12/b12a/B12A-AUDIO-AUDIT.md b/logs/b12/b12a/B12A-AUDIO-AUDIT.md new file mode 100644 index 0000000..77b7925 --- /dev/null +++ b/logs/b12/b12a/B12A-AUDIO-AUDIT.md @@ -0,0 +1,34 @@ +# B12A — audio audit (read-only, desk only), 2026-10-04 + +B11 LED topic closed by the operator: the 15-LED array under the display frame (BOT side, logs/b11/b11c/B11C2-annotated-bot.jpg) is the display backlight. + +## Stock +- DT dtb_01: internal codec. Analog part = PM8916 "Tombak" `msm8x16_wcd_codec@f000/@f100` (qcom,wcd-spmi): speaker class-D IRQs (spk_cnp/clip/ocp), + headset detect/MBHC (ins_rem_det, but_press/rel_det, mbhc_int), earpiece/HPH OCP. Supplies (rails inferred from voltages): VDDA_CP/VDD_PX 1.8–2.2 V (≈S4), VDDA_H/VDD_PA 1.8 V (≈L5), micbias 3.075 V (≈L13). + Card `sound` qcom,msm8x16-audio-codec, model **msm8x16-snd-card-mtp** (generic MTP), ext-pa "primary", US/Euro switch GPIO113, cdc-pdm GPIO63–68. +- Android: the card probed (card0 + "Button Jack"/"Headset Jack" inputs), audiod/mediaserver ran. /system/etc mixer_paths.xml = stock Qualcomm 8916 MTP + (speaker = SPK DAC, handset = EAR PA, headphones = HPHL/R, handset-mic ADC1, headset-mic ADC2); mixer_paths_mtp.xml differs only in speaker-mic → ADC3. + audio_policy.conf: attached EARPIECE|SPEAKER, BUILTIN_MIC|BACK_MIC. **All of it is the unmodified reference config → no evidence the board has any transducer.** +- No Aurora-specific audio code or config found. + +## Photos (logs/b11/b11c/photos, 3 shots) +- No speaker, earpiece, microphone, 3.5 mm jack, or spring contacts labelled SPK/MIC visible. The edge clips at the bottom of TOP = antenna feeds (PRX, next to the RF test connector). + Top-left empty pad field + "DIV" connector = unpopulated diversity RX chain, not audio. BOT test pads: DP, DM, VBUS, VBAT, … (USB/battery). +- Under the shield cans: not visible. The PM8916 codec pins (SPK_P/N, EAR, HPH, MIC_IN1–3) may be routed to nothing. + +## Current Linux +- 7.2.7-aurora-b10b5: `CONFIG_SOUND` not set. QCOM_APR=y, RPMSG SMD=y. +- Upstream path without the DSP: msm8916.dtsi `&lpass` (apq8016-lpass-cpu) + `&lpass_codec` (msm8916-wcd-digital) + `&pm8916_codec` (analog) + `&sound` (qcom,apq8016-sbc-sndcard). + msm8916-pm8916.dtsi already sets codec supplies L5/L5/L13. Many upstream 8916 phones use exactly this. + +## Risks for any later enablement (needs GO) +- micbias on L13 = the USB PHY 3.075 V rail (NCM). The codec must not change the L13 voltage; verify regulator constraints before any boot. +- GPIO63–68 (cdc-pdm) and 113 stay codec-only. The rest of the B11 DO-NOT-TOUCH list stays as it was. +- Without a transducer a test proves only that the driver probes, the DAPM paths and the PMIC registers. No sound can be heard. + +## Verdict +Codec silicon = present (PM8916 + MSM8916 digital codec, always). Audio HW on the board = **not found** (no speaker, mic, or jack visible). Next step depends on the operator: +are there speaker/mic openings in the case, or SPK/MIC pads under the display or near the cans? + +## Closure (2026-10-04) +Operator: the case has no speaker and no microphone. **B12 audio CLOSED — not applicable**: codec silicon present, no transducers. No enablement planned; CONFIG_SOUND stays off. If ever needed: upstream apq8016-sbc path, mind micbias ≈ L13 shared with USB PHY. diff --git a/logs/b12/b12p/B12P-RESULT.md b/logs/b12/b12p/B12P-RESULT.md new file mode 100644 index 0000000..0a1c165 --- /dev/null +++ b/logs/b12/b12p/B12P-RESULT.md @@ -0,0 +1,16 @@ +# B12P — persistent Alpine on eMMC userdata (2026-10-04) — PASS + +- P1 (runtime, telnet): userdata p27 → ext4 `aurora-alpine` (b12/persist/alpine-provision.sh), Alpine 3.24.2 minirootfs + X11 stack + (b12/out/alpine-b13.tar.gz 32edda57, 106 pkgs; mesa/LLVM pruned). Only p27 RW; disk-level RO flag of mmcblk0 must be lifted + (it masks partition RW); every other partition + boot0/1 verified RO. e2fsck -fn clean. +- P2 image b12/b12p: B10 kernel 7.2.7-aurora-b10b5 + prod DTB e47762d1 + B9C cmdline unchanged; initramfs = B10 + overlay: + /init +2 lines (aurora-alpine autostart), aurora-modem check_safety (disk flag may be lifted only if all partitions except userdata are RO), + /usr/sbin/aurora-alpine (label/magic gate, e2fsck -p, mount /alpine noatime,errors=remount-ro, binds proc/sys(ro)/dev/dev/pts), e2fsck bundle. + `aurora.alpine=off` on cmdline disables it. +- Live service test (prod boot): start/stop/persistence/safety gate PASS. +- lk2nd emulator: S12 candidate boots, S10 (B10) regression identical, F1/F3 revert. +- RAM test ram2 (fastboot boot 00af9160): ALPINE OK 9.7 s, modem START OK 73 s, AP 78 s, writes only p27. (ram1 lost: battery pulled.) +- Cache write (HW EDL, B12P-write.sh): **B12P_CACHE_WRITE_VERIFIED**, cache 98d061ef, backup W/pre-cache.bin = B10 c3732515, all other partitions unchanged. +- cold1 (normal power-on): NCM 34 s, ALPINE OK 10.4 s, START OK 73 s, AP 77 s, ping 4/4, charger CHARGING fault none, eMMC writes only p27. +- Rollback: b12/b12p/B12P-rollback.sh → B10 c3732515 (userdata ext4 stays, inert under B10). +- Open: chroot has no network (no default route — deferred to router work); B13W graphics next. diff --git a/logs/b12/b12q/B12Q-ALPINE-PRIMARY-ROOT.md b/logs/b12/b12q/B12Q-ALPINE-PRIMARY-ROOT.md new file mode 100644 index 0000000..d22f261 --- /dev/null +++ b/logs/b12/b12q/B12Q-ALPINE-PRIMARY-ROOT.md @@ -0,0 +1,62 @@ +# B12Q — Alpine as primary rootfs (2026-10-04) — **RAM VALIDATED** + +## Architecture +lk2nd/cache → kernel 7.2.7-aurora-b10b5 (unchanged) + prod DTB e47762d1 (unchanged) + B9C cmdline (unchanged) +→ initramfs **stage0** (`b12/b12q/rootfs/init`): eMMC RO → userdata p27 checks (PARTNAME, geometry, ext4 magic, label `aurora-alpine`) +→ lift disk RO flag + RW p27 only (every other partition + boot0/1 verified RO) → `e2fsck -p` → mount → validate (`/sbin/init`, `/sbin/openrc`, +`/etc/alpine-release`, `/etc/aurora/primary-root`, dev/proc/sys/run/tmp) → boot counter → `mount --move` dev/sys/proc → **switch_root** → Alpine `/sbin/init` +(busybox init from Alpine 3.24.2, inittab → OpenRC 0.63.2). +Rescue: `aurora.alpine=off`, any check failure, or ≥3 unconfirmed boots (`/var/lib/aurora/boot-attempts`, reset by `aurora-bootok`) → relock → `exec /init.lfs` +(= byte-identical B9C/B10 LFS init, never mounts p27). Reason printed to UART + fbcon via kmsg (`ALPINE PRIMARY ROOT NOT USED: …`). + +## Audit (dependencies of the LFS boot) +- All non-Aurora userspace in the LFS initramfs = Alpine packages; every version exists in v3.24 (only pcre2 10.48→10.49, libcrypto 3.5.8→3.5.9 differ). + rmtfs, libqrtr, iw, ModemManager, qmi-proxy, libqmi from v3.24 packages are **bit-identical** to the initramfs files. +- Aurora-specific: aurora-modem (B12P gate), aurora-wifi, aurora-display + service wrappers, /etc/aurora/{modem,wifi}.conf, linebuf.so (rmtfs LD_PRELOAD), + /usr/libexec/aurora/{busybox (ntpd), wifi/hostapd bundle}, WCNSS NV, panel firmware, debug tools. +- Daemons udevd/dbus/polkitd/ModemManager are started by the modem controller itself (unchanged; OpenRC services for them installed but NOT enabled). +- Busybox applets used by the scripts are all in Alpine busybox; telnetd from busybox-extras. No kernel modules (CONFIG_MODULES=n). +- Init duties moved to OpenRC: devfs, sysfs (sysinit); hostname, aurora-ro (RO policy + /tmp tmpfs), aurora-firmware (B1 modem FAT RO + fw path), + aurora-ncm (USB gadget 18d1:d001 + 172.16.42.1 + telnetd) (boot); aurora-display, aurora-modem, aurora-wifi, aurora-bootok (default). + inittab: getty tty1, root shell on ttyMSM0. Time bootstrap stays inside aurora-modem. No syslog (kernel log only) to avoid eMMC writes. + +## p27 changes (B12Q-2, writes only p27) +44 packages from a local signed-package repo (no network): openrc, busybox-extras, eudev, dbus, polkit, modemmanager(+udev), libqmi, qmi-utils, +libqrtr-glib, rmtfs, hostapd, iw, mobile-broadband-provider-info + deps; package scripts ran natively (messagebus/polkitd users). 150 packages total. +Overlay `b12/b12q/alpine-overlay` (contains the Wi-Fi PSK — never publish). Installer `b12/b12q/install-p27.sh`. + +## RAM tests (fastboot boot, nothing flashed) +| | normal1 (aurora-b12q.img a0b66b59) | off1 (aurora.alpine=off) | failtest1 (aurora.b12q.failtest=init) | +|---|---|---|---| +| / | /dev/mmcblk0p27 ext4 rw | rootfs (initramfs) | rootfs | +| PID1 | /bin/busybox (/sbin/init, Alpine) → OpenRC | /bin/busybox sh /init.lfs | /bin/busybox sh /init.lfs | +| stage0 | fsck clean, switch_root 2.5 s, attempt 1/3, BOOT OK → counter 0 | "aurora.alpine=off … p27 not touched" | fsck clean, "/sbin/init.failtest-missing missing" → rescue, relocked | +| modem / LTE | START OK 71.7 s, ping 4/4 | START OK 72.9 s, ping 4/4 | START OK 72.8 s | +| Wi-Fi AP | 76 s, WCNSS+MSS running | 77 s | 77 s | +| NCM/telnet | OK | OK | OK | +| display | DISPLAY READY, getty tty1 | OK (LFS) | OK (LFS) | +| charger | CHARGING fault none | CHARGING | CHARGING | +| apk | 3.0.8, 150 pkgs, B12P file persisted | – | – | +| eMMC RO | all RO except disk flag + p27 | all 30 RO | all 30 RO | +| eMMC writes | p27 only (51) | 0 | p27 only (52, mount/umount) | + +**PASS:** / = p27, PID1 = Alpine/OpenRC, modem, LTE, Wi-Fi, NCM, display, charger, rescue fallback (off + failtest), eMMC writes outside p27 = 0. + +**Verdict: B12Q Alpine primary rootfs: RAM VALIDATED.** + +## Persistent candidate (NOT written) +`b12/b12q/cache/cache-extlinux-b12q.img` **59272cd3** (B12P recipe, initrd 9ade7cff, label b12q). lk2nd emulator: S13 candidate boots +(kernel 8c8bfccb, dtb e47762d1, ramdisk 9ade7cff), S12 current B12P identical, F1/F3 revert. Write: `b12/b12q/B12Q-write.sh` (expects live cache B12P 98d061ef, +backup kept), rollback `b12/b12q/B12Q-rollback.sh` → B12P 98d061ef. Needs separate GO. + +## Notes / open +- Root password locked (tty1 login unusable without one); access = telnet on NCM (no auth, dev) + UART shell. SSH not added yet. +- No default route → apk has no network (router stack next). +- Rescue path: to retry Alpine after 3 failed boots, from LFS rescue `aurora-alpine start` then `echo 0 > /alpine/var/lib/aurora/boot-attempts`. + +## Persistent (2026-10-04, GO) +- HW EDL `B12Q-write.sh`: **B12Q_CACHE_WRITE_VERIFIED** — cache 59272cd3 (readback match), backup W/pre-cache.bin = B12P 98d061ef, all other partitions unchanged, + userdata superblock aurora-alpine. +- cold1 (normal power-on, no RESET): NCM 70 s after observer start, stage0 fsck clean + switch_root 3.1 s (attempt 1/3), BOOT OK 5.4 s, / = p27, PID1 = Alpine init/OpenRC, + modem START OK 70.4 s, ping 4/4, AP 74.9 s, DISPLAY READY, charger CHARGING fault none, apk 3.0.8, RW only disk flag + p27, eMMC writes only p27 (32). +- **B12Q CLOSED: Alpine is the primary rootfs.** Rollback `b12/b12q/B12Q-rollback.sh` → B12P 98d061ef. diff --git a/logs/b12/b12r/B12R-ROUTER-RESULT.md b/logs/b12/b12r/B12R-ROUTER-RESULT.md new file mode 100644 index 0000000..1ab26b6 --- /dev/null +++ b/logs/b12/b12r/B12R-ROUTER-RESULT.md @@ -0,0 +1,41 @@ +# B12R — Wi-Fi → LTE router inside Alpine (2026-10-04) — **RAM VALIDATED** + +## Pieces +- Kernel `7.2.7-aurora-b12` (/home/q/aurora-kbuild/out-b12, Image 1e8837fc): b10b5 production config + NETFILTER/NF_TABLES/NFT_NAT/NFT_MASQ/CONNTRACK/ + NFT_REJECT/NFT_FIB_IPV4/NFT_LOG/LIMIT (+NET_INGRESS/EGRESS/CRC32C pulled in); same tree src-b10b5 (LBC supervisor v4 unchanged). Fragment linux/aurora-b12.config. +- p27 (Alpine): nftables 1.1.6-r1, dnsmasq 2.92_p2-r0 (+ libnftnl, libmnl, jansson, readline) from a local repo; `/usr/sbin/aurora-router up|down|status`, + `/etc/aurora/router.conf`, OpenRC `aurora-router` (need aurora-modem aurora-wifi; waits for START OK + AP ENABLED). **Not in any runlevel yet.** +- RAM image `b12/b12r/out/aurora-b12r.img` 228ebdc3 = kernel b12 + DTB e47762d1 + B9C cmdline + B12Q initramfs. + +## Design (runtime only, state in /run/aurora-router) +LAN wlan0 192.168.77.1/24, DHCP .100–.199 1h, dnsmasq bound to wlan0 only (upstream = bearer DNS); default route `dev wwan0` added only if absent and removed on down; +ip_forward=1; nft table `inet aurora_router`: input — mgmt usb0 + lo accepted, wlan0 only DHCP/DNS/ping to 192.168.77.1 (everything else incl. 172.16.42.1 dropped), +wwan0 only established/related + ICMP errors; forward policy drop — wlan0→wwan0 accept, wwan0→wlan0 established only, wlan0↔usb0 drop; postrouting masquerade LAN→wwan0. + +## RAM test normal1 (2026-10-04) +- Alpine on p27, PID1 Alpine init, kernel 7.2.7-aurora-b12, modem START OK 74.7 s, AP 78 s, `nft list tables` OK. +- Bug found + fixed: busybox `ip route show default` prints all routes → default route not added; now `ip route | grep ^default`. +- Board itself: default via wwan0, `nslookup ya.ru 192.168.77.1` OK, **apk update via https://mirror.yandex.ru/mirrors/alpine/v3.24 OK** (28553 pkgs). +- Operator note: Beeline currently answers foreign hosts with RST (8.8.8.8, dl-cdn/Fastly, neverssl) **with and without** our nft table → operator-side + whitelisting, not the router. Russian hosts (ya.ru, mirror.yandex.ru) work. +- Phone client (): DHCPACK 192.168.77.199, ya.ru opened; forward LAN→WAN 8941 pkts/4.7 MB, WAN→LAN 14149 pkts/16.2 MB, masquerade 147 flows. +- Isolation: phone → http://172.16.42.1 → 10 packets dropped by `ip daddr != 192.168.77.1` (lan_in); http://172.16.42.2 never reached the board + (forward wlan0→usb0 counter 0 — rule present but not exercised by traffic). LTE→LAN default-deny counter 0 (CGNAT; not testable from outside). +- eMMC writes only p27. RAM used 55 MB. + +## Persistent candidate (NOT written) +`b12/b12r/cache/cache-extlinux-b12r.img` **e85d862e** (B12Q recipe, kernel b12, label b12r). Emulator: S14 boots (kernel 1e8837fc 25159688 B, dtb e47762d1, +ramdisk 9ade7cff), S13 = live B12Q identical, F1/F3 revert. Write `b12/b12r/B12R-write.sh` (expects live B12Q 59272cd3), rollback `b12/b12r/B12R-rollback.sh` → B12Q. +After the write + first boot: `rc-update add aurora-router default` (p27 only) to make the router start on boot. + +## Open +- DONE: apk repositories → mirror.yandex.ru (see end of file). +- No IPv6 for LAN; no MSS clamp (NFT_RT off; MTU 1500 both sides); dnsmasq runs as root; DNS query logging off. + +## Persistent (2026-10-04, GO) +- HW EDL `B12R-write.sh`: **B12R_CACHE_WRITE_VERIFIED** — cache e85d862e (readback match), backup W/pre-cache.bin = B12Q 59272cd3, all other partitions unchanged. +- cold1 (no RESET): kernel 7.2.7-aurora-b12 #7, stage0 fsck clean + switch_root (1/3), BOOT OK 5.5 s, / = p27, modem START OK 77.0 s, ping 4/4, AP 81.5 s, + DISPLAY READY, charger CHARGING fault none, RW only disk flag + p27, writes only p27. +- Then `rc-update add aurora-router default` (p27) + start → ROUTER UP OK, default dev wwan0, table inet aurora_router. Autostart on the next boot not yet observed. +- (driver cold message still printed the old hash label "B12R 59272cd3" — cosmetic.) +- 2026-10-04: /etc/apk/repositories on p27 switched to https://mirror.yandex.ru/mirrors/alpine/v3.24/{main,community} (old file kept as /etc/apk/repositories.dl-cdn); apk update OK (28553 pkgs). diff --git a/logs/b13/B13-WAYLAND-WLROOTS-RESULT.md b/logs/b13/B13-WAYLAND-WLROOTS-RESULT.md new file mode 100644 index 0000000..1df5b59 --- /dev/null +++ b/logs/b13/B13-WAYLAND-WLROOTS-RESULT.md @@ -0,0 +1,57 @@ +# B13W — Wayland/wlroots on Alpine (Cage, Sway, wayvnc) next to X11 (2026-10-04) + +System: Alpine 3.24.2 primary root on p27 (B12Q/B12R), kernel 7.2.7-aurora-b12 (no kernel/DTB/cache change in B13W). +Scripts: `b13/run/` (deployed to /usr/local/lib/b13/run): wl-common.sh, cage-up/down.sh, sway-up/down.sh, sway.conf, wayvnc-up/down.sh, pattern.sh. +X11 is the existing `/usr/local/bin/aurora-x start|stop` (the x-up/vnc-up names from the brief do not exist); its files/config were not edited. + +## 1. Audit +- DRM: `/dev/dri/card0` = panel-mipi-dbi-spi (`/soc@0/spi@78b8000/panel@0`), connector **SPI-1** connected, mode **128x128 @ 59.953 Hz**, 1 CRTC, 1 plane, + physical 26x26 mm. **No render node** (no GPU). `/dev/fb0` = DRM fbdev emulation (used by Xorg fbdev driver). +- Input: only `pm8941_pwrkey` (/dev/input/event0). udevd running (started by the modem controller), /run/udev present. +- Seat: seatd 0.9.3 (`seatd -g video`, VT-bound seat0), libseat backend seatd; compositors run as root from the session scripts. +- Packages (apk, v3.24 via mirror.yandex.ru): wlroots0.20 0.20.2-r0, cage 0.3.0-r0, sway 1.12_rc3-r0, wayvnc 0.10.0-r0 (neatvnc 1.0.0, gnutls 3.8.13), + seatd/libseat 0.9.3-r0, wayland 1.25.0, libinput 1.31.3, foot 1.27.0 (test client), font-dejavu, grim (screencopy check), wayland-utils. No Xwayland. +- Note: wlroots links libEGL/libgbm → mesa's libgallium + LLVM must be present even with Pixman (musl resolves all symbols at load). The B13 RAM prune had + removed them → `apk fix mesa* llvm22-libs spirv-tools hwdata-pci xorg-server` restored the package files (p27 +215 MB; also restores Xorg's glamor/glx modules). + +## 2. Renderer +`WLR_RENDERER=pixman WLR_BACKENDS=drm,libinput WLR_DRM_DEVICES=/dev/dri/card0 WLR_LIBINPUT_NO_DEVICES=1 LIBSEAT_BACKEND=seatd` — log: "Creating pixman renderer". + +## 3–5. Results +Test UI = foot fullscreen with pattern.sh (red TL, green TR, blue BL, white BR, white frame, label). Cells 25x10 at 9 px → 3 px right / 8 px bottom of +background (font grid, visible identically in the screencopy) — not a panel offset. + +| check | result | +|---|---| +| Cage on ST7735S | **PASS** — modeset SPI-1 128x128, pattern correct via grim and on the physical panel (operator: corners OK, only the font-grid strip right/bottom) | +| Sway on ST7735S | **PASS** — same pattern fullscreen, no bar/wallpaper/borders, 0 errors after `xwayland disable` + `swaybg_command -` | +| wayvnc on Sway | **PASS** — captures output SPI-1 (same physical output), listens **172.16.42.1:5900 only** (no 0.0.0.0, no IPv6), security types [19 VeNCrypt, 129, 5], **no None**; VeNCrypt X509Plain TLS1.3, pinned cert D4:46:9D…; wrong password → "Invalid username or password"; typed "vnc ok 42" + pointer move visible in the next frame and **on the physical panel (operator-confirmed)** | +| wayvnc on Cage | **PASS** — same: capture, auth, typed "cage 7" + pointer (warning: no output power-management protocol in Cage, harmless) | +| X11 regression | **PASS** — after cage/sway/wayvnc down: VT back to tty1, fbcon bound; `aurora-x start fbdev` → X-START OK 128x128, twm+xterm, x11vnc 172.16.42.1:5900 (IPv4 only) frame OK, X11 visible on the physical panel (operator-confirmed); Xorg (EE) lines identical to B13 (mouse/kbd modules, FBIOPUTCMAP) | + +## 8. Comparison (idle, measured on the board, CPU = utime+stime over 20–30 s) +| stack | RSS | idle CPU | physical output | remote access | 128x128 suitability | +|---|---|---|---|---|---| +| Xorg (fbdev) | 49.0 MB (+ x11vnc 3.8, twm 4.0, xterm 5.9) | 0.05 % | /dev/fb0 (fbdev emulation) | x11vnc, no auth, NCM only | works; legacy toolkit/WM, no per-frame damage path | +| Cage | 47.5 MB (+ foot 7.7, seatd 0.7) | 0.00 % | DRM/KMS card0, Pixman | wayvnc (TLS+password) works | **best**: kiosk, one fullscreen app, start 0.45–0.67 s | +| Sway | 56.8 MB (+ foot 7.7) | 0.00 % | DRM/KMS card0, Pixman | wayvnc works, IPC/swaymsg for debugging | good debug/service shell, start 0.68 s | +| wayvnc | 38.0–38.6 MB | 0.00 % idle | — | — | — | +RSS of the wlroots compositors is dominated by mapped mesa/LLVM pages (VmSize ~1.28 GB), not by rendering. Free RAM stayed ≈ 320 MB available. + +## Verdict +``` +X11 PASS +Cage PASS +Sway PASS +wayvnc on Sway PASS +wayvnc on Cage PASS +``` +- Production modem UI compositor: **Cage** (Pixman). Since wayvnc also works on Cage, production can expose remote view without Sway. +- Remote/debug: **Sway + wayvnc** (or Cage + wayvnc). +- Xorg: keep as fallback until the first Wayland UI exists; afterwards it can be removed (xorg-server, xf86-video-fbdev, x11vnc, twm, xterm) — it shares mesa with wlroots anyway. + +## State left / notes +- p27 changes: packages above, /usr/local/lib/b13/run/*, /etc/aurora/wayvnc/{config,tls_key.pem,tls_cert.pem} (root 600; password + key only in b13/private/ locally — never publish). +- Nothing autostarts (no runlevel changes). At the end X11 (aurora-x) was left running on vt2 for inspection; `aurora-x stop` returns to the console. +- pattern.sh: the echo line clears to EOL and erases the right frame cell (cosmetic). +- 2026-10-04: operator connected interactively with TigerVNC (SecurityTypes X509Plain, X509CA = b13 cert) from 480s to wayvnc on Sway — Sway visible in the viewer. diff --git a/logs/b14/B14-UI-RESULT.md b/logs/b14/B14-UI-RESULT.md new file mode 100644 index 0000000..2b0c9fd --- /dev/null +++ b/logs/b14/B14-UI-RESULT.md @@ -0,0 +1,79 @@ +# B14 — Cage + SDL2 modem UI (2026-10-04/05) — **USERSPACE VALIDATED** + +Changes only in Alpine on p27 (no kernel/DTB/cache/GPT/firmware change). Running kernel 7.2.7-aurora-b12 (cache B12R). + +## 1. SDL2 +- Alpine 3.24: native **sdl2 2.32.10-r0** (not sdl2-compat). Compiled video drivers: `x11 wayland KMSDRM offscreen dummy evdev`; + under Cage `SDL_GetCurrentVideoDriver() = wayland` (aurora-ui refuses to run on anything else). No Xwayland binary on the system. +- Pitfalls found: (a) SDL2/Wayland has no shm framebuffer — every window path goes through EGL; (b) without `wayland-libs-egl` + (libwayland-egl.so.1, dlopened) SDL2 **segfaults** in `Wayland_CreateWindow` (SDL_waylandwindow.c:2055, NULL wl_egl_window_create); + (c) Mesa needs its libgallium/LLVM files (restored in B13W). With `LIBGL_ALWAYS_SOFTWARE=1` the renderer is SDL `opengl` on Mesa + **llvmpipe via EGL/wl_shm** (libgallium); the lavapipe Vulkan ICD (`mesa-vulkan-swrast`, installed while debugging) is **not used** + (UI works with `VK_ICD_FILENAMES=/nonexistent`, no lvp mapping) — removal can be tested separately. +- No SDL_ttf: 8x16 cp437 bitmap font extracted from Linux `lib/fonts/font_8x16.c` (GPL-2.0). + +## 2–5. Application +`b14/src/aurora-ui.c` (~360 lines C, 75 KB binary, `-Wall -Wextra` clean), built on the board (`apk add build-base sdl2-dev`), installed +`/usr/local/bin/aurora-ui`. Software-drawn 128x128 ARGB → streaming texture, redraw 1/s (+ on events), worker thread for slow sources. +Layout (8 rows x 16 cols, 4 colours + grey): +``` +BEELINE LTE operator / RAT (mmcli every 5 s, timeout 3 s, only while ModemManager runs) +▂▄▆█▒ 70% signal quality +10.82.242.155 wwan0 IPv4 (/run/aurora-modem/ipcfg, every 2 s) +WiFi 1 cl AP up + stations (iw info + station dump every 5 s) +↓73B ↑73B throughput (1 s; wwan0 counters are always 0 on bam-dmux → wlan0 LAN counters) +BAT 4.13V HOLD VADC VBAT + supervisor state (B10 /sys/kernel/debug/pm8916_lbc/state, 1 s) — no SOC (no fuel gauge) +PMIC 36°C NAT pm8916-thermal + default route +02:02 ONLINE clock (TZ=MSK-3 from /etc/aurora/aurora-ui.conf) + overall +``` +Before START OK the top rows show `MODEM STARTING` + the controller state; FAIL → `MODEM FAIL`. Missing sources render `--`. +Finding: `pm8916-bms-vm` power_supply reports 4.41 V / "Over voltage" while the validated VADC says 4.19 V → the UI uses VADC only. + +## 6–7. Launcher + wayvnc +`b14/run/ui-up.sh [--stop-gui]` (refuses if Xorg/Cage/Sway hold the panel; one instance; Pixman/seatd env; waits for first frame), +`ui-stop.sh`, `ui-status.sh` → /usr/local/lib/b14/run. Process lookup via /proc/PID/exe (the OpenRC script has the same comm name). +wayvnc (b13/run/wayvnc-up.sh) on Cage+aurora-ui: same output SPI-1, 172.16.42.1:5900 only (IPv6 listeners 0), TLS+password, +VNC pointer/keys do not disturb the app. + +## 8. Test (manual start: X11 stop → Cage → aurora-ui → wayvnc) +- Startup: 0.9–2.2 s to first frame (aurora-ui first frame 1.1–1.5 s after exec). +- Soak 15 min (`soak.log`) + control 5 min (`soak2.log`): aurora-ui RSS 108–114 MB (PSS 95 MB; **private dirty ~34 MB**, ~43 MB clean + mesa/LLVM file pages), Cage 47.6–48.2 MB, both flat; CPU aurora-ui 1.5–1.6 %, Cage 0.16–0.22 %; MemAvailable ~269–271 MB stable. + modem BEARER_CONNECTED + LTE ping ok every minute; phone client: NAT forward counters grew, UI showed `WiFi 1 cl` and live ↓/↑; + charger CHARGING → HOLD shown; PMIC 36–49 °C; NCM ping from 480s 919/920 over the soak; eMMC writes outside p27 = 0. +- Kernel log: **65 `cma: __cma_alloc_frozen: reserved: alloc failed, req-size: 8 pages, ret: -16`** between uptime 3289–4892 s. + Root cause analysis: the 32 MiB CMA area was filled by movable page cache (after the apk/gdb installs: CmaFree 112 kB) and + page migration returned EBUSY; the bitmap had 8000 free pages. Not the UI: 0 failures without UI under LTE load, and after + `drop_caches` (CmaFree 30.6 MB) **0 failures in the 5-min control soak with UI + wayvnc running**. Allocations fell back (no traffic + impact: ping/NAT fine). Follow-up candidate (separate): watch CMA after large apk operations / consider `cma=` or vm tuning. +- Other kernel messages: only the known boot-time ones (wcnss.mdt at 1.4 s before /firmware, regulatory.db). + +## 9. X11 regression — PASS +ui-stop → VT tty1 → `aurora-x start fbdev` → X-START OK 128x128, twm+xterm, x11vnc 172.16.42.1:5900 (IPv4 only), frame OK. UI restored afterwards. + +## PASS matrix +Cage starts ✔ · SDL2 uses Wayland ✔ · 128x128 physical UI correct ✔ (operator) · live LTE ✔ · Wi-Fi ✔ · traffic ✔ (after fix, operator) · +battery/charger ✔ · clock ✔ · wayvnc same output ✔ · LTE/Wi-Fi/router/NCM/charger unaffected ✔ · no kernel errors caused by B14 ✔ +(CMA warnings explained above) · no writes outside p27 ✔ · X11 regression ✔ + +**B14 Cage + SDL2 modem UI: USERSPACE VALIDATED** + +## 10–11. OpenRC autostart (prepared, NOT enabled) +`/etc/init.d/aurora-ui` installed on p27 (source `b14/run/aurora-ui.openrc`), in no runlevel. Activation patch (separate GO): +``` +rc-update add aurora-ui default +``` +Order: `need aurora-display; after aurora-ncm aurora-ro` — it does not wait for the modem (UI shows MODEM STARTING), starts ui-up in the +background so boot never blocks on the GUI; fbcon/getty on tty1 stay as fallback behind Cage; `rc-service aurora-ui stop` returns the console. + +## p27 footprint +New packages: sdl2, wayland-libs-egl, mesa-vulkan-swrast (unused), build-base + sdl2-dev (+deps, build only), gdb/musl-dbg/sdl2-dbg (debug). +p27 now 1.0 GB used of 2.4 GB. Build/debug packages can be removed with `apk del build-base sdl2-dev gdb musl-dbg sdl2-dbg`. + +## Autostart (2026-10-05, GO) +- `rc-update add aurora-ui default` on p27 (default runlevel: aurora-display, aurora-ui, aurora-modem, aurora-wifi, aurora-router, aurora-bootok). +- cold1 (normal power-on, no RESET; logs/b14/cold1/): stage0 fsck clean + switch_root at 4.3 s, BOOT OK 6.6 s, **UI UP at 9.47 s uptime** + (Cage + aurora-ui, first frame 1.04 s after exec, SDL wayland/opengl), modem START OK 81.6 s, AP 85.5 s, **aurora-router autostart: + ROUTER UP OK 88.1 s** (first boot with the router in the runlevel), all default services started, CMA failures 0, eMMC writes only p27. + Screenshot after boot: BEELINE LTE 60 %, 10.87.61.183, WiFi 0 cl, BAT 4.18 V CHG, PMIC 42 °C, NAT, ONLINE. +- Undo: `rc-update del aurora-ui default` (console/getty on tty1 returns on the next boot) or `rc-service aurora-ui stop` for this boot. diff --git a/logs/b15/B15-AUDIT.md b/logs/b15/B15-AUDIT.md new file mode 100644 index 0000000..88b9d74 --- /dev/null +++ b/logs/b15/B15-AUDIT.md @@ -0,0 +1,35 @@ +# B15 audit — WireGuard over FOCSQ/CSQTT (2026-10-05) — **STOPPED: kernel prerequisites missing** + +## Source (canonical = user's private repo, no upstream merge) +- `git.bebrik.xyz/Dan4ick/Uzbek-vpn` (private), branch main, HEAD `5a9ff1d` (2026-10-04: "Deliver flow frames that arrive after the + reassembler stops waiting", "Stop dropping downlink packets that arrive in a flow frame"). Cloned to the private dir + `b15/private/focsq-src` on 480s (token used only as an HTTP header, not stored). Project = CSQTT fork (csqtt-dion), PolyForm-Noncommercial. +- Release `v2.1.9-dion.6` (2026-10-02, **older than HEAD**): `csqtt-client-linux-aarch64` 9.5 MB, sha256 4ce37e50… (matches SHA256SUMS-linux.txt). + +## Headless component (no Flutter/GUI needed) +- `rust-client` = one binary `csqtt-client` (Rust 2024, rust-version 1.97.1, tokio, aws-lc-rs, rustls). Same binary: + - tunnel: `--provider dion|vk --rooms … -peer … -password … -n WORKERS -turn-transport udp|tcp -obfs audio|video -device-id … + --tun-dev csqtt0 --tun-up