Linux Phone Porting
SkillDev toolsUse for every hardware bring-up or debug session that ports mainline Linux to a phone (Android handset). Requires an already-unlocked bootloader; locked devices are out of scope.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Linux Phone Porting skill
What this skill tells your AI
The instructions your AI receives, as published by angelwzr/linux-phone-porting in SKILL.md and read by ahel’s review.
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 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 topology — A/B or not, slot layout, lock state. 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. Unlocked bootloaders only — no unlocking steps; point at the OEM's instructions and resume when unlocked.
- 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 partition except userdata:
- 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 DTB — 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 (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-docsfor component docs,wigolofor 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.
- Rooted stock beats a ROM image. Root keeps the system readable:
getprop,/proc/config.gz, mounted vendor/odm trees, HAL/sensor configs, calibration artefacts, factory field-test modes. User roots it themselves — no rooting steps; resume when root exists. - Record the newest community 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):
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 → siblingfirmware-publishable/repo with its own history, derived only after redacting device-unique identifiers. Device-locallogs/: one subdir per boot/deploy +LATEST-*links. Anartifacts/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
readset 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 a bootable Android for data gathering. 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 Android 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 Android — 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 — whensystemd-pstore.servicehas 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 insidepanic()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/DIRECTwhere 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=directso 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
EINVALwas 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 sshkills the local client only; the remote process keeps running — kill it explicitly.pgrep -f/pkill -fover that ssh path self-match their own remote shell — filter the matcher or match by PID.findneeds-Lwhen 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 DTB, vendor configs, firmware layout) are the device data, as authoritative as a crash log.
Before writing anything:
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 49
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
linux-phone-porting- Source
- github.com/angelwzr/linux-phone-porting