linux-phone-porting · git:20260918.b4254d1 · 2026-09-18 · sha256 ffb39de8edc881c7

linux-phone-porting git:20260918.b4254d1A

Immutable. This exact content is served forever at /api/v1/blob/ffb39de8edc881c7.

---
name: linux-phone-porting
description: Use for every hardware bring-up or debug session that ports mainline Linux to a phone or tablet. Retail-unlock targets require an already-unlocked bootloader; locked retail bootloaders are out of scope. Exploit-booted and firmware-booted targets require a demonstrated boot path; the skill never provides unlock or exploit steps.
---

# Linux Phone Porting

A debug iteration costs a build + flash + boot (5-10 min); blind fixes get refuted more often than they land. Follow this order strictly.

Scope: mainline Linux bring-up on phones and tablets plus integration of the selected OS userspace — a method, not certification of any OS/device combination. The userspace need not be GNU/Linux; non-Linux kernels are out of scope.

## 0. Set up the port

Once, before the first flash. The stock system is the highest-authority research source for this board, and parts stop being readable once overwritten.

**Establish the target** (every later answer depends on it):

- **Exact device identity** — marketing name, codename, SoC, regional/storage variant. Sibling variants of one name differ in panel, touch, modem; a fix researched against the wrong variant looks plausible and fails. Record sibling codenames — phase 2 enumerates their ports.
- **Engineering stage** — EVT/DVT/PVT, board identity, carrier/SKU, bootloader product + build fingerprints. Reconcile before choosing images: one presumed retail handset was an EVT board for a different product while Android reported `user/release-keys`. Build labels do not establish production hardware.
- **Target OS/platform and integration contract** — chosen release, required kernel lineage, startup/service manager, hardware-service interfaces, build/image/update model, evidence/control tools; verify against the target project's current docs. Linux-based ≠ mainline-compatible; a working Linux driver ≠ target-framework or application support.
- **Target user interface/session** — ask when unspecified; never assume. Prefer the platform's supported interface/session stack over replacing it during bring-up.
- **Boot-model class** — classify before anything else about boot; the class selects the write gate, the backup route and the board-authority artefact (boot-image DTB / ACPI tables / DT inside the vendor kernel image):
  - **Retail-unlock** — OEM unlock, boot images, A/B slots. **Unlocked bootloader only** — no unlocking steps; point at the OEM's instructions and resume when unlocked.
  - **Exploit-booted** — bootROM-vulnerable devices; no unlock state exists. Gate: a payload boot path demonstrated on this device; the payload environment is a candidate control channel and recovery path — prove both. No exploit-landing steps; point at the payload project's own instructions.
  - **Firmware-boot** — UEFI or equivalent; lock state is often irrelevant to booting external media, but record it where the chain has one — some UEFI-lineage chains gate writes through their own unlock state and service tooling; where it gates writes, apply the retail-unlock gate to it. Gate: a demonstrated boot path. The board description may be ACPI tables, not a DTB.
- **Boot topology** — A/B or not, slot layout. Decode slot state from the on-disk structure, not documented layouts (one SoC kept it in GPT partition-entry attribute bits, not the documented control partition). After a crash-loop, refill the tries-remaining budget before interpreting the next failure.
- **Recovery** — custom recovery installed? Convenience, not requirement.

Vendor A/B bank names do not establish seamless-update/slot/fallback semantics — establish the implementation before manipulating banks. Empty security properties = unknown, not unlocked. Same-SoC trees/firmware/signed programmers are candidates, not certified recovery paths; an unreleased prototype's only installed system may have no compatible replacement — preserve it, restorable backup before destructive experiments.

**Back up every readable partition except userdata** — a class that exposes no partitions is backed up through its demonstrated route (payload environment, preboot environment, full-storage image) with coverage recorded as it is:

- Userdata excluded: bulk, private — leave a DO-NOT-RESTORE note in the backup. Exclusion does not authorize its loss; before any operation that may wipe it, explain consequences + preservation options and obtain separate informed authorization.
- **Device-unique partitions — back up, never publish.** Modem NV/EFS, persist, calibration siblings carry IMEI, radio calibration, sensor trim; unreproducible; corruption leaves a phone that cannot register. Critical AND private.
- **Private calibration stays in a protected runtime path.** Validate provenance, length, integrity checks before use. Values must not enter source, DT, build inputs/outputs, arguments, logs or public reproductions (one audio integration kept them out of the build store and process args; a lens integration read them at runtime, not compiled in). Missing/invalid calibration blocks the operation — never borrow another handset's values or guess.
- Hash every image; record hashes beside the partition table. Only a hash answers "is the device still what I backed up."
- Verify restorable before the first risky flash, not after.
- **EDL is the fallback backup route when boot-based channels cannot read — prove the channel, don't assume it.** A signed firehose programmer (OEM EDL package or custom-ROM bundle) grants raw storage; its rawprogram XML doubles as an independent partition-map cross-check. Programmers are per-variant; a mismatched one can configure cleanly and still not stream.
- **Bulk dump loops abort at the first zero-byte or short read.** A run whose partition-table read itself returned zero bytes still recorded ~50 void per-partition failures. Probe the smallest read (GPT) first; diagnose the loader/programmer instead of iterating a void list.

**Research value of the backup:**

- **Stock board description — boot-image DTB, ACPI tables, or the DT carried inside the vendor kernel image, per boot-model class** — board-specific configuration authority over a sibling SoC's dtsi once the variant/overlay is established; not proof of fitted silicon, physical wiring or measured voltages. Preserve conflicts until resolved.
- **Firmware blobs + load order**, **vendor kernel cmdline + boot-image layout where the class has one** (offsets, header version, args), **exact kernel version string** (`uname -a` / `/proc/version` — selects the right GPL OEM release in phase 2; vendors ship several, they differ), **vendor configs** (sensor/modem/HAL interfaces mainline must satisfy).

**Check storage health before trusting device reports:**

- Record the wear/lifetime report in phase 0 (UFS life-time %, eMMC health register). Storage is the classic misdiagnosis: I/O errors read as a dying drive; the wear % proved it healthy and one more stock-ROM flash cleared the fault. The % separates "hardware dying" from "software state bad" — opposite next steps.
- A media-failure verdict that would retire the device must survive **re-probe in a later session**: one flash region refused writes twice (cold power-off included), then passed 3/3 the next day against a full zero-error read scan. Absent wear fields = unknown; coarse values exclude nothing.

**Inventory + harvest while the stock system is present:**

- **Inventory every component the stock system names** — panel, touch, sensors, cameras, modem + RF config, WLAN/BT, charger + fuel gauge, audio — and compare against this variant's official spec sheets: `find-docs` for component docs, `wigolo` for web-found material (its cache matters across sessions); else plain web search. Sibling variants differ exactly here.
- **Evidence level** — declared / enumerated / driver-bound / exercised, no implied progression. A HAL name can reflect software ancestry, not silicon; installed firmware may cover unfitted components. One camera advertised 1080p30 in USB descriptors while Android exposed at most 1024×768 and 27 fps; no frames captured, so throughput was never measured.
- **Readable retail beats a ROM image.** On Android that is root: `getprop`, `/proc/config.gz`, mounted vendor/odm trees, HAL/sensor configs, calibration artefacts, factory field-test modes. Other retail OSes have their own readable surfaces — enumerate what this one exposes and harvest it through them. Privilege steps are the user's — no rooting or jailbreaking steps; resume when access exists.
- **Record the newest community custom OS** (on Android: custom ROM) as an optional development source: the most responsive OS ever run on one device was an unofficial recent-Android build, and its boot image shares the stock downstream lineage, so its DTB cross-checks the phase 0 extraction. A bundled EDL package also carries the signed programmer + rawprogram map.

**Keep a persistent component–wiring–document inventory**, seeded from stock, extended in phase 2, in the project's existing format. One research pack linked 134 component/subsystem records to 47 document records — not 134 fitted chips. Per component record:

| Field       | Content                                                                                                                                                                             |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Identity    | Stable ID, function, manufacturer, exact/partial part; distinguish silicon, package suffix, module, flex/board marking.                                                             |
| Evidence    | Source location + applicability; measured chip ID, stock/schematic declaration, external photo, alternative, inference, unknown. Preserve contradictions + what would resolve them. |
| Connections | Bus/address, endpoints, muxes, clocks, supplies, reset/IRQ; claim → evidence link; unresolved routes marked.                                                                        |
| Documents   | Linked document IDs, pages/sections, questions answered, gaps.                                                                                                                      |
| Port status | Driver/binding/firmware dependencies; separate declared/enumerated/driver-bound/exercised results. Identity ≠ functional qualification.                                             |

Integrated functions are not separate packages; alternative populations are not extra fitted chips; a driver-family name need not equal the physical part (one controller shipped both 720×1280 and 720×1440 panel programs — the name alone did not select initialization). Keep unknowns unknown; an inventory is not permission to invent a BOM or blindly probe buses.

**Working conventions.** Resolve the device project root first. Multi-device reference layout (not a restructuring order):

```text
kernel/<version>/     Clean upstream source + generic builder
soc/<vendor-soc>/     SoC-common patches, configuration, packages
os/<platform>/        Device-neutral OS integration and tools
devices/<model>/      Device assembly, patches, calibration, state
```

Shared bases never import device policy; device facts, private captures, per-device agent context stay with the device. Mark a shared kernel tree used for experiments as patched — never pristine upstream.

- **Project layout.** Everything large/private/device-derived lives under gitignored `artifacts/` at the device project root: `private/` (device-unique; partition backup first), `firmware-harvest/` (blobs pending redaction), `android/` (stock-ROM packages, rooted captures), `debug-evidence/` (irreplaceable captures, preregistrations), `reference/` (reading copies). Publishable firmware → sibling `firmware-publishable/` repo with its own history, derived only after redacting device-unique identifiers. Device-local `logs/`: one subdir per boot/deploy + `LATEST-*` links. An `artifacts/` README maps paths to class with hashes, acquisition, coverage — provenance, not a substitute for integrity checks.
- **Control channels.** Before any flash, prove reachability through the target's available control/recovery tools + at least one post-boot channel into target Linux. `adb`, fastboot, USB-gadget Ethernet, serial/ACM, ssh are examples where supported, not individually mandatory. Record working channels, selectors, supported operations — a channel assumed but never tested is discovered missing mid-wedge. Missing a tool does not waive operation-level proof or recovery prerequisites.
- **Bind every operation to the target.** Record handset↔transport mapping; select the transport explicitly per command; re-establish after re-enumeration; verify product + slot immediately before writes. A second connected phone must never become an implicit target. The binding constrains the host too: a probe must never mutate host network config — one absent gadget MAC made a control script rewrite an unrelated USB Ethernet dock's IP settings. Inspect interface identity + route first; refuse ambiguous matches; host network setup is a separate explicit command on an owned, uniquely verified interface.
- **Prove the operation, not just the connection.** Record whether required operations are actually supported before building around them: one unlocked bootloader supported neither RAM boot nor partition fetch, stranding a built rescue image. Deeper: a firehose that connects, configures and advertises the full `read` set still truncated every streamed read to 16 bytes of a 17,408-byte request — stock tool and two custom drivers alike. Advertised commands ≠ working data path; self-test-read a known-content region and verify bytes before scripting bulk transfer.
- Note which recovery path is known to work.

Write into the project's agent context files: all work touching this device goes through this ruleset; no device action outside it. Existing backup? Confirm coverage + hashes still verify.

**Keep the retail OS bootable for data gathering** (Android in the founding port; same principle wherever the class retains one). Read a working vendor driver's probe/firmware sequence live next to a mainline failure; exercise hardware mainline cannot yet drive. Never conclude mainline behaviour from retail-OS behaviour without a cross-check.

## 1. Gather evidence from the live device first

Capture the failing run's full dmesg before changing anything. Enable debug knobs before reproducing — a second reproduction costs another boot. Keep them relevant and understood: do not arm unassessed probes indiscriminately, and never increase physical stress to compensate for unverified protection telemetry.

Evidence interpretation is kernel work: invoke `linux-kernel-development` if installed, else `linux-kernel-crash-debug`, else a subsystem kernel skill; else reason from sources + `Documentation/`. Do not stall.

### Acquisition and claim boundaries

Per artifact record: origin, exact revision/build, acquisition method, coverage, access failures, integrity hashes. A running sysfs DT may include runtime fixups — not an untouched boot-image DTB (one capture: 2,740 property files preserved, 2,222 verified byte-identical, 518 redundant names accounted; all 2,839 manifest files passed — still not a restorable backup). A firmware path inventory does not establish load order.

Photographic identification: bind each observation to a named image/page/crop AND the photographed sample — not output order, not the target handset by assumption. One parallel-image association and one presumed manufacturer marking were withdrawn after original-byte inspection. Preserve uncertain transcriptions; public regional samples do not prove this handset's population.

Carry phase 0 evidence labels into findings. Record running vs deployed identities separately; service readiness ≠ end-to-end behaviour. Registered sound card + silent playback ≠ acoustic qualification; mark untested checks untested.

### Evidence channels, roughly by reliability

- **Live shell** (network/USB) — cheapest; anything the device survives.
- **Bootable retail OS** (Android where retained) — boot deliberately when a question is best answered there; cross-check before transferring conclusions.
- **pstore.** Identify the active crash-record collector, its config and archive destination. Inspect the mounted kernel interface (`/sys/fs/pstore/` where used) and any collector archive — when `systemd-pstore.service` has collected records its configured archive must be harvested too (one system moved everything to `/var/lib/systemd/pstore/` early in boot). Merely running systemd proves nothing about collection; an empty kernel directory alone proves no absence of a crash. Console region is usually a single slot — copy to host first or the next crash overwrites it.
- **ramoops.** Lossy: DRAM charge retention decays unpowered — measured ~6.5 % of bits on one device, enough to fail ECC on zone headers and lose records — and warm reboots rot it too (tens of blocks). Decay follows the power cycle. Grep fuzzily. Let a wedged device log while the console zone holds, then power-cycle and back promptly; a 4 MiB zone absorbs hours of watchdog spam first — check zone size.
- **Kernel ACM console** (`CONFIG_U_SERIAL_CONSOLE=y` + `console=ttyGS0`) — the only channel that survives a wedge, but verify it survives the reporting context: payload is emitted inside `panic()` with IRQs off and other CPUs stopped; a workqueue-deferred console never prints. Test free with a crash injector (LKDTM `/sys/kernel/debug/provoke-crash/DIRECT` where present): synthetic lockup, exact geometry, no flash. A userspace getty yields zero bytes while still enumerating. Both config halves required.

### Rules that turn inference into measurement

- **Confirm what is flashed by reading back** — never notes or build logs. `iflag=direct` so the read doesn't perturb. Size-only comparisons prove nothing (boot images are padded). Hash the payload.
- **Reading a debug interface can manufacture the symptom.** Sampling DRM/GPU crash-state nodes during active submission synthesised fault/recovery lines that never occurred (five reads, five fault/recover pairs; none unobserved). Check whether your own observation produced the log line.
- **Validate stimulus + observation path before interpreting silence.** Confirm the actual register protocol, effective post-init state, physical receiver route and required clocks throughout sampling. The claim that unchanged camera TX-on/off status bytes proved a static bank is **withdrawn**: the TX-off control used the wrong protocol, stream startup rewrote the setting, and earlier silence measurements targeted the wrong PHY. Unchanged bytes prove neither latch nor transmitter failure; a sensor/top-level runtime reference does not retain per-PHY clocks through teardown.
- **Capture a rejection's raw response before interpreting the mapped errno.** A firmware `EINVAL` was ambiguous — wrong encoding or refused policy — until dynamic debug exposed well-formed words carrying explicit refusal. Opposite verdicts, opposite next steps. Classify every rejection: encoding / policy / not-found. Mirror: a clean negative is positive proof — a not-found from a correct lookup validates the encoding and kills a plan branch without touching the loader.
- **A measurement racing an uncontrolled condition returns a plausible void number.** One benchmark scored 55 in two seconds — the compositor held DRM master, every scene failed. The valid 132 → 485 comparison needed the compositor stopped for both runs. Name the voiding condition; verify it held every run.
- **Control ownership through the whole measurement.** One camera reopen failed EPIPE after another client changed the media graph; configure + capture under the clients' compatible lock passed a 90-frame burst without stopping audio. Establish which clients cooperate. For power cleanup, identify open owners before calling an active supply a leak: last-owner close/open/close measured votes 0→1→0; a restored camera monitor legitimately reopened the device.
- **Measure controls on linear data, not enhanced previews.** Fixed region + processing scale, account for black level and clipping, bracket with baselines for scene drift. Exposure doubling measured 1.990×/1.995× signal; 2×/4× gain 1.984×/3.957×; baseline drift <0.23%. Accepted writes and auto-contrasted images are not proof. Physical-unit calibration, per-frame delay, autofocus, color remain separate qualifications.
- **Prove protection before increasing stress.** Enable bits + successful init ≠ limiter intervention. Establish telemetry identity, units, update behavior, failure handling before using it to justify higher output. All five retained candidate temperature values in one investigation were zero; the OEM getter ignored the return code. Never heat/drive harder to compensate for missing observability. Keep output restricted when protection is unverified — one speaker path muted output and revoked access on provisioning loss rather than falling back unprotected. A read-only debug interface is not automatically harmless: an observer-enabled boot locked up, causation unresolved.
- **Record rules you withdraw.** Folklore goes stale with hardware revisions. Keep the overturned version next to the current one with what changed.

### Host-side gotchas

- `timeout N ssh` kills the local client only; the remote process keeps running — kill it explicitly.
- `pgrep -f`/`pkill -f` over that ssh path self-match their own remote shell — filter the matcher or match by PID.
- `find` needs `-L` when the start path is a symlink, or it returns silently empty.
- Any wrapper that rewrites output — token filters, pagers, formatters — corrupts evidence, sometimes inverting a test. Use the raw command for findings.

### Operator hands are a step, not an assumption

- **Authorization is scoped; pauses are binding.** Record the permitted action, target, limits, pause/resume prerequisite. Inspect ≠ restart-service ≠ capture ≠ actuate ≠ reboot. A paused experiment stays paused until its prerequisite is satisfied and required operator authorization confirmed; only explicitly permitted remaining work may continue. Reachable transport and an unlimited iteration budget do not expand permission (one lens session allowed same-position lifecycle checks while optical movement stayed deferred). A higher-output exception expires with its named test, not tomorrow's default.
- **Physical actions**: name the exact action + direction, then verify via an observable signal — appearance (mount, re-enum, slot change) or disappearance (power-off, USB drop, silence). Disappearance is ambiguous: watch the drop as an event, or require done-confirmation + verified post-absence probe failure. Poll briefly; no signal within a bound ⇒ re-ask.
- **Total channel loss after suspend/resume has three branches, not two.** Host-side, live-phone-with-dead-driver is indistinguishable from hung/dead — one "suspended and never came back" was wrong: suspend/resume worked 10.5 more hours; only the USB gadget failed (`HS-PHY not in L2`). After a replug, read the device's journal before declaring a hang.

## 2. Research before implementing — all of these sources

Research precedes implementation. Input: a failure → phase 1 evidence (capturing first; researching an unread symptom is guessing with citations). A new capability → phase 0 artefacts (stock board description, vendor configs, firmware layout) are the device data, as authoritative as a crash log.

Before writing anything:

1. **Reason about the subsystem** — identify the driver/binding/firmware interface owning the failure (or capability). Invoke `linux-kernel-development` if installed, else `linux-kernel-crash-debug`, else a subsystem skill; else reason directly. Invoke `find-docs` for kernel-side docs — binding + dt-schema, Kconfig, subsystem docs — for the **exact kernel version being built**; else read `Documentation/`. A recalled binding constraint or Kconfig gate is a guess; stale bindings are what phase 3's battery catches late.
   **Graft navigation optional** when installed + indexed: locate implementation, trace dependencies across layers; verify decisive details against the exact revision. An index miss is not proof of absence — check coverage, then direct search/LSP/reads. Graft navigates; it is not an evidence source.
2. **Look up the target platform, userspace libs and tools** — current docs, not recalled APIs. Consult the selected OS release's docs, implementation, hardware adaptations, contribution rules, plus relevant projects discovered during research (`find-docs` per item; else the project's docs over the web). The named families below are research destinations, not a compatibility list or exhaustive set.
3. **Web research across every source family** (parallel subagents work well). Invoke `wigolo` for the sweep (cache matters); else plain web search. All of these, in order:
   - **SoC vendor's mainline collaboration project** (e.g. Qualcomm's — search the vendor name + "mainline"; don't assume a host). Mainline-first vendor work: updated dtsi, bindings, enablement, tooling, often ahead. Check before authoring a missing node/driver yourself — the vendor may have landed it; its trees show intended mainline driving, the most authoritative template.
   - **LKML archives (lore.kernel.org) + Patchwork** — the kernel's own patch review: threads carry rationale, hardware context and register detail that commit messages drop, show reworks and maintainer objections, and distinguish merged, in-flight, reworked or rejected — resolve a fix's state there before porting or re-deriving it, and check it landed in the lineage being built, else it is a backport candidate, not an existing fix. Subsystem trees and linux-next show what the next release carries. With the optional `lei` CLI installed, query the same archive locally — diff prefixes (`dfn:` filename, `dfhh:` hunk header) reach the threads touching an exact driver file or function, and saved searches (`lei q` / `lei up`) follow a subsystem while the port iterates; without it, the lore web interface is the fallback.
   - **GPL-published OEM kernel.** An exact release for this device usually exists: OEM open-source portal (Samsung, Sony, Xiaomi, Fairphone, OnePlus/OPPO, Motorola), GitHub/GitLab mirrors, XDA archives when portals die. Select the tarball matching phase 0's `/proc/version` — they differ. Compliance varies: treat the tree as an oracle to mine (board dts + defconfig; out-of-tree touch/panel/charger/modem drivers with register sequences and firmware handshakes), not something that builds. No exact release? A sibling's usually carries the SoC dtsi — but better is **the stock DTB pulled off the device** (`dd` untouched boot slot, scan `d00dfeed`, `dtc -I dtb -O dts`): ground truth for this board, has corrected wrong sibling-SoC guesses including a SMMU stream ID.
   - **AOSP / Android platform source** — android.googlesource.com, source.android.com: HAL interfaces, framework/services, init + VINTF, SELinux policy, boot-image tooling, native libraries. Record exact branch/tag. Complements, not replaces, the OEM source + stock DTB + vendor implementation.
   - **Apple-platform mainline enablement** — Asahi Linux, `m1n1` and its device forks, the checkm8/pongoOS payload ecosystems, and the lineage that booted Linux on iBoot-class phones (Project Sandcastle): how the payload chain brings up hardware, where the platform's DT lives, storage access, and what the kernel work assumed. On exploit-booted targets this is usually the only precedent.
   - **Windows-on-ARM / UEFI ARM enablement** — vendor and Linaro mainline work for compute-platform tablets and laptops, ACPI enablement, EFI-stub bring-up: on firmware-boot targets the same silicon is often enabled here first.
   - **Windows-Phone-lineage ports** — the conversion and unlock tooling ecosystems around that boot chain (service programmers, bootloader-unlock services, W10M-to-Android conversion guides) and their postmarketOS device packages: usually the only precedent for the loader handoff, DT delivery and modem bring-up on those boards.
   - **postmarketOS** — pmaports device packages + APKBUILDs, merge requests, wiki device pages, SoC-mainline tree.
   - **Halium / UBports** — `halium/android_device_*`, `hybris-boot` configs, UBports ports. Best source for how vendor firmware expects to be driven: blob paths + load order, properties + sockets, sensor/modem HAL configs. Firmware handshake failing? Usually written down here.
   - **Mobian** — the Debian device repos.
   - **NixOS** — mobile-nixos patterns, nixpkgs packaging.
   - **General-purpose and desktop distributions** — inspect ARM platform enablement, kernel patches/config, package sources, integration changes, bug/review threads — not only mobile editions. Follow fixes to their owning upstream project and exact revision; distro copies are not independent corroboration. Graphics/media/audio/power fixes may be architecture-independent — do not exclude a change because it was packaged or tested on x86. An ARM image alone proves neither this phone's SoC/GPU/firmware support nor boot-chain compatibility; establish applicability against actual interfaces before adoption.
4. **Map component documents to the inventory.** `find-docs` + `wigolo` as above. Search manufacturer documentation, OEM/ODM board schematics + service material, FCC exhibits, repair archives, **Scribd** original-document search. Use exact part/package/module suffixes, board identifiers, internal codenames — alongside retail names. Whole-device inventory covers every record; a symptom sweep refreshes affected records. Missing documents are gaps, not reasons to abandon source-derived implementation.

   Each document record: stable ID linked to components; original title/publisher; part/package + board applicability; revision; document kind (full datasheet/register map vs short brief vs clock calculator vs configuration sequence vs module drawing vs board schematic); relevant pages/sections; questions answered + limitations; URL/document ID; access actually obtained; local path, size/hash; acquisition failures. A C-PHY sequence is not a D-PHY mode; another vendor's module pinout is not the fitted module's wiring.

   Validate original cover/body over generated titles — Scribd titles misidentified phone schematics. Landing page, preview, readable text capture, full PDF = different acquisition states; a listed confidential filename is only a lead. Keep a deduplicated index, acquisition manifest, unresolved-download queue; reuse existing downloads. Two differently named board downloads were byte-identical — one source, not corroboration. Legitimate access only: gaps do not authorize account changes, purchases, uploads, bypass. Preserve third-party markings; keep collections local under ignored reference artifacts — share URLs/index, not the collection.

**Reconcile wiring before tuning a silent peripheral.** Compare selected stock DT/overlay, vendor config, actual schematic sheets, current implementation end-to-end — mux controls, supplies, clocks. Match physical controller addresses, not downstream index names: one audit found reversed rear/depth PHY routes despite successful probes; the camera mux served neither path. Correcting routes still yielded zero bytes — necessary, not sufficient; earlier wrong-receiver silence did not establish sensor failure.

Match board revision; inspect rendered wiring: one schematic's stale contents page named a different SoC; visual inspection corrected a pull-up assigned to the wrong GPIO. Functional reconstructions are not verified electrical schematics: DT supply references are not exact board nets; configured voltages are not measured voltages or safe bench settings; functional boxes need not be separate chips. Label inferred connections and unknown pinouts; neither stock declarations nor a plausible schematic erase conflicting evidence.

**Separate timing clocks from data clocks.** Trace each domain and its effective rate before another timing sweep. One receiver's settle timer already ran at 200 MHz while its separate data clock sat at 19.2 MHz; changing only the latter to a supported 320 MHz produced a complete 15,000,000-byte real-scene RAW frame. An internal generator had captured 2,400,000 bytes first — downstream path proven at that format, not physical receiver input. Measured settings for one configuration, not portable prescriptions.

**Verify what you carry against every downstream DT you hold.** Patches go stale: one port re-verified against the newest stock OTA DT, the original backup, a custom ROM tree — four confirmed, one CPU-capacities discrepancy no single source showed. Read the vendor flash script before trusting it: at least one ships slot-blind, hard-coding the wrong image for the active slot.

**Sweep sister devices.** Enumerate ports of the same family — sibling models under one marketing name, near-neighbours on the same board/SoC generation — in pmaports, Halium/UBports trees, XDA, the OEM tree's `dts/`. Diff board files (dts, defconfig, firmware blob lists) component-by-component against the phase 0 inventory: match = proven driver+firmware combination; divergence = where the family split, itself a hypothesis. A sister device is a lead, not a precedent — adopt only what the diff confirms against this variant's spec sheet + stock DTB.

**Search internal codenames and OEM/ODM archives.** An alternate-system image can reveal vendor implementation absent from retail docs. Preserve archive provenance; inspect board identity, config, firmware lineage. Archive contents are not evidence the handset runs the image, is compatible, or that flashing authorization/recovery exist.

### Security findings exposed by bring-up

- **A bulletin is not proof of a fix in your tree.** Compare advisory, fixing commits, upstream implementation, AOSP against the exact revision built; map source to executed binary. One FastRPC investigation reproduced an unchecked DSP-length path against a recent AOSP release despite an older bulletin describing the class. Similar mechanism + affected-chip list ≠ exact CVE identity; qualify until technical evidence or maintainer confirmation.
- **Firmware data crosses a trust boundary.** Trace raw length words, decoding, signedness, arithmetic, cursor advancement, copy bounds before blaming firmware/hardware. One listener crash combined an unchecked decoded length with a remaining-space calculation that narrowed and wrapped positive. Protocol desync never justifies unsafe parsing.
- **Make the shared defect reproducible upstream.** Reduce to the real parser + smallest portable reproducer, not a handset workaround. One gate exercised five lengths — captured value, remaining+1, `0x7fffffff`, `0x80000000`, `0xffffffff` — SIGSEGV before fix, containment after. Submit affected revisions, field evidence, reproduction prerequisites, minimal patch, before/after results to the owning project. Redact; follow its disclosure policy. `/port-upstream` owns destination research and the submission ledger. Submitted/accepted/merged/released are distinct states, each with evidence — a report or PR is not evidence consumers have the fix.
- **Thin results are the normal case.** Report "no applicable work found in the searched sources" with revisions, coverage, access failures — not "nobody published it." Derive from primary artefacts: the out-of-tree vendor driver already driving the peripheral, the stock DTB node, the vendor HAL config, the closest mainline driver as structural template. Read, derive, write the missing piece — DT node, quirk, small driver. Authoring is the ordinary terminal state of bring-up; phase 0's backup + one-variable experiments bound software risk, not physical-damage risk.
- **Empty sweep ≠ empty scan.** Empty sweep = no applicable result within recorded coverage; inaccessible sources are gaps, not negative evidence. Empty scan = void until the method found a known positive with verified coverage (file counts, segment lists, glob matches). Measured near-misses: a glob matched 9 of 27 firmware segments and nearly recorded a false "the firmware omits it"; a library scan became trustworthy only once it found a known chip id. Read success claims in context: one forum's "got it working" concerned provisioning, not root or alternative-OS bring-up.
- **Downstream identifiers are downstream's private numbering.** Never copy a vendor id/enum into a mainline node. One downstream thermal trip pointed at "sensor 5"; mainline sensor 5 read constant 0 and the real tracker was sensor 6 — settled by stimulus (heated the cluster, watched which zone moved). A copied mapping reads dead ⇒ suspect the numbering; settle by stimulus.

## 3. Implement only with a promising, evidence-backed hypothesis

- **An authorized reboot is yours** when a demonstrated channel exists (ssh, USB gadget, fastboot, target control tool): reboot within the permitted experiment and continue. Reachability does not override a pause or expand permission. Operator hands only for what no channel reaches: battery pull, key combo, moving media.
- **Define the physical-test envelope before output.** Before sound, actuator motion, charging changes or other physical output: record device-specific permitted limits, units, duration, stop conditions, final safe state. Apply and read back limits on the actual execution path; bypassed or unverified enforcement ⇒ do not use it. One audio test distinguished 15% desktop slider from 15% linear amplitude — the latter required separate approval. Factory lens endpoints are operational calibration bounds, not certified mechanical limits. No universal safe level. Stop at the bound; verify the agreed safe state and resource release — a host timeout alone does not stop a remote process.
- **Invoke `find-docs` for the selected build/image tooling before assembling** — kernel build system + config fragments, target image format and builder, update/install procedure; current docs, not recalled flags; else the tool's own docs. `mkbootimg`/`mkdtboimg` constraints apply to those formats; pmbootstrap/mobile-nixos/Nix flakes are platform examples, not required tooling. A wrongly recalled flag burns a full rebuild+flash cycle.
- **One variable per flash, falsifier before build.** State the hypothesis; write the falsifier — the smallest experiment that kills the plan either way — and build it first: one single module load returned the refusal that killed a 9.4k-line port premise. Order by cost per information, not plan order.
- **Prefer runtime tests over reflashes:** tar-pipe, unbind/rebind, `insmod` — seconds vs minutes. But probes leave state: failed init can leave the platform device registered (reboot per retry); a crashed probe can wedge the board. Stage inputs, verify hashes host-side, make it idempotent, expect one reboot per failed attempt. Staged inputs persist across reboots — re-verify after any kernel change. Never probe through the channel the probe rebinds — one unbind/rebind test severed its own USB control channel.
- **Minimize the successful experiment before making it permanent.** A working configuration does not prove every accumulated workaround necessary. Remove them one at a time under the same acceptance check: one camera still captured 15,000,000 bytes after removing a forced settle override, and again without experimental receiver power hacks. Replace necessary helpers with native initialization and resource ownership; verify reboot, idle/reopen, released owned votes without manual helpers. Retain recovery evidence; runtime success ≠ boot persistence ≠ camera-application readiness.
- **Device-tree-only changes are cheap** — build just the DTB when possible, then check the runtime surface: a clean-building DT can bind to nothing (one thermal driver registered one zone per sensor id and silently dropped a duplicate — the zone list, not the build log, was the verdict).
- **Never edit-and-hope.** Read the exact current lines, write the exact replacement. Tool can't edit precisely ⇒ stop, re-read. Hand-repairing self-inflicted breakage is a defect, not a workflow — and on a port the sloppy edit can reach a flash before a build.
- **Separate restructuring from upgrades.** Hold upstream version, patches, config fixed while moving layers; compare built payloads before/after (one split preserved `Image` + `System.map` bytes). Upgrade separately. Verify downstream DTB + boot-image assembly too: kernel equivalence ≠ complete image still builds.
- **Start the patch battery pristine.** Fresh disposable tree at the exact intended revision; apply patches in series order so later checks see prerequisites. Never classify an apply failure as "already upstream" from a used tree — one contaminated rebase check dropped a required header on that premise; the build failure forced restoration. Establish absorption from upstream code/history before retiring a patch.
- **A rebuild is the most expensive step — verify before paying.** Measured: four consecutive rebuilds burned on unchecked missing symbols. Before any full rebuild: every patch applies cleanly (`git apply --check`); every referenced symbol defined where the patch sees it; every config option a patch/defconfig sets exists in this kernel's Kconfig (a nonexistent option is silently ignored — a clean config read is not evidence it took effect); compile touched directories/units first.
- **Build the artifact you selected; report per artifact.** One bundled job hid three of four finished targets behind a still-compiling patched desktop and reported only "still running." Select explicitly; keep progress + cancellation separate; status queries must never start a build. A finished store output survives a sibling's cancellation.
- **Source preparation is transactional or it lies.** Derive readiness from the actual assembly: one helper searched for declarations the current flake no longer contained, applied zero of 47 patches, stamped itself ready. Apply each patch against its predecessors' result; missing files/check failures fatal; success marker only after full success; fingerprint readiness on source identity + ordered patch contents. Partial/failed preparation leaves the previous ready tree usable.
- **After 3 refuted fixes: stop guessing, not stop working.** Three misses = the failure model is wrong; a fourth tweak misses too. Return to step 2 wider — different layer, or primary artefacts — and return with findings stacked. Worked shape: five blind DT tweaks failed; five researched findings stacked — trustzone memory mode, vendor firmware paths, host-capability quirk, SMMU stream ID off the stock DTB, one obsolete property dropped. No single tweak would have got there.

Before writing, recheck phase 0's handset/transport mapping, product, slot. **A deployment is a state machine.** Separately observe: artifact preparation, transfer/installation, next-boot selection, live activation (where supported), boot. Map to the target's actual update model; do not invent profiles/slots/live activation where absent. No earlier state proves a later one. Nix-style: a closure present ≠ activation; a matching profile symlink does not prove activation completed; an explicit activation request reruns even when nothing needed copying. Only a separately observed reboot marks booted — never flash or reboot to make recorded states agree. After the authorized deployment: readback, required reboot through an available channel, identify running kernel/modules/system. Recheck boot-time init + runtime functions: one upgrade lost sound-card registration to module coldplug ordering though later manual init worked. Service readiness, silent playback, operator-confirmed acoustics are separate results; retain deferred/untested criteria explicitly. Revalidation honesty: on a required probe, empty output, timeout, or transport failure is a **FAIL, never a SKIP** — one checker scored a timed-out partial output PASS and a dead transport SKIP, then exited 0 — and a verification tool must never trigger an implicit build for a missing image argument.

**Reconcile completion and resume records.** Before reporting completion or resuming: make current target, tested revision, acceptance results, limitations, authorization state and next action mutually consistent. Preserve superseded checkpoints with transition evidence; do not erase history or treat completion as renewed permission. One result carried passing optical measurements while retaining paused/not-verified fields — such contradictions block automatic continuation until reconciled. Target met with an unresolved new fault is not clean integration: a passing optical test did not explain a new boot-time controller timeout.

**Cleanup only after the new deployment passes real reboot + runtime checks.** Inventory obsolete kernels, module trees, images/packages, boot entries, staging — include generations/roots where the platform uses them. Preserve the running system, next-boot target, a complete known-good recovery path with dependencies — a saved boot image alone may not recover the system. Archive needed historical images/symbols/logs on the host before retiring device copies. Select retired deployments explicitly; remove through the target's supported mechanism — package/image/slot management, or scoped generation/GC for Nix-style systems; never manual deletion of managed files or blanket deletion. Record space before/after, mechanism-reported reclamation, retained recovery targets. One operator reported substantial GC recovery with no precise total — never attribute total GC savings to kernels without separate accounting.