linux-phone-porting · git:20260920.86ae6d3 · 2026-09-20 · sha256 85f8f7d6ec23f77f
linux-phone-porting git:20260920.86ae6d3A
Immutable. This exact content is served forever at /api/v1/blob/85f8f7d6ec23f77f.
--- 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. - **Publication hygiene runs at every boundary, not only upstream.** Whatever leaves the project — an upstream payload, a published repo or install guide, a status report, an issue/PR/forum reply — is swept first for operator-supplied secrets (Wi-Fi SSIDs and passwords, accounts and app passwords, tokens) and device-unique identifiers (IMEI, serial and MAC addresses, board/asset ids, calibration values, private captures, local hostnames and addresses). Redact or generalize in the payload itself, then verify by scanning the exact bytes that leave the machine — a sweep run from memory misses the value it forgot. - **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. Provision missing host tools through the host OS's own package route — on declarative hosts via its shell/profile mechanism; on container/WSL setups, prove USB passthrough before a workflow depends on it. 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. - **On compositor targets the screen is an evidence channel.** A screenshot pulled over the demonstrated channel returned the live UI state in seconds, and a loopback VNC server reached through the existing ssh tunnel added touch/keyboard injection — no new network exposure. Read the panel instead of inferring UI state from logs; the UI is part of the runtime surface. ### 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. - **Classify one-direction-dead USB networking by counters, not symptoms.** Gadget still enumerated + host ARP failing is ambiguous: ping from the host while reading the device's interface TX/RX counters over a surviving channel — counters rising only inbound proved the device could receive but not transmit. Check the rootfs first, too: a read-only remount after a failed resume kills sshd and networking with identical outward silence. - **A host-side USB id proves neither the running OS nor a failed boot.** One enumerated vendor id plus dead ssh was repeatedly logged as an "Android fallback" while the target OS was up and responsive; host-visible signals establish channel state only. Obtain OS identity through a working channel; until then record a channel failure, not a boot verdict. - **A session or compositor swap re-opens its layer's defaults.** The previous session claimed the power key (screen lock); the new one claimed nothing, so the logind stock handler turned one short press into a clean shutdown — journal `-b -1` shows the keypress, pstore stays empty: a shutdown masquerading as a crash. Config-variable errors can render as a near-invisible error banner instead of the UI, and autostarted input methods fail silently. After a session change verify: config loads clean (reload success), key/suspend handlers claimed, autostarts actually running, scale/orientation/idle behavior intact. - **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. - A detached recovery unit (`systemd-run`) runs with a minimal PATH: one script's `sleep`s silently no-opped, it raced its own readiness check, and its reboot safety net fired on a false negative — destroying the state it existed to diagnose. Absolute paths; every step logged with timestamps; a destructive fallback gated on preconditions verified inside the same script. ### 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. - **A hard reset during suspend entry masquerades as a resumed-but-dead device.** One "gadget never survives resume" was the phone rebooting mid-entry — no panic, no pstore; `boot_id` + journal continuity is the discriminator. Bisect entry with the `pm_test` stages (freezer → devices → platform → processors → core): surviving all five puts the fault after driver work, in the SoC power-collapse transition — missing wake-controller node, absent parent collapse domain, firmware-inconsistent idle StateIDs — not in any device driver. - **Check charge state before a boot-failure verdict on a silent, dim device.** One gadget-USB drop stopped charging; successive reboot attempts then drained the battery until the device showed a dim console and no enumeration — read as a boot hang until the cable was re-seated and the device returned, exonerating the software change. Charging evidence (LED, current) is cheaper than a reflash. ## 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. Android-app compatibility is a target capability, not an afterthought: enumerate the candidate layers (waydroid, Android-runtime ports such as lepton, and peers), gate each on its kernel requirements — binder, PSI/memfd, GPU stack — against the exact kernel configuration being built, then race the survivors on the device and benchmark the winner against the native session: one port measured the Android container faster than the phone session it ran under. 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. The retail config, not convention, selects which of several HAL/transport variants is live — port the selected one (one board's FM HAL factory chose the shared-Bluetooth UART transport over the commonly suggested radio driver). 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. - **General-purpose and desktop distributions** — NixOS (mobile-nixos patterns, nixpkgs packaging) among them: 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. - **Module load order is an experiment variable.** Camera/lens modules in `modules-load` ahead of the audio DSP's bring-up broke speaker-path registration on every boot; loading the same modules at runtime after audio init kept both paths working — order-dependence, not module presence. Isolate with a closure A/B (offending modules removed) against a runtime load, then defer the modules behind an ordered service on the subsystem they must not race. - **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.