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
64 KiB
PAF · Modem bring-up
ARCH STATUS (2026-07-10): WORKING — verified live on the Arch install.
mmcli: state connected, LTE, operator Fido, packet service attached,
signal 73%. The whole solved stack made the jump to Arch: modemsmem
is in our kernel tree (0c2eb6bf3 "soc: qcom: add modemsmem, wire up on
blueline"), rmtfs/tqftpserv/pd-mapper/ModemManager run as services, and
blueline-clat.service handles 464XLAT. Everything below is the research
record of HOW the 52 wall fell (pmOS era, 2026-06/07) — the mechanism is
identical on Arch; read it before touching anything modem-adjacent.
Deep evidence docs referenced below live in evidence/.
RESOLVED 2026-07-03 — modem ONLINE, registered, passing LTE traffic. Root cause of the 52 wall: missing modemsmem driver (SMEM item 134).
Google Pixel modem firmware reads SoC info from SMEM_ID_VENDOR0 (item 134) during boot;
downstream writes it via drivers/soc/qcom/modemsmem/. Mainline never had the driver, so
the MPSS RF task found nothing there and parked op-mode OFFLINE → every RF transition
illegal → QMI 52. Fix: Richard Acayan's sdm670 mainline port of the driver (commit
d9afe44b) + google,modemsmem/qcom,smem = <134> DT node — canonical tree 4ef9b6943,
pmaports 9d52b47813 (CONFIG_QCOM_MODEMSMEM=y). Verified live: op-mode online, Fido LTE
registration home, IPv6 bearer passing packets. Full details in HANDOFF.md (2026-07-03).
Everything below this line is the (closed) investigation record; the leads/ledger remain
correct as history. Remaining open threads are identity (SFS/test-IMEI) and polish
(IPv4 bearer, calls/SMS), not RF.
TOP LEAD 2026-07-02 #15 — DIAG_DATA glink channel: AP CAN request it, modem acks but never completes its own open. Real firmware-behavior finding, not a kernel gap.
Correction to this doc's own earlier framing tonight: an initial pass concluded mainline's glink
core lacked any AP-initiated ("locally initiated") channel-open primitive at all, and that reaching
DIAG_DATA would need core qcom_glink infrastructure work. That was wrong — re-read
drivers/rpmsg/qcom_glink_native.c properly: qcom_glink_create_local() (a complete, working
implementation of exactly this, sends GLINK_CMD_OPEN, waits for both acks) already exists and is
already wired into the standard .create_ept op every transport uses. The real, and much smaller,
entry-point gap was just: no driver was calling rpmsg_create_ept() against a fresh channel name
from an already-bound rpdev. Fixed with zero new kernel code — userspace's existing
RPMSG_CREATE_EPT_IOCTL on /dev/rpmsg_ctrl2 (the modem-edge control device; rpmsg_ctrl0/1 are
CDSP/ADSP respectively, confirmed via /sys/devices/.../4080000.remoteproc/.../rpmsg_ctrl2) reaches
this exact path already. Built a 60-line test binary (diag_open_test.c, cross-compiled via
pmbootstrap chroot) to drive it.
Live result, confirmed clean and repeatable across multiple fresh single-attempt modem boots:
GLINK_CMD_OPENsent forDIAG_DATA→ modem.- Modem sends
GLINK_CMD_OPEN_ACKback, consistently in <10ms when it responds at all. - The modem never sends its own reciprocal
GLINK_CMD_OPENback — the other half of glink's bidirectional handshakeqcom_glink_create_local()requires (wait_for_completion_timeout(&channel ->open_req, ...)) before a channel counts as usable. - Tested whether this was just slow: bumped the wait from 5s→60s (kernel commit
7441ca3dc), rebuilt, reflashed, ran ONE clean attempt on a fresh modem boot, waited the full 63.84s. Still nothing. Falsified the "just needs more time" theory outright. Reverted the bump (b4185e71b) — no reason to ship a 60s stall for a case that structurally doesn't complete. - Cross-checked against downstream (
references/los-kernel-blueline-4.9/drivers/soc/qcom/glink.c): itsGLINK_CONNECTEDnotification requires the exact same bidirectionalch_is_fully_opened(ctx)condition (checked from both the incoming-open and the open-ack RX handlers) — downstream isn't looser about what counts as "open," it's just event-driven with no hard timeout instead of blocking-with-timeout. So this isn't mainline enforcing an extra requirement Android doesn't have; it's the same requirement, and the modem doesn't satisfy it either way under mainline's bring-up.
What this means: the modem's DIAG service is reachable and responsive at the glink-negotiation
level (it correctly acks a request for a channel it recognizes by name) but will not commit to
opening it — a "deliberate partial refusal" shape that rhymes with the entire rest of this frontier
(SIM/QMI all healthy, DMS answers reads, refuses RF-arming writes; modem parks offline not LPM).
Whatever state gate blocks RF-arm plausibly also gates DIAG. F3 access via this channel is not
reachable by any change on the AP side alone — this is now a real, evidenced, narrow finding, not
an assumption. Retest if/when the LPM-vs-offline or any other frontier item moves; a modem that
reaches a healthier init state might complete this handshake as a side effect.
Test binary + method preserved: diag_open_test.c (cross-built via pmbootstrap chroot -b aarch64),
RPMSG_CREATE_EPT_IOCTL against /dev/rpmsg_ctrl2, then open() the resulting /dev/rpmsgN — same
approach works for probing ANY named glink channel on ANY edge (swap /dev/rpmsg_ctrl0/1/2 for
cdsp/adsp/modem) if a similar question comes up for a different service.
TOP LEAD 2026-07-02 #14 — frontier #8's 3-precondition test finally run: provisioning-churn fixed & shipped, LPM-vs-offline confirmed unreachable from AP side
Fixed and shipped precondition (b) from frontier #8 below: blueline-uim-select.sh was
unconditionally deactivating+reactivating the primary-gw provisioning session even when it already
matched the slot/AID it was about to select — churn on an already-correct session, and exactly the
class of write the raw-QMI RESULTS.md proved times out during the modem's not-ready window. Patched
to skip the deactivate/reactivate when the existing binding already matches
(device-google-blueline-modem-tools 7-r18→7-r21, committed 1ed2df1788). Verified live: the
log now reads "primary-gw session already bound to slot 1 app 1 ... skipping stock
deactivate/reactivate" — the session is genuinely left alone across a modem restart for the first
time this whole investigation.
Result: does NOT flip DMS online. Immediately after the clean-session restart,
--dms-set-operating-mode=online still returns DeviceNotReady (52). Polled operating-mode every
3s for 18s post-restart: steady offline the entire window, never once observed LPM. So
precondition (c) — "modem in LPM (not offline) for the online flip" — is now confirmed, not just
suspected, to never occur spontaneously on pmOS: the firmware decides offline vs LPM before any
AP-side QMI write is even possible, so no host script/wrapper/choreography (this fix included) can
create the window frontier #8 needed.
Where this leaves it: frontier #8's 3-precondition experiment (configs active + clean
provisioning + LPM-not-offline) is no longer "untested" — it's tested, with (a) and (b) now cheaply
satisfiable and (b) fixed for real, and (c) confirmed firmware-internal and unreachable from any
AP-side lever. This converges with TOP LEAD #13's conclusion (SLPI/ADSP/fastrpc/choreography all
closed, F3 narration is the remaining instrument) via a different path — both land on "the
offline-vs-LPM decision happens inside the firmware before Linux gets a vote." Next: same F3
instrument #13 named, now aimed at a sharper question — not generic RF-init narration, but
specifically why the firmware selects offline over LPM at its own boot, since that's now the
single remaining precondition standing between here and an online modem.
Also confirmed same session: mainline blueline has no DIAG transport at all (/dev/diag absent,
no diagchar-equivalent config, no DIAG glink channel among the live rpmsg devices) — so
diag_capture.md's Side B is a real, confirmed dead end on the AP side as of kernel
7.1.1_p20260702080259-r2; F3 access (if pursued) needs either a from-scratch DIAG-over-glink
kernel path or stays Android-side-only (Side A) for comparison purposes.
TOP LEAD 2026-07-01 #13 — fastrpc pageslen kernel bug FIXED & shipped; 52 wall now proven independent of adsprpcd/ADSP health too
Real, named, diff-attached kernel defect — the first of the whole investigation — found, fixed, shipped, verified. fastrpc_init_create_static_process() in drivers/misc/fastrpc.c advertised inbuf.pageslen = 0 while actually passing one remote_heap page in args[2]; the sibling dynamic fastrpc_init_create_process() correctly sets pageslen = 1 for the identical one-page layout. The DSP trusts the metadata field, believes zero pages were passed, and aborts INIT_CREATE_STATIC — surfacing in userspace as Broken pipe and the adsprpcd coredump cascade (dsp information is incorrect err: -1 for ADSP+CDSP). One-line fix (0→1); matches FisiFla/nabu-cachyos 0005-fastrpc-fix-init-create-static-pageslen.patch (SM8250, independently shipped for the identical signature, commit claims it also killed an SLPI crash loop there). Committed e3c47d44e in references/linux-7.1.1-sdm845-CANONICAL. Also committed the previously-floating-uncommitted RTC read-only fix (68ffb014b — the load-bearing half of the 0-SPMI-writes result; it had never been committed, only sitting in the worktree). Both shipped in kernel 7.1.1_p20260701093924-r1, flashed to boot_a.
Process correction (saved to memory pmos-src-skips-apkbuild-patches): pmbootstrap --src builds the working tree as-is, not committed-only. A first build silently included the two then-uncommitted RTC/DTS files; caught by inspecting chroot_native/mnt/pmbootstrap/source-override/. "Bake as commits" is necessary but not sufficient — the worktree itself must be in the intended state at build time.
Verified live post-flash:
- adsprpcd: no longer a fatal crash-loop. Previously coredumped every boot with
dsp information is incorrect/Broken pipe. Now: coredumps ONCE on first probe, systemd restarts it (NRestarts=1), then runs stably — successfully opensadsp_default_listenerhandles on ADSP (domain 0). Old fatal signatures absent from the journal. Residual non-fatal noise remains:remote_handle64_invoke ... method 2 ... Operation not permitted+open_mod_table_handle_invoke ... Error 0xeon domain 0 — a separate ADSP-invoke-permissions issue, not the pageslen bug; next fastrpc thread if pursued. - SPMI write flood: still 0 — RTC read-only fix preserved across the rebuild.
/dev/fastrpc-sdspstill ABSENT →hexagonrpcd-sdspstill "Could not attach to FastRPC node: Operation canceled". The pageslen fix targets ADSP/CDSP static-process creation; the SDSP node is a different path (depends on SLPI remoteproc fastrpc edges), so the SLPI/sensor-island story is not changed by this fix.- dms-ui-ready (r17 C binary): ran clean, same
DMS 0x0064 RESULT: FAILURE (result=1 error=58)as before (unchanged, expected — orthogonal to fastrpc).
Modem 52 wall: STILL THERE — now proven independent of adsprpcd/ADSP health too. First mmcli --enable of the entire session with a non-crashing adsprpcd (all chain units active: modem-qrtr-ready, qcril-autoselect, qcril-prov, dms-ui-ready, ModemManager): identical QMI protocol error (52): 'DeviceNotReady' on Set Operating Mode=online, op-mode stays offline, state disabled, lock sim-pin2, SIM on slot 2 (correct). Combined with TOP LEAD #12's SLPI proof, the modem's RF-ready gate is now robust to the entire host-side DSP coordination layer (SLPI state × 3 boots, adsprpcd/ADSP health × 1 boot). The ADSP-PD/servreg candidate is substantially weakened. The wall is the modem firmware's own internal decision; nothing host-side we can move has touched it.
Where that leaves the candidate space: of los-mss-translated.c's four deltas — (1) EFS NV item, (2) bootloader/ABL verified-boot state, (3) subsystem coordination [SLPI/PD], (4) host-QMI-replay timing — #3 is now closed and #4 is substantially exhausted (DMS 0x0064 sends+responds but error 58; NAS mode-pref fails DeviceUnsupported pre-RF). #1/#2 were researched to ground with no static delta. The one instrument that could show the modem's own RF-init reasoning — F3 narration — remains uncaptured (QCSuper's blanket arm skips the NAS/RRC SSID ranges; per-range SET_RT_MASK arms cleanly but yields no traffic, and we lack a Qualcomm SSID name table to confirm those ranges are NAS/RRC). That F3 capture + SSID identification is the most likely remaining source of new signal.
TOP LEAD 2026-07-01 #12 — SLPI independence now PROVEN (not just argued); DMS 0x0064 "UI ready" built, untested
SLPI-state dependency is now definitively closed, with hard repeated evidence (2026-06-30
night → 2026-07-01, three separate boots, live hardware). Fixed a real regression along the way:
blueline-slpi-ready.sh disabled SLPI crash-recovery for a "single-shot" start; when the SAR/CHRE
firmware fault crashed it, the disabled-recovery path left the glink-edge channel permanently torn
— intent request timed out storming every ~10s indefinitely, which took the modem's own QMI/QRTR
transport down with it (endpoint hangup, Connection reset by peer). Fix: recovery must stay
enabled (matches Android's ssr_setup/ssr_diag, which never disables it) so the kernel's own
crash handler tears down/rebuilds glink cleanly; the script then does ONE clean attempt and parks
SLPI (echo stop) once caught in a healthy running window, instead of crash-looping forever.
Verified over three clean boots: SLPI crashed (0 times / 50 times / 0 times depending on timing),
every single time DeviceNotReady on RF-enable was byte-for-byte identical regardless of SLPI's
state (crashed continuously, running, offline, or cleanly parked). Also wired modem-qrtr-ready.service
After=/Wants=blueline-slpi-ready.service to match Android's real architecture — vendor.qcom.devup
(what modem-qrtr-ready mirrors) is literally set by init.qcom.devstart.sh right after it kicks
SLPI (echo 1 > /sys/kernel/boot_slpi/boot; setprop vendor.qcom.devup 1), confirmed by reading the
real Android script off the mounted LOS vendor image. Architecturally correct, safe, zero cost — but
does not move the 52 wall. Candidate #4 ("Subsystem coordination — SLPI PD state?") in
los-mss-translated.c's delta list is now closed.
Checked and closed a real diagnostic gap: qmicli --dms-get-operating-mode's output includes an
optional "Offline reason:" line (libqmi qmi_message_dms_get_operating_mode_output_get_offline_reason,
bitmask of HOST_IMAGE_MISCONFIGURATION/PRI_IMAGE_MISCONFIGURATION/PRI_VERSION_INCOMPATIBLE/
DEVICE_MEMORY_FULL) that no prior capture had actually looked for. Checked the existing captured
output (tools/modem-online-provisioner/modem-online-provision-20260622-063154.log): the field never
appears — the modem doesn't populate any of the four documented reasons. Dead end, but a clean one.
Built AND tested: dms-ui-ready.py/.service + a raw-C fallback. Sends clean, modem responds,
52 unchanged. PAF/qcril_online_sequence.md (prior session, full disassembly of
libril-qc-hal-qmi.so) identified DMS message 0x0064 ("telephony UI ready") as the one
load-bearing pre-online step QCRIL performs that pmOS had never replayed. The Python/GI
implementation (Qmi.Message + raw TLV writers) failed at runtime: this libqmi build does
not expose a generic raw-message type via GObject-Introspection at all — only per-message typed
classes exist in the typelib, confirmed by enumerating dir(Qmi) live on the phone. Built a
small C program instead, cross-compiled against the same libqmi (openimsd fork, 1.39.0) via
pmbootstrap chroot -b aarch64, linked directly against the .so already on the phone. Ran it
live: the modem answered 0x0064 with a real, understood protocol response (not "unsupported
message"), RESULT: FAILURE (result=1 error=58). Error 58's standard libqmi meaning
(WMS_ENCODING, an SMS-service code) can't be its true meaning for an OEM DMS message — almost
certainly a vendor-private error space reusing that numeric slot. Immediately retried
mmcli --enable: DeviceNotReady (52), byte-identical to every attempt all night. Full
detail + the C source path in qcril_online_sequence.md. Kept dms-ui-ready.service wired in
the boot chain (best-effort, harmless, now proven to do something real) — the Python version in
the package needs porting to the same raw-C approach before it'll actually work at boot; that's
the concrete next step, not another blind guess.
TOP LEAD 2026-06-26 #11 — THE 52 IS A MODEM XPU (SECURE-MEMORY) CRASH-LOOP
🔴 SUPERSEDED 2026-06-29. Captured on the pre-DELTA-1 r2 LLVM kernel, where the modem genuinely crash-looped on an XPU violation. DELTA-1 (
c2923cb49MPSS assign-ordering) fixed the load — on the current kernel the modem MSS runs steady (MBA boots, MPSS loads,remoteproc running, no XPU crash-loop, zero −22 on4080000; the only crash-lines are the SLPI'sSNS_REG_INIT). Yet it still parks OFFLINE / model 0 / 52. So 52 is NOT an XPU crash-loop — it's a firmware-internal RF-init decision, kernel-independent (6.16.7 and 7.1-rc1 both give 52, see #10). The −22 referenced below is the SLPI's fastrpc heap. Kept as the XPU/load investigation record.
Captured live on the rebuilt r2 LLVM kernel (slot A, full system, ssh). The
DeviceNotReady (52) wall is now explained at the mechanism level: the modem
boots then crash-loops on a hardware secure-memory protection violation, so it
can never complete RF-init and parks offline. dmesg (qcom-q6v5-mss 4080000.remoteproc, remoteproc3):
Booting fw image qcom/sdm845/Google/blueline/mba.mbn ... MBA booted ... loading mpss
remote processor 4080000.remoteproc is now up
crash #1: fatal error received: SFR Init: wdog or kernel error suspected.
crash #2: fatal error received: modem_xpu_int.c:456:[modem_sec_xpu_I]XPU violation
... port failed halt ... modem offline ... starting ... up ... crash ... (loop)
XPU = Qualcomm eXtreme Protection Unit — hardware memory guard programmed by
TrustZone. modem_sec_xpu XPU violation = the modem firmware accessed a memory
region the XPU does not permit it → the modem's secure memory regions are not
assigned/programmed correctly for mainline. The modem comes up, touches a
protected region, XPU faults it, q6v5 recovers it, repeat. The "running" state in
a one-shot remoteproc/state read is just a momentary up-window between crashes —
do not trust a single-sample running; watch journalctl -b -p err over ~30s.
This is the same TZ layer as the SLPI/fastrpc failure. Every modem AND slpi
restart also logs qcom_scm firmware:scm: Assign memory protection call failed -22
(hyp-assign / qcom_scm_assign_mem → -EINVAL). The fastrpc patch tolerates it;
the modem cannot — it needs its secure regions actually assigned. Shared root:
this blueline's TZ rejects mainline's secure-memory-assign, → modem XPU violation
→ 52. See secure_load_tz_frontier.md.
Ruled OUT as the cause of 52 (this session, hard evidence):
- Provisioning / MCFG — software MCFG
Rogers_Canada(Casey's real carrier) isActive(PDC--pdc-list-configs=software). Selecting the carrier config — the thing qcril/Set-Feature does on Android — is already done, and 52 reproduces anyway. Platform configs = 0 (blueline EFS gap) and PDC Set Feature 0x2E times out, but neither is the 52: the modem crashes below the QMI layer. - Kernel config regression — 52 reproduces on the corrected 1135-module r2 LLVM kernel with SLPI/SSC in. Not the GCC-stripped-config disaster.
- libqmi — confirmed the openimsd fork is installed (apk desc: "openimsd fork,
adds PDC Set Feature msg 0x2E"; ships
Qmi-1.0.typelib). Not the blocker.
[RETIRED 2026-07-01 — see HANDOFF CORRECTIONS] The one-per-boot online attempt was
spent this boot ... Modem now poisoned until power-cycle. Online attempts are retry-safe
within a boot; QMI Set Operating Mode = RESET (4) cleanly reinitializes, no reboot needed
(tools/modem-online-provisioner/RESULTS.md). Kept for history: that boot's attempt gave 52,
mode stayed offline, serving-system not-registered/detached, signal InformationUnavailable.
Next frontier: make TZ accept the modem's secure-memory assignment — i.e. the
XPU/hyp-assign region setup. Compare LOS pil-tz secure-load (mba region
qcom,mba-mem, mpss region assign, qcom_scm_assign_mem VMID lists) vs mainline
q6v5_mpss region handling on this device. The assign -22 is the thread to pull.
(qmicli also segfaults on --pdc-list-configs — separate libqmi/fork bug to log.)
TOP LEAD 2026-06-21 #10 — KERNEL-REGRESSION HYPOTHESIS: DISPROVEN. Kernel layer ruled out airtight.
Result (clean-boot test, 2026-06-21 eve): built blueline on sdm845-6.16.7-r0 (last pre-regression
stable, 2025-09-19), flashed boot_b, booted clean. The modem boots fully on 6.16.7 — firmware
loads (Booting fw image …/mba.mbn → remote processor is now up), ALL QMI services register
(qrtr-lookup shows DMS/NAS/UIM/Voice/WDS/PDC…), UIM is healthy (card present, USIM ready, Primary-GW
slot1/app1), remoteproc2 = running. Yet --dms-set-operating-mode=online → DeviceNotReady (QMI
52), mode stuck offline, Networks '', Max TX/RX 0 — byte-for-byte identical to 7.1rc1. A
second run on a clean boot (modem auto-started at boot-time, UIM freshly provisioned) reproduced the
same 52, removing the manual-start confound. Per this lead's own triage rule (still-52 on the
pre-regression kernel) = the kernel is ruled out airtight. The online wall is NOT a kernel
regression; it is device-internal (Route B) — the modem's own firmware RF-init never self-completes, so
it parks offline and refuses online (Android never has to ask; see frontier #8 REFRAME).
6.16.7 is now a known-good working kernel for blueline (modem boots clean); either 6.16.7 or 7.1rc1 is
fine going forward (both give 52). The live frontier is Route B — see frontier #8 REFRAME below.
Firmware-path gotcha found + fixed (RECORD — bites any upstream-tree blueline DTB)
The 6.16.7 upstream sdm845-google-blueline.dts requests firmware at
qcom/sdm845/pixel3/{mba,modem,adsp,cdsp,ipa_fws,a630_zap}.mbn, but pmaports packages blueline
firmware at qcom/sdm845/Google/blueline/. So the upstream-tree DTB silently fails request_firmware
(ENOENT -2) → modem/ADSP/CDSP never boot (remoteproc: Boot failed: -2). The 7.1rc1 pmaports DTB
uses Google/blueline/ (correct) — which is why 7.1rc1 loaded firmware and 6.16.7 did not until fixed.
Fix applied on the rootfs (persists across reboots):
ln -snf /lib/firmware/qcom/sdm845/Google/blueline /lib/firmware/qcom/sdm845/pixel3
Any future build using the upstream blueline DTB needs this symlink (or a DT patch correcting
firmware-name paths) or the modem will not load firmware. (Stray: the 6.16.7 DTB also references
qcom/sdm845/oneplus6/venus.mbn for venus — harmless, video-decoder only.)
Kernel-swap procedure (RECORD — the modular-modem-stack gotcha)
The blueline modem stack is fully modular on this config: qcom_q6v5_mss, qrtr/qrtr-smd,
qcom_glink_smem, qcom_sysmon, mhi* all =m. So swapping kernels needs the matching modules on the
rootfs or the modem never loads (a false 52). UFS + USB-gadget-NCM are built-in (=y), so a
module-mismatched boot still reaches rootfs + USB. Working procedure (validated this session):
build kernel .apk → tar xzf it → extract lib/modules/<kver>/ → scp to phone → sudo cp -a into
/lib/modules/ + depmod <kver> → reboot. Boot.img = cat vmlinuz <blueline>.dtb + the standing
initramfs, mkbootimg (header v0, base 0x0, kernel_off 0x8000, ramdisk_off 0x1000000, tags 0x100, the
real cmdline quiet splash … pmos_boot_uuid=2CE8-836F pmos_root_uuid=26733d78…). USB iface
enp0s20f0u6 (cdc_ncm) needs manual bring-up each boot: nmcli dev set … managed no; ip addr add 172.16.42.2/24 dev …; ip link set … up.
Original #10 thesis + bisect-window analysis (HISTORICAL — hypothesis disproven above)
Thesis (Casey): the 7.1rc1 bump (pmaports MR !8446, merged 2026-04-28, "basic smoke testing… Pixel 3")
never validated blueline's modem; the modem may have regressed there. Corroboration: beryllium wiki
flags 7.1.0-rc1-sdm845 for modem breakage on the same SoC; Joel Selvaraj had SDM845 modem working on
mainline back at 5.11, so the capability predates the regression. Running test: rebuild blueline
kernel against sdm845-6.16.7-r0 (last pre-regression stable, 2025-09-19), flash boot_b, retry
--dms-set-operating-mode=online. Outcomes: online → regression confirmed, bisect the bump; flaky
like beryllium → kernel layer validated, inherit beryllium recipe; still DeviceNotReady(52) → kernel
ruled out airtight, it's device-internal firmware/EFS state (Route B).
Kernel-tree pull (2026-06-21) — what the AP-driver diff says about the bisect window
Pulled references/sdm845-linux-20260610-ONHOLD-DAVID-TREE (codeberg sdm845-next-20260610, base 7.1.0) and the
sdm845-next-20260306 tarball (base 7.0.0) and diffed the modem-relevant subsystems:
drivers/remoteproc/qcom_q6v5_mss.c: functionally identical 7.0→7.1 (brace-whitespace only).drivers/soc/qcom/pdr_interface.c+pdr_internal.h: rename-only —SERVREG_LOCATOR_SERVICE→QMI_SERVICE_ID_SERVREG_LOC(both 0x40),SERVREG_NOTIFIER_SERVICE→QMI_SERVICE_ID_SERVREG_NOTIF(both 0x42),reason[257]→reason[SERVREG_PFR_LENGTH+1](=257). No behaviour change.qcom_pd_mapper.cpresent in BOTH trees → the in-kernel pd-mapper is NOT new to 7.1 (already in-kernel by 20260306 / 7.0.0; it landed ~mainline 6.13, so 6.16.7 has it too). The "in-kernel pd-mapper is the regression" sub-hypothesis is weak; the sdm845-mainline rebase is the live one.qmi_interface.c,qmi_encdec.c,rmtfs_mem.c: identical 7.0→7.1. Implication for the bisect: in the codeberg 7.0→7.1 window there is NO functional change in mss/pdr/qmi/rmtfs — so if the regression is real and in that window, it is NOT in those drivers (look at clk/regulator/interconnect/scm/smem/glink/IPA instead). Caveat: pmaports tracksgitlab.com/sdm845-mainline/linux, NOT codebergsdm845/linux(the two repos the Matrix thread argues over), and my window (7.0→7.1) does not cover the 6.16.7→7.0 jump. So this scopes, it does not pin — Casey's 6.16.7 build is still the right swing.
Upstream blueline DTS confirms our DT is already correct (kills #9 a second way)
The 20260610 tree ships an in-tree sdm845-google-blueline.dts + sdm845-google-common.dtsi
(model "Google Pixel 3"). Its modem nodes: &mss_pil = blueline/mba.mbn+blueline/modem.mbn,
&ipa = blueline/ipa_fws.mbn, mpss memory@8e000000 size 0x9800000, rmtfs-region@f2700000
vmid MSS_MSA. dt-work/blueline-pmos.dts already matches all of these byte-for-byte. Zero
WTR/QFE/RFFE/FEM/GRFC regulator nodes in the upstream blueline DTS — same as fajita, same as pmOS.
So the pmOS modem DT is upstream-aligned, not a fajita guess. RF power is modem-firmware/RFFE driven
(see dt-work/rf-rail-diff-FINAL.txt). Lead #9 (add 11 pm8998 LDOs) is refuted by (a) the
upstream blueline DTS not declaring them either, and (b) all 11 candidate LDOs showing use=0 from the
AP on live working Android. Do NOT flash dt-work/blueline-live.dtb for this reason.
qrild finding folded in — there is no hidden online handshake
references/qrild (abandoned AOSPM RIL): its modem bring-up (qrild_android_modem.cc ctor) is
DMS Set Operating Mode RESET → powerup → NAS register-indications, with setRadioPower a no-op stub.
Same levers ModemManager already pulls — no secret QMI sequence that arms RF. This corroborates
the #8 REFRAME (qcril_bringup_capture.md): on Android the modem firmware SELF-onlines at its own
RF-init; replaying a "QCRIL/RIL online handshake" on pmOS is a dead end because there isn't one.
(REFUTED 2026-06-21) #9 — pmOS DT under-defines the pm8998 regulator set
Refuted by #10 above (upstream blueline DTS does not declare these LDOs either; all 11 are use=0 on
live Android) and by dt-work/rf-rail-diff-FINAL.txt (RF power is firmware/RFFE-driven, not AP-DT).
Kept for the record; do NOT flash the LDO DTB. Original reasoning below.
Direction (Casey + GLM): the fix is in OUR pmOS implementation/DT, make it mimic Android. Concrete, evidenced bug found this session:
Our mainline blueline DT instantiates only ~14 of the 24 pm8998 LDOs. Android defines all of them.
Missing from dt-work/blueline-pmos.dts rpmh-regulators (present in downstream Android DTB):
ldo4, ldo6, ldo8, ldo9, ldo10, ldo11, ldo15, ldo16, ldo18, ldo23, ldo27.
Voltages (from dt-work/android-dtb/android-base.dts, full diff in dt-work/regulator-diff-android-vs-pmos.txt):
ldo6=1.856V ldo8=1.2V ldo9=1.704V ldo10=1.704V ldo11=1.0V ldo15=1.8V ldo16=2.704V ldo18=2.704V ldo23=3.0V.
Why this matters even though all 11 are deviceless/use=0 in BOTH regulator_summaries: the modem
votes its RF-frontend rails (WTR5975/PA/tuners) directly via RPMh, not the AP regulator framework.
If the AP DT doesn't define a rail in the rpmh-regulators node, it isn't in the Linux RPMh regulator
set — so when the modem's RF-init requests it, the vote can't be satisfied/arbitrated. Modem RF-init
fails → modem parks in offline and refuses LPM/online (DeviceNotReady / silent no-op). This fits
every symptom: modem boots (DSP up) but RF never inits; offline won't flip; configs don't help.
Supporting evidence this session:
- Modem FATAL crash in pmOS dmesg:
qcom-q6v5-mss: fatal error received: ...Asserted in wlan_vdev.c:_wlan_vdev_create+port failed halton stop (subsystem not halting cleanly → modem can't flush NV → MCFG selection lost across restart → 0 configs every boot). - Modem comes up
offline(not LPM like Android);set low_power/onlinereturn rc=0 but mode never changes; PDC autoselect (Set Feature) does NOT persist across modem restart (RIL-layer, not firmware NV).
Executable plan (start here next session — do NOT re-derive):
- Diff our blueline DTS rpmh-regulators block vs UPSTREAM linux-next
sdm845-google-blueline.dts(Heidelberg's). Our pmaports kernel build may predate the full regulator set. If upstream already defines all LDOs, bump/patch the kernel DTS; if not, add them. - Add the 11 missing LDOs to the
pm8998-rpmh-regulatorsnode in the blueline DTS at the Android voltages above; mark the RF-candidate analog railsregulator-always-onso they're up at RF-init. - Build via pmbootstrap (
linux-postmarketos-qcom-sdm845), flashboot_b(see platform.md), boot. dmesg | grep -iE 'regulator|vreg|q6v5|fatal'— confirm no rail/halt errors; then--dms-set-operating-mode=online→--dms-get-capabilitiesNetworks non-empty = RF armed.- Leverage other devs (Casey): do NOT silo. Cross-check the SDM845 mainlining wiki, sargo/bonito
(Google sister devices) DTS, and the
#sdm845:postmarketos.orgchannel for how RF rails are declared. Heidelberg's blueline series on lore.kernel.org is the authoritative reference.
Artifacts: dt-work/regulator-diff-android-vs-pmos.txt, dt-work/android-dtb/android-base.dts
(decompiled downstream DTB), dt-work/blueline-pmos.dts.
The modem is a self-contained DSP (MPSS) running Qualcomm firmware. Linux only loads it, powers it, and talks QMI to it. RF is the modem's own internal state machine — Linux cannot drive RF directly. Read this stack bottom-up; the blocker is at L6.
The bring-up stack
| L | Layer | What it is | Status (2026-06-19) |
|---|---|---|---|
| L0 | Silicon / RF | SDM845 SoC; SDR845 + WTR5975 transceivers; pm8005/pm8998 PMICs | fixed HW |
| L1 | Firmware load | mba.mbn (238 KB) → modem.mbn (67 MB MPSS), via remoteproc q6v5-mss |
healthy — "remote processor is now up" ~12.8 s |
| L2 | Power | rpmhpd domains cx/mx/mss; mss→mss.lvl ARC→pm8005_s2_level |
healthy — q6v5 votes mss INT_MAX; verified vs Android |
| L3 | Transport | QMI over QRTR; glink-edge to the modem PID |
healthy — all QMI services register |
| L4 | Host services | rmtfs (EFS), pd-mapper, tqftpserv, qrtr-ns |
CRITICAL 06-19: rmtfs binary was replaced (69KB→141KB) and config changed from -r -P -s to -P -s, causing EFS corruption + modem crash + system freeze at "[stack] dump". Original binary at /usr/bin/rmtfs.orig-20260618 must be restored. CORRECTION (06-19 #2): -r = read-only is WRONG for arming the modem — Android serves EFS read-write and the modem writes modemst2 on boot. Use -P -s (read-write). The 02:38 corruption was the wrong binary, not the rw flag. |
| L5 | SIM / UIM | card detect, app select, primary-GW provisioning | healthy — usim (2) ready, slot1/app1 |
| L6 | RF arm | DMS Set Operating Mode → online arms the RF stack |
BLOCKED — DeviceNotReady (QMI 52). The 06-19 #2 "rmtfs read-only" root-cause is DISPROVEN (2026-07-02): rmtfs runs -P -s read-write live (drop-in enforced, all EFS partitions present) and 52 persists. rmtfs ruled out. Real cause is the modem's own boot-time RF-init decision (Route B), not an AP EFS-write path. |
| L7 | Data / voice | IPA/rmnet, ModemManager, calls/SMS/data | gated behind L6 |
The blocker, precisely: DMS Set Operating Mode=online → DeviceNotReady (QMI 52);
DMS Get Capabilities → zero channel rates / empty networks = the RF stack never initialized
inside the DSP. Everything upstream (L1–L5) is proven healthy. ModemManager reaches enabling,
hits this, falls back to disabled, cycles.
Eliminated leads — DO NOT RE-CHASE
Each killed with proof. The "proof" column is why it's dead, not a hypothesis.
| Lead | Verdict | Proof |
|---|---|---|
rmtfs NV-files / msm-cros-efs-loader (serve EFS from file copies) |
DEAD ( UPDATED 06-19 ) | REAL CAUSE FOUND: rmtfs binary was replaced (69KB→141KB) on 06-19 02:38 and service config changed from -r -P -s to -P -s. This broke EFS partition access, causing modem crash → "[stack] dump" → system freeze. Fixed by restoring /usr/bin/rmtfs.orig-20260618 (69192 bytes) and resetting /etc/systemd/system/rmtfs.service.d/writable.conf to -r -P -s. The NV-files approach is dead; the original partition-mode rmtfs MUST be used. |
removed-region@88f00000 carveout (was "lead #1") |
DEAD | Android iomem: 88f00000-8aafffff : System RAM. The device whose modem works leaves it allocatable. OnePlus-specific quirk. (06-19) |
cmd-db maps mss.lvl to wrong PMIC (was "lead #3") |
DEAD | Android regulator tree: 4080000.qcom,mss → pm8005_s2_level (correct rail). cmd-db is the same firmware blob on both OSes. pm8005 absent from pmOS regulator_summary is expected — mainline drives it via rpmhpd/genpd, not the regulator framework. (06-19) |
vdd_mss-supply / SPMI pm8005@4 in DT |
DEAD | Mainline q6v5-mss is power-domains-only for SDM845 (no devm_regulator_get). qcom,pm8005 has no SPMI driver in mainline → adding pmic@4/5 panics the kernel. Only smps1/2 under the rpmh-regulators-2 block is safe. (06-18/19) |
"blueline lacks soc-qcom-sdm845-modem dep" |
DEAD (stale doc) | Current pmaports pkg is soc-qcom-modem; blueline depends on it just like enchilada. Device-pkg deps + firmware manifests are equivalent. (06-19) |
| fcc-unlock missing | DEAD | fcc-unlock exists nowhere in pmaports; no SDM845 device uses one. Modem is not FCC-locked-waiting. (06-19) |
| Modem DT node differs from working device | DEAD | blueline modem node is byte-identical to fajita (same power-domains/clocks/resets); only firmware path + phandles differ. (06-18) |
| Storage / EFS calibration is missing | DEAD | rmtfs writable + Android-primed EFS (modem online in Android first) → still DeviceNotReady in pmOS. EFS content is not what's missing. (06-18) |
Net: every kernel / DT / power / packaging layer is proven equal to blueline-Android (which runs the modem online, Fido LTE) on the same silicon. The blocker is not there.
The frontier (REVISED 2026-06-20 #8 — QCRIL does more at bring-up; mainline stack under-initializes the modem)
Casey's standing thesis (correct, and the through-line of this whole effort): QCRIL powers and initializes more of the modem than ModemManager does — and mainline pmOS, as a minimal port, under-powers/under-initializes the board. We are hunting the specific delta. Evidence gathered:
-
QCRIL sends a
'UI ready'/ power-on-optimization handshake to the modem that MM never sends. Android cold-boot capture (android-live-20260619/deep/radio-boot.txt):qcril_qmi_nas_telephony_ready: poweron_opt: send 'UI ready' to modem(qcril_qmi_nas.cpp:17527), plus an active radio-power state machine (qcril_qmi_nas_update_radio_power_request_info→BOOTUP_RADIO_POWER_OFF,qcril_qmi_nas_radio_power_transaction_handler, state linetelephony LPM | ril NONE | card UP | modem ONLINE). ModemManager has no equivalent of this poweron_opt / UI-ready signalling. -
No DT RF node on either OS (see retracted #6) — so the delta is QMI-level (modem-side init/power), not a Linux regulator we can add. The mainline DT brings up cx/mx/mss + the basic stack; it does NOT replicate QCRIL's QMI bring-up choreography.
-
Provisioning race (real bug, partially validated).
msm-modem-uim-selectionfails every boot: it fires during the early QRTR window ("node with id 0 not found"), then its primary-GW provisioning ACTIVATE times out → service exits FAILURE → the liveprimary-gw-provisioningsession loopsend-with-failure/app-reset. Run manually AFTER boot it SUCCEEDS (Successfully changed provisioning session). KEY: with provisioning freshly+cleanly active,--dms-set-operating-mode= onlineno longer returns DeviceNotReady(52) (returns rc=0) — though the modem still staysofflinebecause PDC configs were 0 that boot. We have never had configs-active AND clean-provisioning AND the QCRIL init handshake all at once. -
QCRIL online path captured (airplane off→on,
android-capture-20260620/qcril-online-capture.txt):qcril_qmi_nas_dms_handle_modem_operating_mode(qcril_qmi_nas.cpp:18289) → "modem's operating mode needs to be changed" → ril SETTING_OP_MODE → modem LPM → ONLINE in ~30ms (a plain DMS set-operating-mode=online). post-transition:set_bootup_power_optimization_state=2, ATEL UI-READY, enable networking/sys-info indications. No hidden RF-power QMI command — the modem is already powered (LPM) and the flip arms RF. KEY STATE DIFFERENCE: Android modem starts in LPM (low_power) and does LPM→online; the pmOS modem sits inoffline(deeper state) and offline→online is what fails. So the gate is the combination: (a) MCFG configs loaded, (b) primary-GW provisioning active, (c) the modem in LPM (not offline) for the online flip. We have NEVER had all three at once on pmOS — that is the decisive untested experiment.
The decisive instrument (next): a QMI-message-level capture of QCRIL's bring-up on the rooted
Android (enable QMI verbose radio logging, cold boot or airplane-toggle), then diff the QMI request
sequence against ModemManager's (we already have MM's via --debug). The delta = exactly what to
replay on pmOS (UI-ready/poweron_opt, any NAS/DMS/RF config QCRIL issues). Then replay on pmOS with
configs active + provisioning clean. Rooted Android (slot B) is the instrument; keep this doc current
as the capture lands.
The frontier (REVISED 2026-06-20 #7 — live RF-init trace: blocker reframed to primary-GW provisioning, NOT RF cal)
Ran the decisive pmOS-side RF-init trace (slot B, kernel 7.1.0-rc1, modem over qrtr://0). Two big results — one closes a thread, one opens a sharper one.
The modem is not faulting and nothing it requests is missing. dmesg: clean boot, mba→mpss→
running→handover, NO crash/coredump/fatal in the RF-arm window; an online attempt produces ZERO
dmesg delta. tqftpserv serves only the readonly MBN tree (read OK) + an empty mcfg.tmp scratch
file the modem RRQ/WRQ-loops on — and mcfg.tmp being 0 bytes is NORMAL (tqftpserv is bind-mounted
to the persistent persist partition /dev/sdf2[/rfs] — NOT volatile tmpfs as first feared —
and persist is SHARED with Android, so if the working modem needed content there it would persist;
it's empty on both). So serving is correct and complete. The modem boots healthy and simply
REFUSES online at the QMI layer (DeviceNotReady = clean refusal, not a load failure).
NEW lead — primary-GW SIM provisioning session fails. ModemManager's log during the refusal:
refresh indication received: session type 'primary-gw-provisioning', stage 'end-with-failure', mode 'app-reset' — looping every MM retry (~72s), alongside a NAS system-info indication that
returns FAILURE: InformationUnavailable. Operating mode offline, HW restricted no (not
FCC/HW-locked), serving system fully detached, radio interface none. AND msm-modem-uim-selection
failed this boot: "Application was already selected" → operation failed: Transaction timed out
→ service exit FAILURE. Yet the static card read is healthy: Primary GW bound (slot1/app1), USIM
app ready, personalization ready, PIN1 disabled. So the binding exists but the live
provisioning SESSION keeps failing + app-resetting. This is distinct from (and more proximate than)
the RF-cal lead — the modem may refuse online because primary-GW subscription provisioning never
completes, not because RF is uncalibrated. (mBands=[] is non-diagnostic per #6; generic IMEI works
on Android per Casey — so cal/QCN lead is weak.)
OPEN — causality not yet proven. Why does the provisioning session end-with-failure while the
GW binding shows bound+ready? Candidates: (a) msm-modem-uim-selection timing out leaves the
subscription half-selected; (b) the modem triggers an app-reset refresh (post-MCFG) that nothing
on the AP side completes the way Android's RIL does; (c) downstream symptom of offline state.
Decisive next test (fresh boot, one clean shot): on a fresh slot-B boot, before MM enables,
(1) confirm whether the primary-gw-provisioning refresh fails on its own; (2) re-run UIM selection
cleanly (the service timed out) and retry online; (3) capture the UIM/provisioning QMI sequence.
Compare to what Android's RIL does on the same refresh. Known: a remoteproc reset (online is retry-safe, see CORRECTIONS)
both clear UIM provisioning, so this needs a clean boot.
The frontier (REVISED 2026-06-20 #5 — MCFG SOLVED; online blocker isolated to device-specific RF bring-up)
Live pmOS run this session SOLVED the MCFG provisioning blocker AND, by doing so, proved MCFG was
never the RF-arm blocker. The online wall (DeviceNotReady QMI 52) survives every config/stack/SIM
fix. Full elimination, all on hardware (slot B, kernel 7.1.0-rc1-sdm845):
SOLVED — persistent MCFG self-provisioning. qcril-autoselect.py (PDC Set Feature 0x2E,
Selection Mode=3) made the modem self-load 2 platform (SS-LA-SDM845) + 25 software (Rogers_Canada)
configs from the tqftpserv tree; survives reboot (self-loads at RF-init). Both ACTIVE; software
version 0x801481A == Android's exact value. The "0/0 every cold boot" blocker (frontier #3/#4)
is dead. Tooling: tools/qcril-autoselect.py + tools/build-openimsd-libqmi.sh (openimsd libqmi
fork, has Set Feature). Believed a Pixel 3 mainline first.
RULED OUT as the online (RF-arm) blocker — every one tested on hardware:
- MCFG/PDC — both configs ACTIVE, matching Android. online still 52.
- UIM/SIM — card present, USIM app
ready, Primary GW slot1 app1 provisioned. - timing/lock — MM stopped, 10 patient online retries over 30s, all 52.
- ModemManager — its full 9-step enable (power-state → op-mode indication register → Set Operating Mode 0x2E=online) returns the same 52. Not a manual-qmicli artifact.
- missing stack — pd-mapper, hexagonrpcd, q6voiced, rmtfs (
-P -srw), tqftpserv ALL running;soc-qcom-modeminstalled; PD JSONs (modemr.jsn/modemuw.jsn) + firmware present; no errors. Matches working SDM845 devices (enchilada/fajita) component-for-component. - EFS not served — modemst1/2, fsg, fsc all populated (~260K nonzero) + served. (modemcal sdd4 is all-zero; persist sdf2 has data.)
- firmware mismatch — modem sw version
g845-00194-210812-B-7635520IDENTICAL to Android; revisionMPSS.AT.4.0.c2.17-00194-SDM845_GEN_PACK.
THE REMAINING BLOCKER = device-specific RF bring-up. Same firmware + same shared EFS + complete
stack + DT byte-identical to fajita, yet: NAS get-system-info empty, nas-get-rf-band-info →
no bands, mode stuck offline, DeviceNotReady on online. The modem boots but its RF never inits.
Two leads:
- RF cal / QCN — generic test IMEI
990012001581294, model0, no bands. Classic no-calibration signature. UNCONFIRMED: does the WORKING Android show the REAL IMEI (→ pmOS isn't loading cal/QCN) or the same generic one (→ red herring)? Decisive next test: reboot slot A, read IMEI + DMS caps + bands; compare. (Not done autonomously — avoids leaving the phone off-pmOS while unattended.) - Board RF-frontend — fajita (OnePlus 6T) works on mainline; blueline (Pixel 3) shares the q6v5-mss DT node but has a different PMIC/RF frontend (WTR/QFE/antenna tuners). If blueline's RF frontend needs power/GPIO/cal mainline doesn't provide for THIS board, the modem can't arm RF even with identical firmware. (Distinct from the DEAD pm8005/mss-rail lead — this is the RF frontend, not the modem-subsystem rail.)
2026-06-20 #6 — RETRACTED: do NOT treat the board-power lead as closed
I previously wrote this section as "lead #2 CLOSED with positive proof." That was an overclaim and
Casey was right to reject it. What the regulator/DT evidence below actually shows is narrower: there
is no labeled RF-frontend regulator node in the device tree — confirmed on BOTH mainline AND the
downstream Android DTB (dt-work/android-dtb/android-base.dts, decompiled from slot-A boot_a;
its qcom,mss@4080000 declares only vdd_cx/vdd_mx/vdd_mss, no RF/WTR/antenna node anywhere). That
means we can't fix this by copying an "RF rail" node — NOT that board power is irrelevant. Mainline
blueline is a minimal fresh port: it instantiates only rails with explicit consumers and leaves
every other board rail off, whereas the downstream stack + QCRIL bring up far more (subsystems,
power-on handshakes, NV/RF init via QMI). The live lead remains: what does the modem need powered/
initialized — via Linux rails the mainline port omits, or via QMI commands QCRIL sends — that pmOS
does not provide. See the #8 frontier (top of file) for the QCRIL-does-more evidence.
(original text retained below for the evidence it contains)
Brought the "power Android provides" thread full circle on live hardware (Android slot A, rooted):
- No Linux-side RF rail exists on EITHER OS. In both the Android (
rf-compare/regulators-android.txt) and pmOS (regulators-pmos.txt) regulator dumps, the modem4080000.qcom,mssconsumes ONLY the Q6 DSP level rails (s6_level=CX, s9_level=MX, s2_level=MSS). There is no WTR/PA/tuner regulator in Linux on Android either — RF-frontend power is driven by the modem firmware over RFFE + GRFC, not the Linux regulator framework. So a "missing regulator in the pmOS DT" was never possible. - DT modem node is correct + complete (
dt-work/blueline-pmos.dts): power-domains cx/mx/mss, xo/bus/mem clocks, glink "modem" transport,gpio-reserved-ranges = <0 4, 81 4>(Linux leaves the modem-owned GPIOs alone). No regulator-supply phandles — correct for mainline q6v5-mss. - Pinctrl: no AP/modem GPIO conflict. Android TLMM (
3400000.pinctrl) owns only camera-CCI, gpio-keys, and USB-extcon pins; the modem's GRFC/RF-control pins are MSS-internal and not AP-routed. The AP cannot be stepping on an RF pin. - "No bands" is NOT a distinguishing signal. The fresh working-Android radio capture
(
android-capture-20260620/android-radio-log.txt) showsmBands=[]in CellIdentityLte while fully attached to Fido LTE (REG_HOME, EARFCN 2000 = B4). So the empty mBands we saw on pmOS does not by itself mean RF is dead — weakens (does not kill) lead #1's reasoning.
Verdict: every AP-side power/GPIO/pinctrl/DT input the modem receives is provably equal to blueline-Android (which arms RF on the same silicon). Lead #2 is closed the same way IMEI closed lead #1. The blocker lives strictly inside the modem firmware's RF-init, which the AP only feeds via: firmware blobs, EFS (rmtfs), persist/rfs (tqftpserv), power-domains, clocks, QMI. All proven present/equal — EXCEPT we have never traced what the modem requests from rmtfs/tqftpserv during its RF-init, nor captured the modem's own failure reason.
Decisive next experiment (pmOS-side, not yet run): boot slot A, attempt online, and capture
(a) tqftpserv/rmtfs request trace during RF-init — which file/NV path the modem asks for and
whether it's served (frontier #4 showed Android rmt_storage reads on boot; we never logged the
pmOS read side), and (b) the modem remoteproc coredump / SSR reason on the failed online. This is
the one byte-for-byte trace we still lack; it points at the specific RF-init resource that differs.
Route B (autoselect EFS NV item) is DEPRIORITIZED: it would only persist the config state we already achieved, and config-active does not fix online.
The frontier (REVISED 2026-06-20 #4 — Android boot is VERIFY-ONLY; the modem persists MCFG itself)
Source: analysis of the captured Android radio logs in android-live-20260619/deep/
(radio-boot.txt, radio-full.txt, ssr-capture.txt), 2026-06-20. This overturns both
frontier #3's "just make the platform config active" and the qcril_full_provisioning.md §D.4
"load+activate every boot" model. Read this before either.
What the captures actually show
Across all three logs (~12K PDC log lines, including a full modem SSR) QCRIL made only query
calls — never load/select/activate, never pdc_enable_auto_selection:
ssr-capture.txt:24293— the decision point:qcril_qmi_pdc_is_modem_mbn_updated: prev_ver_info: MCFG-g845-00194-220517-B-8604334, cur_ver_info: MCFG-g845-00194-220517-B-8604334→ prev == cur → reload SKIPPED.ssr-capture.txt:23949—get_active_config_info_ind: config_version = 0x801481a,Update CNV from 0x48 to 0x48(0x48 = 72 =cnv=[72]=persist.vendor.radio.mbn0=72).- Only PDC functions logged anywhere:
get_active_config_info,get_selected_mbn_config,get_selection_carrier,is_modem_mbn_updated. Zero*_load_config,*_select_config,*_activate_config,enable_auto_selection,enable_modem_update.
What that means
Android's steady-state boot does almost nothing to PDC. The modem holds its MCFG persisted in
its own EFS (modemst1/2 — the 2 MB write to modem_fs2 the rmt_storage capture caught),
self-applies it at firmware RF-init every boot, and QCRIL just confirms the active version
matches persist.vendor.radio.cnv.ver_info and stops. The full load→select→activate path runs
only on first provision or an MCFG version change (the prev_ver != cur_ver branch) — which
we have never captured, because every capture was an already-provisioned device. That is
exactly why pdc_enable_auto_selection never appears: it lives on the unobserved path.
Consequences for the two prior theories
- Frontier #3 ("just need an active platform config") — necessary but NOT sufficient. PROGRESS L165–172 had BOTH configs active (SS-LA-SDM845 + Rogers) and DMS Networks stayed empty. Active ≠ armed. Don't chase "make it active" as the finish line.
§D.4("load+activate every boot, accept two restarts") — that is a reconstruction of the unobserved first-provision path, not what Android does in steady state. It remains a valid pmOS workaround (seetools/qcril-prov.py), but it is unproven on hardware and is not the mechanism that actually arms RF on Android.
The pmOS problem, restated from the evidence
The "0/0 configs every cold boot" volatility (PROGRESS L140–144) is not something QCRIL fixes per-boot. It's that the modem cannot persist/restore its PDC selection in EFS the way Android does. The earlier "rmtfs read-write didn't fix it" result (ledger L38) is a chicken-and-egg: writable EFS is useless if the modem never had a valid MCFG to commit, and a one-shot userspace activate is useless if it cannot persist to survive the next firmware RF-init. Both are required: a valid config loaded AND modem-side persistence that survives reboot.
Two candidate paths forward
- (A) Per-boot provisioner workaround —
tools/qcril-prov.pyloads HW+SW, selects, activates each boot. Mirrors the reconstructed first-provision path. Unproven; "active ≠ armed" is the open risk. - (B) Provision-once + persistence (the Android mechanism) — get a valid MCFG into the modem
once, then ensure
modemstpersistence so the modem self-restores + self-applies at RF-init, and userspace reverts to verify-only. This is what the captures show actually works on hardware.
The capture we still need (next experiment)
A normal-boot capture only re-shows the skip path. To record the real first-provision load
sequence (and settle whether pdc_enable_auto_selection / enable_modem_update is a required
precondition), force a first-provision on Android: clear /data/vendor/modem_config/ and
persist.vendor.radio.cnv.ver_info, reboot, then capture. Runbook:
PAF/first_provision_capture.md.
The frontier (SUPERSEDED 2026-06-19 #3 — see #4 above — PDC platform config is the blocker)
After a full day of Android-vs-pmOS forensics, the blocker is identified:
What's actually wrong
The modem's PDC (Policy Decision Control) stores MBN configs in modem NV:
- 25 software (carrier) configs loaded — Rogers_Canada is ACTIVE (133KB, v0x801481A)
- 2 platform (hardware) configs loaded — SS-LA-SDM845 is INACTIVE (56KB)
Without an active platform config, the modem doesn't know its RF hardware capabilities.
DMS reports Networks: '', NAS home network returns NotProvisioned (QMI 16), and
Set Operating Mode=online returns DeviceNotReady (QMI 52).
On Android, QCRIL (libril-qc-hal-qmi.so) loads MBN files from /vendor/rfs/msm/mpss/readonly/vendor/mbn/
via QMI PDC, activates both platform and software configs, and the modem restarts with full RF.
ModemManager does none of this — it assumes the modem is pre-provisioned.
Attempted: direct PDC activation
qmicli --pdc-activate-config=platform,6FE571A9... returned "Successfully requested config
activation" but triggered a modem restart that cleared the platform config (0 configs after
restart). The activation request succeeded at the QMI level but the modem's internal handling
removed the config during restart. Needs Android QCRIL to re-load it.
qmicli --pdc-load-config=... segfaults on MBN binary files (qmicli 1.39.0 / libqmi 1.38.0).
Cannot load MBN files from pmOS side — libqmi bug or MBN format incompatibility.
Key facts confirmed (06-19)
- IMEI: identical on Android and pmOS (
990012001581294). NOT the blocker. Android works on Fido LTE with this same test IMEI. - Modem firmware: identical SHA256 (
97c1d19c...) between slots A and B. - Modem crashes (SFR Init / XPU violation): intermittent — some boots 0 crashes, some 2. Even clean boots get DeviceNotReady. Not the blocker.
- rmtfs DOES serve correct partitions: partlabels are GPT-stable; UFS LUN renumbering changes
/dev/sdXnames between boots butby-partlabelsymlinks always resolve correctly. - DMS Get Capabilities consistently shows
Networks: ''andMax TX/RX: 0on pmOS. Android shows populated networks (Fido LTE, Rogers 302720, full band support). - NAS Get System Selection Preference shows FULL band tables on both OSes (43 LTE bands + extended). NAS has the data; DMS doesn't. The bridge is the platform MBN activation.
- Android rmt_storage: reads EFS on boot (no writes), writes 2MB to modem_fs2 AFTER going online. Normal operation — not initial provisioning.
- pmOS rmtfs journal: empty (rmtfs doesn't log successful reads/writes). No errors either.
Fix path
- Boot Android to let QCRIL re-load the platform MBN into PDC (it was there before activation)
- Boot pmOS — do NOT touch PDC, do NOT run
qmicliactivation - Verify:
qmicli --pdc-list-configs=platformshows SS-LA-SDM845 as Active - Verify:
qmicli --dms-get-capabilitiesshowsNetworks: 'gsm, wcdma, lte' - Let ModemManager drive enable→online (NEVER manual qmicli online)
- If platform config is still Inactive after Android: need to fix
qmicli --pdc-load-configsegfault, or write a minimal MBN loader using libqmi directly
What we have on device
- rmtfs: read-write (
-P -s), serving correct EFS partitions via partlabels - tqftpserv: bind-mounted to
/mnt/vendor/persist/rfs(persist partition) - pd-mapper: installed and running from boot
- MBN files: 179 files (11MB) at
/mnt/vendor/persist/rfs/readonly/vendor/mbn/(extracted from Android/vendor/rfs/msm/mpss/readonly/vendor/mbn/) - MCFG version:
g845-00194-220517-B-8604334 - Rogers Canada MBN:
mcfg_sw/generic/NA/Rogers/Commercial/CA/mcfg_sw.mbn - SDM845 LA/SS platform MBN:
mcfg_hw/generic/common/SDM845/LA/SS/mcfg_hw.mbn(56KB) - Kernel: building from
sdm845/7.1-devHEAD (85f1df2a), config fixed tosdm845
Android vs pmOS service comparison
| Component | Android | pmOS | Notes |
|---|---|---|---|
| EFS storage | rmt_storage r/w | rmtfs -P -s r/w |
Parity |
| TFTP | tftp_server → vendor/rfs | tqftpserv → persist bind | Parity |
| pd-mapper | running (maps PDs) | running | Parity |
| RIL | QCRIL (libril-qc-hal-qmi.so) |
ModemManager | Missing PDC provisioning |
| ipacm | running (/dev/ipa FDs) |
not installed | Data path only |
| Keymaster | decrypts persist blobs | none | IMEI encrypted at rest |
| QMI services | comparable (50+) | comparable (50+) | Parity |
(superseded) earlier frontiers
Read-only rmtfs theory (06-19 #2): rmtfs read-only prevented modem from persisting EFS writes. Fixing to read-write didn't resolve DeviceNotReady. EFS r/w is necessary but not sufficient — the modem needs valid MCFG in EFS first, which comes from PDC provisioning.
QMI-handshake theory (original): "MM doesn't send QCRIL's init sequence." Dead — same MM binary works on OnePlus 6. The missing sequence is PDC MBN loading, not a handshake gap. Other SDM845 devices likely have platform config pre-loaded from factory.
fsg_oem theory: Modem requesting modem_fsg_oem_1/2. Patched rmtfs to serve them.
Modem never actually requests these when EFS is properly provisioned. Symptom, not cause.
XPU violation theory: Modem crashes twice on some boots. Even on clean (0-crash) boots, DeviceNotReady persists. Not the blocker.
Useful commands (pmOS)
qrtr-lookup | awk '$1==11' # UIM service → note Port
qmicli -p -d qrtr://0/<port> --uim-get-card-status
qmicli -d qrtr://0 --dms-get-operating-mode
qmicli -d qrtr://0 --dms-set-operating-mode=online
qmicli -d qrtr://0 --dms-get-capabilities # zero rates = RF uninit
# clean modem re-init (rmtfs runs -P -s read-write; see 2026-07-02 correction):
echo stop | sudo tee /sys/class/remoteproc/remoteproc2/state ; sleep 3
echo start | sudo tee /sys/class/remoteproc/remoteproc2/state
# preferred: QMI Set Operating Mode = RESET (4) reinitializes without touching UIM/provisioning.
Forcing a remoteproc reset clears UIM provisioning; msm-modem-uim-selection only re-runs at
boot. (The old note here claimed -r -P -s read-only was required for re-provisioning — stale
and wrong; rmtfs runs -P -s read-write live and the modem re-provisions fine across a
stop→start. For a clean reinit that does NOT drop provisioning, use QMI RESET, not remoteproc.)
Cross-references worth mining
- Working SDM845 (same SoC): OnePlus 6/6T (enchilada/fajita), Poco F1 (beryllium),
SHIFT6mq (axolotl) — data + SMS work via the same mainline kernel + pmaports packages.
Their AP-side config is already in
~/pmos/pmaports; nothing to pull. Their working firmware/EFS is device-specific and not transplantable. - Pixel 3a (sargo) / 3a XL (bonito): the closest Google analogs with modem support in pmaports (SDM670). Best target if a Google-specific RIL quirk is suspected.
- Android on this exact device is the cleanest reference: same silicon, modem works. Diff blueline-Android vs blueline-pmOS, not against OnePlus.
TOP LEAD 2026-07-02 #16 — locally-initiated DIAG_CTRL/DIAG_DATA open CRASHES the modem (glink_channel_migration assertion), repeatably
Real, causal, previously-unknown finding — not a theory. Following up #15's ack-but-no-reciprocal-
open result: tried the same locally-initiated open against DIAG_CTRL (not just DIAG_DATA).
Confirmed via dmesg -T (not my own client's stderr, which kept getting lost across SSH session
churn — the KERNEL'S OWN log line is the ground truth here): rpmsg rpmsg0: failed to open DIAG_CTRL
fires cleanly at the expected 5s mainline timeout, exactly as designed. But roughly 18-40 seconds
LATER, every single time this was attempted (three separate occasions, two of them minutes apart on
different modem boots), the modem hard-crashes:
qcom-q6v5-mss 4080000.remoteproc: fatal error received: [disable_ssr]glink_channel_migration.c:602:
[IST336]Assertion status == GLINK_STATUS_SUCCESS failed
remoteproc remoteproc3: crash detected in 4080000.remoteproc: type fatal error
remoteproc remoteproc3: handling crash #N in 4080000.remoteproc
This is a repeatable causal chain, confirmed across crashes #5 and #6 in the same dmesg buffer,
both immediately following a failed to open DIAG_CTRL line, both with the identical
glink_channel_migration.c:602 assertion. glink_channel_migration.c is the FIRMWARE's own glink
channel-transport-migration logic (this is a modem-firmware-side log line, reported via SFR/fatal
error, not an AP-side kernel message) — meaning the incomplete/abandoned local-open handshake left
channel/migration bookkeeping in a state the modem's own firmware asserts on and can't tolerate.
Practical, urgent implication: DO NOT locally-initiate an open for DIAG_DATA or DIAG_CTRL
casually or repeatedly during further investigation — it reliably crashes the whole modem MSS a
short time later, disrupting whatever else was being tested (this crashed WLAN's firmware too, via
the shared XPU/hyp-assign path, matching TOP LEAD #11's earlier finding about shared secure-memory
fragility). If this path is pursued further, it needs to be done RARELY, with full crash-recovery
tolerance, ideally on a boot with nothing else running that a modem SSR would disrupt.
What this actually tells us, functionally: the modem's glink/migration layer is NOT robust to an AP-initiated open attempt that times out — downstream never hits this because (a) it doesn't time out in 5s (event-driven, no hard deadline, per #15's downstream comparison) and/or (b) Android's DIAG daemon presumably completes successfully before any abandonment would occur. This is now a second, independent data point (alongside offline-vs-LPM, #14) that mainline's bring-up leaves this modem's firmware in a MORE FRAGILE state than Android's does — not just "missing a feature" but "structurally less tolerant of imperfect AP-side timing/sequencing." Worth weighing against #14's LPM finding: both point at the same underlying idea -- whatever Android's fuller bring-up sequence does before touching these channels, mainline's minimal sequence skips, and the modem firmware was never hardened against being touched without it.
Honest gap, not yet resolved: unclear if the crash is specific to DIAG_CTRL/DATA's particular migration-eligible status, or would happen for ANY locally-initiated open of ANY channel abandoned via timeout. Worth testing later (carefully, expecting a crash) against a channel we know is NOT normally migration-sensitive, to isolate whether this is DIAG-specific or a general "don't abandon a local open" hazard on this modem.