Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/saf/device/evidence/pmos_fix_plan.md
Fimeg bde961c6f2 saf: one spine — device, state, and work under the index
PAF becomes saf/device (history kept), STATE.md dissolves
into saf/state.md with the dated era archived, the substrate
SAF moves up from souveraine, and every agreement points at
saf/INDEX.md and nowhere else. one map, nothing to remember
2026-08-18 09:47:30 -04:00

148 lines
9.5 KiB
Markdown

# pmOS Modem Fix — Implementation Plan (autoselect, EFS-persisted)
Consolidates the 2026-06-20 live-Android + binary-RE session. This is the build plan for the
pmOS side. Evidence is cited to live capture lines or binary addresses; nothing here is inferred
without a source.
## The mechanism (works — but is NOT the gate)
> **🟡 RE-STAMPED 2026-06-29.** The MCFG auto-select-by-UIM mechanism is real and PDC configs do go
> Active (`qcril-autoselect` runs, Set Feature accepted) — but this is **proven NOT to be the gate**:
> the modem parks OFFLINE / model 0 and refuses every pre-RF write (online→52, low_power→60,
> NAS SSP→25) regardless of provisioning state. Choreography is downstream of the OFFLINE state.
> See `modem.md` eliminated-leads ledger. Kept as the mechanism reference (how MCFG/EFS autoselect
> works), not as a live fix path.
The modem provisions itself by **MCFG auto-selection keyed on the SIM (UIM)**, applied at the
modem's own firmware RF-init, and **persisted in the modem's EFS** (`modemst1/2`, which is
**shared** between the Android and pmOS slots — `sdf3/sdf4`, not slotted).
1. **Persistent form (community-proven on SDM845 / OnePlus 6/6T):** an EFS NV item
`/nv/item_files/mcfg/mcfg_autoselect_by_uim` plus the mcfg uploaded into EFS. Written over
DIAG (EfsTools). Once set, the modem self-selects + self-applies every boot.
2. **Runtime form (reverse-engineered from `libril-qc-hal-qmi.so`):**
`pdc_enable_auto_selection(enable, id)` @ `0x1d45c40` builds an 80-byte
`pdc_set_feature_req_msg_v01` (feature value `3` when enabling, `0` when disabling) and calls
`pdc_set_feature` @ `0x1d45ac0`, which issues **QMI_PDC_SET_FEATURE**:
- **message ID `0x2E` (46)** (`mov w1,#0x2e` at `0x1d45b4c`)
- request length `0x50` = 80 bytes (`mov w3,#0x50`), response 8 bytes (`mov w5,#0x8`),
timeout 10000 ms (`mov w6,#0x2710`).
## Live-Android ground truth (this session, device 89JX0AAJP)
- Modem fully working: `dumpsys telephony` → IN_SERVICE, LTE, Fido 302/720, voice+data.
- **Self-persist confirmed:** blanked `persist.vendor.radio.cnv.ver_info`, restarted live
`qcrild`. `is_modem_mbn_updated` logged `version info updated` (prev empty), `start_mbn_update`
fired — but QCRIL only queried `get_active_config_info` (`config_version=0x801481a`, CNV
`0x48`=72), re-synced, and **never loaded/activated**. The modem already held the config in its
own NV. → userspace is reconcile-only; the modem persists. (`/tmp/qcril-re/live-firstprov.txt`)
- **Online-arm reference captured** (the step that returns `DeviceNotReady` on pmOS):
airplane toggle → `dms_handle_modem_operating_mode → "operating mode needs to be changed" →
SETTING_OP_MODE → modem ONLINE`. (`/tmp/qcril-re/airplane-arm.txt`)
- `/data/vendor/modem_config/` does **not exist** on this device — QCRIL reads MBNs straight from
`/vendor/rfs/msm/mpss/readonly/vendor/mbn/` (the readonly vendor tree), not a `/data` cache.
## Why pmOS fails, and what the fix must do
pmOS boots with the modem at 0/0 configs and `DeviceNotReady`. The Android-equivalent state is:
modem set to auto-select by UIM + the mcfg tree reachable at RF-init + EFS writable so the modem
persists what it applied. The fix must guarantee all three on pmOS:
1. **mcfg tree served to the modem at RF-init**`tqftpserv` serving
`/mnt/vendor/persist/rfs/readonly/vendor/mbn/` (present, per modem.md). Verify the modem's
requested path matches.
2. **EFS writable**`rmtfs -P -s` (read-write, drop `-r`) so the modem can commit/persist its
applied config (frontier #4; the read-only `-r` shadow is why nothing persisted before).
3. **Auto-selection enabled** — set it so the modem self-applies. Two routes below.
## Build — two routes (do A first; B is the persistent backstop)
### Route A — runtime QMI via PDC Set Feature (ALREADY AVAILABLE in pmOS's libqmi fork)
**This is the path. It is not new work — the pmOS community already built the libqmi side.**
Stock libqmi PDC has no Set Feature. But the **openimsd libqmi fork**
(`https://gitlab.postmarketos.org/modem/openimsd/libqmi`, base 1.39.0) already defines
**`QMI_PDC_SET_FEATURE` at id `0x2E`** — the *exact* message ID we independently reverse-engineered
from `pdc_set_feature` (`0x1d45b4c: mov w1,#0x2e`). Two sources, same message. Its request TLVs
(`data/qmi-service-pdc.json`, "since 1.40"):
| TLV | id | type | meaning |
|---|---|---|---|
| Slot ID | common-ref | guint32 | slot |
| Token | common-ref | guint32 | request token |
| **Selection Mode** | 0x11 | guint32 | **set to 3 = auto-select-by-UIM** (matches the `enable→feature 3` in `pdc_enable_auto_selection`) |
| Refresh Mode | 0x14 | gboolean | refresh on change |
| IMS | 0x19 | gboolean | ims feature |
Output: Operation Result (+ a Set Feature *indication* carrying the Token).
Implementation on pmOS:
- Build/install the **openimsd libqmi fork** (it generates `Qmi.MessagePdcSetFeatureInput` with
`set_selection_mode` / `set_refresh_mode` / `set_ims` / `set_slot_id` / `set_token`).
- Call it from **python-GI** — the fork's **qmicli has no `--pdc-set-feature` CLI**, so do it the
way `qcom-imsd` does (Qmi GI over `qrtr://0`), not via a qmicli flag. Set Selection Mode = 3,
Refresh Mode on. This is the runtime equivalent of QCRIL's `pdc_enable_auto_selection`.
- pmOS boot service: enable auto-select, let the modem self-provision from the served mbn tree.
Note: the fork still carries the **load-config `g_free` segfault at `qmicli-pdc.c:1130`** (it's
1.39.0). Only relevant if we fall back to explicit `--pdc-load-config`; the autoselect path
shouldn't need it. Our one-line patch + `tools/build-qmicli.sh` still apply if so.
**Prior art / proof:** `qcom-imsd` (same fork, libqmi-GI over qrtr PDC) is **known-working on
Pixel 3a (sargo)** — blueline's SDM845 sister. It does IMS-layer PDC (select/activate/register);
we add the lower-layer Set Feature autoselect. The pmOS PDC-over-qrtr approach is proven on this
silicon.
### Route B — persistent EFS NV item (DIAG, the community method)
Write `/nv/item_files/mcfg/mcfg_autoselect_by_uim` (enable) into the modem EFS via a DIAG-EFS2
client on pmOS. pmOS diag is **not** HAL-locked the way the production-Android Pixel gadget is
(this session: the Pixel USB gadget HAL reverted configfs to adb-only, blocking USB-diag;
`/dev/diag` local access remained available). Because `modemst` is shared, this is a one-time
write that both slots inherit. Tooling: EfsTools (Linux/.NET — `dotnet` is available) or a small
DIAG-EFS2 client against pmOS `/dev/diag`.
## Test loop (on pmOS, next boot)
1. Confirm rmtfs `-P -s` (rw) + tqftpserv serving the mbn tree.
2. Apply Route A (or B) to enable auto-selection.
3. Reboot; check `qmicli --dms-get-capabilities``Networks` non-empty = RF armed.
4. If armed: confirm it survives a second reboot (persistence). If not, apply Route B so the
modem commits to EFS.
## LIVE RESULT 2026-06-20 (Route A run on pmOS hardware — config blocker SOLVED)
Ran the full fix on pmOS slot B (kernel 7.1.0-rc1-sdm845), live modem over qrtr://0:
1. Built + installed the **openimsd libqmi fork** on-device (Alpine); `Qmi.MessagePdcSetFeatureInput
present: True`.
2. Ran `qcril-autoselect.py` → PDC client allocated, **Set Feature (0x2E) accepted**. Signal names
`set-feature`/`get-feature` were correct (indications arrived; only a strict-parse warning:
the modem's Get Feature indication omits the mandatory Result TLV 0x02 libqmi expects — cosmetic).
3. **Immediately after Set Feature, the modem self-loaded its configs: PDC 0/0 → 2 platform
(SS-LA-SDM845 ACTIVE) + 25 software (Rogers_Canada).** It pulled them itself from the
tqftpserv-served mbn tree. This is the modem auto-selecting by UIM — the mechanism working.
4. **Rebooted with nothing enabled → the modem self-loaded 2+25 again at RF-init, platform ACTIVE.**
So the autoselect **persisted** (Set Feature is sticky, not just runtime). This is the documented
project blocker (0/0 every cold boot, frontier #3/#4) **SOLVED**: blueline's modem now
self-provisions MCFG persistently on mainline pmOS — believed a first for Pixel 3.
**Remaining wall (NEW, deeper than config):** despite firmware up (mba+mpss booted, IPA running,
handover signaled), platform config ACTIVE, and ModemManager seeing the configs,
`--dms-set-operating-mode=online` still returns **DeviceNotReady (QMI 52)**; modem stays offline,
Networks ''. `low-power` → InvalidTransition (already LPM). A full reboot with configs active did
NOT arm RF — so it is not a re-init-count problem.
**Leading hypothesis for the remaining wall → Route B (firmware-level autoselect via EFS NV).**
Our Set Feature operates at the **PDC/RIL layer** (post-firmware-boot): the modem loads+activates
the config in PDC, but the **firmware RF-init at ~12s already ran without it**, so PDC-ACTIVE ≠
RF-consumed. The Android/OnePlus mechanism writes the EFS NV item
`/nv/item_files/mcfg/mcfg_autoselect_by_uim` so the **firmware itself** auto-selects at its
earliest RF-init, before RF comes up. Next step: write that EFS NV item on pmOS (diag/EfsTools —
not HAL-locked here) so the firmware applies MCFG at RF-init. Alternative to rule out: a genuinely
separate non-config blocker (cal/IMEI) — but the same modem arms on Android, so config-apply timing
is the stronger lead.
## Artifacts from this session
`/tmp/qcril-re/`: `live-firstprov.txt` (qcrild restart, reconcile-only proof),
`airplane-arm.txt` (online-arm reference), `auto_sel.txt` + objdump of
`pdc_enable_auto_selection` / `pdc_set_feature` (msg 0x2E). EFS clones already in repo:
`efs-postandroid/`, `efs-backup-20260618/` (`modemst1/2`, `fsg`, `fsc`).