Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/saf/device/evidence/modem_consumer_chain_20260626.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

24 KiB
Raw Blame History

PAF · The modem userspace CONSUMER CHAIN — why the modem stays OFFLINE (2026-06-26, Opus)

SESSION-3 UPDATE (2026-06-27, Opus) — SLPI SAR CRASH LOOP FIXED; PROVEN the SLPI does NOT gate the modem 52

TL;DR

Built the missing AP-side SNS registry provider (a patched hexagonrpcd) that feeds the SLPI its sensor registry over FastRPC apps_std. The SLPI's sar.cc:27:chre_utils fatal crash-loop (150+ crashes/boot) is broken — SLPI now stays up indefinitely (0 crashes over multiple 2540s windows). Then enabled the modem anyway → still QMI error 52 DeviceNotReady. So the SLPI/SAR subsystem is independent of the modem-52 wall. That question (could not be answered by disabling SLPI — only by making it actually work) is now settled: error 52 is not SLPI-gated.

🟠 SUPERSEDED 2026-06-29 (Casey) — this conclusion is INVALID. The "SLPI fixed / 0 crashes / stays up" above was only remoteproc-not-crashing: this doc's own body (search "never exposes an SNS QMI service") shows the SLPI firmware never completed CHRE init / never exposed SNS QMI — i.e. not a functioning sensor stack. The reasoning "disabling couldn't answer it, only making it work can" was correct — but "making it work" was never achieved, only "stopped crashing." If the modem's RF-init queries the SLPI and gets silence from a never-initialized sensor stack, it parks OFFLINE regardless. The SLPI is a candidate root-cause, NOT proven-independent. See slpi.md top banner + HANDOFF.md.

HOW the SLPI was fixed (the real consumer chain Android calls sscrpcd)

The SLPI boots → makes FastRPC reverse calls (apps_std fopen/fread) back to the AP to read /persist/sensors/registry/.... pmOS shipped no AP-side listener, so the registry sensor starved and the SAR (sx932x) CHRE nanoapp fataled → crash loop. The pmOS-native listener hexagonrpcd (linux-msm, already in the repo, -s = attach sensorspd on /dev/fastrpc-sdsp) is the right tool but had two gaps:

  1. Ran as User=fastrpc → couldn't read the persist registry files. Fixed with a systemd drop-in User=root (/etc/systemd/system/hexagonrpcd-sdsp.service.d/10-root.conf).
  2. construct_root_dir() (rpcd_builder.c) only mapped registry/registry/* — its virtual FS had no nodes for the top-level sns_reg_config / sns_reg_version / sns_reg_ctrl that blueline's SLPI requests at /persist/sensors/registry/<file>. hexagonfs is node-based (no real-FS fallthrough), so those ENOENT'd no matter what was on disk. Patched to add those three nodes (hfs_map_or_empty). NOTE: hfs_map_or_empty presents a missing physical file as an empty directory (S_IFDIR), which still fails a file open — so the physical file must EXIST (even 0 bytes). sns_reg_version is absent from the Android dump → touch an empty one and it serves as a regular empty file, which satisfies the SLPI.
  3. Registry DATA staged to /usr/share/qcom/sensors/registry/ (hexagonrpcd's default -R root; the 177 groups from the LineageOS persist dump + sns_reg_config, empty sns_reg_version, sns_reg_ctrl). Persist files also chmod -R a+rX (they were 0600 casey:casey from a tar restore).

STATE OF THE FIXES — now converted from live temps to REAL in-code fixes (2026-06-27)

All of the live drop-in/temp changes have been turned into aport sources so a clean pmbootstrap install reproduces them (idempotent fresh + upgrade). What's in code now:

  • hexagonrpcd patch → local aport pmaports/temp/hexagonrpcd/ (fork of Alpine community aport, pkgrel=4, adds hexagonrpcd-blueline-registry-nodes.patch, checksums regenerated). pmbootstrap build hexagonrpcd produces the patched binary instead of the live hand-installed /usr/bin/hexagonrpcd overwrite.
  • root drop-indevice-google-blueline/hexagonrpcd-sdsp-root.conf, installed to /usr/lib/systemd/system/hexagonrpcd-sdsp.service.d/10-root.conf (APKBUILD package()).
  • registry data → NOT shipped as blobs. blueline-sns-registry-stage.sh + .service (oneshot, Before=hexagonrpcd-sdsp, ordered after persist mount) stage the registry from the device's OWN /mnt/vendor/persist/sensors/registry into /usr/share/qcom/sensors/registry (hexagonrpcd's served root; persist layout is off-by-one for a single -R). Touches the empty sns_reg_version. Idempotent, ships no proprietary calibration.
  • service enable → preset enables blueline-sns-registry-stage.service + hexagonrpcd-sdsp.service.
  • persist perms: handled by the stage script's chmod -R a+rX on the served copy; the persist chmod I did live is NOT needed by the real fix (hexagonrpcd reads /usr/share/qcom, not persist).

REMAINING build/verify steps (need device + reflash — do NOT half-run unattended):

  1. pmbootstrap build hexagonrpcd (local aport) + pmbootstrap build device-google-blueline.
  2. Install/upgrade on device (pmbootstrap sideload or apk add the built .apk) — replaces the hand-installed binary + lands the drop-in/stage-service/preset.
  3. Reboot clean and VERIFY idempotency: SLPI stays up (0 crash # delta), provider active, blueline-sns-registry-stage populated /usr/share/qcom/sensors/registry.
  4. Revert the hand-installed temps once the packages own them: restore /usr/bin/hexagonrpcd.orig is NOT needed (apk upgrade overwrites), remove the manual systemctl enable (preset handles it).
  • Kernel DT vmid change (sdm845.dtsi sdsp fastrpc qcom,vmids = <HLOS SSC_Q6>): I hand-patched the DTB into boot_a to test the TZ hyp-assign theory. LOGGED RESULT: with <3 5> live (confirmed via dtc decompile of /sys/firmware/fdt), dmesg still prints SDSP fastrpc hyp-assign rejected by TZ (-22); registering node without reserved-heap assignment (32 occurrences). So the vmid change did NOT change the TZ result — the crash fix was the registry serving, not the heap. The change is harmless but unnecessary.
    • Orig boot.img backups: scratchpad boot.img + /tmp/boot-backup_a.img on device.
    • NOTE on a reboot during testing: lsusb showed 18d1:d001 and SSH returned Network is unreachable; root cause was the HOST iface enp0s20f0u6 DOWN (per ip -br addr), fixed by ip link set up + ip addr add 172.16.42.2/24 → phone answered, uptime = up 2 min. The phone had booted normally; it was a host USB-net enumeration issue, not a device boot failure.

THE REMAINING WALL — modem QMI 52 DeviceNotReady, now provably INDEPENDENT of SLPI

With SLPI fully up + sensors registry served, mmcli -m 0 -e still fails at DMS set-operating-mode with QMI 52. modem0 enumerates (QUALCOMM, SIM present, slot 2 active), sits disabled / power off, enable → enabling → (QMI 52) → disabled. The MSS remoteproc (4080000.remoteproc) is running. LOGGED: mmcli -m 0 -estate changed (disabled -> enabling), couldn't enable interface: 'Couldn't set operating mode: QMI protocol error (52): DeviceNotReady', state changed (enabling -> disabled). So 52 comes from the modem path, with the SLPI fully up — the two are independent. Scope note: the goal is the WHOLE device, not just telephony. The SLPI/sensors fix is a real bringup win on its own (sensors infra now functional). For the 52 specifically, the SLPI is not the lever. Next session for 52: modem NV/EFS/identity (the model 0 / LOW_POWER hypothesis from session 2), rmtfs/EFS sync, or modem firmware-init.

Open loose end (minor): hexagonrpcd reconnect churn

Provider shows activating with ~18 restarts/min — it exits on FastRPC "Broken pipe" when the SLPI closes the session after serving, then Restart=always reattaches. SLPI stays up regardless (0 crashes), so it's cosmetic, but worth a RestartSec/keepalive tweak later.


SESSION-2 UPDATE (2026-06-26 late, Opus) — CHAIN BUILT + WORKING; wall is now the modem 52 / SLPI-SAR firmware crash

What got BUILT this session (all in pmaports device/testing/device-google-blueline/, deployed live + wired into APKBUILD/preset — idempotent)

The whole consumer chain now comes up automatically on a clean boot and ModemManager starts + creates the modem. Verified on hardware.

  1. modem-qrtr-ready.service + modem-qrtr-ready.sh (NEW) — the pmOS equivalent of Android's wait_for_prop vendor.qcom.devup 1. Blocks until modem QMI answers and is stable (2 probes/3s) before releasing consumers. Killed the boot race (node with id 0 not found in QRTR bus). VERIFIED: gate waits, then releases.
  2. UIM-selection fixblueline-uim-select.sh (NEW) wraps the stock binary: it selects the SIM app fine but then a trailing QMI op times out (SLPI/XPU churn) and exits 1 — a false negative that failed MM's hard Requires=. Wrapper treats "app selected" as success. uim-wait.conf rewritten: gate on modem-qrtr-ready, Type=oneshot, run the wrapper. VERIFIED msm-modem-uim-selection now = active.
  3. qcril-prov.py — fully fixed (was crashing on out.get_configs()):
    • list_configs + get_selected_config reworked to read the PDC indication (IndicationPdc*Output.get_configs()/get_active_id()), not the response.
    • GI array-length args fixed (set_id(id), set_type_with_id_v2(t,id)).
    • Targets configs by sha1(.mbn) == PDC config id; selects+activates HW + Rogers SW.
    • Skip-if-already-active: checks get_selected_config; if the inherited config is already active, skips select/activate → 0 modem restarts (was 2). VERIFIED: "HW/SW already SELECTED+ACTIVE — no restart".
    • Deadlock fix: exits 0 after provisioning even if RF not armed (RF arming is MM's job, and MM is gated behind this service — requiring RF here deadlocked). VERIFIED exit 0.
  4. blueline-dpm-open-port.service / qcril-prov.service — gated on modem-qrtr-ready + retry.
  5. Kernel: mem-protect skip reverted (980c4ae1f); on clean baseline 7.1.1; verify-fixes.sh green.

THE WALL NOW (single, precise)

Full chain runs → ModemManager starts, creates modem0 (qcom-soc plugin, 3 ports), reads the SIM ATR, goes disabled → enabling → calls set-operating-mode → QMI error 52 DeviceNotReady → falls back to disabled. This is the SAME 52 from day one. The modem is OFFLINE, model 0 (up-but-unprovisioned) and refuses to enable even when driven correctly by MM. So the entire userspace plumbing is now proven-good; the wall is the modem itself refusing RF.

SLPI / SAR — the suspected gate (Casey's thesis), what we proved

  • SLPI crash-loops sar.cc:27:chre_utils fatal every ~10s (crash #15+). SAR = Semtech SX932x, the modem's RF SAR/TX-power sensor.
  • It is a CHRE nanoapp crash INSIDE the SLPI firmware at init, and qrtr-lookup is empty — the SLPI never exposes an SNS QMI service before it dies. So an AP-side QMI "SAR consumer" has nothing to attach to; the producer fatals first. A downstream subscribe-consumer is NOT the missing piece.
  • The real missing piece is whatever feeds the SLPI its sensor registry / CHRE settings at init: the SAR registry data (sx932x_0.sar, .config, _platform.*) IS present at /persist/sensors/registry/registry/ but is NOT delivered to the SLPI (its RFS dir /persist/rfs/msm/slpi is empty; tqftpserv serves only /persist/rfs, not /sensors).
  • Android's consumer stack (proprietary, NO open-source equivalent): chre_daemon_msm + sensors.qti + sscrpcd (in android-reference/vendor-stack-.../vendor/bin/), plus the registry under android-reference/persist-sensors-20260625/. Our drivers/soc/qcom/qcom_ssc_sensors.c is a passive ABI shim — it does NOT deliver the registry or run CHRE, so it cannot prevent the firmware crash. This is the "shim lacking components LineageOS has" Casey referenced.

NEXT ANGLE (Casey's call — start here next session)

  1. DECISIVE CHEAP TEST FIRST — is SAR actually the modem gate? Stop the SLPI remoteproc so it is NOT crash-looping (echo stop > /sys/class/remoteproc/remoteproc2/state), then let MM try to enable the modem (or qmicli --dms-set-operating-mode=online once). If still 52 → SLPI/SAR is NOT the modem gate (independent symptoms) → stop chasing SAR for telephony and look elsewhere for the 52. If it goes online → the SLPI churn was the blocker and the SAR/CHRE consumer becomes critical. Run this before building anything.
  2. If SAR IS the gate: the buildable "consumer" is a registry/CHRE provider that feeds the SLPI at boot (reverse the SNS/CHRE QMI registry protocol; or port pieces; or boot SLPI on-demand AFTER preparing its env, like LineageOS sensors_ssc.c does — it does NOT auto-boot the SLPI raw). Investigate chre_daemon_msm / sscrpcd behaviour + how the registry reaches the SLPI on Android (RFS path? SNS QMI? direct?).
  3. Independent of SAR: chase why the modem boots OFFLINE/model-0 (vs Android's LOW_POWER). model 0 = NV/identity not fully loaded — possible EFS/NV or firmware-init issue.
  4. Fallback: downstream-kernel hybrid (Droidian/Halium) — the realistic telephony path if mainline SLPI/CHRE proves intractable.

SAR consumer — EXACT mechanism (from slpi.mbn strings) + the build blocker

The SLPI firmware reveals precisely what the consumer must be:

  • Reads registry from /persist/sensors/registry/registry (+ sns_reg_config, sns_reg_version).
  • Sending registry request for group name:%s / SNS_REG_INIT / Decoding registry group %s / Error decoding registry event → the SLPI requests registry groups over the SNS QMI service and expects encoded registry-event responses. The SAR (sx932x) nanoapp fatals (sar.cc:27 chre_utils fatal) when it can't get its group → crash-loop.
  • Fallback strings exist: Registry disabled, leaving default mode enabled / LPI Registry Disabled — there may be a registry-disabled mode worth forcing (SAR likely still needs its device config though).
  • The consumer = an SNS registry provider (QRTR/QMI daemon) that reads /persist/sensors/registry/registry/<group> and answers the SLPI's requests. On Android this is sensors.qti + sscrpcd + chre_daemon_msm (binary-only in android-reference/vendor-stack-20260621/vendor/bin/).
  • BUILD BLOCKER (why it's not done): the SNS protocol is Qualcomm proprietary protobuf (sns_client.proto / sns_registry); there are NO .proto defs or SNS source anywhere in the tree — only the registry DATA and the binary daemons. A working provider needs that encoding; building blind yields non-functional code. Options: (a) extract/RE the SNS protobuf from the vendor .so (libsnsapi/sensors.qti) — multi-day; (b) find an open SNS impl to port; (c) run the vendor sscrpcd/sensors.qti binaries under a libhybris/Halium shim; (d) downstream-kernel hybrid (sidesteps mainline SLPI entirely).

CLEAN decisive test to run next boot (settles whether SAR even gates the modem — do BEFORE any SNS build)

The session test was contaminated (modem already poisoned by prior online attempts). Clean version: systemctl mask ModemManager qcril-prov qcril-autoselect blueline-dpm-open-port → reboot → on boot immediately echo stop > /sys/class/remoteproc/remoteproc2/state (kill SLPI before it crash-loops) → wait for modem QMI up → ONE qmicli -p -d qrtr://0 --dms-set-operating-mode=online.

  • Still 52 with SLPI cleanly stopped from boot → SLPI/SAR is NOT the modem gate (the 52 / model-0 is independent — chase modem NV/EFS/firmware-init instead; don't invest in the SNS build).
  • Goes online → SLPI/SAR churn WAS the blocker → the SNS registry provider is worth the build. Unmask afterwards: systemctl unmask ModemManager qcril-prov qcril-autoselect blueline-dpm-open-port.

Files touched this session (for the next agent)

  • references/linux-7.1.1-sdm845-CANONICAL @ 980c4ae1f (revert skip); tools/verify-fixes.sh (skip-absent check)
  • pmaports device/testing/device-google-blueline/: qcril-prov.py, modem-qrtr-ready.sh+.service, blueline-uim-select.sh, uim-wait.conf, blueline-dpm-open-port.service, qcril-prov.service, APKBUILD, 80-device-google-blueline.preset. All also deployed live to the phone.
  • NOTE: APKBUILD sha512sums NOT yet regenerated for the new/changed sources — run pmbootstrap checksum device-google-blueline before the next package build.

THE session that reframed the whole problem. The modem-online blocker is not (only) a kernel/secure-load issue — it is that the userspace consumer chain that drives the modem is half-built / failing, exactly as Casey kept saying. Everything below is VERIFIED LIVE on the reverted-baseline 7.1.1 build, not inference.

TL;DR causal chain (verified end to end)

  1. Modem firmware boots, MBA loads MPSS, modem reaches running. It has identity (IMEI 990012001581294, ESN 8074BB5D, MEID, SW g845-00194-210812). The assign/-22/secure-load is NOT the modem blocker — the modem loads. (The only -22s are fastrpc + ath10k wifi, both tolerated.) One XPU violation at boot, self-recovers (Casey was right).
  2. BUT the modem comes up op-mode OFFLINE (3), model 0 = up-but-unprovisioned/not-ready. --dms-set-operating-mode=onlineQMI 52 DeviceNotReady. Forcing online does nothing.
  3. SLPI crash-loops every ~10s: err_qdi.c:456:EF:sensor_process:1:sar.cc:27:chre_utils fatal → recover → up → crash. SAR (Semtech SX932x) is the modem's RF SAR/TX-power sensor.
  4. The modem+SLPI instability makes the QRTR node flap, so the boot-time consumer services race an unready modem and fail:
    • msm-modem-uim-selection.servicenode with id 0 not found in QRTR bus (boot) / Transaction timed out (later). This is a hard Requires= of ModemManager.
    • blueline-dpm-open-port.servicenode with id 0 not found at boot (succeeds if re-run later).
    • qcril-prov.service → crashed on a code bug (now FIXED, see below).
  5. Because msm-modem-uim-selection (hard dep) failed → ModemManager never started at boot. So nothing ever drove the modem: enable → set-power → register. It just sits OFFLINE.
  6. Started MM manually (bypassing deps): it DOES reach the modem over qrtr://0, creates modem0, reads the SIM ATR — then fails: couldn't start parent initialization: Couldn't get primary port and couldn't load carrier config: Operation timed out. MM can't initialize a modem that's OFFLINE/flapping.

What is SOLID now (do not redo)

  • On reverted-baseline 7.1.1 (blueline branch, mem-protect skip reverted 980c4ae1f). Built, flashed boot_a, deployed. verify-fixes.sh green (13 checks; mem-protect check now asserts the skip is ABSENT). Android slot B / EFS / userdata untouched.
  • pd-mapper: in-kernel CONFIG_QCOM_PD_MAPPER=m, module loaded … pd-mapper is fine — ruled out. WRONG (corrected 2026-06-29). The pmaports config sets =m, but the running kernel has # CONFIG_QCOM_PD_MAPPER is not set — the in-tree arch/arm64/configs/sdm845.config carries the upstream =n and merge_config.sh -m applies it after the pmaports config, so =n wins every build. Userspace pd-mapper is also uninstalled ⇒ no pd-mapper of either kind runs. Caught via the wifi outage (no wlan0; ath10k_snoc bound but probe defers with no wlan PD). The Mobian 0171 observation stands, but "pd-mapper is fine" did not. Fixed: in-tree fragment → =m (commit pending), then rebuild+reflash. Whether this also unblocks the modem-52 is now an open re-test, not a closed lead.
  • Infra present & correct: rmtfs -P -s (block EFS), tqftpserv (serves /persist/rfs → modem mcfg), sensor registry 177 files at /mnt/vendor/persist/sensors/registry/registry/, all mcfg carrier configs incl mcfg_sw/generic/NA/Rogers/Commercial/CA/mcfg_sw.mbn.
  • udev tags MM correctly: ID_MM_QCOM_SOC=1, ID_MM_CANDIDATE=1 on rmnet_ipa0; MM qcom-soc plugin loads. Transport (QMI-over-QRTR) works — MM exchanges QMI with the modem.

FIXED this session — qcril-prov.py (the MBN provisioner)

Source: pmaports/device/testing/device-google-blueline/qcril-prov.py (also live-deployed to /usr/bin/qcril-prov.py on phone). Was crashing; now runs end-to-end and provisions Rogers. Bugs fixed (all real, in code):

  1. list_configs read configs from the response (out.get_configs() — doesn't exist; MessagePdcListConfigsOutput only has get_result). The config list arrives via the list-configs INDICATIONIndicationPdcListConfigsOutput.get_configs(); each element has .id + .config_type only. Reworked to indication-driven (mirrors the working load-config).
  2. GI array-length args: set_id(cfg_id, len)set_id(cfg_id); set_type_with_id_v2(t,id,len)set_type_with_id_v2(t,id). (set_config_chunk was already correct.)
  3. Strategy: blueline INHERITS Android's full mcfg set (1 platform + 25 software) on shared persist. Don't delete+reload (delete is a no-op; reload → error 41 "already exists"). Instead: compute target id = sha1 of the .mbn file (== the PDC config id, verified: HW sha1 6fe571a9… matched the on-modem id), confirm present, select + activate by sha1. Treat load error_code=41 as success. Helpers added: _sha1_id, _hex, _id_present. Result: provisioner selects+activates HW (platform) and Rogers (software) configs cleanly. The modem still ends OFFLINE afterward (carrier config alone doesn't lift it — needs MM to drive it).

THE PLAN (next session — ordered)

Goal: get ModemManager to fully initialize the modem and drive OFFLINE → online → registered. Two intertwined blockers: (A) modem won't initialize while OFFLINE/flapping; (B) SLPI crash-loop.

  1. Make MM actually manage the modem (primary blocker).

    • Why Couldn't get primary port? Check MM qcom-soc plugin port expectations for a pure-QRTR SDM845 modem; check nothing else holds the QMI client (our qcril-* one-shots, the stray dbus-monitor, manual qmicli -p). Try MM with everything else stopped.
    • Fix the boot ORDERING/retry so msm-modem-uim-selection + blueline-dpm-open-port + qcril-prov don't race the unready modem: add wait-for-QRTR-node / Restart=on-failure / proper After= on a "modem-qrtr-up" condition. This is how the chain stays up like Lineage (Lineage's RIL waits for the modem to be ready before UIM select / PDC).
    • Once MM inits: it should set-power-up and the OFFLINE→online may resolve via MM's sequence (it owns DMS set-operating-mode). The manual online=52 is because nothing did the proper enable sequence first.
  2. SLPI SAR crash-loop (sar.cc:27). SAR = SX932x, governs RF TX-power; Casey's position is it gates modem RF. The SLPI sensor REGISTRY (/persist/sensors/registry) is NOT served to the SLPI — tqftpserv serves /persist/rfs only (sibling tree). Leads:

    • Does the SLPI read registry via tqftpserv (needs /sensors served too) or a different path? The tqftp log shows only modem mcfg requests, no /sensors requests → SAR may crash BEFORE requesting registry (early init / SX932x i2c on the SLPI bus). Investigate how LineageOS sensors_ssc.c + sensor HAL bring SAR up (registry delivery + SX932x i2c/GPIO).
    • Our qcom_ssc_sensors.c is a passive ABI shim; it does NOT boot or feed the SLPI. Mainline auto-boots the SLPI (DTS firmware-name) with no consumer → SAR talks to nobody → crash. LineageOS boots SLPI ON DEMAND after the sensor HAL is up.
  3. Verify on a clean boot: fix the services so the whole chain comes up at boot (not manually), reboot, confirm MM auto-starts, modem → registered. One online attempt per boot still applies.

Hard constraints (unchanged)

  • pmOS = slot A; flash ONLY boot_a. NEVER touch Android slot B / EFS (modemst1/2,fsg,fsc) / userdata / TZ-XBL-ABL. rmtfs -P -s. No pmic@4/5 SPMI nodes. One online attempt per boot. sudo pw 147147. USB 172.16.42.1 (host iface enp0s20f0u6 → ip addr add 172.16.42.10/24).
  • pmOS is Alpine/ash: embedded sh -c "...(...)..." with parens fails — use script files via scp.