B11–B22: LED/buttons audit, Alpine primary root, router, Wayland + aurora-ui, VPN (WireGuard via TURN relay, direct-first), standalone/SSH, USB OTG, netwatch/blackbox

This commit is contained in:
q 2026-10-07 00:44:19 +03:00
parent cf24356a83
commit fb86b1dfe7
139 changed files with 6432 additions and 4 deletions

View file

@ -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. - 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. - 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 # 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. - 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. - 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.

View file

@ -26,7 +26,14 @@
| B6P — автозапуск модема из flash (persistent) | **PASS / CLOSED** | | B6P — автозапуск модема из flash (persistent) | **PASS / CLOSED** |
| B7 — время от сети (DMS/NITZ, без RTC) | **PASS / CLOSED** | | B7 — время от сети (DMS/NITZ, без RTC) | **PASS / CLOSED** |
| Wi-Fi (WCNSS/Pronto + wcn36xx, тестовая AP WPA2, автозапуск из flash) — B8 | **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** (см. ниже) | | Дисплей ST7735S 128×128 (SPI): DRM/fbcon tty1, подсветка, автозапуск из flash — B9 | **PASS / CLOSED** (см. ниже) |
| Батарея / зарядка: супервизор CHARGE → HOLD → батарея, FAULT safe clamp, автозапуск из flash — B10 | **PASS / CLOSED** (см. ниже) | | Батарея / зарядка: супервизор CHARGE → HOLD → батарея, FAULT safe clamp, автозапуск из flash — B10 | **PASS / CLOSED** (см. ниже) |
| Ускоренная зарядка: ток CC 450 → 720 mA (только DTB) — B23 | **PASS / persistent** (см. ниже) | | Ускоренная зарядка: ток 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 с) 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) Fallback: extlinux не найден / не парсится / не грузится → Android boot image из раздела boot (emmc1 rescue)
``` ```
@ -49,13 +57,26 @@ Fallback: extlinux не найден / не парсится / не грузит
| hyp | qhypstub, до 512 KiB | `1a963047` | | hyp | qhypstub, до 512 KiB | `1a963047` |
| aboot | lk1st (lk2nd 23.1 + патч B9L: SPI-панель ST7735S + splash) autoboot, подписан qtestsign, до 1 MiB | `15208dbb` (откат: B5a lk1st `3b8cd266`) | | aboot | lk1st (lk2nd 23.1 + патч B9L: SPI-панель ST7735S + splash) autoboot, подписан qtestsign, до 1 MiB | `15208dbb` (откат: B5a lk1st `3b8cd266`) |
| boot | emmc1 rescue image (+ нули до 16 MiB) | `e9579f33` | | 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` | | 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`. 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). Все хэши целиком: `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) ## 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). Отчёт: [`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 (запись/откат/эмулятор) | | `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) | | `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 (запись/откат/эмулятор) | | `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 (сборка/эмулятор/запись/откат) | | `b23/` | B23: ток зарядки 720 mA — сборка RAM-образа, монитор, cache B23 (сборка/эмулятор/запись/откат) |
| `tools/` | утилиты: UART-логгер, Sahara probe, распаковка boot.img, sanitizer публикации | | `tools/` | утилиты: UART-логгер, Sahara probe, распаковка boot.img, sanitizer публикации |
| `logs/` | sanitized логи: UART, QMI, ModemManager, EDL, результаты тестов | | `logs/` | sanitized логи: UART, QMI, ModemManager, EDL, результаты тестов |
@ -181,6 +203,7 @@ Safety:
Полные дампы eMMC и разделов, NV/EFS (`modemst1/2`, `fsg`, `fsc`), `persist`, `userdata`, файлы прошивки модема, Полные дампы eMMC и разделов, NV/EFS (`modemst1/2`, `fsg`, `fsc`), `persist`, `userdata`, файлы прошивки модема,
стоковые SBL1/RPM/TZ/aboot/boot/recovery/system, Firehose-программер, DB410c TZ, собранные бинарники, стоковые 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. 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, журналы действий, дампы регистров, выводы эмулятора) не публикуются — Начиная с B10 сырые логи прогонов (мониторы, dmesg, snapshot, UART, журналы действий, дампы регистров, выводы эмулятора) не публикуются —
только отчёты `*.md` и компактные таблицы `*-diff.txt` (правила в `tools/publish-sanitize.py` и `.gitignore`). только отчёты `*.md` и компактные таблицы `*-diff.txt` (правила в `tools/publish-sanitize.py` и `.gitignore`).
Идентификаторы (IMEI, IMSI, ICCID, eMMC CID/serial, Sahara serial, MAC, номера сот, cookies) заменены на `<PLACEHOLDER>`. Идентификаторы (IMEI, IMSI, ICCID, eMMC CID/serial, Sahara serial, MAC, номера сот, cookies) заменены на `<PLACEHOLDER>`.

14
b11/audit/b11-ro.sh Normal file
View file

@ -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

14
b11/run/b11-btn-watch.sh Normal file
View file

@ -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"

65
b11/run/b11-led-run.sh Normal file
View file

@ -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 <<X
$l
X
eval "v=\$INIT_$n"; echo "$v" > "$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 <<X
$l
X
[ "$1" = on ] && echo $on > $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 <<X
$l
X
eval "INIT_$n=$(cat $p)"; eval "say \"INIT $n $p = \$INIT_$n\""; done
snap PRE
r=$(check) || { say "PRECHECK FAIL$r -> 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 <<X
$l
X
say "STAGE4 CHASE c$c $n ON"; echo $on > $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"

View file

@ -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)"

21
b12/alpine/chroot-up.sh Normal file
View file

@ -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 <alpine-b13.tar.gz | -> (- = 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 <tar.gz|->"; 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)"

View file

@ -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

View file

@ -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
SubSectionSub "Display"
Depth 16
EndSubSection
EndSection

View file

@ -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

12
b12/b12p/B12P-rollback.sh Normal file
View file

@ -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

53
b12/b12p/B12P-write.sh Normal file
View file

@ -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<SAHARA_SERIAL>" $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"

40
b12/b12p/b12p-boot.sh Normal file
View file

@ -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 <EMMC_SERIAL> (power off, then hold RESET while powering on)"
i=0; until fastboot devices 2>/dev/null | grep -q <EMMC_SERIAL>; 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 <EMMC_SERIAL> && { 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"

28
b12/b12p/build-b12p.sh Normal file
View file

@ -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

6
b12/b12p/cache/extlinux-b12p.conf vendored Normal file
View file

@ -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

27
b12/b12p/cache/mkfs-cache-b12p.sh vendored Normal file
View file

@ -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

12
b12/b12q/B12Q-rollback.sh Normal file
View file

@ -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

53
b12/b12q/B12Q-write.sh Normal file
View file

@ -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<SAHARA_SERIAL>" $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"

View file

@ -0,0 +1 @@
1790935600

View file

@ -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"

View file

@ -0,0 +1 @@
B12Q: this p27 is an Aurora primary root (stage0 switch_root target)

View file

@ -0,0 +1 @@
tmpfs /tmp tmpfs nosuid,nodev,mode=1777,size=64m 0 0

View file

@ -0,0 +1 @@
aurora

View file

@ -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"; }

View file

@ -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; }

View file

@ -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
}

View file

@ -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; }

View file

@ -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 <EMMC_SERIAL> > $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"
}

View file

@ -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
}

View file

@ -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; }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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 <ipv4>
# 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<bd+0)){b=o; bd=d; s=$4}}
END{ if (b!="") print b, bd, s }' $D/sntp.txt)
log "SNTP check ($_ips): $(grep -c 'reply from' $D/sntp.txt) replies; best offset delay server: ${_b:-none} (offset > 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 <ipv4>"; exit 2;;
esac

View file

@ -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 <<C
interface=wlan0
driver=nl80211
ctrl_interface=$D/ctrl
ssid=$SSID
hw_mode=g
channel=$CHANNEL
ieee80211n=1
wmm_enabled=1
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=$PSK
logger_stdout=-1
logger_stdout_level=2
C
printf 'trap "" HUP\nexport LD_LIBRARY_PATH=%s/lib\nexec %s/hostapd -t %s/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

45
b12/b12q/b12q-boot.sh Normal file
View file

@ -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 <EMMC_SERIAL> (RESET held at power-on)"
i=0; until fastboot devices 2>/dev/null | grep -q <EMMC_SERIAL>; 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 <EMMC_SERIAL> && { 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"

25
b12/b12q/build-b12q.sh Normal file
View file

@ -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)

6
b12/b12q/cache/extlinux-b12q.conf vendored Normal file
View file

@ -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

27
b12/b12q/cache/mkfs-cache-b12q.sh vendored Normal file
View file

@ -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

14
b12/b12q/install-p27.sh Normal file
View file

@ -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"

12
b12/b12r/B12R-rollback.sh Normal file
View file

@ -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

52
b12/b12r/B12R-write.sh Normal file
View file

@ -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<SAHARA_SERIAL>" $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"

45
b12/b12r/b12r-boot.sh Normal file
View file

@ -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 <EMMC_SERIAL> (RESET held at power-on)"
i=0; until fastboot devices 2>/dev/null | grep -q <EMMC_SERIAL>; 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 <EMMC_SERIAL> && { 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"

12
b12/b12r/build-b12r.sh Normal file
View file

@ -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)

6
b12/b12r/cache/extlinux-b12r.conf vendored Normal file
View file

@ -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

27
b12/b12r/cache/mkfs-cache-b12r.sh vendored Normal file
View file

@ -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

8
b12/b12r/install-p27.sh Normal file
View file

@ -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"

View file

@ -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

View file

@ -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; }

View file

@ -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 <<N
table inet aurora_router
delete table inet aurora_router
table inet aurora_router {
chain input {
type filter hook input priority filter; policy accept;
ct state established,related accept
iifname "lo" accept
iifname "$MGMT_IF" accept
iifname "$LAN_IF" jump lan_in
iifname "$WAN_IF" jump wan_in
}
chain lan_in {
udp sport 68 udp dport 67 accept comment "DHCP (incl. broadcast)"
ip daddr != $LAN_ADDR counter drop comment "Wi-Fi may not touch mgmt/WAN addresses of the board"
udp dport 53 accept
tcp dport 53 accept
icmp type echo-request limit rate 20/second accept
meta l4proto ipv6-icmp accept
counter drop
}
chain wan_in {
icmp type { echo-reply, destination-unreachable, time-exceeded } accept
counter drop comment "no unsolicited inbound from LTE"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state invalid counter drop
iifname "$LAN_IF" oifname "$WAN_IF" ip saddr $LAN_NET counter accept
iifname "$WAN_IF" oifname "$LAN_IF" ct state established,related counter accept
iifname "$LAN_IF" oifname "$MGMT_IF" counter drop comment "Wi-Fi -> 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

23
b12/mk-alpine-rootfs.sh Normal file
View file

@ -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

14
b12/mk-e2fs-bundle.sh Normal file
View file

@ -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

17
b12/mk-router-bundle.sh Normal file
View file

@ -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

View file

@ -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"

3
b12/pull.sh Normal file
View file

@ -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"

16
b12/push.sh Normal file
View file

@ -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; }

View file

@ -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; }

11
b12/router/router-down.sh Normal file
View file

@ -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"

View file

@ -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

77
b12/router/router-up.sh Normal file
View file

@ -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 <<N
table inet aurora_router
delete table inet aurora_router
table inet aurora_router {
chain input {
type filter hook input priority filter; policy accept;
ct state established,related accept
iifname "lo" accept
iifname "$MGMT_IF" accept
iifname "$LAN_IF" jump lan_in
iifname "$WAN_IF" jump wan_in
}
chain lan_in {
udp sport 68 udp dport 67 accept comment "DHCP (incl. broadcast)"
ip daddr != $LAN_ADDR counter drop comment "LAN may not touch mgmt/WAN addresses of the board"
udp dport 53 accept
tcp dport 53 accept
icmp type echo-request limit rate 20/second accept
meta l4proto ipv6-icmp accept
udp dport 547 drop
counter drop
}
chain wan_in {
icmp type { echo-reply, destination-unreachable, time-exceeded } accept
counter drop comment "no unsolicited inbound from LTE"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state invalid counter drop
iifname "$LAN_IF" oifname "$WAN_IF" ip saddr $LAN_NET counter accept
iifname "$WAN_IF" oifname "$LAN_IF" ct state established,related counter accept
iifname "$LAN_IF" oifname "$MGMT_IF" counter drop comment "Wi-Fi -> 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"

12
b12/router/router.conf Normal file
View file

@ -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

3
b12/tn.sh Normal file
View file

@ -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'

38
b12/vncshot.py Normal file
View file

@ -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))

9
b13/b13w-vnc-sway.txt Normal file
View file

@ -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

4
b13/run/cage-down.sh Normal file
View file

@ -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)"

8
b13/run/cage-up.sh Normal file
View file

@ -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"

19
b13/run/pattern.sh Normal file
View file

@ -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

4
b13/run/sway-down.sh Normal file
View file

@ -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)"

8
b13/run/sway-up.sh Normal file
View file

@ -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"

15
b13/run/sway.conf Normal file
View file

@ -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

4
b13/run/wayvnc-down.sh Normal file
View file

@ -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"

10
b13/run/wayvnc-up.sh Normal file
View file

@ -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}')]"

19
b13/run/wl-common.sh Normal file
View file

@ -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"; }

60
b13/vnc-vencrypt.py Normal file
View file

@ -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'))

19
b14/README.md Normal file
View file

@ -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.

View file

@ -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)

14
b14/run/aurora-ui.openrc Normal file
View file

@ -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; }

24
b14/run/soak.sh Normal file
View file

@ -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

8
b14/run/ui-status.sh Normal file
View file

@ -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

10
b14/run/ui-stop.sh Normal file
View file

@ -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

32
b14/run/ui-up.sh Normal file
View file

@ -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

8
b14/src/Makefile Normal file
View file

@ -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

379
b14/src/aurora-ui.c Normal file
View file

@ -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 <SDL2/SDL.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h>
#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;
}

259
b14/src/font8x16.h Normal file
View file

@ -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,
};

19
b14/src/sdlprobe.c Normal file
View file

@ -0,0 +1,19 @@
/* B14: SDL2 probe — which video drivers are compiled in, which one initialises, which renderer we get. */
#include <SDL2/SDL.h>
#include <stdio.h>
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;
}

12
b15/B15-rollback.sh Normal file
View file

@ -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

52
b15/B15-write.sh Normal file
View file

@ -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<SAHARA_SERIAL>" $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"

15
b15/b15-boot.sh Normal file
View file

@ -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 <EMMC_SERIAL> (RESET held at power-on)"
i=0; until fastboot devices 2>/dev/null | grep -q <EMMC_SERIAL>; 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'\""

11
b15/build-b15.sh Normal file
View file

@ -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)

6
b15/cache/extlinux-b15.conf vendored Normal file
View file

@ -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

27
b15/cache/mkfs-cache-b15.sh vendored Normal file
View file

@ -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

95
b15/run/aurora-router Normal file
View file

@ -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 <<N
table inet aurora_router
delete table inet aurora_router
table inet aurora_router {
chain input {
type filter hook input priority filter; policy accept;
ct state established,related accept
iifname "lo" accept
iifname "$MGMT_IF" accept
iifname "$LAN_IF" jump lan_in
iifname "$WAN_IF" jump wan_in
}
chain lan_in {
udp sport 68 udp dport 67 accept comment "DHCP (incl. broadcast)"
ip daddr != $LAN_ADDR counter drop comment "Wi-Fi may not touch mgmt/WAN addresses of the board"
udp dport 53 accept
tcp dport 22 accept comment "B17: SSH (dropbear) from Wi-Fi to the board LAN address only"
tcp dport 53 accept
icmp type echo-request limit rate 20/second accept
meta l4proto ipv6-icmp accept
counter drop
}
chain wan_in {
icmp type { echo-reply, destination-unreachable, time-exceeded } accept
counter drop comment "no unsolicited inbound from LTE"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state invalid counter drop
iifname "$LAN_IF" oifname "$WAN_IF" ip saddr $LAN_NET counter accept
iifname "$WAN_IF" oifname "$LAN_IF" ct state established,related counter accept
iifname "$LAN_IF" oifname "$MGMT_IF" counter drop comment "Wi-Fi -> 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

130
b15/run/aurora-vpn Normal file
View file

@ -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 - <<NFT || fail "nft"
table inet aurora_vpn {
chain input {
type filter hook input priority filter; policy accept;
ct state established,related accept
iifname "lo" accept
iifname "$MGMT_IF" accept
iifname "$LAN_IF" jump lan_in
iifname { "$WAN_IF", "$T" } jump wan_in
}
chain lan_in {
udp sport 68 udp dport 67 accept
ip daddr != $LAN_ADDR counter drop
udp dport 53 accept
tcp dport 22 accept comment "B17: SSH (dropbear) from Wi-Fi to the board LAN address only"
tcp dport 53 accept
icmp type echo-request limit rate 20/second accept
counter drop
}
chain wan_in {
icmp type { echo-reply, destination-unreachable, time-exceeded } accept
ct state established,related accept
counter drop
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state invalid counter drop
iifname "$LAN_IF" oifname "$T" ip saddr $LAN_NET tcp flags syn tcp option maxseg size set $MSS
iifname "$LAN_IF" oifname "$T" ip saddr $LAN_NET counter accept
iifname "$T" oifname "$LAN_IF" ct state established,related counter accept
iifname "$LAN_IF" oifname "$WAN_IF" counter drop comment "fail-closed: no LTE leak"
iifname "$LAN_IF" oifname "$MGMT_IF" counter drop
counter drop
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "$T" ip saddr $LAN_NET counter masquerade
}
}
NFT
}
route_lan() {
T=$(tif)
ip route replace default dev "$T" table $TBL
ip rule show | grep -q "lookup $TBL" || ip rule add from "$LAN_NET" lookup $TBL priority $RULE_PRI
ip rule show | grep -q "from $LAN_NET to $LAN_NET lookup main" || ip rule add from "$LAN_NET" to "$LAN_NET" lookup main priority $((RULE_PRI-1))
echo 1 > /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

16
b15/run/aurora-vpn-diag Normal file
View file

@ -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

View file

@ -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; }

Some files were not shown because too many files have changed in this diff Show more