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
9.5 KiB
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-autoselectruns, 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. Seemodem.mdeliminated-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).
- Persistent form (community-proven on SDM845 / OnePlus 6/6T): an EFS NV item
/nv/item_files/mcfg/mcfg_autoselect_by_uimplus the mcfg uploaded into EFS. Written over DIAG (EfsTools). Once set, the modem self-selects + self-applies every boot. - Runtime form (reverse-engineered from
libril-qc-hal-qmi.so):pdc_enable_auto_selection(enable, id)@0x1d45c40builds an 80-bytepdc_set_feature_req_msg_v01(feature value3when enabling,0when disabling) and callspdc_set_feature@0x1d45ac0, which issues QMI_PDC_SET_FEATURE:- message ID
0x2E(46) (mov w1,#0x2eat0x1d45b4c) - request length
0x50= 80 bytes (mov w3,#0x50), response 8 bytes (mov w5,#0x8), timeout 10000 ms (mov w6,#0x2710).
- message ID
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 liveqcrild.is_modem_mbn_updatedloggedversion info updated(prev empty),start_mbn_updatefired — but QCRIL only queriedget_active_config_info(config_version=0x801481a, CNV0x48=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
DeviceNotReadyon 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/datacache.
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:
- mcfg tree served to the modem at RF-init —
tqftpservserving/mnt/vendor/persist/rfs/readonly/vendor/mbn/(present, per modem.md). Verify the modem's requested path matches. - EFS writable —
rmtfs -P -s(read-write, drop-r) so the modem can commit/persist its applied config (frontier #4; the read-only-rshadow is why nothing persisted before). - 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.MessagePdcSetFeatureInputwithset_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-featureCLI, so do it the wayqcom-imsddoes (Qmi GI overqrtr://0), not via a qmicli flag. Set Selection Mode = 3, Refresh Mode on. This is the runtime equivalent of QCRIL'spdc_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)
- Confirm rmtfs
-P -s(rw) + tqftpserv serving the mbn tree. - Apply Route A (or B) to enable auto-selection.
- Reboot; check
qmicli --dms-get-capabilities→Networksnon-empty = RF armed. - 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:
- Built + installed the openimsd libqmi fork on-device (Alpine);
Qmi.MessagePdcSetFeatureInput present: True. - Ran
qcril-autoselect.py→ PDC client allocated, Set Feature (0x2E) accepted. Signal namesset-feature/get-featurewere correct (indications arrived; only a strict-parse warning: the modem's Get Feature indication omits the mandatory Result TLV 0x02 libqmi expects — cosmetic). - 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.
- 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).