Start here
This site documents gbook: bringing mainline Linux up on the Dell XPS Googlebook, a Snapdragon X Elite (X1E80100)
ChromeOS laptop whose board name is mica (ChromeOS firmware branch firmware-bluey-16702.B). Everything here is
exported from the project’s working repository at a release tag and rebuilt per release; the dates on each page are the
dates the facts were read or measured.
Two public ports exist for this machine. The Nura (postmarketOS) port by Anton Bambura installs
through pmbootstrap and is the way to get a packaged system today. gbook builds its own kernel image for the ChromeOS
kernel partition and a Debian rootfs on a USB stick, and documents the parts the other record leaves open: the ChromeOS
boot path (depthcharge, FIT, EL2), the SCMI mailbox stall and the decision it led to, the sound card’s SoundWire wake
fix, and a status matrix with a line of evidence for every row.
Where to look:
- Status: what works, where it works, what is upstream, with evidence for every row.
- The guide: How mica boots Linux, SCMI over the CPUCP mailbox, Sound on mica, the device tree compared with the Nura port.
- How-to: Boot a gbook image, with the recovery steps first.
- Reference: Device tree, Local patches, Kernel configuration, Glossary, Releases.
- Decisions, Research and Evidence: the records behind the claims.
There is no image to download yet: the recipes are documented and the public repository holds no binaries. The two released images so far are described on the Releases page by their tag messages.
Nothing on this site has been sent upstream. The project holds its kernel and device-tree submissions until its milestone M5 (decision 0010); four local patches are listed by their status lines only until then.
A warning: this is experimental work on a device whose firmware can refuse to boot an unsigned image or need a recovery stick to return to stock. Read the recovery decisions (0001, 0002) before touching developer mode.
Licence: prose CC BY-SA 4.0, tooling MIT, device tree BSD-3-Clause, patches GPL-2.0-only (details).
About this build
Generated by scripts/export-public.sh from the gbook repository at v2026.11.05-21-gf05400b on 2026-11-05 4:42 PM EST.
Files exported: 85, plus the generated pages. Links to files that stay private are plain text.
Rebuilt once per release tag; nothing between tags is published.
Licence
gbook: mainline Linux on the Dell XPS Googlebook (mica)
Copyright (c) 2026 m3
This tree carries work under four licences. A file's path says which one applies; the texts are in LICENSES/.
1. Prose: every Markdown file and the site rendered from them.
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0): LICENSES/CC-BY-SA-4.0.txt.
Attribution: "m3, gbook", with a link to this site.
2. Tooling: gate.sh, book.toml, the workflow under .github/, the kernel configuration fragments
(src/kernel/*.config) and the build-script headers quoted under src/scripts/.
The MIT License: LICENSES/MIT.txt.
3. Device tree sources (src/dts/*.dts, src/dts/*.fixups): BSD-3-Clause, as their SPDX headers state,
with the copyright lines they carry (Qualcomm, Aleksandrs Vinarskis, and this project's additions).
LICENSES/BSD-3-Clause.txt.
4. Kernel patches (src/kernel/patches/*.patch): GPL-2.0-only, as Linux kernel code. Each patch names
its author; the FROMLIST ones are copies of patches posted to the kernel lists by their authors.
LICENSES/GPL-2.0-only.txt.
Vendor firmware is not in this tree: src/kernel/firmware.sha256 lists hashes only.
Texts: CC BY-SA 4.0, MIT, BSD-3-Clause, GPL-2.0-only.
How mica boots Linux
The Dell XPS Googlebook does not boot like other Snapdragon X laptops. There is no Qualcomm ABL (Android Bootloader) and no UEFI: the machine runs ChromeOS firmware, and Linux gets in through the door that firmware already has for self-signed kernels (boot-path note, “Answer” and “Route C”). Sources: the boot-path note, the FIT packaging note, the EL2 handoff note, ADR 0001 and ADR 0003.
The firmware stack
The firmware is coreboot plus the depthcharge bootloader, with Android boot support added on top; vboot (verified boot) sets the rules depthcharge enforces about what may run (boot-path note, “Answer”). The firmware build carries no hypervisor image at all, no Gunyah and no QHEE (EL2 handoff note, “1. Firmware chain”, also the table’s source).
| Stage | Exception level | Role |
|---|---|---|
| coreboot | EL3 | board init; loads BL31 and the secure OS |
| BL31 (Trusted Firmware) | EL3 | starts the payload at EL2 |
| QTEE (Qualcomm’s trusted execution environment) | secure EL1 | secure OS |
| depthcharge | EL2 | picks and verifies the kernel |
| Linux | EL2 | entered with a plain branch, no eret |
Developer mode is the “unlock”
Google’s “Unlock bootloader” menu item is ChromeOS developer mode under a new label, and Android’s “OEM unlocking” switch is the vboot flag that withholds developer boot until it is on. The Chromebook USB-boot settings are preset: in normal mode the firmware turns external boot on and signed-only boot off (boot-path note, “2. Unlock = developer mode”).
“Self-signed” is precise: developer mode takes the keyblock on its hash, but the kernel body must still verify against the keyblock’s own key, so the image is signed with a self-consistent key pair, by convention the public vboot developer keys (boot-path note, “2. Unlock = developer mode”; FIT packaging note, “4. Dev keys”).
The recovery gates come first
Unlocking wipes the device, and nobody had shown that Google’s recovery media restores an unlocked unit or relocks the bootloader. So no unlock happens until Gate 1 passes: recovery media made and booted to the recovery screen, the key combination recorded, the recovery image and build saved, the fingerprint and bootloader version recorded, and the data the next phase needs backed up. The owner does those steps by hand; agents never unlock, flash or enter recovery (ADR 0001, “Context” and “Decision”). On 2026-11-02 Gate 1 passed: Device Recovery Utility media reached the recovery screen and served the installed build. The public recovery zip alone had not passed it, because its build 16471258 is older than the installed 16560795 and anti-rollback might refuse it (ADR 0001, “Update 2026-10-21” and “Update 2026-11-02”).
The route: a FIT image on a ChromeOS kernel partition
vboot treats a GPT entry as bootable when its type is the ChromeOS kernel GUID fe3a2a5d-4f32-41a7-b725-accc3285a309;
depthcharge loads the body into its kernel buffer and boots it as a FIT, U-Boot’s Flattened Image Tree (boot-path note,
“Route A”). ADR 0003 chose that route on a USB stick: the stock SSD is never written during boot tests, and a bad image
falls back to the next partition or the menu, not a brick (ADR 0003, “Decision”, “Consequences”).
The partition holds a vboot kernel blob: a 64 KiB vblock followed by a FIT with one kernel, one device tree blob (DTB)
and one ramdisk. mkdepthcharge from depthcharge-tools builds and signs it in one command, wrapping mkimage and
vbutil_kernel with the developer keys (FIT packaging note, “Answer” and “1. mkdepthcharge for a custom board”). The
firmware’s rules (same note, “2. Compression and the 256 MiB limit”):
| Constraint | Rule |
|---|---|
| Kernel format | plain arm64 Image; depthcharge checks the arm64 magic, so Image.gz fails |
| Compression | none, lz4 or lzma only; the FDT and ramdisk nodes must be none |
| Size | the packed body must fit the firmware’s 256 MiB kernel buffer; the decompressed kernel goes elsewhere |
The first-boot image is 256 MiB and holds a 128 MiB KERN-A, rewritten whole for every attempt; since milestone M3 the
rootfs is partition 2 of the same stick, from 256 MiB on (ADR 0003, “Update 2026-10-21 (first-boot details)”; ADR
0012, decision 3).
Configuration matching. depthcharge prefers google,mica-rev<board>-sku<sku> down to a plain google,mica, ranks
each FIT configuration by its best match, and boots /configurations/default when nothing matches (boot-path note,
“Route A”). A DTB whose root compatible holds google,mica matches any board-id and sku-id, and the default is a safety
net and a trap, since a wrong DTB still boots (FIT packaging note, “Answer”, step 6). On 2026-11-02 the coreboot table
reported board-id 3 and no sku-id (first boot, “Result”).
Partition attributes. vboot keeps priority, tries and successful bits in each entry’s GPT attributes: it picks the
highest priority above zero and spends a try on each boot of an entry whose successful bit is clear (FIT packaging note,
“5. Partition type and attributes”). The project writes cgpt add -t kernel -S 0 -T 3 -P 15, three tries then fall
back. pmbootstrap’s -S 1 -T 5 -P 10 is for an installed system: with successful already set, tries are ignored and a
kernel that panics is picked again on every boot (same note, “Answer”, step 5).
Entering at EL2
depthcharge jumps to the kernel with a plain branch, no eret, so Linux starts at EL2 with no hypervisor underneath
(EL2 handoff note, “Answer”). On 2026-11-02 the first boot logged CPU: All CPU(s) started at EL2 (first boot,
“Result”).
Mainline’s hamoa.dtsi assumes Gunyah owns some hardware, so the FIT carries x1e80100-google-mica-el2.dtb: the mica
DTS with the kernel’s x1-el2.dtso overlay applied. The overlay enables the Qualcomm APSS watchdog and the PCIe SMMU
(the system IOMMU), maps the PCIe controllers onto that SMMU, disables the generic SBSA watchdog, the GPU zap shader and
the IRIS video block, and adds iommus to the ADSP and CDSP remote processors (ADR 0003, “Update 2026-10-21 (first-boot
details)”; EL2 handoff note, “2. What x1-el2.dtso changes”). Overlays cannot delete properties, so mica’s differences
live in the fixups file: it removes those remoteproc iommus again, because the stock mica tree
has none, and with them the PAS (peripheral authentication service) driver asks TrustZone for a resource table that
mica’s TrustZone refuses with -EIO, leaving the ADSP offline (attempt 2026-11-02-4; fixups header; dts
README, “EL2 overlay” row).
The command line and the console
mkdepthcharge prepends kern_guid=%U to the stored command line; at boot depthcharge replaces %U with the kernel
partition’s PARTUUID in lowercase, appends cros_secure and writes the result into /chosen/bootargs. The rest is
console=ttyMSM0,115200n8 earlycon=qcom_geni,0x894000; the UART address was spelled out because the draft DTS then
lacked /chosen/stdout-path, which a bare earlycon needs; the DTS gained it on 2026-10-21 (FIT packaging note, “3.
Kernel command line”; dts README, “Console” row).
The console is read through Closed Case Debugging (CCD): with the Google Security Chip (GSC) still locked, a SuzyQ cable
exposes the AP (application processor) and EC (embedded controller) consoles, read-only here, though Google’s
documentation warns that the AP console is disabled on production devices (ADR
0006, “Context” and “Decision”; boot-path note, “5. Closed Case Debugging”).
On 2026-11-02 the AP console carried kernel output only from 0.67 s on, once ttyMSM0 probed; the full log came from
ramoops, read back on the next stock boot (first boot, “Channels”).
What the initramfs does
The FIT’s ramdisk holds /init, which finds the block device whose PARTUUID equals kern_guid, takes that device’s
parent disk, and requires the disk’s partition 2 to carry the label gbook-root. It mounts that partition on
/newroot, moves /dev, /proc and /sys over, and runs switch_root into the Debian rootfs; without it, or on a
failed mount, it falls back to capture-and-reboot (ADR 0012, decision 4; the M3 rootfs
note, “4.2”). The rootfs path has worked on the device since the 2026-11-04 attempts
(status, “Rootfs from USB”).
Since 2026-11-05 /init also enforces “the stock drive is never written”: right after the stick is identified, it sets
every other block device read-only at the block layer with blockdev --setro; an unknown GUID locks nothing. It is
advisory (root can undo it) and was untested on the device when recorded (ADR 0003, “Update 2026-11-05”).
What this page does not cover
The Nura postmarketOS port installs through pmbootstrap and is the way to get a packaged system today: Dell XPS
Googlebook (google-mica). There is no image to download
from this site: the recipes are documented in the notes above, and the public repository holds no binaries.
SCMI over the CPUCP mailbox, and why PSCI cpuidle is off
On mica the three CPU clusters take their performance levels from a firmware core, and mainline talks to it with the
System Control and Management Interface (SCMI) over a mailbox. That path stalled on most boots, and the board device
tree now turns Power State Coordination Interface (PSCI) cpuidle off because of it. The evidence is in
the SCMI research note and the recon note,
which number boot attempts per day as 2026-11-03-N, shortened to -N below; the decisions are
ADR 0008 and ADR 0009.
What SCMI does on this SoC
The mailbox is the CPUSS Control Processor (CPUCP) mailbox controller, qcom,x1e80100-cpucp-mbox, a doorbell between
the application processor and the CPUCP (the pinned kernel’s binding, qcom,cpucp-mbox.yaml). The SCMI node uses
mailbox channels 0 and 2 with shared memory in SRAM, and each CPU takes its perf domain from scmi_dvfs; the stock
tree drives the same block the same way (recon note, “Why”). Up to attempt -7 the mailbox driver was a
module and the initramfs loads none, so firmware:scmi deferred forever. With QCOM_CPUCP_MBOX=y (attempt -8), SCMI
bound at 0.358 s, read Firmware version 0x20000, and scmi-cpufreq came up on all three clusters, 710.4 MHz to
3417.6 MHz under schedutil (recon note, “Result: pass”). Status lists it as works.
What failed
The next boot, attempt -9, had the same SCMI configuration and failed. From 0.437 s the log shows ten timed out in resp lines about 36 ms apart (the 30 ms default timeout rounded up to jiffies at 250 Hz), then replies for requests
the kernel had already dropped: Message for 7 type 0 is not expected!. The version query was among the timeouts, so
the base protocol read Firmware version 0x0, and scmi-cpufreq probed with -110 and never retried (research note,
section 1). The note reads this as a slow CPUCP, not lost interrupts, since other messages in the same window succeeded
(inference). Every upstream x1e80100 SCMI and CPUCP fix was already in the pinned kernel (research note, “Summary”,
section 2).
A version of 0x0 carries a hazard. The quirk that forces the fast channel for PERF_LEVEL_GET is declared for
Qualcomm firmware from 0x20000 up; with the version unread it stays off, and the driver falls back to the regular
message that Sibi Sankar (Qualcomm) said crashes this firmware when idle states are enabled (research note, “Summary”
item 5 and section 4, the review thread).
Cold boot versus warm reset
The embedded controller’s console separates cold starts (G3->S5) from warm resets out of running stock Android. On
2026-11-03 SCMI worked in both cold boots and failed in every warm reset; the same image failed warm (-11) and worked
cold (-12). The recon note’s inference is that the stock kernel leaves the CPUCP in a state that survives a warm
reset, so stock is powered off before every attempt (recon note, “Update: cold boot vs warm reset”, 5:25 PM ET). By
5:59 PM ET three cold starts had given one -110, one run of timeouts and one pass, so a cold start is no guarantee
either (recon note, “Update: cold starts fail too”).
Measuring the replies
The research note recommended measuring first, then sizing arm,max-rx-timeout-ms from the measured latency (“Ranked
recommendation”). On attempt -22 the trace showed the base protocol’s discovery messages taking 23 to 36 ms
each; the implementation-version message took 36.0 ms, which the 30 ms default misses, leaving the version 0x0.
Other replies took 155 to 222 ms, four requests timed out at 250 ms (one message twice), and the boot still came up
(recon note, “Update: reply latency per message”).
| Window | Boots | Outcome | Source (recon note) |
|---|---|---|---|
| 30 ms (default) | 7 cold starts | 2 good | “Update: 1000 ms window” |
| 250 ms | -20, -21, -22 | -20 failed (7 timeouts of 78, version 0x0); -21 clean, 4.0 ms peak; -22 up with 4 timeouts | “Update: the 250 ms window and the trace”, “Update: reply latency” |
| 1000 ms | -23, -24, -25 | all pass; -23 and -24 with single replies at 0.6 to 1 s, -25 within 3.6 ms | “Update: 1000 ms window” |
When the CPUCP answers, it answers in milliseconds; on a bad boot single messages wait up to a second or never come,
and the window only absorbs that (recon note, “Update: 1000 ms window”). The board DTS carries
arm,max-rx-timeout-ms = <1000> on /firmware/scmi. No stock node has it, so the DT README lists it
as unverified.
The stall
With cpuidle.off=1 every reply came within 0.1 ms on 3 of 3 cold starts (recon
note, “Update: cpuidle.off=1 A/B”). With arm,no-completion-irq (polling) and idle states on, the CPUCP freed the
shared-memory channel within 0.1 ms of every request, 3 of 3: the CPUCP is not slow, the completion interrupt is. In
interrupt mode every boot counted one mailbox interrupt per reply, all on CPU 0, stalled boots included: the
interrupts arrive, late (recon note, “Update: arm,no-completion-irq A/B”).
The wake trace on -32 shows what CPU 0 did during one 534 ms stall: it entered cpuidle state1 (the PSCI retention
state cluster_c4) and left it 26694 times, about every 20 µs, while taking only three interrupts. The stall ended
when the arch_timer, a Private Peripheral Interrupt (PPI), ran; 6 µs later the mailbox interrupt was taken (recon
note, “Update: the wake trace”). The idle counters on -33 showed PSCI refusing the system-level composite state about
598000 times in 95 s (“Update: idle counters”). Removing every domain idle state gave 3 of 3
clean boots (“Update: no PSCI domain idle states”), but two later boots on that tree stalled with zero refusals and
CPU 0’s loop at 130000 to 156000 entries (“Update: ADR 0008 is not enough”). A diagnostic kernel patch then routed the
mailbox’s Shared Peripheral Interrupt (SPI) to CPU 1: the loop moved with it (CPU 1 at 134862 entries, CPU 0 at 6131)
and the reply stalled for 872 ms (“Update: the stall follows the interrupt’s CPU”). The reading, marked inference in
every source, is that a core in PSCI retention is woken by a pending SPI but does not take it until a PPI runs.
The A/B tests
The counts are the recon note’s own tally lines (scripts/attempt.sh tally reads machine-recorded boot health; a
“clean” boot has no timeout and every reply under 100 ms: status, “What clean means”).
| Configuration | Stalled boots | Source |
|---|---|---|
| PSCI domain idle states present, interrupt mode (-23 to -27, -30 to -33) | 7 of 10 known, -31 unknown | recon note, “Update: the counts from attempt.sh tally” |
| ADR 0008’s DT, no domain idle states (-34, -35, 2026-11-04-1 to -3) | 3 of 7 | same |
Polling, arm,no-completion-irq (-29) | 0 of 3 | same |
| No PSCI cpuidle (-28, 2026-11-04-4) | 0 of 4 | same |
| No PSCI cpuidle at ten boots (adds 2026-11-04-5 to -10) | 0 of 10 | “Update: the tally at ten boots without PSCI cpuidle” |
At ten boots, no PSCI cpuidle against domain states present is a one-sided Fisher p of 0.0015; against ADR 0008’s DT it is 0.051 (same section). ADR 0009 later records 0 of 17 (“Update 2026-11-04 (11:24 AM ET)”), the count status carries today.
Two decisions
ADR 0008 (accepted 2026-11-03) deleted the cluster and system domain-idle-states from the board DTS and kept per-CPU
retention. The 2026-11-04 boots above showed the stall did not need the composite states, and the ADR was superseded
the same day (ADR 0008, “Status” and “Update 2026-11-04”).
ADR 0009 (accepted 2026-11-04) deletes cluster_c4 from cpu_pd0 to cpu_pd11 instead. With no idle state on any
CPU, cpuidle-psci does not load and every core idles in WFI, as under cpuidle.off=1; the cluster and system
states return to stock, since nothing requests them; the 1000 ms window stays (ADR 0009, “Decision”). Attempt
2026-11-04-4 confirmed it: no cpuidle driver, 78 replies within 0.1 ms, cpufreq on all three clusters (ADR 0009,
“Update 2026-11-04 (12:57 AM ET)”). Both ADRs are deliberate deviations from the stock tree under
ADR 0004, recorded in the DT README.
What it costs: no core enters PSCI retention and no cluster or the SoC powers down when idle, so idle power is higher by an amount that has not been measured (ADR 0009, “Consequences”). Status lists CPU idle as partial for that reason; the measurement is parked until a battery-only session, because the debug cable powers the bench (status, “CPU idle” and “Idle power”). An s2idle round trip completes on this DT, in WFI, but which low-power state the SoC reaches is unmeasured (ADR 0009, “Update 2026-11-04 (5:01 AM ET)”).
What is still open
- Why a core in retention does not take a pending SPI until a PPI runs: the GIC’s wake path or the PSCI firmware (ADR 0009, “Consequences”).
- Whether the CPUCP also answers within 0.1 ms when every core sleeps: untested (recon note, “Update: what -29 shows and does not”).
- A warm reset from stock now works, 1 of 1, but with the window and ADR 0009 both in place, so which change fixed it is not separated (status, “SCMI after a warm reset from stock”, inference).
- The 1000 ms window stays unverified (recon note, “Update: 1000 ms window”).
A report on the mechanism is drafted. It is held, not sent: under ADR 0010 nothing goes to a mailing list before milestone M5, and the owner reviews and sends it then (ADR 0009, “Update 2026-11-04 (10:07 AM ET)”).
Sound on mica
How the speakers and microphones of the Dell XPS Googlebook (board mica) came to work on mainline Linux.
Status lists “Speakers” and “Microphones” as works on the released image v2026.11.05; the Nura
port’s wiki lists Audio as Broken as of its 2026-10-31 edit (https://wiki.nura.eco/wiki/Dell_XPS_Googlebook_(google-mica)).
Sources: the audio recon note, the DT README,
the audio fragment, the patch list. The owner’s hearing reports,
with their times, are the evidence that sound came out; the register reads say why.
The hardware
The card is Qualcomm’s qcom,x1e80100-sndcard, model X1E80100-Dell-XPS-13-9345, on the Low Power Audio Subsystem
(LPASS). Four WSA8845 smart amplifiers (Qualcomm’s WSA family) sit on two MIPI SoundWire buses, two per bus; four digital
microphones (DMICs) hang off the voice-activation (VA) macro. The audio DSP’s topology file loads at the card’s probe, and
Debian’s ALSA Use Case Manager (UCM) profile for the XPS 13 9345 resolves through a link the rootfs adds (audio recon
note, “Result”).
| Part | Stock ChromeOS tree | Mainline | Pins and rails |
|---|---|---|---|
Two WSA8845, SoundWire link 1 (swr0) | wsa884x@02170220 SpkrLeft (unique id 0), @02170221 SpkrRight (id 1), wsa_swr_master | sdw:1:0:0217:0204:00:0, :1 | LPI gpio10/11; shared reset TLMM 204 |
Two WSA8845, link 4 (swr3) | @02170220 Spkr2Left, @02170221 Spkr2Right, wsa2_swr_master | sdw:4:0:0217:0204:00:0, :1 | LPI gpio15/16; shared reset TLMM 205 |
| Supplies | vdd-1p8 = L15B | plus vdd-io = L12B | L12B unverified |
| Four DMICs, VA macro | dmic01_default, dmic23_default | LPI 6/7, 8/9 | EN_PP3300_MIC, TLMM 69, as vdd-micb-supply |
| PCM devices | playback 1, capture 3 | UCM Speaker, Mic |
(audio recon note, “What the boots proved about the mapping”; DT README, “What changed from the 9345”, row “Audio”)
Where the mapping came from
The node was modelled on the Lenovo Yoga Slim 7x tree at the pinned commit a804c57, then checked against the stock
ChromeOS tree from the public recovery image: the SoundWire masters and codecs, the shutdown pins, L15B, the VA macro and
the DMIC pins trace to the stock base tree, the microphone rail to the stock board overlay.
It booted as a TEST IMAGE from 2026-11-04, moved into the board DTS on 2026-11-05 and was proven on the base image over
three clean cold boots. One deviation: stock drives the shutdown pins high when active; mica starts them low, the Yoga’s
way, and the codec driver’s reset-gpios releases them; all four play that way (DT README, row “Audio”).
The two kernel options that blocked the card
The initramfs loads no modules, so the audio fragment builds in every driver the node needs;
all were =m in the arm64 defconfig (audio fragment, header). Two were not obvious. The WSA884x driver asks the reset framework for
its shutdown pin; the reset core turns reset-gpios into an auxiliary reset-gpio device and defers the codec until that
device’s driver, CONFIG_RESET_GPIO, binds. With it built in (kernel #31) the reset-gpio devices deferred in turn: TLMM
204 and 205 are each shared by two codecs, and the kernel’s shared-GPIO support routes such a pin through a proxy chip
that only CONFIG_GPIO_SHARED_PROXY registers. Both are =y since kernel #32, and the card bound on attempt
2026-11-04-28. A distribution kernel loads both as modules, so the Yoga never showed this (audio recon note, “The two
blockers, both kernel options”).
The first sound, and the wake problem
The speaker test on boot -28 (2026-11-04, 11:51 PM EST): the owner heard two sounds, one louder and one lower, and the spoken names gave “Front Right” and “Rear Right”; after some minutes, nothing. The masters’ enumerator registers showed one enumerated slot per bus (unique id 1) and a slave stuck at device number 0. The note’s reading (inferred): the right-side units played, the id-0 pair never enumerated, and every re-enumeration after a clock stop failed the same way (audio recon note, “The speaker test”).
The debug boot (2026-11-05, 12:25 AM EST) showed all four amplifiers enumerating at boot, with the unique ids the DT says. The exit from clock stop lost them: after the SoundWire masters runtime-suspend, each wake races the hardware enumerator against the attach interrupts, bus by bus, and which bus wins is chance: both on the first wake, neither on the first tone, one on the second; the owner heard nothing at either tone. With the masters’ runtime power management forced on, both buses re-enumerated and the owner heard “front left, front right, rear right, rear left” (audio recon note, “The debug boot”). A udev rule forcing the masters on was rejected: its own write is the first wake, and on that boot (1:01 AM EST) one bus lost the race and stayed lost (audio recon note, “The udev-rule boot”).
The fix: two local patches, one of them the fix
Both patches carry the same first line, quoted because the files stay unpublished until milestone M5 and the patch list gives them by status line only:
Status: LOCAL gbook (ours; not sent: upstream hold until M5), checked 2026-11-05
Patch 0010 re-arms a stalled SoundWire enumerator in Qualcomm’s master driver (audio recon note, “The udev-rule boot”).
Patch 0011 sets clk_stop_mode1 on the WSA884x codec, so every resume of the masters runs the full reset-and-init sequence
that has enumerated at every boot, instead of the bare clock restart (audio recon note, “The patch boot, revision 3 and
patch 0011”).
With both (kernel #35, 1:34 AM EST) three wakes came back whole, where two earlier revisions of 0010 alone had lost a bus
on the first; the owner heard all the beeps each time (same section). The rule-free boot (2:05 AM EST) met the ticket’s
bar, a tone after ten minutes idle included (audio recon note, “The rule-free boot”). The 0011-alone boot (kernel #36,
12:32 PM EST) set 0010 aside and every wake came back whole; the owner heard tone 1, tone 2 and the four names twice (12:30
and 12:32 PM EST). So 0011 carries the fix; the owner decided at 12:38 PM EST that 0010 stays until the M5 patch review and
is then dropped (audio recon note, “The 0011-alone boot”). The released v2026.11.05 is kernel #35, with both (status,
“Speakers”).
The microphones
The capture test (2026-11-05, 9:26 AM EST) enabled UCM’s Speaker and Mic, recorded ten seconds from capture device 3
while the owner spoke, and played the file back through the speakers. The owner heard their own voice. The file, measured
on the build host, varies second by second on both channels; the journal has no VA macro, DMIC or LPASS line for the
capture. So the mic rail and the DMIC clock work as described (audio recon note, “The capture test”).
Boot by boot
| Date, time (EST) | Boot, kernel | Finding | Heard (owner) |
|---|---|---|---|
| 2026-11-04 | -26 #30, -27 #31 | codecs, then reset-gpio devices, deferred; no card | no test |
| 2026-11-04, 11:32 and 11:51 PM | -28, #32 | card 0 bound; speaker test: one slot per bus, then nothing | two sounds, then silence |
| 2026-11-05, 12:25 AM | -1, #32, debug | all four at boot; wakes lose buses by chance; masters on: all four | nothing, nothing, four names |
| 2026-11-05, 1:01 AM | -1, udev rule | the rule’s write is the first wake; one bus lost | no tone |
| 2026-11-05, 1:34 AM | -4, #35, 0010 + 0011 | three wakes, all four back | all the beeps, three times |
| 2026-11-05, 2:05 AM | -4, #35, no rule | five wakes whole, one after 10 min idle | eight beeps; four names |
| 2026-11-05, 9:26 AM | boot 7, #35 | DMIC capture, played back | own voice |
| 2026-11-05, 12:32 PM | -6, #36, 0011 alone | every wake whole | two tones, four names twice |
(audio recon note, “Result” through “The 0011-alone boot”)
What is unverified
Audio values the DT README marks as not traced to the stock tree (DT README, “Unverified (check at first boot)”):
vdd-io-supplyL12B on the four codecs: not in the stock codec nodes; everya804c57X1E80100 board uses it.qcom,dmic-sample-rate = <4800000>: the Yoga’s; stock sets only the clock divider 16.- The 3.3 V
EN_PP3300_MICasvdd-micb-supply: the Yoga’s is a 1.8 V LDO; the stock rail is always-on, so a wrong binding is harmless. - Which physical unit each UCM prefix (
WooferLeft,TweeterLeft,WooferRight,TweeterRight) drives: stock callsswr0’s pair SpkrLeft/SpkrRight andswr3’s Spkr2Left/Spkr2Right; open.
What is left
A read FIFO underflow burst on every wake and two Parity error detected lines remain open; they did not stop playback
(audio recon note, “The debug boot”). PipeWire and WirePlumber are not yet in the rootfs; a per-card WirePlumber drop-in is
a recorded proposal for when they are (SteamOS on ARM research note, “What gbook could
adopt”).
The device tree, compared with the Nura port
Two public device trees describe mica, and both carry the same compatible string, "google,mica", "qcom,x1e80100".
This page says how gbook’s tree was built, what the other one does differently, and what the two authors would have
to settle before either goes upstream. The comparison was made on 2026-11-05, 2:37 PM EST by compiling both sources against the
same pinned kernel tree (torvalds a804c57) and diffing the compiled trees node by node; facts about the other
tree are structural (which nodes exist and are enabled, which properties are deleted), not quotations of its text.
gbook’s tree in one paragraph
x1e80100-google-mica.dts starts from mainline’s Dell XPS 13 9345 file, the closest
X1E80100 laptop, and changes every board-specific pin to what the stock ChromeOS tree says (the DT README,
“What changed from the 9345”). The stock tree is extracted from Google’s public recovery image for this model, so each
change cites a line in it; a value with no stock counterpart is listed as unverified (“Unverified (check at first boot)”).
The first-boot tree adds a framebuffer reservation at the stock splash address and is built as the board DTB plus
mainline’s x1-el2.dtso overlay, followed by mica-el2.fixups, two property deletions that an
overlay cannot express (“First-boot additions”). Every enabled node binds since 2026-11-04
(the unbound-nodes note).
The other tree
Anton Bambura’s x1e80100-bluey-mica.dts (authored 2026-10-19, in his kernel tree for the postmarketOS-based
Nura port; pmaports merge request 9856 “google-bluey: new
device”, merged 2026-11-03) names the board "Google Mica" and includes the same two SoC files, hamoa.dtsi and
hamoa-pmics.dtsi. It compiles against a804c57 once one binding header his tree carries is stubbed (a camera
orientation header not in the pinned tree), with three dtc warnings about thermal zones that use the PMIC’s ADC as a
sensor without a sensor-cells property at this commit. Compiled, the two trees have 1,466 and 1,451 nodes; 1,383 and
1,366 of them are enabled.
What each tree enables that the other does not
| Area | gbook only | Nura port only |
|---|---|---|
| Sound | the sound card, both SoundWire buses with two WSA8845 amplifiers each, the VA macro’s microphone links, the two speaker-shutdown pin states, the microphone 3.3 V rail (Sound on mica) | nothing; the port’s wiki page lists audio as broken |
| USB | both USB MP (multiport) QMP PHYs enabled, as the 9345 has them (stock disables them; unverified) | the two eUSB reset pin states are named differently; a usb-mp-reset-n state |
| Video | the Iris video codec enabled | Iris disabled, with a note that it needs IOMMU work to start from EL2 |
| Input | touchpad and touchscreen pin states grouped per device, the TPM interrupt state | gpio-keys with a volume-up key on a PMIC GPIO, the PMIC’s power-key resin node |
| Timers and watchdogs | the PMIC RTC, the SBSA generic watchdog | the APSS watchdog |
| SoC blocks | the crypto engine explicitly disabled (its driver is marked broken at this pin and the unbound node held two interconnect paths open) | the crypto engine, the two GPI DMA controllers and the PCIe SMMU enabled |
| Regulators | fixed regulators for the backlight, fingerprint MCU, touchpad, touchscreen, camera and microphones, named per consumer | the same rails under other names, plus a PWM power switch; eight RPMh regulators (seven LDOs and one SMPS) declared on the PMICs that gbook’s tree does not declare |
| Reserved memory | the SoC file’s regions kept, one framebuffer reservation added at the stock splash address | the SoC file’s reserved-memory deleted as a whole and the needed regions redefined, with a comment that most of them overlap what depthcharge inserts at boot |
The 47 nodes only gbook enables and the 34 only the port enables are counted from the compiled trees; pin-state subnodes make up most of both lists.
Where the two trees make the same choice
- Both enter the kernel at EL2 and both drop the
iommusproperty from the ADSP and CDSP remoteproc nodes. gbook does it with the fixups file after applying mainline’s EL2 overlay; the port folds the overlay’s changes into the source (its commit message says it includes thex1-el2.dtsochanges except those two properties, which make the firmware fail to load). The reason gbook recorded on 2026-11-02: withiommusset the driver asks TrustZone for a resource table and mica’s TrustZone answers with an error (the fixups). - Both load the ADSP and CDSP from the stock firmware. gbook names the stock split files (
adsp.mdtandadsp_dtb.mdt) under a lowercaseqcom/x1e80100/google/mica/path and ships hashes only (firmware.sha256, ADR 0005); the port names single.mbnfiles under a capitalised path and redistributes them in a firmware package. The two paths differ in case, so a rootfs built for one tree will not find the other’s firmware without a symlink (inferred). - Both keep the console on
uart21and the Bluetooth UART onuart14, and both drive the ChromeOS EC overspi13with the keyboard matrix behind it.
Where they differ in substance
| Topic | gbook | Nura port |
|---|---|---|
| CPU idle | domain-idle-states deleted from the twelve CPU power domains, so PSCI cpuidle does not load and the cores idle in WFI (ADR 0009; why: SCMI over the CPUCP mailbox) | the SoC file’s idle states kept |
| SCMI | arm,max-rx-timeout-ms = <1000> on the SCMI node, marked unverified | the SoC file’s default |
| Sound | described, booted, speakers and microphones working as of 2026-11-05 | absent |
| Thermal | the SoC file’s zones | board thermal zones on the PMIC ADC (warnings at this kernel pin) |
| Model string | "Dell XPS Googlebook" | "Google Mica" |
Whether the port’s machine shows the SCMI stall gbook measured is unknown here: its tree does not change the SCMI timeout or the idle states, and no report either way was found (inferred from the tree alone).
What this means for upstream
One compatible string can have only one board file in mainline. gbook holds every submission until its milestone M5
(ADR 0010); the project’s owner intends to write to the port’s author
before then to coordinate a single google,mica tree and to offer the sound description and the SoundWire wake fix. Until
that conversation, this page is the diff. The DT README’s “Before sending upstream” list (the binding entry, the block
order, the attribution header) applies to whichever tree goes first.
Boot a gbook image on mica
A how-to for one boot attempt of a gbook kernel image from a USB stick, written from the project’s own runbook with the host-specific tooling left out. It assumes you have an image: the build recipe is documented (How mica boots Linux, the kernel configuration and patch pages) and the build scripts are published with a later release. No image download exists yet.
Roles in the project: a person does every key press, every unlock and every write to a stick; an agent builds, checks and reads consoles (ADR 0001). If you are alone, you are both.
Before the first attempt (once)
- Prove the way back first. Make a Google recovery stick for this model with the ChromeOS recovery tool, boot it once with the recovery keys (Esc + Refresh (F2) + Power) and confirm you reach its screen, then label it and never rewrite it. The project calls this Gate 1; nothing else happens before it passes (ADR 0001).
- Back up what you care about on the machine. Developer mode wipes user data.
- Unlock the bootloader on your own responsibility. On this model the steps are Google’s: turn on OEM unlocking in the system’s Developer options, hold Esc + Refresh (F2) + Power to reach the recovery screen, then Advanced options, Unlock bootloader. The firmware screen reads “Bootloader is unlocked” from then on, and self-signed kernels boot from an external drive without further settings (the boot-path note, chromeos-boot-path.md, “Unlock = developer mode”).
- A second stick for images: USB 3, 4 GB or more, used for nothing else. Keep it visibly different from the recovery stick; the project once put the recovery stick in by mistake and Google recovery ran.
- Optional but useful: a SuzyQ debug cable in the left port gives the AP console (ADR 0006). Without it the panel and, once Wi-Fi is up, the network log are the only channels.
Write the stick
- Write the whole image to the stick’s block device with
dd, addressing the stick by its stable device path (for example under/dev/disk/by-id/), never by a letter that can change. The image is a complete disk of 256 MiB: a GPT with a 128 MiB ChromeOS kernel partition holding the signed FIT, and, on the same stick from 256 MiB on, a second partition for a rootfs when one is used (ADR 0003). - Read the stick back and compare it with the image before you unplug it. Every rewrite also resets the kernel partition’s tries counter: depthcharge spends one try per boot, and the image sets 15.
- Power the stick off or eject it cleanly, then carry it straight to the laptop.
Boot
- Power the laptop off. Check: image stick in the right port, no charger, no hub, lid open. Battery at half or more.
- Power on with no keys held. On the “bootloader is unlocked” screen choose Boot from external drive.
- Never press the recovery keys with the image stick in. Recovery mode scans the stick for a Google-signed kernel, rejects the developer-signed one and marks the kernel partition bad; the stick then needs a rewrite (the project lost two boots to this on 2026-11-03).
- If you see “Recovering your device”, the recovery stick is in. Interrupt nothing. It reinstalls the same build and leaves developer mode on; a cut firmware update can brick the device.
- The first lines appear on the AP console at about 0.6 s and on the panel once the display driver is up. The
initramfs prints
GBOOK:status lines; with a rootfs on the stick it hands over to it, otherwise it reboots after its sweep. Pull the stick as soon as it reboots so the next boot is stock; with the stick still in, the firmware waits on its boot screen. - SCMI needs a cold boot. After a warm reset from stock the CPUCP answers late or not at all and cpufreq fails, so power the machine off before each attempt (SCMI over the CPUCP mailbox).
If nothing appears
| What you see | What to do |
|---|---|
| Looks hung | Wait two minutes for the kernel’s own panic reboot before holding Power (8 s); a forced reset usually loses the ramoops log |
| Firmware says “No valid image detected” | The first refusal marks the kernel partition bad on the stick; re-inserting cannot work. Rewrite the stick |
| Prints, then hangs at a driver | Disable that device-tree node for the next attempt, one change per attempt |
| LED code 3,5 | EC power-on timeout: charge, end the session, retry the same image later |
| Nothing new after five attempts | Stop and ask; the project’s failure signatures are in the boot-path and first-boot notes |
Back to stock
Esc + Refresh + Power with the recovery stick in the right port, then “Use USB drive or SD card”. It erases the disk and installs the build the stick carries. The project’s own return-to-stock plan, including the untested probes with a spare SSD, is in the M4 recovery matrix.
Feature status
As of 2026-11-05, 1:57 PM EST. Kernel: torvalds a804c57 plus kernel/patches/0001-0011. Board DTS:
dts/x1e80100-google-mica.dts. The default state is “untested”. Every row cites
the recon note it rests on, or “none” when no note covers it.
This is the feature matrix of ADR 0010: the milestones note, recommendation 2, which the owner adopted on 2026-11-04 (“all a” to its section 5). Asahi and Nura publish their feature tables the same way.
Patch numbers: 0008 and 0009 here are the PCIe GDSC and PCIe PHY GDSC retention patches. Both numbers named other
patches before the rule against reusing numbers. 0008 was a GENI I2C timing test (commit 16a2828, attempt
2026-11-04-2), and 0009 was the IRQ-to-CPU-1 diagnostic (commit cee735d, attempt 2026-11-04-3, deleted by
ADR 0009). Recon notes about those two attempts mean the old patches.
Legend
- State:
works,partial,brokenoruntested. A parked row keeps its state, and its note says “parked until” (ADR 0010, update 2026-11-04). - Where it works:
mainline at a804c57: the driver is unchanged;+ patch NNNN: a local patch inkernel/patches/;board DTS: needs the mica device tree;config fragment (name):kernel/<name>.configbuilds the driver in, because the initramfs loads no modules.- These combine as needed;
—means it works nowhere yet. In the Upstream column,—means there is nothing to report.
- Upstream: what mainline or linux-firmware already has, and what gbook would have to send.
- “mica DTS not sent” covers both the board DTS and the
google,micabinding (dts/README.md, “Before sending upstream”). - The patch words follow ChromiumOS, with
LOCALin place of itsCHROMIUM:FROMLISTmeans posted to a list but not merged, andLOCALmeans carried here and not sent. - Nothing is sent before M5.
- “mica DTS not sent” covers both the board DTS and the
- Evidence: the dated recon note, plus the attempt id where the note gives one. “none” means no note covers the feature.
- Note: kept short. inference marks a judgement call, such as the choice of state.
Boot and consoles
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
| Boot from USB (dev-signed FIT, KERN-A) | works | mainline at a804c57 + board DTS | mica DTS not sent | firstboot, 2026-11-02-2 | right port; power on with no keys held, because the recovery keys mark the stick bad (wifi, update) |
| Kernel at EL2 | works | mainline at a804c57 + board DTS (with x1-el2.dtso) | overlay in mainline; mica DTS not sent | firstboot, 2026-11-02-2 | All CPU(s) started at EL2; KVM not tried |
AP console (uart21, ttyMSM0) | partial | mainline at a804c57 + board DTS | mica DTS not sent | firstboot, 2026-11-02-2 | output from about 0.6 s; earlycon prints nothing before ttyMSM0 probes (inference: partial) |
| Panel console (simpledrm, then msm fbcon) | works | mainline at a804c57 + config fragment (mica-firstboot, mica-display, mica-console) | drivers in mainline | firstboot, 2026-11-02-2; display, 2026-11-03-4 | coreboot framebuffer first; msm takes over at 1.6 s; banner on tty1 |
| ramoops, read back through dropbox | works | mainline at a804c57 + config fragment (mica-firstboot) | drivers in mainline | firstboot, 2026-11-02-2; wifi, 2026-11-03-3 | read on the next stock boot; later boots start mid-log, with no banner |
| Netlog over Wi-Fi | works | n/a (gbook /init, attempt.sh netlog) | n/a | wifi, 2026-11-03-3 | whole boot from 0.0 s, sent after GBOOK: end |
| Reboot back to stock | works | mainline at a804c57 | n/a | firstboot, 2026-11-02-2; gpu, 2026-11-03-5 | stock boots once the stick is out |
SoC: power, clocks, SCMI/cpufreq, idle, suspend
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
| Every enabled DT node binds | works | mainline at a804c57 + patch 0002-0007 + board DTS + config fragments (mica-sync and others) | drivers in mainline; GMU driver FROMLIST (patches 0002-0006); mica DTS not sent | unbound, 2026-11-03-35; gmu-crypto, 2026-11-03-26 | unbound= 0: every interconnect and clock provider reaches sync_state(). A proxy, not a per-node bind check: a failed probe with no sync_state() consumer is not counted (the touchscreen’s HID probe still fails -22) |
| SCMI cpufreq (CPUCP mailbox) | works | mainline at a804c57 + board DTS (1000 ms window, ADR 0009) + config fragment (mica-scmi) | driver in mainline; window and ADR 0009 not sent | scmi, 2026-11-03-8, 2026-11-04-4; ADR 0009 | 3 clusters, 710 MHz-3.42 GHz; 0 of 17 boots stalled without PSCI cpuidle; the window is unverified |
| SCMI after a warm reset from stock | works | mainline at a804c57 + board DTS (1000 ms window, ADR 0009) + config fragment (mica-scmi) | driver in mainline; window and ADR 0009 not sent | scmi, 2026-11-03-9, -11; attempt 2026-11-04-13 | 1 of 1 warm reset on the current tree (Restart from stock; record: warm, AP reset in S0 with no G3->S5): firmware 0x20000, cpufreq 3 of 3, 0 timeouts, replies 0.1 ms. On 2026-11-03 2 of 2 warm resets failed (-110) with the 30 ms window and PSCI cpuidle on; which change fixed it is not separated (inference: ADR 0009 or the window) |
| CPU idle | partial | board DTS (ADR 0009) | LOCAL DTS deviation; stall report drafted, not sent | scmi, 2026-11-04-3, -4; ADR 0009 | a deliberate deviation (ADR 0009, accepted): PSCI cpuidle is off and cores idle in WFI; with it on, SCMI replies stalled up to 1 s; the revisit is open, since idle and suspend power are unmeasured (inference: partial) |
| s2idle round trip | works | mainline at a804c57 + patch 0008 + patch 0009 + board DTS + config fragment (mica-ec, mica-pcie) | 0008 FROMLIST (LKML 2026-01-02); 0009 LOCAL, not sent | suspend, 2026-11-04-11 (3 boots) | 3 of 3 cold boots, against 0 of 7 before 0009; test images (gbook.suspend=1); which low-power state was reached is unmeasured |
| Wake from s2idle: EC RTC alarm | works | mainline at a804c57 + board DTS + config fragment (mica-ec) | drivers in mainline; mica DTS not sent | suspend, 2026-11-04-5, -11 | rtc0 is cros-ec-rtc; suspend-wakeup stays empty, so no wakeup source counted the wake |
| Wake from s2idle by keyboard | partial | mainline at a804c57 + patch 0008 + patch 0009 + board DTS + config fragment (mica-ec) | drivers in mainline; mica DTS not sent | attempts 2026-11-04-14, -15 | every s2idle wake seen came on the EC interrupt (msmgpio 66 chromeos-ec, IRQ 181). On -15’s second boot, space woke it 7.1 s into s2idle and it resumed (rc 0, about 13 s of resume). On -15’s first boot, space did not wake it, and a power-button tap then left it powered off (no netlog, no SuzyQ, ramoops lost). On -14, an EC event woke it after 18.7 s with no key pressed. Lid and power-button wake untested |
RTC (cros-ec-rtc, rtc0) | works | mainline at a804c57 + board DTS + config fragment (mica-ec) | drivers in mainline; mica DTS not sent | ec, 2026-11-03-14; suspend; 2026-11-04-12 | its alarm woke every suspend test. It counts through power-off: 242 s on the RTC against 240 s on the build host between two 2026-11-04-12 boots, with G3 between them (netlog rtc-at-send against the file’s write time). It is not set to real time: it reads 1970-01-19 |
Storage and buses: USB, Type-C, PCIe, NVMe
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
| USB 2 host, all four controllers | works | mainline at a804c57 + board DTS + config fragment (mica-usb) | drivers in mainline; mica DTS not sent | usb, 2026-11-02-3 | webcam, FPMCU and the stick enumerate |
| USB 3, right port | works | mainline at a804c57 + board DTS + config fragment (mica-usb, mica-adsp) | drivers in mainline; mica DTS not sent | usb, 2026-11-02-5; dp, 2026-11-03-6 | 5000M; needs the ADSP for pmic_glink; runs alongside DP |
| USB 3, left port | untested | — | — | usb | parked until a session without the SuzyQ in the left port (owner, 2026-11-04 park round); the SuzyQ sits there; no device tested |
Type-C ports (UCSI over pmic_glink) | works | mainline at a804c57 + board DTS + config fragment (mica-usb, mica-adsp) | drivers in mainline; mica DTS not sent | usb, 2026-11-02-5; dp, 2026-11-03-6 | 2 ports, roles read; every read so far shows orientation=normal; connector@0 is the left port |
| PCIe, both controllers | works | mainline at a804c57 + board DTS + config fragment (mica-pcie) | drivers in mainline; mica DTS not sent | pcie, 2026-11-02-6 | Gen4 x4 (NVMe) and Gen3 x2 (WCN7850) link up |
| NVMe SSD (driver, I/O) | untested | — | driver in mainline | pcie, 2026-11-02-6 | parked until the spare SSD (M4, ADR 0002) (owner, 2026-11-04 park round); enumerates; the driver is held out (# forbid-y) until the owner approves a read-only step |
Network: Wi-Fi, Bluetooth
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
| Wi-Fi (WCN7850, ath12k) | works | mainline at a804c57 + board DTS + config fragment (mica-pcie) | driver, firmware and board data in mainline and linux-firmware; mica DTS not sent | wifi, 2026-11-03-1, -2 | 6 GHz at 320 MHz EHT, DHCP, ping; relinks after s2idle (suspend) |
Bluetooth (WCN7850 on uart14) | works | mainline at a804c57 + board DTS + config fragment (mica-bt) + firmware (linux-firmware qca/hmtbtfw20.tlv, hmtnv20.b112) | drivers and firmware in mainline and linux-firmware; mica DTS not sent | attempt 2026-11-04-14 (netlog) | hci_uart_qca binds, hci0 registers, setup completes (BTFW.HAMILTON.2.0.1, board id 0x112); no userspace, so no scan or pairing was tried. In s2idle something woke the system after 18.7 s from IRQ 181, possibly Bluetooth (inference) |
Display and GPU
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
| eDP panel (LG LP134WQ, 2560x1600) | works | mainline at a804c57 + patch 0001 + board DTS + config fragment (mica-display) | driver in mainline; panel entry LOCAL patch 0001, not sent; mica DTS not sent | display, 2026-11-03-4; panel-edp, 2026-11-03-9 | without 0001 it runs on conservative timings; 0001’s delays do not come from a datasheet |
| Backlight (PWM on the LPG) | works | mainline at a804c57 + board DTS + config fragment (mica-display) | drivers in mainline; mica DTS not sent | display, 2026-11-03-4; 2026-11-04-12 (3 boots) | set to 1023 and read back 1023, then restored to 2048, in 3 of 3 base-image boots (2026-11-04-12) |
DP alt mode, right port (connector@1) | works | mainline at a804c57 + board DTS + config fragment (mica-usb, mica-adsp, mica-display) | drivers in mainline; mica DTS not sent | dp, 2026-11-03-6 | a 1920x720 monitor on DP-2, with USB 3 alongside |
DP alt mode, left port (connector@0) | untested | — | — | dp | parked until a session without the SuzyQ in the left port (owner, 2026-11-04 park round); needs the SuzyQ out, so netlog only |
| GPU probe (Adreno X1-85) | works | mainline at a804c57 + board DTS + config fragment (mica-gpu) | driver and firmware in mainline and linux-firmware; mica DTS not sent | gpu, 2026-11-03-5; gmu-crypto, 2026-11-03-26 (with 0002-0007) | SQE and GMU firmware load; zap off under EL2; msm.separate_gpu_kms=1; no rendering test |
| GMU driver binding | works | mainline at a804c57 + patch 0002-0007 + board DTS + config fragment (mica-gpu) | FROMLIST (patches 0002-0006: v1 of “drm/msm: Attach a driver to GMU”, superseded by v2 on 2026-10-30); 0007 LOCAL (Omarchy’s fix-up, not ours to send) | gmu-crypto, 2026-11-03-26 | bound 3d6a000.gmu; the GMU left the unbound= list |
Input and EC
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
ChromeOS EC (cros-ec-spi) | works | mainline at a804c57 + board DTS (GPIO chip-select) + config fragment (mica-ec) | drivers in mainline; mica DTS not sent | ec, 2026-11-03-14 | the hardware chip-select gave garbage (attempt 2026-11-03-13, error -28) |
| Built-in keyboard | works | mainline at a804c57 + board DTS + config fragment (mica-ec) | drivers in mainline; mica DTS not sent | ec, 2026-11-03-14 | 389 events in 20 s; layout, Fn and top-row keys not checked |
| Keyboard backlight | works | mainline at a804c57 + board DTS + config fragment (mica-ec) | drivers in mainline; mica DTS not sent | ec, 2026-11-03-14 | 0 and 100 set and read back; the owner saw it light |
| Touchpad (I2C HID) | works | mainline at a804c57 + board DTS + config fragment (mica-touch) | drivers in mainline; mica DTS not sent | touch-tpm, 2026-11-03-16 | 15554 events in 30 s |
| Touchscreen (I2C HID) | broken | — | driver in mainline; cause unknown | touch-tpm, 2026-11-03-15 to 2026-11-04-2; 2026-11-04-12 | parked until a new touch lead (owner, 2026-11-03): reads turn to 0xff partway through; the DT matches stock. On 2026-11-04-12 it probed in 1 of 3 boots (2731 events from event4 while touched, IRQ 202 from 1 to 377) and failed -22 in the other two |
| Fans | partial | mainline at a804c57 + board DTS (EC on spi13) + config fragment (mica-ec) | drivers in mainline; mica DTS not sent | ec, 2026-11-03-14 | EC hwmon reads 2 fans at 0 RPM (idle); the EC drives them; never seen spinning (inference: partial) |
| Battery reporting | partial | mainline at a804c57 + board DTS + config fragment (mica-adsp) | drivers in mainline; ADSP firmware not in linux-firmware | usb, 2026-11-02-5; suspend, 2026-11-04-11 | capacity has never been read: empty at 2026-11-02-5 and in every 2026-11-04 netlog capture; status reads Charging or Not charging; power_now sign unchecked (inference: partial) |
| Lid switch | untested | — | — | none | parked until a stock-tree or EC lead (owner, 2026-11-04 park round); no lid GPIO in the stock tree (dts/README.md) |
Power button and EC buttons (cros_ec_buttons) | works | mainline at a804c57 + board DTS + config fragment (mica-ec) | drivers in mainline; mica DTS not sent | ec, 2026-11-03-14; 2026-11-04-12 | one brief press gave 4 and 6 events (ec-buttons, event3) in two 2026-11-04-12 boots; the third had no press. What a press does without userspace is untested |
| Ambient-light sensor | untested | — | — | none | parked until a stock-tree or EC lead (owner, 2026-11-04 park round); not described in the DTS (dts/README.md) |
| Glowbar (lid light) | untested | — | — | none | parked until a stock-tree or EC lead (owner, 2026-11-04 park round); not described in the DTS (dts/README.md) |
Security
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
TPM (Ti50, cr50_i2c) | works | mainline at a804c57 + board DTS + config fragment (mica-tpm) | driver in mainline; mica DTS not sent | touch-tpm, 2026-11-03-15 | tpm0 and tpm-rng-0; probe traffic only (TCG_TPM2_HMAC kept out) |
| Crypto engine (QCE) | broken | — | QCE depends on BROKEN at the pin | gmu-crypto, 2026-11-03-26 | parked until upstream drops BROKEN for QCE (owner, 2026-11-04 park round); node disabled in the board DTS (Omarchy 2210); not parked by the owner |
Remoteprocs
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
| ADSP | works | mainline at a804c57 + board DTS (mica-el2.fixups) + config fragment (mica-adsp) | driver in mainline; stock firmware not in linux-firmware; mica DTS not sent | usb, 2026-11-02-5 | stock modem_a firmware; its start waits 5 s on the CDSP’s SSCTL reply (cdsp) |
| CDSP | works | mainline at a804c57 + board DTS (mica-el2.fixups) + config fragment (mica-adsp) | driver in mainline; stock firmware not in linux-firmware; mica DTS not sent | cdsp, 2026-11-03-7 | still running at about 130 s; FastRPC is =m, so nothing uses it yet |
Multimedia
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
| Speakers | works | base image bc62c69e… (v2026.11.05, the sound node in the board DTS; proven first on the TEST IMAGE with dts/diag/sound.dtsi), kernel #35: all four units play and survive the SoundWire masters’ runtime PM wakes with local patches 0011 (WSA884x clk_stop_mode1) and 0010 (boot 6 of 2026-11-05: tones a minute apart, a tone after 10 minutes idle, the four names heard); in the board DTS since 2026-11-05 (9d120a5) | no (M5 hold) | audio-2026-11-04.md: aplay -l card 0 X1E80100-Dell-XPS-13-9345, playback device 1; 2026-11-05-1 with both masters’ power/control=on: the owner heard “front left, front right, rear right, rear left”; without it the clock-stop exit loses the re-enumeration by chance (two units on -28, none at 69.9 s on -1) | kernel/mica-audio.config (RESET_GPIO=y, GPIO_SHARED_PROXY=y, attempts -26 and -27); ticket 16 closed 2026-11-05 (the udev rule was not a workaround and is gone; patches 0011 and 0010); the node is in the board DTS; released as v2026.11.05 on 2026-11-05 (attempt 2026-11-05-5: 3 clean cold boots, the four names heard on each) |
| Microphones | works | the same image, boot 7 of 2026-11-05: arecord on capture device 3 (UCM Mic) recorded the owner’s voice and the playback was heard | no (M5 hold) | audio-2026-11-04.md, “The capture test”: the owner heard the playback (9:26 AM EST); the file’s RMS varies second by second | mic rail EN_PP3300_MIC (TLMM 69, stock overlay) as the VA macro’s vdd-micb-supply, the DMIC rate the Yoga’s (dts/README.md, “Unverified”); in the board DTS since 2026-11-05 |
| Headphone output | untested | — | — | none | parked until M3 (owner, 2026-11-04 park round); the stock tree disables the WCD938x headset codec (9345 diff); whether mica has a jack is not confirmed |
| Camera | untested | — | — | usb, 2026-11-02-3 | parked until M3 (owner, 2026-11-04 park round); the USB webcam 1bcf:28dc enumerates on usb_2; no capture tried |
| Fingerprint | untested | — | — | usb, 2026-11-02-3 | parked until M3 (owner, 2026-11-04 park round); the FPMCU 18d1:5406 enumerates on usb_mp; nothing more tried |
Power
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
| Idle power | untested | — | — | suspend | parked until a battery-only power session (owner, 2026-11-04 park round); unmeasured, because the SuzyQ powers the bench; ADR 0009 raises it by an unknown amount |
| Suspend power | untested | — | — | suspend, 2026-11-04-11 | parked until a battery-only power session (owner, 2026-11-04 park round); unmeasured; the battery reads as charging on the bench |
Deep suspend (mem_sleep=deep) | broken | — | — | attempt 2026-11-04-16 (AP and EC consoles, ramoops) | parked until a debug boot with no_console_suspend (owner, 2026-11-04 park round: a failed test may be parked). mem with mem_sleep=deep: the EC saw the AP sleep (“Handle sleep: 1”, EC 145.3 s), then “AP_RST_L transitions not expected” (152.3 s, about 7 s in, before the 20 s alarm) and a host reboot. The kernel log stops at “Suspending console(s)” (136.0 s) |
Userspace
| Feature | State | Where it works | Upstream | Evidence | Note |
|---|---|---|---|---|---|
Initramfs (busybox /init) | works | n/a (gbook kernel/initramfs/init) | n/a | firstboot, 2026-11-02-2 | captures, then reboots to stock; loads no modules |
| Rootfs from USB | works | board DTS + kernel/initramfs/init (switch_root) + the M3 rootfs (scripts/build-rootfs.sh) | n/a | rootfs, 2026-11-04-18, -19; release boots -21 to -23; fast sweep -24 (m3-end 74 s); rootfs side -25 (m3-end 49 s, cage 37.8 s) | Debian sid arm64 from the stick’s partition 2 (gbook-root): /init finds it by kern_guid, systemd boots, Wi-Fi joins in userspace (wpa_supplicant, networkd DHCP), Ghostty 1.4.0 runs under cage on the panel; the rootfs sends its netlog and journal over that link, and the build host reaches it by key-only ssh (ADR 0012 update) |
| Mesa (freedreno) | works | mainline at a804c57 + patch 0002-0007 + the M3 rootfs (Mesa 26.2.6-1) | drivers in mainline; GMU driver FROMLIST | rootfs, 2026-11-04-18, -19; release boots -21 to -23 | eglinfo: OpenGL 4.6 Core on “Adreno X1-85”; kmscube on the display card (card2) exit 0, renderer Adreno X1-85; cage’s wlroots renderer “Adreno X1-85”, GLES 3.2; Vulkan on Turnip: vkcube through cage’s Wayland socket selected “Adreno X1-85” and rendered (boot 3) |
Milestone M2: where it stands
M2 is done (2026-11-04, 12:38 PM ET): all four criteria are met. Criterion 1 is met by the unbound= proxy, and the s2idle row’s boots ran on test images.
M2 is “board on the console” in ADR 0010, from the milestones note, section 4. The ADR’s bar has four criteria:
- every enabled DT node binds;
- the M2 rows read
works: USB host, Type-C, PCIe enumeration, Wi-Fi, eDP + backlight, GPU probe, EC keyboard, touchpad, TPM, SCMI cpufreq and the s2idle round trip; - every other row reads
worksorpartial, or is parked with a note; - one base image (no debug command-line or DT extras) has 3 clean cold boots by
scripts/attempt.sh tally.
| Criterion | Met? | Why |
|---|---|---|
| 1. Every enabled DT node binds | met (by proxy) | measured by unbound= (no sync_state() pending line), not per node: 0 since attempt 2026-11-03-35 (unbound), and unbound=0 in the health column of every 2026-11-04 row of dumps/firstboot/attempts.tsv |
2. The M2 rows read works | met | all twelve; the backlight was set and read back in 3 of 3 base-image boots (2026-11-04-12). The s2idle row’s boots ran on test images (gbook.suspend=1) |
3. Every other row is works, partial or parked | met | Bluetooth works; keyboard wake from s2idle is partial (1 of 2); deep suspend failed and is parked (a reset about 7 s into deep sleep); 16 more rows are parked (owner, 2026-11-04 park round), and the touchscreen was parked on 2026-11-03 after three boots: 18 parked rows in all |
| 4. A base image with 3 clean cold boots | met | attempt 2026-11-04-12, kernel #26 (7.3.0-gbook-gf12f3f6-ga804c5742bff-dirty), attempt.sh stick: base image; 3 cold boots, 3 clean (below) |
Under criterion 3, rootfs, Mesa and the audio state are also M3 criteria in the ladder (section 4).
Counts (checked by scripts/status_check.py): 55 rows: 36 works, 5 partial, 3 broken, 11 untested; 14 parked.
The line above is the one scripts/check.sh compares with the tables (retro-8/06), so it changes with them.
Criterion 4, read-only from dumps/firstboot/attempts.tsv (gitignored), at 11:24 AM ET:
$ scripts/attempt.sh tally '^2026-11-04-12$'
group 1 /^2026-11-04-12$/: attempts=1 health=1 boots=3 known=3 stalled=0 clean=3 unknown=0 max=0.1-0.1ms
2026-11-04-12 0/3
- The base image: packaged with no command-line or DT extras;
attempt.sh verifyrecorded “base image: no command-line or DT extras” and image sha2562f08fb87463eeac6. All three boots started cold (ECG3->S5). - What “clean” means: in
tally, “clean” is an SCMI verdict: no timeout, and every reply under 100 ms (scripts/boot_health.py). It does not judge the whole boot. - Before 2026-11-04-12 the only row with 0008 + 0009 was
2026-11-04-11, a test image (gbook.suspend=1). - Next: M3, userspace from USB (a rootfs with Mesa, ADR 0010).
dts
x1e80100-google-mica.dts is the mainline device tree for the Dell XPS Googlebook (mica). Draft, never
booted (2026-10-21). Booted since 2026-11-02; each feature’s state is in docs/status.md.
- Experiments: device-tree diagnostics go in
diag/as.dtsifragments, built on top withGBOOK_DTS_EXTRA, not into this file (2026-11-03). - Base: forked from
arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dtsat torvalds/linuxa804c5742bff224d4cb4d25dd383554d598be32b. The first commit is the unmodified copy, sogit log -pshows every mica change. The SoC dtsi at that pin ishamoa.dtsi(withhamoa-pmics.dtsi), notx1e80100.dtsi. - Board changes: from the stock Googlebook OS tree (stock-dtb.md), mapped to
mainline labels in 9345-dts-diff.md. The stock tree is two files:
dumps/recovery-16471258/dtbo/entry00-base.dts(the SoC) andentry01-overlay.dts(the board: regulators, the sound card’s overrides, thermal trips). Search both; a value “not in the stock tree” was found in the overlay on 2026-11-04 (retro-12/01). - Compatible:
"google,mica", "qcom,x1e80100", so depthcharge’s FIT matcher (google,mica-rev*-sku*down togoogle,mica) picks it.
Build and check
scripts/setup-toolchain.sh # once: sparse kernel checkout at a804c57 + dtschema venv + schema, in ~/.cache/gbook
scripts/build-dtb.sh ~/.cache/gbook/linux-a804c57 dts/x1e80100-google-mica.dts /tmp/mica.dtb # cpp + dtc, Kbuild W=0
~/.cache/gbook/venv-dtschema/bin/dt-validate -s ~/.cache/gbook/schema-a804c57.json /tmp/mica.dtb
# first-boot DTB: the same base plus the pinned x1-el2.dtbo, applied with fdtoverlay like Kbuild's -dtbs targets
scripts/build-dtb.sh ~/.cache/gbook/linux-a804c57 dts/x1e80100-google-mica.dts /tmp/mica-el2.dtb \
~/.cache/gbook/linux-a804c57/arch/arm64/boot/dts/qcom/x1-el2.dtso
scripts/check.sh builds and validates both DTBs, and also fails when the keymap drifts from stock. When the google,mica binding
lands, remove its allowance in check.sh.
State 2026-10-21:
- Build: no warnings.
dt-validate: one finding.google,micais not yet inDocumentation/devicetree/bindings/arm/qcom.yaml; upstreaming needs that binding patch. The unmodified 9345 validates clean with the same schema.- The keymap in the built DTB is identical to stock (119 entries).
- Every board-specific TLMM pin in the built DTB traces to the stock tree. A programmatic check compared the
decompiled DTB with the stock overlay and base. The only other pins are SoC-level states from
hamoa.dtsi(QUP UARTs, LPASS) and the PCIe clkreq pins the stock base also sets. No 9345-only pin survived.
What changed from the 9345
| Area | Change | Evidence |
|---|---|---|
| Identity | model, compatible | depthcharge fit.c matching |
| Console | /chosen/stdout-path = "serial0:115200n8" (serial0 = uart21), added 2026-10-21 | stock base chosen; ADR 0006 |
| Keyboard, lid | removed the I2C keyboard (i2c0 0x5), GPIO lid (92) and camera LED (110); added cros_ec on spi13 (int TLMM 66, 3 MHz) with cros-ec-keyb 8x18 and the stock keymap; spi13 CS on gpio55 (stock pin) as cs-gpios, a mainline choice after trogdor/herobrine; the hardware CS gave garbage, the GPIO CS works (attempts 2026-11-03-13 and -14, ec-2026-11-03.md) | stock overlay; no lid or LED GPIO in the stock tree |
| Touchpad | moved from i2c17 to i2c0 0x2c; int TLMM 3; reset TLMM 2 held high by pinctrl; vdd L12B, vddl a 3.3 V switch on TLMM 36 | stock overlay |
| Touchscreen | hid-descr-addr 0; vdd L15B, vddl a 3.3 V switch on TLMM 88 (150 ms startup); report-enable TLMM 89 low (replaces the 9345’s TLMM 75) | stock overlay |
| SCMI | arm,max-rx-timeout-ms = <1000> on /firmware/scmi (250 on attempts -20 to -22), unverified diagnostic with no stock counterpart (2026-11-03: the default 30 ms failed on 5 of 7 cold starts) | scmi-cpucp-timeouts.md, option 2 |
| CPU idle | deviation from stock (ADR 0009, superseding ADR 0008): domain-idle-states (cluster_c4) deleted from cpu_pd0-11, so cpuidle-psci does not load and cores idle in WFI; the cluster and system states stay as stock. A core in PSCI ret did not take a pending SPI until a PPI ran (SCMI stalls on whichever CPU the IRQ was routed to, 2026-11-04-3) | stock base has cluster_c4 on every CPU; scmi-2026-11-03.md |
| Crypto engine | &crypto disabled, not from the stock tree: QCE is BROKEN at the pin, and the unbound node kept aggre2_noc and mc_virt from sync_state() (Omarchy 2210, 2026-11-03) | drivers/crypto/Kconfig:531 |
| TPM | google,cr50 on i2c10 0x50, int TLMM 91, EDGE_FALLING | stock overlay; tpm_tis_i2c_cr50.c:751 requests IRQF_TRIGGER_FALLING at a804c57 |
| eUSB2 repeaters | i2c9 → i2c5; 0x4f (reset 184) and new 0x4b (reset TLMM 10) for usb_2 | stock overlay |
| USB | usb_2 host plus usb_2_hsphy | stock overlay (usb4, usb2_phy_4) |
| Backlight | pwm-backlight: pmk8550_pwm channel 0, 270000 ns; enable PMC8380-3 GPIO 4; supply switch PMC8380-3 GPIO 10. Panel enable TLMM 74 removed. 2026-11-03: the edp_bl_en pin state moved from TLMM 74 (left over from the 9345) to PMC8380-3 GPIO 4; the PWM pin (PMK8550 GPIO 5) takes the stock edp_pwm_active config | stock overlay (sde_edp qcom,edp-backlight-en-gpio, fragment 76 edp_pwm_active); same wiring as mainline T14s |
| Always-on rails | camera 3.3 V (TLMM 206), fingerprint MCU (TLMM 22) | stock overlay |
| Reserved GPIOs | union of stock <34 2> <44 4> and the 9345’s <76 4> <238 1> | stock base gpio-reserved-ranges |
| EL2 overlay | x1-el2.dtso applied, then dts/mica-el2.fixups deletes iommus from remoteproc_adsp and remoteproc_cdsp (2026-11-02) | stock base entry00-base.dts:5513, :12925 have none; with it, mica’s TrustZone refuses the PAS resource-table call (-EIO) |
| Audio | added 2026-11-05 (from dts/diag/sound.dtsi, booted as a TEST IMAGE 2026-11-04-26 to 2026-11-05-6; the moved node proven on the base image bc62c69e…, attempt 2026-11-05-5, 3 clean cold boots, the four names heard on each, released as v2026.11.05): sound (qcom,x1e80100-sndcard, model X1E80100-Dell-XPS-13-9345), 4x WSA8845 on swr0 and swr3 (speaker@0,0/@0,1 per bus, reset TLMM 204 for swr0’s pair, 205 for swr3’s, vdd-1p8 L15B), 4 DMICs on lpass_vamacro (LPI 6/7, 8/9; capture heard 2026-11-05), the mic rail EN_PP3300_MIC (TLMM 69, fixed, always-on) as vdd-micb-supply; pin states dmic_pp3300_en, spkr_02_sd_n_active, spkr_13_sd_n_active. Modelled on the Yoga Slim 7x at a804c57. Deviation: stock’s active state drives the shutdown pins high, the pin states here start them low (the Yoga’s way, the codec driver’s reset-gpios releases them); all four units enumerate and play that way (ticket 16) | stock base entry00-base.dts: card 17905 (routing 17921, dmic pins 17925), wsa_swr_master 17641 (codecs 17658, 17675; pins 16697, 16760; shutdown 18214 = TLMM 204, 5080), wsa2_swr_master (pins 17049, 17112; shutdown 18222 = TLMM 205, 5115), pm_humu_l15 10857, va-macro@7660000 17499; stock overlay entry01-overlay.dts 1389 (regulator-dmic-en), 1374 (dmic_pp3300_pin); audio-2026-11-04.md |
| Firmware paths | qcom/x1e80100/google/mica/… | not in linux-firmware. 2026-11-02: ADSP and CDSP names changed to the stock split files adsp.mdt/adsp_dtb.mdt and cdsp.mdt/cdsp_dtb.mdt, read from the stock modem_a partition’s image/ (recovery stick copy, build 16560795) |
Kept from the 9345 and confirmed in the stock tree: retimers and all six rail GPIOs; USB-C orientation GPIOs 121
and 123; eDP 3.3 V on TLMM 70 and HPD on 119; NVMe 3.3 V on TLMM 18; PCIe4 (146-148) and PCIe6a (152-154); Wi-Fi
3.3 V on TLMM 214, WLAN 117, BT 116; uart14 BT; uart21 debug.
First-boot additions
Decided 2026-10-21 (What goes into the first-boot DTB), in the tree since 2026-11-02:
reserved-memory:framebuffer@e4800000,reg = <0x0 0xe4800000 0x0 0x2200000>,no-map. The address comes from the stocksplash_region, the size from coreboot’s reservation.- The first-boot DTB is
x1e80100-google-mica-el2.dtb= this DTB +x1-el2.dtbofrom the pinned kernel.scripts/build-dtb.shandcheck.shbuild and validate both. 2026-11-02: both build with no warnings, both give only thegoogle,micadt-validatefinding, and the-el2DTB keepscompatible = "google,mica". The reservation overlaps nohamoa.dtsiregion (nearest:tags_mem1, ending at0xe36a0000).
Unverified (check at first boot)
The left/right labels on the USB-C connectors (copied from the 9345).2026-11-03: swapped on mica.connector@0(retimer oni2c3,usb_1_ss0,mdss_dp0) is the left port,connector@1(i2c7,usb_1_ss1,mdss_dp1) the right one (dp-2026-11-03.md).usb_2_hsphysupplies (from other hamoa boards; the stock overlay does not set them).usb_mpQMP PHYs: stock disables both, and this draft keeps the 9345’s enabled ones sousb_mpprobes.- The panel model (
edp-panelprobes it over AUX) and the PWM period. - The
edp_bl_enpin config on PMC8380-3 GPIO 4 (function = "normal", from the T14s; stock has no pin state for it). - Through the EC, no DT node needed (2026-11-03, attempt -14): keyboard backlight (
chromeos::kbd_backlight), fan RPM (cros_echwmon). - Audio (2026-11-05, moved from
dts/diag/sound.dtsi):vdd-io-supplyL12B on the four codecs (not in the stock codec nodes; everya804c57x1e80100 board uses it);qcom,dmic-sample-rate = <4800000>(the Yoga’s; stock sets only the clock divider 16); the 3.3 VEN_PP3300_MICas the VA macro’svdd-micb-supply(the Yoga’s is a 1.8 V LDO; the stock rail is always-on, so a wrong consumer binding is harmless); which physical unit each prefix (WooferLeft,TweeterLeft,WooferRight,TweeterRight, the UCM profile’s names) drives: stock callsswr0’s pair SpkrLeft/SpkrRight andswr3’s Spkr2Left/Spkr2Right. The speakers play (ticket 16, 2026-11-05). 9:26 AM EST 2026-11-05: capture passed (the owner heard the recording played back, audio-2026-11-04.md, “The capture test”), so the DMIC rate and the mic rail work as written; the values stay the Yoga’s where stock has no counterpart, and the unit-to-prefix question stays open. - Not described yet: EC PWM, Glowbar, ambient-light sensor (the 9345 file has no sound node at this commit; mica’s is above), camera (USB), fingerprint (USB).
Before sending upstream
- Add
google,micatoDocumentation/devicetree/bindings/arm/qcom.yaml(x1e80100 boards). - Reorder the
&labelblocks alphabetically, as qcom reviewers expect. They are left in place here so the diff against the 9345 copy stays readable. - Replace the copied 9345 copyright header with the right attribution for a derived file.
Source files
x1e80100-google-mica.dts (the board DTS) and mica-el2.fixups (the
differences from mainline’s x1-el2.dtso), as exported.
Kernel configuration fragments
Merged onto the arm64 defconfig in name order after mica-firstboot.config (scripts/build-kernel.sh). A # require:
line must end =y in the result; a # forbid-y: line must not. Each fragment’s own header follows. Also here:
firmware.sha256 (hashes of the vendor firmware the initramfs stages; the blobs are not
redistributed), dt-gaps.txt and known-failures.txt.
mica-adsp.config
Phase 3, ADSP: boot the audio DSP so pmic_glink (Type-C orientation and mode, battery) can come up.
Source: the USB run (docs/recon/usb-2026-11-02.md): pmic-glink stayed deferred with "failed to initialize pdr".
Firmware: the stock modem_a partition's image/adsp.mdt + .bNN and adsp_dtb.mdt + .bNN (build
ADSP.HT.5.9.c5-00032.4-HAMOA-1), packed into the initramfs from ~/.cache/gbook/firmware (ADR 0005: not in git).
The remoteproc driver for qcom,x1e80100-adsp-pas and its helpers (all =m in defconfig at a804c57).
require: REMOTEPROC
require: RPMSG_QCOM_GLINK
require: QCOM_SMEM
require: QCOM_SMP2P
require: QCOM_AOSS_QMP
require: QCOM_IPCC
require: QCOM_PDR_HELPERS
require: QCOM_QMI_HELPERS
require: FW_LOADER
mica-audio.config
M3, audio (ticket 10, 2026-11-04): the sound card of the board DTS (dts/diag/sound.dtsi until 2026-11-05; 4x WSA8845 on swr0 and swr3, 4 DMICs on
the VA macro, the ADSP's APM and PRM services over GPR). scripts/dt-kconfig-map.py on the sound -el2 DTB,
2026-11-04: every driver below was =m, and the initramfs loads no modules. The topology
qcom/x1e80100/X1E80100-Dell-XPS-13-9345-tplg.bin is requested at the card's component probe
(sound/soc/qcom/qdsp6/topology.c:1422, q6apm.c q6apm_audio_probe), so scripts/stage-firmware.sh puts it in the
initramfs. Firmware-side: the stock ADSP (adsp.mdt) must offer the "avs/audio" protection domain; QCOM_PD_MAPPER
and QCOM_PDR_HELPERS are =y already (pmic_glink uses them).
require: SND_SOC_QDSP6_COMMON
require: SND_SOC_QDSP6_APM
require: SND_SOC_QDSP6_APM_DAI
require: SND_SOC_QDSP6_APM_LPASS_DAI
require: SND_SOC_QDSP6_PRM
require: SND_SOC_QDSP6_PRM_LPASS_CLOCKS
require: SND_SOC_QCOM_COMMON
require: SND_SOC_QCOM_SDW
require: SND_SOC_TOPOLOGY
require: SND_SOC_LPASS_MACRO_COMMON
require: REGMAP_SOUNDWIRE
require: QCOM_PDR_HELPERS
require: QCOM_PD_MAPPER
require: RPMSG_QCOM_GLINK_SMEM
mica-bt.config
M2, Bluetooth (owner, 2026-11-04 park round: "test"): the WCN7850's Bluetooth on uart14 (dts: &uart14 { bluetooth {
compatible = "qcom,wcn7850-bt" } }). scripts/dt-kconfig-map.py names BT_HCIUART (=m in arm64 defconfig) for it, so
the node never bound. hci_uart's QCA protocol (BT_HCIUART_QCA, already =y) probes it over serdev, powers it through
the WCN power sequencer (pwrseq-qcom-wcn) and, when the core powers the new controller on, downloads
qca/hmtbtfw<rom>.tlv and an NVM file chosen by board id (btqca.c:825-827, 919-921, qca_get_nvm_name_by_board at
a804c57); scripts/stage-firmware.sh stages all five hmtnv20.* candidates. No userspace (no BlueZ): the controller
is set up, then the core powers it off again; nothing scans or pairs.
require: BT_HCIUART_SERDEV
require: BT_HCIUART_QCA
require: SERIAL_DEV_BUS
require: SERIAL_QCOM_GENI
require: POWER_SEQUENCING_QCOM_WCN
mica-console.config
The owner's view on the Googlebook's own panel (2026-11-03): /init mirrors a readable status page to tty1 (fbcon on
the eDP panel; the kernel console stays on the serial port, console=ttyMSM0). The default 8x16 font gives 320x100
characters on the 2560x1600 13.4" panel, too small to read at arm's length; Terminus 16x32 gives 160x50
(lib/fonts/Kconfig:126-134). The command line selects it with fbcon=font:TER16x32 (scripts/package-in-container.sh).
require: FRAMEBUFFER_CONSOLE
require: DRM_FBDEV_EMULATION
require: VT_CONSOLE
mica-debug.config
Event tracing for the boot captures (owner round 2026-11-03: 1a), docs/research/debug-tools.md option 1.
arm64 defconfig:1970 sets "# CONFIG_FTRACE is not set", so the kernel had no tracefs and no trace events. The command
line (scripts/package-in-container.sh) arms scmi, i2c and qcom_geni_i2c events with trace_event= from start_kernel;
/init saves a filtered copy into the netlog. ENABLE_DEFAULT_TRACERS selects TRACING, which selects EVENT_TRACING,
RING_BUFFER and TRACEPOINTS (kernel/trace/Kconfig:179-188, :588-591). No function tracer.
require: EVENT_TRACING
require: TRACEPOINTS
mica-display.config
Phase 3, display: msm DRM drives the eDP panel, so the panel stays lit after the regulator core's unused-supply
cleanup. Source: attempt 2026-11-03-3 (docs/recon/wifi-2026-11-03.md): "VREG_EDP_3P3: disabling" at about 33 s,
because only simpledrm drives the screen and nothing claims the panel supply. The mica DTS already describes the
path (&mdss, &mdss_dp3 with an edp-panel on its aux-bus, &mdss_dp3_phy, pwm-backlight); every driver on it is =m
in defconfig at a804c57 and the initramfs loads no modules.
The GPU is left out: the command line carries msm.skip_gpu=1 (scripts/package-in-container.sh), because the GPU
would join the MDSS component match (drivers/gpu/drm/msm/msm_drv.c:1076-1086) and wait on gpucc.
MDSS/DPU/DP (drivers/gpu/drm/msm/Kconfig:3). Sub-options DPU, DP and KMS_FBDEV default to y.
require: DRM
require: DRM_MSM_DPU
require: DRM_MSM_DP
require: DRM_MSM_KMS_FBDEV
require: DRM_FBDEV_EMULATION
require: PWM
require: REGULATOR_FIXED_VOLTAGE
require: DRM_AUX_HPD_BRIDGE
mica-ec.config
Phase 3, EC: the ChromeOS EC on spi13, its keyboard and its children (owner round 2026-11-03: 1a 2a).
Source: every boot on 2026-11-03 (10 in dumps/firstboot/2026-11-03-netlog.txt) logs 77 lines of
"sync_state() pending due to a94000.spi" and not one cros_ec line. a94000 is spi13 (hamoa.dtsi:1905), the bus of
cros_ec ec@0 in dts/x1e80100-google-mica.dts. Its driver (qcom,geni-spi, drivers/spi/Kconfig:1013) is =m in arm64
defconfig, and the initramfs loads no modules, so the EC and cros-ec-keyb never probed.
The EC's children are MFD cells that cros_ec_dev adds per EC feature (drivers/mfd/cros_ec_dev.c:106-153):
cros-keyboard-leds for EC_FEATURE_PWM_KEYB ("chromeos::kbd_backlight", cros_kbd_led_backlight.c:201), the EC LEDs,
and the cros_ec hwmon (fan RPM, temperatures; SENSORS_CROS_EC is already =y).
require: CROS_EC
require: CROS_EC_SPI
require: CROS_EC_PROTO
require: MFD_CROS_EC_DEV
require: KEYBOARD_CROS_EC
require: SENSORS_CROS_EC
forbid-y: CROS_EC_TYPEC
forbid-y: CROS_EC_UCSI
mica-firstboot.config
First-boot config fragment for mica, merged onto arm64 defconfig at torvalds/linux a804c57:
scripts/kconfig/merge_config.sh -m arch/arm64/configs/defconfig kernel/mica-firstboot.config
make ARCH=arm64 LLVM=1 olddefconfig
Decided in .scratch/phase-2-first-boot/issues/06-kernel-tree.md (inputs: tickets 04 and 05).
Boot arguments are not set here; they go on the mkdepthcharge command line (ticket 08).
scripts/check.sh checks offline that every CONFIG_ line and every "# require:" symbol exists in a804c57 Kconfig.
After the merge, the build fails unless all of them are =y.
Silent-boot channel 1: the failed boot's printk in depthcharge's ramoops region (defconfig: PSTORE_RAM=m).
require: SERIAL_QCOM_GENI
require: SERIAL_QCOM_GENI_CONSOLE
require: QCOM_GENI_SE
require: PINCTRL_MSM
require: PINCTRL_X1E80100
require: QCOM_PDC
require: COMMON_CLK_QCOM
require: CLK_X1E80100_GCC
require: QCOM_RPMH
require: QCOM_COMMAND_DB
require: QCOM_CLK_RPMH
require: QCOM_RPMHPD
require: INTERCONNECT
require: INTERCONNECT_QCOM
require: INTERCONNECT_QCOM_X1E80100
require: ARM_SMMU
require: ARM_SMMU_QCOM
require: ARM_PSCI_FW
require: ARM_PSCI_CPUIDLE
require: ARM_PSCI_CPUIDLE_DOMAIN
mica-gpu.config
Phase 3, GPU: the Adreno X1-85 (gpu@3d00000, qcom,adreno-43050c01) with its GMU (gmu@3d6a000).
Source: attempt 2026-11-03-4 (docs/recon/display-2026-11-03.md): 3da0000.iommu (the GPU SMMU) stayed deferred on
"supplier 3d90000.clock-controller not ready", and the display only bound because the command line carried
msm.skip_gpu=1. scripts/dt-kconfig-map.py on the mica-el2 DTB: gpucc is the one GPU-path driver still =m.
The command line now carries msm.separate_gpu_kms=1 instead (drivers/gpu/drm/msm/msm_drv.c:57-66,1082;
adreno_device.c:275), so the GPU gets its own DRM device and a GPU failure cannot take the display with it.
Firmware: qcom/gen70500_sqe.fw and qcom/gen70500_gmu.bin (a6xx_catalog.c:1573-1577), loaded on the first open of
the GPU's DRM device (msm_drv.c msm_open -> load_gpu), staged by scripts/stage-firmware.sh from
linux-firmware-qcom 20260916-1. The zap shader is disabled by x1-el2.dtso, so a6xx_gpu.c:1507-1516 writes
SECVID_TRUST_CNTL directly and warns "Zap shader not enabled".
The GPU clock controller (drivers/clk/qcom/Kconfig:278): GDSCs and clocks for the GPU, GMU and adreno_smmu.
require: DRM_MSM
require: ARM_SMMU
require: ARM_SMMU_QCOM
require: PM_DEVFREQ
require: DEVFREQ_GOV_SIMPLE_ONDEMAND
require: PM_OPP
require: QCOM_LLCC
mica-pcie.config
Phase 3, PCIe: bring up both PCIe controllers and enumerate their devices.
Source: every boot so far left pcie@1bf8000 and pcie@1c08000 deferred ("Failed to parse Root Port: -517");
scripts/dt-kconfig-map.py on the mica-el2 DTB shows their suppliers are =m in defconfig at a804c57.
Safety: the NVMe slot holds the stock SSD, which is never written (ADR 0003, ticket 07). BLK_DEV_NVME stays =m
(not loaded), so enumeration reads PCI config space only and no NVMe command reaches the SSD.
QMP PCIe PHYs: gen4x4 phy@1bfc000 (pcie@1bf8000) and gen3x2 phy@1c0e000 (pcie@1c08000). Their menu parent
PHY_QCOM_QMP is already =y in mica-usb.config.
forbid-y: BLK_DEV_NVME
require: PCIE_QCOM
require: PCIEPORTBUS
require: PCI_MSI
require: POWER_SEQUENCING
require: PCI_PWRCTRL
require: PCI_PWRCTRL_PWRSEQ
mica-pmdebug.config
Suspend debugging (2026-11-04, ADR 0009's suspend test): attempts 2026-11-04-5 to -8 reset the AP about 0.7 s after
the s2idle wake. With patch 0008 the PCIe GDSC warning is gone and the kernel prints nothing before the reset, so
/init (gbook.suspend=1) turns on /sys/power/pm_print_times and pm_debug_messages: every device's suspend and resume
callback is logged as "calling <cb> @ <pid>, parent: <dev>" before it runs (drivers/base/power/main.c:227-241 at
a804c57), and the last such line before the reset names the device. Both files exist only with PM_SLEEP_DEBUG
(kernel/power/main.c:644, a def_bool on PM_DEBUG && PM_SLEEP, kernel/power/Kconfig:257-259).
require: PM_SLEEP_DEBUG
mica-rootfs.config
M3, the rootfs (ADR 0012, decision 6; .scratch/m3-rootfs ticket 01). systemd v262's README ("REQUIREMENTS") says
userspace firmware loading "is not supported and should be disabled in the kernel"; arm64 defconfig sets
FW_LOADER_USER_HELPER=y (built config, read 2026-11-04). Off here; firmware still loads from the filesystem
(Documentation/driver-api/firmware/fw_search_path.rst at a804c57). USB Attached SCSI for the rootfs stick: the stick
ran bulk-only so far (USB_UAS not set); UAS falls back to bulk-only for devices that lack it.
CONFIG_FW_LOADER_USER_HELPER is not set
forbid-y: FW_LOADER_USER_HELPER
mica-scmi.config
Phase 3, SCMI: CPU performance domains (cpufreq) through the CPUCP mailbox.
Source: every boot up to attempt 2026-11-03-7 left "firmware:scmi: supplier 17430000.mailbox not ready", then
"arm-scmi: failed to setup channels": the mailbox driver for qcom,x1e80100-cpucp-mbox
(drivers/mailbox/qcom-cpucp-mbox.c:205, Kconfig:343) is =m in defconfig, and the initramfs loads no modules.
The stock tree drives the same block: cpucp@18830000 (qcom,cpucp-v2) with reg 0x17430000 + 0x18830000, scmi mboxes
<cpucp 0 cpucp 2>, shmem at 0x18b4e000 (entry00-base.dts:384-402, :908-926, :10534-10540; stock also lists a vendor protocol@80 that hamoa.dtsi has not), as hamoa.dtsi does.
The CPUs take their "perf" power domain from scmi_dvfs (hamoa.dtsi:78-79 and the other cpu nodes).
require: MAILBOX
require: ARM_SCMI_PROTOCOL
require: ARM_SCMI_TRANSPORT_MAILBOX
require: ARM_SCMI_PERF_DOMAIN
require: ARM_SCMI_CPUFREQ
require: DEBUG_FS
mica-sync.config
Phase 3, the last never-bound devices (handoff 2026-11-03 item 3; kernel/dt-gaps.txt "sync_state lead").
Source: attempt -26 left unbound=6: four bandwidth-monitor PMUs (pmu@24091000 qcom,sc7280-llcc-bwmon;
pmu@240b3400/@240b5400/@240b6400 qcom,sdm845-bwmon), videocc@aaf0000 and camcc@ade0000, all =m in defconfig while
the initramfs loads no modules (scripts/dt-kconfig-map.py at a804c57). An unbound interconnect consumer keeps the
providers from sync_state() and their boot-time votes in place (the crypto node did, Omarchy 2210); whether these
do is what the boot shows. Kconfig at a804c57: QCOM_ICC_BWMON drivers/soc/qcom/Kconfig:266 (selects PM_OPP,
REGMAP_MMIO), SM_VIDEOCC_8550 drivers/clk/qcom/Kconfig:1981 (covers X1E80100), CLK_X1E80100_CAMCC :247.
require: QCOM_ICC_BWMON
require: SM_VIDEOCC_8550
require: CLK_X1E80100_CAMCC
mica-touch.config
Phase 3, touch: the I2C HID touchpad and touchscreen (owner 2026-11-03, after the retro's DT gap check).
Source: scripts/dt-kconfig-map.py --gaps listed "hid-over-i2c I2C_HID_OF=m touchscreen@10,touchpad@2c": the
OF glue for hid-over-i2c (drivers/hid/i2c-hid/Kconfig:25, selects I2C_HID_CORE) is =m in arm64 defconfig, and the
initramfs loads no modules, so neither node has ever bound. Nodes: touchpad@2c on i2c0 (descriptor 0x20, TLMM 3)
and touchscreen@10 (LG SW42101M, descriptor 0x0, TLMM 51) in dts/x1e80100-google-mica.dts.
HID_MULTITOUCH: a precision touchpad and a touchscreen report as multitouch; hid-generic would see a mouse at best.
require: I2C_HID
require: HID_GENERIC
require: INPUT_EVDEV
require: I2C_QCOM_GENI
mica-tpm.config
Phase 3, TPM: the Ti50 GSC's TPM on I2C (owner 2026-11-03, after the retro's DT gap check).
Source: scripts/dt-kconfig-map.py --gaps listed "google,cr50 TCG_TIS_I2C_CR50=m tpm@50": the driver
(drivers/char/tpm/Kconfig:116) is =m in arm64 defconfig, so tpm@50 has never bound. It knows the Ti50 by its
DID/VID ("ti50 DT" / "ti50 OT", tpm_tis_i2c_cr50.c:693-702) and prints "<name> TPM 2.0 (i2c 0x50 irq N id 0x..)".
TPM traffic: probe sends startup/self-test and capability reads (tpm_chip_register); reboot sends
TPM2_Shutdown(CLEAR) (tpm-chip.c:272), as any Linux kernel with a TPM does. No NV or key writes.
require: TCG_TPM
require: HW_RANDOM_TPM
forbid-y: TCG_TPM2_HMAC
mica-usb.config
Phase 3, USB: build the whole USB chain in, so the module-free initramfs can use it.
Merged after kernel/mica-firstboot.config by scripts/build-kernel.sh.
Source: the first boot (docs/recon/firstboot-2026-11-02.md): all four dwc3 controllers stayed deferred
("failed to register DWC3 Core") because every PHY, repeater, retimer and I2C driver below was =m in defconfig
at a804c57 and no module was loaded. Each line maps a compatible in x1e80100-google-mica-el2.dtb to its driver.
I2C buses that carry the PS8830 retimers and PTN3222 redrivers (qcom,geni-i2c).
require: USB_DWC3
require: USB_DWC3_QCOM
require: USB_XHCI_HCD
require: USB_XHCI_PLATFORM
require: USB_STORAGE
require: USB_HID
require: USB_ROLE_SWITCH
require: SPMI_MSM_PMIC_ARB
require: MFD_SPMI_PMIC
Local kernel patches
Applied by the build onto torvalds a804c57 in name order. Line 1 of each file is its status (the ChromiumOS words,
LOCAL for CHROMIUM: decision 0007). The four LOCAL gbook patches stay unpublished until milestone M5
(decision 0010); their status lines are listed.
| Patch | Status | File |
|---|---|---|
0001-drm-panel-edp-add-lgd-0x0804.patch | LOCAL gbook (not sent: the EDID gives only part of the model name), checked 2026-11-04 | held until M5 |
0002-drm-msm-a6xx-cxpd-device-link-stateless.patch | FROMLIST [RFT 1/5] of “drm/msm: Attach a driver to GMU” v1 (Akhil P Oommen) 20260513-gmu-sync-state-fix-v1-1-6e33e6aa9b4f@oss.qualcomm.com; v2 has it as 2/8 20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04 | 0002-drm-msm-a6xx-cxpd-device-link-stateless.patch |
0003-drm-msm-centralize-standalone-drm-check.patch | FROMLIST [RFT 2/5] of “drm/msm: Attach a driver to GMU” v1 (Akhil P Oommen) 20260513-gmu-sync-state-fix-v1-2-6e33e6aa9b4f@oss.qualcomm.com; dropped in v2 20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04 | 0003-drm-msm-centralize-standalone-drm-check.patch |
0004-drm-msm-adreno-fix-drvdata-typecast.patch | FROMLIST [RFT 3/5] of “drm/msm: Attach a driver to GMU” v1 (Akhil P Oommen) 20260513-gmu-sync-state-fix-v1-3-6e33e6aa9b4f@oss.qualcomm.com; v2 has it as 1/8 20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04 | 0004-drm-msm-adreno-fix-drvdata-typecast.patch |
0005-drm-msm-component-model-for-standalone-gpu.patch | FROMLIST [RFT 4/5] of “drm/msm: Attach a driver to GMU” v1 (Akhil P Oommen) 20260513-gmu-sync-state-fix-v1-4-6e33e6aa9b4f@oss.qualcomm.com; v2 has it as 5/8 20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04 | 0005-drm-msm-component-model-for-standalone-gpu.patch |
0006-drm-msm-attach-a-driver-to-the-gmu.patch | FROMLIST [RFT 5/5] of “drm/msm: Attach a driver to GMU” v1 (Akhil P Oommen) 20260513-gmu-sync-state-fix-v1-5-6e33e6aa9b4f@oss.qualcomm.com; v2 has it as 6/8 20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com; copy: Omarchy linux-omarchy-aarch64 (below), ported to v7.2.8 by Brandon Williams; checked 2026-11-04 | 0006-drm-msm-attach-a-driver-to-the-gmu.patch |
0007-drm-msm-gmu-optional-and-lock-init.patch | LOCAL Brandon Williams via Omarchy (third-party fix-up on 0002-0006), https://github.com/omacom/omarchy-pkgs/tree/master/pkgbuilds/linux-omarchy-aarch64, checked 2026-11-04 | 0007-drm-msm-gmu-optional-and-lock-init.patch |
0008-clk-qcom-gcc-x1e80100-pcie-gdsc-retention.patch | FROMLIST [6/7] (Krishna Chaitanya Chundru) 20260102-pci_gdsc_fix-v1-6-b17ed3d175bc@oss.qualcomm.com, changes requested on linux-arm-msm patchwork; regenerated against a804c57; checked 2026-11-04 | 0008-clk-qcom-gcc-x1e80100-pcie-gdsc-retention.patch |
0009-clk-qcom-gcc-x1e80100-pcie-phy-gdsc-retention.patch | LOCAL gbook (ours; not sent: upstream hold until M5), checked 2026-11-04 | held until M5 |
0010-soundwire-qcom-rearm-enumerator-after-clock-stop.patch | LOCAL gbook (ours; not sent: upstream hold until M5), checked 2026-11-05 | held until M5 |
0011-asoc-wsa884x-clk-stop-mode1.patch | LOCAL gbook (ours; not sent: upstream hold until M5), checked 2026-11-05 | held until M5 |
Build scripts
The header of each build script in the private repository, as of this build. The scripts themselves run on the project’s build host and are not published yet (their paths and listeners are specific to it).
setup-toolchain.sh
Set up the DTS toolchain outside the repo (idempotent): a sparse torvalds/linux checkout at the pinned commit
(DTS sources, bindings, every Kconfig file and the arm64 defconfig), a dtschema venv, and the processed binding schema.
scripts/setup-toolchain.sh -> $GBOOK_TOOLS (default ~/.cache/gbook)
PYTHON=python3.14 scripts/setup-toolchain.sh (the macOS host: bare python3 is 3.15 there)
GBOOK_FULL=1 scripts/setup-toolchain.sh (the build host: the whole tree, for scripts/build-kernel.sh; ~1.5 GB)
Then: scripts/build-dtb.sh ~/.cache/gbook/linux-<short> dts/x1e80100-google-mica.dts /tmp/mica.dtb
~/.cache/gbook/venv-dtschema/bin/dt-validate -s ~/.cache/gbook/schema-<short>.json /tmp/mica.dtb
The commit is the one the mica DTS was forked from (ADR 0004); change both together.
build-dtb.sh
Compile a board DTS against a mainline kernel tree the way Kbuild does (cpp, then dtc), without building a kernel.
scripts/build-dtb.sh <kernel-tree> <file.dts> [out.dtb] [extra.dtsi ...] [overlay.dtso ...] [fixes.fixups ...]
A .dtsi is a diagnostic built on top of the board DTS (a wrapper #includes the DTS, then each .dtsi in order), so an
experiment from dts/diag/ never edits the stock-traceable DTS (retro 2026-11-03; GBOOK_DTS_EXTRA in package-image.sh).
With overlays, out.dtb is the base with each overlay applied by fdtoverlay, as Kbuild builds "-dtbs" targets
(scripts/Makefile.dtbs), e.g. the first-boot DTB:
scripts/build-dtb.sh <tree> dts/x1e80100-google-mica.dts /tmp/mica-el2.dtb <tree>/arch/arm64/boot/dts/qcom/x1-el2.dtso \
dts/mica-el2.fixups
A .fixups file lists "delete-property <label> <property>" lines (labels from /__symbols__), applied with fdtput
after the overlays, because overlays cannot delete properties.
The kernel tree only needs arch/arm64/boot/dts/qcom, include/dt-bindings and scripts/dtc/include-prefixes
(a sparse checkout is enough). The DTS may live outside the tree: its own directory is searched first.
Preprocesses with $CC -E like Kbuild (Apple's cpp wrapper misparses -o). Prints dtc warnings; exits non-zero on any error.
build-kernel.sh
Build the mica kernel Image on the build host: arm64 defconfig at the pinned commit + every kernel/*.config fragment
(mica-firstboot.config first, then the rest in name order),
clang/lld (LLVM=1), out of tree. Image only: no modules, no in-tree dtbs (scripts/build-dtb.sh builds the DTB).
GBOOK_FULL=1 scripts/setup-toolchain.sh # once: the full kernel tree
scripts/build-kernel.sh [kernel-tree] [out-dir]
Defaults: $GBOOK_TOOLS/linux-a804c57 and $GBOOK_TOOLS/build-a804c57 (GBOOK_TOOLS defaults to ~/.cache/gbook).
Applies kernel/patches/*.patch first (ADR 0007; the kernel release then ends in -dirty).
Writes <out-dir>/localversion-gbook (gbook_localversion below), so the release names the gbook commit too: it reads
like 7.3.0-gbook-g6934235-ga804c5742bff-dirty (a release keeps that form: ADR 0011 maps the hash to its tag).
Fails if any CONFIG_ line or "# require:" symbol of any fragment is not =y after the merge (ticket 06), or if any
"# forbid-y:" symbol is =y (safety rails, e.g. the NVMe driver near the stock SSD).
Needs clang, ld.lld, make, flex, bison, bc, perl and openssl headers.
package-image.sh
Package the first-boot image on the build host: stage Image, the -el2 DTB and /init, then run scripts/package-in-container.sh
in the digest-pinned Debian sid container through pkexec (the owner stays out of the docker group, ticket 06).
scripts/build-kernel.sh && scripts/package-image.sh
Output: $GBOOK_TOOLS/out/{mica-usb.img,mica.kpart,manifest.txt,...} (GBOOK_TOOLS defaults to ~/.cache/gbook).
Firmware: scripts/stage-firmware.sh rebuilds $GBOOK_TOOLS/firmware (stock modem_a + the build host's linux-firmware, never in
git: ADR 0005) and checks it against kernel/firmware.sha256; the tree goes into the initramfs at /lib/firmware.
Wi-Fi: ~/.local/state/gbook/wifi.conf (owner-written wpa_supplicant network block), if present, goes in as
/etc/wpa_supplicant.conf.
Writes nothing to a USB drive; the owner does that with pkexec dd (ticket 13).
build-rootfs.sh
Build the M3 rootfs (ADR 0012; .scratch/m3-rootfs ticket 03): Debian sid arm64 from one snapshot.debian.org
timestamp, mmdebstrap in the digest-pinned container of scripts/package-image.sh, run rootless with podman
(retro-8/05). Output in $GBOOK_TOOLS/rootfs (GBOOK_TOOLS defaults to ~/.cache/gbook), never in git:
out/rootfs.ext4 the ROOT image (GBOOK_ROOTFS_SIZE, default 4G; label gbook-root), from out/rootfs.tar
out/lock.txt every installed package as pkg:arch=version, sorted (the lock list)
out/manifest.txt snapshot, SOURCE_DATE_EPOCH, container digest, tool versions, counts and sha256s
out/mmdebstrap.log the build log; cache/ keeps every .deb (snapshot answered 503/504 on 2026-11-04)
scripts/build-rootfs.sh [YYYYMMDDTHHMMSSZ] (default: today 00:00 UTC; ticket 02 sized 20261104T000000Z)
GBOOK_ROOTFS_MIRROR=http://deb.debian.org/debian while snapshot.debian.org is down: the manifest then says snapshot=none
and requested_snapshot=<ts>, so release tooling (ticket 12) sees it
GBOOK_ROOTFS_OFFLINE=1 rebuild from cache/ alone, pinned to the previous out/lock.txt (no network for packages)
The manifest's lock_changed= line counts lock.txt lines that differ from the previous build's (kept as lock.prev.txt).
Mode (ADR 0012, decision 9): the container runs mmdebstrap inside its own user namespace (unshare --map-root-user
--map-auto, the substance of mmdebstrap's unshare mode; as container root mmdebstrap would skip the namespace and
fail to mount, 2026-11-04). GBOOK_ROOTFS_MODE=sysadmin instead adds SYS_ADMIN to the rootless container and runs
mmdebstrap's root mode directly: a capability inside podman's user namespace, not on the host.
Writes nothing to a USB drive; the owner writes ROOT (ticket 11).
stage-firmware.sh
Rebuild the initramfs firmware tree $GBOOK_TOOLS/firmware (default ~/.cache/gbook/firmware) from its two sources,
and check every file against kernel/firmware.sha256 (hashes only in git; the blobs never are: ADR 0005).
scripts/stage-firmware.sh stage and check; fails on any hash change or missing file
scripts/stage-firmware.sh --update stage and rewrite kernel/firmware.sha256 (after a deliberate change)
scripts/stage-firmware.sh board2 [regex] list board-2.bin entry names matching regex (e.g. 'subsystem-vendor=105b')
Sources:
stock = the recovery-stick copy's modem_a partition (dumps/gate1/), read with scripts/fat_ls.py, no mount
distro = the build host's linux-firmware-atheros and wireless-regdb packages (/usr/lib/firmware), zstd-decompressed
pkg = files from an Arch package the build host does not install (linux-firmware-qcom: the Adreno X1-85 GPU firmware and
the Dell XPS 13 9345 audio topology),
downloaded once into $GBOOK_TOOLS/pkgs, signature-checked against pacman's keyring, extracted, never
installed. The version matches the distro linux-firmware packages (20260916-1).
package-image.sh copies the staged tree into the initramfs at /lib/firmware.
mkpatch.sh
Assemble a local kernel patch (ADR 0007) from the edited kernel tree, then restore the tree (retro-15/02: three
revisions of patch 0010 were 40-line heredocs each, and a prose "Status:" line failed check 34 once).
scripts/mkpatch.sh <NNNN-slug> --subject '<text>' --notes <file> [--body <file>] [--status '<WORD> <reference>']
[--assisted-by <id>] [--tree <dir>] [--out <file>]
Edit the files in the tree first (its HEAD is the pin). The patch is the tree's diff of its modified tracked files:
line 1 Status: <status>, checked <today> default: LOCAL gbook (ours; not sent: upstream hold until M5)
prose the notes file: why, evidence, what it changes (gbook's block before the mail header)
mail From 0000…, From: the gbook repo's git identity, Date: now (date -R), Subject: [PATCH] <subject>,
the body file (or the subject alone), Assisted-by: <id> (default Claude), ---, diffstat, diff
Then the touched files go back to HEAD, so the tree stays what build-kernel.sh expects (it applies every patch itself).
--out names the file (default kernel/patches/<NNNN-slug>.patch; a new number takes the next unused one, check 34).
release-draft.sh
Draft a release's annotated tag message (ADR 0011 and its 2026-11-04 update; .scratch/m3-rootfs ticket 12). Checks the
release bar first, prints the message, writes it to $GBOOK_TOOLS/out/tag-<version>.txt and names the owner's git tag
command. Tags nothing: the owner tags (CLAUDE.md, "Git").
scripts/release-draft.sh <milestone> <attempt id regex> [vYYYY.0M.0D[.MICRO]] (default version: today's date)
The bar (ADR 0011): one base image, 3 clean cold boots. "clean" is scripts/attempt.sh tally's SCMI verdict over the
rows the regex selects (anchor it: '^2026-11-04-12$'); "cold" is each row's start column (boot_start.py, the row's first
boot); "base image" is what attempt.sh stick last read back from the stick. The rootfs must be pinned by date
(manifest snapshot=, not none: a GBOOK_ROOTFS_MIRROR build is not release material).
The rootfs on the stick's partition 2 must be that manifest's (attempt.sh root-verify's record, retro-9/01).
check.sh 46 runs the two checks alone: release-draft.sh --self-check <tally line> <starts> <stick file> <manifest> [<root file>].
Project glossary
mica
The Googlebook’s Android device codename (ro.product.device, read over adb 2026-10-20).
bluey
The ChromeOS reference baseboard for Qualcomm X1 devices in coreboot (src/mainboard/google/bluey); mica is one
of its variants. Firmware branch firmware-bluey-16702.B.
Hamoa
Qualcomm’s platform name for the X1E80100 family; mainline Linux renamed x1e80100 device-tree files to hamoa.
Tributo
Codename of the Dell XPS 13 9345, the Windows sibling whose mainline device tree
(x1e80100-dell-xps13-9345.dts, compatible dell,xps13-9345) is the starting point for this one.
Stock DT
The device tree Googlebook OS boots with: an LZ4-compressed vendor base DTB plus board and keyboard overlays in
the dtbo_a partition (dtb_a is empty). It is used only as a hardware map (ADR 0004).
Recovery image
Google’s public mica-user/16471258/recovery.zip: one 19 GB GPT disk image. Build 16471258 is older than the
installed 16560795.
Stock OS
Googlebook OS (codename Aluminium): Android 17 with a desktop shell, as shipped. Phase 1 reads it without changing it.
Stock drive and spare drive
The stock drive is the M.2 2230 SSD the laptop shipped with; it stays untouched in a drawer during experiments. The spare drive is a second 2230 SSD that takes every experiment (ADR 0002).
Gate
The condition that ends a phase. Work from the next phase does not start until its gate passes.
Gate 1 = a proven way back to stock.
Gate 2 = a mainline kernel’s own Linux version line, captured from the AP console, the panel or the ramoops readback.
Milestone
A named rung between the gates, M0 to M7 (ADR 0010), each with a
“done when” bar. M0 and M1 coincide with Gates 1 and 2, M4 with Gate 3 and M6 with Gate 4. M5 (upstream-ready) ends
the upstream hold. Per-feature state lives in docs/status.md.
Attempt
One boot of one image, with an ID YYYY-MM-DD-N and a row in the gitignored dumps/firstboot/attempts.tsv
(scripts/attempt.sh). Repeat boots of the same image are added to its row.
Base image and test image
A base image is packaged with no debug command-line or DT extras. A test image has some (for example
gbook.suspend=1). scripts/attempt.sh stick names which one the stick holds. Only a base image can become a release.
Release and version
A release is a base image with 3 clean cold boots by scripts/attempt.sh tally, which the owner would hand on. Its
version is a CalVer annotated tag vYYYY.0M.0D[.MICRO] on the commit that built it
(ADR 0011). A version names an image, and an attempt names one boot of it.
uname -r carries the gbook commit it was built from, for example -gbook-g6934235 (+ for a tree with changes).
A released image keeps that form, and its tag message maps the hash to the version.
Patch status
The first line of a kernel/patches/ file: Status: LOCAL, FROMLIST, FROMGIT or UPSTREAM, plus a reference
(ChromiumOS’s words, with LOCAL for its CHROMIUM; ADR 0007 update).
Patch numbers are never reused.
Recovery media
A USB drive (32 GB+) written by Google’s Device Recovery Utility Chrome extension; it reinstalls stock OS from the firmware recovery screen (Esc + F2, then Power, per Google’s Googlebook article; Esc + Refresh is the Chromebook combo).
Developer mode
ChromeOS firmware state that allows booting non-Google-signed kernels. On mica the recovery screen calls it “Unlock bootloader” / “Bootloader is unlocked”; it needs OEM unlocking turned on in Android first.
Kernel partition and FIT
A GPT partition of the ChromeOS-kernel type holding a vboot-signed image. On Arm the payload is a FIT
(Flattened Image Tree): kernel plus one or more device trees, picked by compatible (google,mica-rev*-sku*).
ALink
Google’s USB bridge cable or dongle that depthcharge’s fastboot looks for as USB host; a Prolific PL27A1 also works. Without one, firmware fastboot runs over TCP via a USB-Ethernet adapter.
ABL
Qualcomm’s Android Bootloader, which loads Android boot images and speaks fastboot. mica does not use it (it boots through depthcharge, found 2026-10-21); kept here because the older plan assumed it.
RAM boot
fastboot boot <image>: boots an image once from memory without writing it to storage. mica’s depthcharge
fastboot does not implement it; booting from a USB drive replaces it.
GSC
Google Security Chip on the board in place of a Windows TPM; on mica it is a Ti50 (coreboot TPM_GOOGLE_TI50). On Chromebooks it provides CCD.
CCD
Closed Case Debugging: UART consoles for the main processor and the EC over USB-C through the GSC, without
opening the case. mica has it: CCD is Locked, with AP and EC console reads allowed Always (gsctool -a -I, 2026-10-21).
SuzyQ
The USB-C debug cable (here the Fyra Labs SuzyQ Board) that exposes CCD consoles to a second computer. On mica
it works in the left port. the build host sees Ti50 18d1:5066, with ttys for the GSC shell, AP (uart21), EC and FPMCU.
EC
Embedded controller: battery, fans, keyboard, lid, Glowbar, LEDs. On mica it is a ChromeOS EC (Zephyr, Nuvoton NPCX9)
over SPI, so mainline cros_ec drivers are the starting point; its mica overlay is partly private.
DTS and DTB
Device tree source and its compiled blob: the hardware description the kernel boots with on Arm.
GSI and DSU
Generic System Image, an AOSP system image for any Treble device; Dynamic System Updates boots one from new partitions without touching stock. The cheapest test after unlocking.
ROOT
The kernel stick’s partition 2, GPT name gbook-root, 8 GiB from sector 524288, declared by every KERN-A image’s
GPT (ADR 0012, decision 3). The owner writes rootfs.ext4 to it; /init finds
it as partition 2 of the disk that holds the kern_guid partition.
M3 rootfs
The Debian sid arm64 root filesystem of milestone M3, built by scripts/build-rootfs.sh from rootfs/packages.txt
with mmdebstrap in the pinned container; rootfs/customize.sh adds its units. gbook-m3 is its capture service
(GBOOK: m3-<key>= lines, a second netlog transfer); gbook-cage runs Ghostty under cage as user gbook.
mica (ssh host)
the build host’s ssh mica entry: the booted M3 rootfs at the Wi-Fi lease, key-only, user gbook with sudo (ADR 0012 update
2026-11-04). Reads are free; writes need the owner; one connection attempt per boot.
Releases
One entry per annotated tag vYYYY.0M.0D[.MICRO] (decision 0011); the tag message is the release note.
v2026.11.05
v2026.11.05: M3
Milestone: M3 (ADR 0010)
Kernel base: torvalds a804c57 (v7.3 + 1703, the 7.4 merge window; ADR 0011)
Kernel release: 7.3.0-gbook-2026.11.04-35-g7c6a1aa-ga804c5742bff-dirty (uname -r; build 69edd54 of gbook)
Local patches (kernel/patches/, ADR 0007):
0001-drm-panel-edp-add-lgd-0x0804.patch: LOCAL gbook (not sent: the EDID gives only part of the model name), checked 2026-11-04
0002-drm-msm-a6xx-cxpd-device-link-stateless.patch: FROMLIST [RFT 1/5] of "drm/msm: Attach a driver to GMU" v1 (Akhil P Oommen) <20260513-gmu-sync-state-fix-v1-1-6e33e6aa9b4f@oss.qualcomm.com>; v2 has it as 2/8 <20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com>; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04
0003-drm-msm-centralize-standalone-drm-check.patch: FROMLIST [RFT 2/5] of "drm/msm: Attach a driver to GMU" v1 (Akhil P Oommen) <20260513-gmu-sync-state-fix-v1-2-6e33e6aa9b4f@oss.qualcomm.com>; dropped in v2 <20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com>; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04
0004-drm-msm-adreno-fix-drvdata-typecast.patch: FROMLIST [RFT 3/5] of "drm/msm: Attach a driver to GMU" v1 (Akhil P Oommen) <20260513-gmu-sync-state-fix-v1-3-6e33e6aa9b4f@oss.qualcomm.com>; v2 has it as 1/8 <20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com>; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04
0005-drm-msm-component-model-for-standalone-gpu.patch: FROMLIST [RFT 4/5] of "drm/msm: Attach a driver to GMU" v1 (Akhil P Oommen) <20260513-gmu-sync-state-fix-v1-4-6e33e6aa9b4f@oss.qualcomm.com>; v2 has it as 5/8 <20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com>; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04
0006-drm-msm-attach-a-driver-to-the-gmu.patch: FROMLIST [RFT 5/5] of "drm/msm: Attach a driver to GMU" v1 (Akhil P Oommen) <20260513-gmu-sync-state-fix-v1-5-6e33e6aa9b4f@oss.qualcomm.com>; v2 has it as 6/8 <20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com>; copy: Omarchy linux-omarchy-aarch64 (below), ported to v7.2.8 by Brandon Williams; checked 2026-11-04
0007-drm-msm-gmu-optional-and-lock-init.patch: LOCAL Brandon Williams via Omarchy (third-party fix-up on 0002-0006), https://github.com/omacom/omarchy-pkgs/tree/master/pkgbuilds/linux-omarchy-aarch64, checked 2026-11-04
0008-clk-qcom-gcc-x1e80100-pcie-gdsc-retention.patch: FROMLIST [6/7] (Krishna Chaitanya Chundru) <20260102-pci_gdsc_fix-v1-6-b17ed3d175bc@oss.qualcomm.com>, changes requested on linux-arm-msm patchwork; regenerated against a804c57; checked 2026-11-04
0009-clk-qcom-gcc-x1e80100-pcie-phy-gdsc-retention.patch: LOCAL gbook (ours; not sent: upstream hold until M5), checked 2026-11-04
0010-soundwire-qcom-rearm-enumerator-after-clock-stop.patch: LOCAL gbook (ours; not sent: upstream hold until M5), checked 2026-11-05
0011-asoc-wsa884x-clk-stop-mode1.patch: LOCAL gbook (ours; not sent: upstream hold until M5), checked 2026-11-05
KERN-A image sha256 (attempt.sh stick): bc62c69ecf560525
Rootfs image sha256: 6fea12f2dc824f63947e7251fafd31ae799f44ea6c5e40056ebaddba0f72ac56
Rootfs snapshot: 20261104T000000Z (snapshot.debian.org); lock list sha256 12f96fa08768262e228319783831fa28bd229d9e821b2b5bdfd8be2d02cc1388; 321 packages
Proof (attempt.sh tally '^2026-11-05-5$'): group 1 /^2026-11-05-5$/: attempts=1 health=1 boots=3 known=3 stalled=0 clean=3 unknown=0 max=0.1-0.1ms
Attempts: 2026-11-05-5; start: cold
Changes since v2026.11.04: 45 commit(s)
69edd54 Release bar met for v2026.11.05: base image bc62c69e (sound node in the board DTS), 3 clean cold boots, the names heard on each
e4820b0 Audio: the capture test passes (the owner heard the recording played back); Microphones: works; boot 7 added to 2026-11-05-4
9d120a5 Audio: the sound node moves from dts/diag/sound.dtsi into the board DTS (mapping and unverified values in dts/README.md)
176058d Retro 15: check.sh -q, mkpatch.sh, localversion on the kernel's inputs, watch replaces and wait-rootfs, attempt lifecycle in CLAUDE.md, audio-state --names
b9c6ca2 Audio: ticket 16 closed (the owner heard the idle tone and the four names); Speakers works; README resume line
b93bd13 Audio: the rule-free boot meets ticket 16's bar (tones a minute apart, after 10 minutes idle, the four names)
86d6fd2 Ticket 16: tone 3 of boot -4 has no hearing report; audio-state.sh's masters label is stale
e7c2791 Audio: the udev rule leaves the rootfs (patches 0011 and 0010 fix the wakes); check 45 now refuses it; status row for the speakers
9a61e0b Audio: boot 2026-11-05-4 recorded, patch 0011 with 0010 revision 3 passes the first bar (three wakes whole)
c08dc60 Audio: boot 2026-11-05-3 recorded (revision 2 sees the stall, the re-arm does not clear it)
7c6a1aa Audio: patch 0010 revision 3 (a wedged enumerator gets a controller reset and init) and patch 0011 (wsa884x clk_stop_mode1)
ed3af1e Audio: boot 2026-11-05-2 recorded (patch 0010 revision 1 did not fire); ideas #29 and #30 on the task list
d4c3b68 Audio: patch 0010 revision 2, a delayed check re-arms the enumerator while a peripheral sits at device 0 (ticket 16)
22ffd5c Audio: the udev rule is not a workaround (its write is the first wake, bus 1 lost it); attempt 2026-11-05-2 opened for patch 0010
50d16d2 Research sources: CodeLinaro through its GitLab API, patchwork and GitHub commits API for upstream-fix reads
4921d2f Audio: ticket 16 comment, recon Next and log row for patch 0010 and the udev-rule rootfs build
50ac603 Audio: local patch 0010, re-arm the SoundWire enumerator when re-enumeration stalls after clock stop (ticket 16)
ffb7273 Retro 14: recon.sh stock-kmsg, audio-state UCM path and masters runtime PM, sdw status caveat, Next pruned
f9b770d Audio: all four speakers play with the SoundWire masters kept clocked (boot 2026-11-05-1); udev workaround, ticket 16 fix shape
c23ab29 Retro 13: gaps check reads reset-gpios and shared GPIOs; audio-state, swrm_enum, mica-ssh; one watch at a time
dbfd2ef Research: ASoC and SoundWire debugging tools (ticket 16 reads)
7f9fe97 Audio: speaker test, the left amplifiers never enumerate (ticket 16); ssh user joins audio
9b8fc6b Audio: the sound card binds (attempts 2026-11-04-26 to -28)
c613c1d mica-audio.config: GPIO_SHARED_PROXY=y (the shared reset pins, attempt -27)
e8c45c0 mica-audio.config: RESET_GPIO=y (the codecs deferred on their reset-gpios, attempt -26)
afca45f Retro 12: stock tree is two files, + kernel release refused at the bar, navigation lines
a7d9d52 Log: the audio offline part; attempts row 2026-11-04-26
c80b1a3 sound.dtsi: the DMIC supply from the stock overlay (EN_PP3300_MIC, TLMM 69)
7f5199a Audio attempt, offline part (M3 ticket 10)
7072998 Log: retro 11 done but ticket 03 (RTK.md, owner); README next line
752eb8b Retro-11 tickets 02, 04, 05 and retro-9 07: journal group, tally pending row, Evidence column, the ssh block
a71c205 watch: classify the EC power sequence (retro-11/01)
ca9cb6f Retro 11: five tickets for the rootfs-side session
4ec4dae Docs: the EC's G3->S5 after power-down is soft-off; Ghostty's 38 s marked inferred
e085d11 Log: boot -25 powered off over ssh
8079c55 Docs: the rootfs side measured, m3-end at 48.7 s (attempt 2026-11-04-25)
e6d0e06 rootfs: cage at boot, polled cage read, joined channel handed over (task #26)
82549ae Recon: the rootfs side of boot 2026-11-04-24, by the journal
54d6e82 Log: the fast-sweep boot powered off over ssh
a5ab9d2 Docs: the fast-sweep boot is still up; times from logged anchors
... and 5 more
v2026.11.04
v2026.11.04: M3
Milestone: M3 (ADR 0010)
Kernel base: torvalds a804c57 (v7.3 + 1703, the 7.4 merge window; ADR 0011)
Kernel release: 7.3.0-gbook-g82c43ed-ga804c5742bff-dirty (uname -r; build 506159b of gbook)
Local patches (kernel/patches/, ADR 0007):
0001-drm-panel-edp-add-lgd-0x0804.patch: LOCAL gbook (not sent: the EDID gives only part of the model name), checked 2026-11-04
0002-drm-msm-a6xx-cxpd-device-link-stateless.patch: FROMLIST [RFT 1/5] of "drm/msm: Attach a driver to GMU" v1 (Akhil P Oommen) <20260513-gmu-sync-state-fix-v1-1-6e33e6aa9b4f@oss.qualcomm.com>; v2 has it as 2/8 <20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com>; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04
0003-drm-msm-centralize-standalone-drm-check.patch: FROMLIST [RFT 2/5] of "drm/msm: Attach a driver to GMU" v1 (Akhil P Oommen) <20260513-gmu-sync-state-fix-v1-2-6e33e6aa9b4f@oss.qualcomm.com>; dropped in v2 <20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com>; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04
0004-drm-msm-adreno-fix-drvdata-typecast.patch: FROMLIST [RFT 3/5] of "drm/msm: Attach a driver to GMU" v1 (Akhil P Oommen) <20260513-gmu-sync-state-fix-v1-3-6e33e6aa9b4f@oss.qualcomm.com>; v2 has it as 1/8 <20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com>; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04
0005-drm-msm-component-model-for-standalone-gpu.patch: FROMLIST [RFT 4/5] of "drm/msm: Attach a driver to GMU" v1 (Akhil P Oommen) <20260513-gmu-sync-state-fix-v1-4-6e33e6aa9b4f@oss.qualcomm.com>; v2 has it as 5/8 <20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com>; copy: Omarchy linux-omarchy-aarch64 (below); checked 2026-11-04
0006-drm-msm-attach-a-driver-to-the-gmu.patch: FROMLIST [RFT 5/5] of "drm/msm: Attach a driver to GMU" v1 (Akhil P Oommen) <20260513-gmu-sync-state-fix-v1-5-6e33e6aa9b4f@oss.qualcomm.com>; v2 has it as 6/8 <20261030-gmu-sync-state-fix-v2-0-89d3082d97af@oss.qualcomm.com>; copy: Omarchy linux-omarchy-aarch64 (below), ported to v7.2.8 by Brandon Williams; checked 2026-11-04
0007-drm-msm-gmu-optional-and-lock-init.patch: LOCAL Brandon Williams via Omarchy (third-party fix-up on 0002-0006), https://github.com/omacom/omarchy-pkgs/tree/master/pkgbuilds/linux-omarchy-aarch64, checked 2026-11-04
0008-clk-qcom-gcc-x1e80100-pcie-gdsc-retention.patch: FROMLIST [6/7] (Krishna Chaitanya Chundru) <20260102-pci_gdsc_fix-v1-6-b17ed3d175bc@oss.qualcomm.com>, changes requested on linux-arm-msm patchwork; regenerated against a804c57; checked 2026-11-04
0009-clk-qcom-gcc-x1e80100-pcie-phy-gdsc-retention.patch: LOCAL gbook (ours; not sent: upstream hold until M5), checked 2026-11-04
KERN-A image sha256 (attempt.sh stick): 9673d6d782ef4cdb
Rootfs image sha256: 4b0e2e027c0b2d74699eaa907a0eaca8597504a9eab6caec394b28c6f15c5d3c
Rootfs snapshot: 20261104T000000Z (snapshot.debian.org); lock list sha256 12f96fa08768262e228319783831fa28bd229d9e821b2b5bdfd8be2d02cc1388; 321 packages
Proof (attempt.sh tally '^2026-11-04-2[123]$'): group 1 /^2026-11-04-2[123]$/: attempts=3 health=3 boots=3 known=3 stalled=0 clean=3 unknown=0 max=0.1-0.1ms
Attempts: 2026-11-04-21 2026-11-04-22 2026-11-04-23; start: cold,cold,cold
Changes since the first commit (first release): 256 commit(s)
506159b M3 release bar met: snapshot rootfs on ROOT, 3 clean cold boots; release-draft.sh fixes
0d297ff Log: the snapshot-pinned release rootfs built (20261104T000000Z, 321 packages)
07885c4 Retro-9/01: ROOT write and read-back as attempt.sh root-cmd and root-verify
8b5181a Docs: README resume, docs index, glossary and ADR 0010 updated for the first ROOT boots
4f59c59 Retro-9/02 resolved: udiskie honours the sticks' no-automount hint
584702c Retro 9: tickets for the ROOT-boot session's tooling gaps
d21096d Third ROOT boot: ssh from the build host, the netlog with the journal, Vulkan
6fe07af M3 ticket 13: sshd for the build host in the rootfs, keys handed over at boot
575493b Research: how the build host reaches the booted rootfs (sshd, reverse ssh, journal, USB gadget)
e11ae23 The rootfs boots from USB: Mesa on the GPU, Wi-Fi in userspace, Ghostty
b9514cc Ticket 09: note the terminfo symlink for the next rebuild
a2dee5a M3 ticket 12: release-draft.sh checks the bar and drafts the tag message
879a770 M3 ticket 09: Ghostty under cage in the rootfs
1e0ebb5 M3 ticket 07: the rootfs's units and the M3 captures
d8123c7 Rootfs: offline mode from the .deb cache, and byte-identical builds
5e3956f M3 ticket 08: Ghostty 1.4.0 built for arm64 by cross compilation
ebd263f Research: pinned, reproducible rootfs builds when snapshot throttles
acfbcf4 M3 ticket 05: /init finds ROOT and switch_roots into it
be3091b Portable checks and an honest manifest for a non-snapshot rootfs build
98a9391 M3 ticket 03: build-rootfs.sh builds the rootfs reproducibly
e95ebd4 Retro-7/08 resolved: stick boot tries counted and raised to 15
61258b6 Retro-8/01 resolved: watch and verify say when a console tty is gone
d2e8a94 Retro-8/04 resolved: known parked failures collapse in watch
a9a53d4 Retro-8/06 resolved: a format check for docs/status.md
4cf4b78 Retro-7/07 resolved: check 32 reads every added markdown line
f9e2532 Retro-8/05 resolved: rootless podman for the rootfs sizing
80064c8 Retro 8: tickets for the M2 bench session's tooling gaps
148a2d7 M3 tickets 04 and 06 verified on attempt 2026-11-04-17
82c43ed M3 tickets 04 and 06: ROOT in every image's GPT; one netlog sender
d573942 M3 ticket 02: size the rootfs (1.45 GiB installed, 303 packages)
f2bac5a M3 ticket 01 resolved: build #28 has FW_LOADER_USER_HELPER off, USB_UAS on
f5cb508 M3 plan: ADR 0012 (Debian sid rootfs on the stick), 12 tickets
139bcdc docs/research/sources.md: where the evidence comes from, how to read it
ca3c851 Research: the M3 rootfs (Debian sid now, Omarchy aarch64 at M4)
ce13a86 Milestone M2 reached; deep suspend resets the AP and is parked
b97066c Keyboard wake from s2idle: partial (1 of 2), via the EC interrupt
cfe5ed6 Bluetooth works (2026-11-04-14); capture the s2idle wake IRQ
635448d Bluetooth for M2: mica-bt.config, QCA firmware, /init capture
8ebbda3 /init suspend modes: key (wake by key, 120 s safety alarm) and deep
48fc483 M2 park round: 16 rows parked, 3 tests left (wake, deep, Bluetooth)
... and 216 more
Decisions
Architecture decision records, Nygard’s shape (status, context, decision, consequences) with dated updates.
- 1. No bootloader unlock until a way back to stock is proven
- 2. Experiment on a spare M.2 2230 SSD; keep the stock drive intact
- 3. First boot through a ChromeOS kernel partition on external storage
- 4. Mainline device tree forked from the XPS 13 9345; stock tree as hardware map only
- 5. Vendor content and per-unit identifiers stay out of git; extraction stays reproducible
- 6. Debug console through GSC CCD and a SuzyQ, read-only while CCD is locked
- 7. Local kernel patches live in kernel/patches/ and apply at build time
- 8. The board DTS removes the PSCI domain idle states (cluster cl4/cl5, system ss3)
- 9. No PSCI cpuidle on mica: the per-CPU domains lose
cluster_c4 - 10. Milestones M0 to M7, a feature status table, and the upstream hold ends at M5
- 11. Versions: CalVer tags for released images, the gbook version in
uname -r - 12. M3 rootfs: Debian sid from a snapshot, on the stick’s partition 2, entered by switch_root
- Decisions
1. No bootloader unlock until a way back to stock is proven
Status
Accepted (from the project log, 2026-10-17; recorded as an ADR 2026-10-21)
Context
Unlocking the bootloader wipes all data and disables device security (Google Help). The device is new and the only one the owner has. Dell’s Service Manual (Rev. A00, pages 65-66) documents USB recovery media from Google’s Device Recovery Utility, but nobody has shown that it restores a device that was unlocked, that has a blank or foreign SSD, or that it lets the bootloader be relocked. Google may serve only the latest image, so anti-rollback could block an older build.
Decision
Phase 2 (unlock) starts only after Gate 1 passes:
- recovery media made and tested to reach the recovery screen,
- the working recovery key combo recorded,
- the recovery image and its build number saved,
- build fingerprint and bootloader version recorded,
- device-specific data that Phase 2 needs is backed up or dumped.
Agents never unlock, flash or enter recovery or fastboot; the owner does those steps by hand.
Consequences
Phase 1 stays on the stock OS, read-only over adb, which slows the start but keeps the device returnable. The spare-SSD approach (ADR 0002) reduces risk further but does not replace this gate, because the bootloader state lives in firmware, not on the SSD.
Update 2026-10-21
- Google’s recovery image for mica is public:
mica-user/16471258/recovery.zip(downloaded todumps/, sha256 in stock-dtb.md). Its buildCL3B.260622.273.R1/16471258(AVB rollback index 1339) is older than the installedCL3B.260622.276.R2/16560795. Whether anti-rollback accepts it is unknown, so it does not pass Gate 1 by itself. - Unlocking is ChromeOS developer mode (ADR 0003). “Lock bootloader” on the developer screen returns to normal mode. Whether that restores verified boot without a reinstall is still untested.
- Still required for Gate 1: Device Recovery Utility media made and booted to the recovery screen, and the build it serves compared with the installed one.
Update 2026-10-21 (recovery steps)
Google’s Recover your Googlebook (read 2026-10-21) documents:
- Media: recovery media from the Device Recovery Utility in Chrome on a Chromebook, Windows or Mac. Linux is not listed. The drive is 32 GB or more and gets erased.
- Recovery mode: Esc + F2, then Power. Esc + Refresh in Dell’s Service Manual is the Chromebook combo.
- Recovery erases: recovering “permanently erases everything”.
For Gate 1, the owner stops at the recovery screen and does not select Use USB drive. The checklist lives in Pass Gate 1.
Update 2026-11-02 (Gate 1 passed)
Device Recovery Utility media reaches the recovery screen with Esc + F2, and serves
CL3B.260622.276.R2/16560795, the installed build (rollback index 1339). A full copy of the stick is in dumps/gate1/.
Details: Pass Gate 1. The relock test runs on unlock day,
before any Linux boot (unlock-day ticket).
2. Experiment on a spare M.2 2230 SSD; keep the stock drive intact
Status
Accepted (from the project log, 2026-10-17; recorded as an ADR 2026-10-21)
Context
Dell’s Setup and Specifications guide (Rev. A00) lists one M.2 2230 PCIe Gen4 x4 NVMe slot, and the Service Manual reaches the SSD right after the base cover (4 Torx T5 screws). The SSD is a field-replaceable part. Dell names “Google Qualified” drives, so an unlisted drive may or may not be accepted.
Decision
All Linux experiments run on a second 2230 SSD. The stock drive comes out and stays in a drawer. Before any internal work: disconnect the battery-cable bracket and hold power for 20 s (Service Manual).
Consequences
- Returning to stock is a drive swap, as long as the bootloader and firmware state allow it (see ADR 0001).
- Open, testable questions: does the firmware accept an unlisted drive; does a cloned stock drive boot (tells whether the drive is bound to the device); do recovery and fastboot live in the 32 MB firmware flash (a blank drive answers that, since fastbootd lives on the SSD).
- Opening the case may matter for warranty; the owner decides when.
Update 2026-10-21
The stock internal disk (from the recovery image’s GPT, stock-dtb.md) mixes ChromeOS
KERN-A/B and ROOT-B with Android A/B partitions (boot_* use the ChromeOS-kernel type GUID). The first Linux
boot goes on external or spare storage (ADR 0003). Nothing in
that plan needs the stock drive.
Update 2026-10-21 (first boot)
Owner decision (boot media and port hookup):
- First boot: Phase 2 runs from a dedicated USB stick in the right USB-C port, on battery.
- Spare SSD: deferred to Phase 3, when Linux needs persistent storage.
- Stock SSD: for Phase 2 it stays installed rather than in a drawer, and it is never written (ADR 0003). It is the fallback boot, and the stock OS on it reads back a failed boot’s ramoops through dropbox.
The rest of this decision applies once Phase 3 starts.
3. First boot through a ChromeOS kernel partition on external storage
Status
Accepted 2026-10-21. Supersedes the log’s original Phase 2 plan (fastboot boot of an Android boot image, then
Mu-Silicium as a fallback).
Context
The original plan assumed Qualcomm ABL and Android fastboot. Recon and source research showed otherwise (recon, chromeos-boot-path.md):
- The bootloader is ChromeOS depthcharge (
Google_Mica.16702.*, branchfirmware-bluey-16702.B). - Google’s “Unlock bootloader” is vboot developer mode. After the transition, the firmware enables external boot and fastboot, and it boots self-signed kernels.
- depthcharge’s fastboot has no
bootcommand, so there is no RAM boot. It runs only through an ALink or PL27A1 bridge, or over TCP via a USB-Ethernet adapter. - vboot accepts ChromeOS-type kernel partitions (FIT image: kernel plus DTBs) and Android vbmeta partitions.
depthcharge picks a FIT configuration by
compatible, down togoogle,mica.
Decision
The first Linux boot uses a ChromeOS kernel partition (GUID fe3a2a5d-…) on a USB drive or the spare SSD:
- Image: a FIT image with the mainline
Image, the mica DTB and an initramfs, signed with the public vboot developer keys. - Build: depthcharge-tools with a
[boards/custom/mica]section. - Boot: from the “Boot from external drive” menu.
The Android GKI route (modified boot/vendor_boot/dtbo) and RW_LEGACY alternate firmware are fallbacks, not
the plan.
Consequences
- The stock SSD and its Android partitions are never written during first-boot tests.
- No special cable is needed. The SuzyQ on the left port gives the AP console (ADR 0006).
- Partition attributes (priority, tries, successful) decide what boots. A bad image falls back to the next partition or the menu, not to a brick.
- postmarketOS’s trogdor recipe (
deviceinfo_generate_depthcharge_image, cgpt) applies almost directly. - Still to confirm on the device: the board-id/sku-id DT match, and whether depthcharge leaves a framebuffer.
Update 2026-10-21
Source research answered both items under “Still to confirm on the device”, pending a live boot:
- DT match: not a blocker. A DTB whose root compatible holds
google,micamatches any board-id and sku-id, and a single-DTB FIT keeps a default config that boots even without a match. depthcharge prints the real ids on the AP console (fit-packaging.md). - Framebuffer: depthcharge adds no
simple-framebuffernode. coreboot’s scan-out at0xE4800000stays live, and mainline can use it throughCOREBOOT_FRAMEBUFFERand simpledrm only if the DTS reserves that regionno-map(silent-boot-observation.md). - Exception level: the kernel starts at EL2 with no hypervisor, so the DTB needs the
x1-el2.dtsochanges (el2-handoff.md).
Update 2026-10-21 (first-boot details)
Decided on the first-boot map:
- Image: a 256 MiB image holding only KERN-A (128 MiB),
cgpt add -i 1 -t kernel -S 0 -T 3 -P 15. It is rewritten whole for every attempt, which resets vboot’s tries (Iteration loop). - Hookup: a dedicated USB stick in the right USB-C port, on battery, with the SuzyQ on the left. The owner writes
the stick with
pkexec dd(Boot media and port hookup). - Firmware fastboot over TCP is ruled out: it writes only the single fixed disk, the stock SSD (depthcharge
694eabfsrc/fastboot/disk.c:40-44). - DTB: the FIT carries
x1e80100-google-mica-el2.dtb, because the kernel enters at EL2 with no hypervisor (EL2 handoff).
Update 2026-11-05 (12:20 PM EST): the stock drive locked read-only from /init
“The stock drive is never written” is now enforced mechanically for the Linux side of a boot: /init’s lock_stock()
(task #36, after the Steam Frame initramfs’s blockdev_ro hook, re-implemented from its description in
steamos-arm.md) runs right after the stick is identified by its kern_guid partition and sets
every other block device, whole disks and partitions, read-only at the block layer (blockdev --setro); ram, loop, zram
and dm devices are skipped, and an unknown GUID locks nothing (locking the stick blindly would break ROOT). The capture
GBOOK: lock-ro= names what was locked, none: only the stick (sda) while the NVMe driver stays # forbid-y, or
skipped: with the reason. It is advisory against mistakes: a root process can --setrw. check.sh 58 drives it on a
synthetic /sys/class/block with a fake blockdev. Untested on the device until the next boot.
4. Mainline device tree forked from the XPS 13 9345; stock tree as hardware map only
Status
Accepted 2026-10-21.
Context
- No mica DTS exists anywhere: not in mainline, not in the ChromiumOS 6.12 kernels, not on lore.
- The stock DT is a Qualcomm vendor tree: an LZ4 base plus overlays in
dtbo_a, withsde,qupv3_seNandqcom,…properties. Mainline cannot boot it. - The 9345 is a close match: address-by-address comparison (9345-dts-diff.md)
shows mica reuses its retimer block, touchscreen, PCIe, eDP and
uart21debug console. The parts that differ are ChromeOS ones: cros-ec, Ti50, QSPI flash, a USB fingerprint MCU.
Decision
dts/x1e80100-google-mica.dtsstarts as an unmodified copy of mainlinex1e80100-dell-xps13-9345.dtsat a pinned commit (torvaldsa804c57), committed alone. Mica’s changes go in later commits, so the diff is the record.compatible = "google,mica", "qcom,x1e80100".- The stock tree is used only as a hardware map: buses, addresses, GPIOs, regulators, keymap. Every board-specific pin must trace to it. Anything copied from the 9345 without a stock match is marked “unverified”.
- Checks before a commit counts:
scripts/build-dtb.shwith no warnings, anddt-validatewith no findings beyond the pendinggoogle,micabinding.
Consequences
- Upstreaming later needs a
qcom.yamlbinding patch, alphabetical node order and correct attribution. - Mainline changes to the 9345 file should be followed by re-basing onto a newer pinned commit.
- Missing pieces (keyboard backlight, Glowbar, ALS, fans, audio, camera) get added once something boots.
Update 2026-10-21
- EL2 changes come from mainline’s
x1-el2.dtso, applied at build time (x1e80100-google-mica-el2.dtb), the way mainline builds every x1 board. The DTS stays the plain board file (What goes into the first-boot DTB). - One board fact traces to firmware source rather than the stock tree. The framebuffer reservation’s address
matches the stock
splash_region(0xe4800000). Its size,0x2200000, comes from coreboot’smemlayout.ld; stock uses0x21c0000with nono-map. /chosen/stdout-path = "serial0:115200n8"is now in the DTS, traced to the stock base (commit709212e).
5. Vendor content and per-unit identifiers stay out of git; extraction stays reproducible
Status
Accepted 2026-10-21. It records rules that CLAUDE.md has applied since the scaffold.
Context
- Vendor blobs: the work needs Google’s and Dell’s binaries (the recovery image, the stock DTB, and the DSP and GPU firmware), and they are not ours to redistribute.
- Identifiers: recon output is meant for public channels (#aarch64-laptops, linux-arm-msm), but adb, the GSC and the recovery image all expose per-unit identifiers (serials, chip IDs, MACs, pcr0).
- Remote: the repo is private on the private remote today and may become public.
Decision
dumps/is gitignored. It holds downloads and extractions (recovery zip and image, partitions, stock DTBs, firmware).*.img,*.bin,*.mbn,*.elf,*.dtband.firecrawl/are gitignored too.- Commit derived facts, not blobs: hashes, sizes, node maps, addresses, keymaps as text, and source URLs.
- Extraction is scripted from public inputs (
scripts/extract-stock-dtb.shfrom the public recovery URL), so anyone can reproduce it. - Redaction happens before writing, for anything committed:
scripts/recon.shredacts serial numbers, boot ids and MACs. GSC console captures leave out the Ti50 serial, chip ID and pcr0.
Consequences
- A fresh clone cannot boot anything without running the extraction scripts first.
- Before any push, check:
git ls-files | rg '^dumps/'must be empty, and no serial-like strings indocs/.
Update 2026-11-05, 2:08 PM EST: the public export
The identifier list grows for publication (public-writeup.md, “Build round”; task #39).
LAN addresses are redacted at source like serials (commit bf34583: ADR 0012 and two recon notes). Host, user and remote
names and the owner’s mail domain stay in this private repo’s text and are substituted when scripts/export-public.sh
builds the public tree; that script holds the name list, and site/gate.sh (which ships with the export) holds only the
generic classes (MACs, serials, chip IDs, RFC 1918 addresses). Check 61 runs the export and the gate before every commit.
6. Debug console through GSC CCD and a SuzyQ, read-only while CCD is locked
Status
Accepted 2026-10-21.
Context
First boot needs a console that works before display or USB drivers do. Tested 2026-10-21 (ccd-gsc.md):
- A Fyra Labs SuzyQ in the Googlebook’s left USB-C port exposes the Ti50 as
18d1:5066, with Shell, AP, EC and FPMCU consoles. ccdstateshowsUARTAP+TX: the AP UART is bridged both ways while CCD is Locked.- The stock and mainline DTs both name
uart21(0x894000) asserial0. - Opening CCD (
gsctool -a -oplus power-button presses) needs developer mode, and it changes GSC state.
Decision
- Kernel console:
uart21is the first-boot console:stdout-path = "serial0:115200n8",console=ttyMSM0,115200. the build host reads it through the SuzyQ’s AP console. - Read only: agents run the SuzyQ consoles read-only. Before reading, run
stty raw -echoon the tty so nothing echoes back. On the GSC shell, agents send only read-only commands (version,ccdstate,help). Neverccd open/unlock/reset,wp,rebootor any flash command. - Opening CCD (for a full AP flash backup over
raiden_debug_spi) is an owner decision. It happens after unlocking and gets its own ADR. - the build host access: the
uaccessudev rule/etc/udev/rules.d/70-ti50-ccd.rules(owner choice over joininguucp).
Consequences
- The left USB-C port is taken by the SuzyQ during bring-up. Boot media and power go through the right port (a hub with PD pass-through) or the spare SSD.
- If the AP console stays silent under Linux, the fallbacks are ramoops (depthcharge always reserves it) and the panel.
Update 2026-10-21
- The DTS has the console now: it sets
stdout-path = "serial0:115200n8"(commit709212e). - Read-only still applies with our own kernel: the first-boot initramfs prints its captures, then reboots, and has no shell (Initramfs contents and pass criteria).
- Logs: raw AP-console logs go to
dumps/firstboot/. - Second channel: when the console is silent, the stock OS’s dropbox
SYSTEM_LAST_KMSGholds the failed boot’s last 64 KiB (stock pstore).
7. Local kernel patches live in kernel/patches/ and apply at build time
Status
Accepted 2026-11-03 (owner, “1a 2a 3b”).
Context
- Until now the first-boot kernel was torvalds
a804c57plus config fragments only (kernel/*.config). - The first change that needs kernel source is a
panel-edpentry for the mica panel (LGD 0x0804): without it the driver warns and uses fallback timings (panel-edp.c:755-772). - The kernel tree lives outside the repo (
~/.cache/gbook/linux-a804c57, set up byscripts/setup-toolchain.sh), and the DTS scripts andcheck.shread the same tree.
Decision
- Patches are files in git:
kernel/patches/NNNN-<topic>.patch, unified diffs against the pinned commit, each with a header saying what it changes, its source and whether it went upstream. Name order is apply order. scripts/build-kernel.shapplies them before every build: it restores the files the patches touch from the pinned commit, fails if the tree has any other change, then runsgit applyon each patch. The patches stay applied after the build, so the kernel release ends in-dirty.check.shcheck 20 fails when a patch no longer applies (or reverts) cleanly against the tree.- Upstream is a separate decision per patch. The first one (
0001, panel-edp) stays local: the EDID gives only part of the model name (“LP134WQ…”), and an upstream entry needs the full name.
Consequences
- The pinned tree is no longer pristine after a build;
git -C ~/.cache/gbook/linux-a804c57 statusshows the patched files. Runningbuild-kernel.shagain is safe, and any foreign edit stops it. - Moving the pin (ADR 0004) means re-checking every patch; check 20 shows which ones break.
Update 2026-11-04 (10:07 AM ET): a status line on every patch, and numbers are never reused
Owner: “all a” to question 5 of milestones-versioning.md, section 5.
-
Line 1 of every patch reads
Status: <WORD> <reference>. The words are ChromiumOS’s patch-tagging words, withLOCALin place of itsCHROMIUMand noBACKPORT(a modified copy says so in its reference):LOCAL <who>: carried downstream and not posted to a list (gbook’s own, or a third party’s downstream fix-up);FROMLIST <list reference>: posted to a list, not in a maintainer tree when checked;FROMGIT <tree> <branch>: in a maintainer or -next tree, not in mainline;UPSTREAM <tag or commit>: in mainline, a backport onto the pin.
A lore Message-ID goes in the reference when one is known, and the line says “not recorded” otherwise. The free-text header below the line stays.
git applyignores everything before the first diff, so check 20 is unaffected. -
A patch number is never reused. A deleted patch’s number is retired, and the next patch takes the next unused number. Two numbers were reused before this rule:
0008first named a GENI I2C timing patch (16a2828) and now names the PCIe GDSC patch;0009first named the IRQ-to-CPU-1 diagnostic (cee735d, deleted by ADR 0009) and now names the PCIe PHY GDSC patch.
The new patches arrived on 2026-11-04 (
5272658at 4:07 AM ET andddecbd8at 4:47 AM ET). A log entry older than those commits that says “0008” or “0009” means the old patch. -
check.shcheck 34 enforces both rules. It allows exactly those two old reuses.
8. The board DTS removes the PSCI domain idle states (cluster cl4/cl5, system ss3)
Status
Superseded 2026-11-04 by ADR 0009 (owner round 4): the stall does not need the
domain states; the board DTS now drops the per-CPU cluster_c4 instead and keeps the domain states as stock.
Accepted 2026-11-03 (owner, grilling round 2: “yes” to all four). A deliberate deviation from the stock tree (ADR 0004).
Context
- On mica the CPUCP’s SCMI replies stalled for 0.5 to 1 s on most cold boots, with a timeout now and then. That
needed the local
arm,max-rx-timeout-ms = <1000>to come up at all (scmi-2026-11-03.md, attempts -20 to -26). - A/B boots, all cold, interrupt mode unless noted:
- Without
cluster_cl5(-27) or withoutdomain_ss3(-30): both still stalled. - No stalls:
cpuidle.off=1(-28), polling witharm,no-completion-irq(-29), and no domain idle states at all (-34). Each was 3 of 3, with replies within 0.1 to 4 ms. - With the domain states present: 8 of 11 boots stalled.
- Without
- The wake trace (-32): during a stall, CPU 0, which takes the mailbox interrupt, left the PSCI
retstate every ~20 µs without taking any interrupt. - The counters (-33): PSCI in OS-initiated mode refused the composite
ss3request about 598000 times in 95 s, from the last CPU of clusters 1 and 2, and genpd retried each time. - The stock tree has the same four states and topology (
entry00-base.dts:cluster_pd0-2withcl4andcl5,system_pdwithsystem_ss3). Stock runs Qualcomm’s own cpuidle and SCMI stacks, so its behaviour does not carry over (inference).
Decision
x1e80100-google-mica.dtsdeletesdomain-idle-statesfromcluster_pd0,cluster_pd1,cluster_pd2andsystem_pd. The per-CPU domains keepcluster_c4, so CPUs still enter PSCI retention.- The 1000 ms SCMI window stays as a safety net. It costs nothing on a fast boot and is still marked unverified.
- Polling stays a diagnostic.
arm,no-completion-irqis not in the board DTS: the binding describes hardware without a completion interrupt, and mica has one. - Upstream waits. A report draft lives in scmi-cpucp-stall-report.md, and sending it is a separate owner decision after the suspend test.
Consequences
- The clusters and the SoC never power down when idle, so idle power is higher. How much has not been measured.
- Suspend is untested either way. It is the trigger to revisit this ADR, together with a firmware update or an
upstream fix. The finer cut, keeping
cl4and droppingcl5andss3, was not tested. - Why the firmware refuses the composite states, and how that delays the interrupt to CPU 0, is still open.
dts/README.mdlists the change under “Changes” as a deviation, citing this ADR.
Update 2026-11-04 (12:26 AM ET): not a complete fix
Attempts 2026-11-04-1 and -2 stalled on this board DTS (max replies 1019.2 ms with 2 timeouts, then 652.0 ms):
2 of 6 cold boots without domain idle states, against 8 of 11 with them. Both stalled boots show CPU 0’s state1
loop (130000-156000 entries by the early read, against 4700-19000 on clean boots) with 0 PSCI refusals, so the
stall does not need the composite states (scmi-2026-11-03.md, “ADR 0008 is not
enough”). The decision is open again: keep, revert or replace this change. The 1000 ms window keeps every boot
working either way.
9. No PSCI cpuidle on mica: the per-CPU domains lose cluster_c4
Status
Accepted 2026-11-04 (owner, grilling round 4: “yes” to all four). Supersedes ADR 0008. A deliberate deviation from the stock tree (ADR 0004).
Context
The evidence is in scmi-2026-11-03.md, attempts -27 to 2026-11-04-3.
- The symptom: SCMI replies from the CPUCP stalled for 0.5 to 1 s, with an occasional timeout, on most cold boots. The 1000 ms window kept every boot working.
- No stalls: with plain WFI (
cpuidle.off=1, -28) and with polling (arm,no-completion-irq, -29), 0 of 3 each. Polled replies take 0.1 ms, so the CPUCP answers fast. - During a stall (-32), the core that takes the reply’s SPI leaves the PSCI
retstate (cluster_c4,arm,psci-suspend-param0x4) about every 20 µs. It takes IPIs but not the SPI, until a timer PPI runs. - The stall follows the interrupt’s CPU (2026-11-04-3,
kernel/patches/0009routing SPI 28 to CPU 1): CPU 1 looped (134862state1entries) and the reply stalled for 872 ms, while CPU 0 stayed normal. - The domain states are not the cause. Removing the cluster and system states (ADR 0008) still left 2 of 6 cold
boots stalling. The PSCI refusals of
ss3(-33) ran alongside the stall. - Other SPIs stalled too: GENI I2C timeouts on -30, and a loop with no SCMI pending on -32 (inference).
- Mainline gives no per-CPU way out.
irqaffinity=always keeps the boot CPU (kernel/irq/irqdesc.c:36). A CPU without idle states makes cpuidle-psci fail for every CPU (cpuidle-psci.c:399-401,:437-447ata804c57).
Decision
x1e80100-google-mica.dtsdeletesdomain-idle-states(cluster_c4) fromcpu_pd0tocpu_pd11.cpuidle-pscithen does not load, and every core idles in WFI, as withcpuidle.off=1.- The cluster and system domain states stay as stock. Without cpuidle-psci nothing requests them, so ADR 0008’s deletions are reverted.
- The 1000 ms SCMI window stays as a safety net and is still marked unverified.
kernel/patches/0009(the IRQ-to-CPU-1 diagnostic) is deleted. The idle-counter and wake-trace tooling stays.- The upstream report is rewritten around this mechanism in scmi-cpucp-stall-report.md and is not sent. Sending is a separate owner decision after the suspend test.
Consequences
- No core enters PSCI retention, and no cluster or the SoC powers down when idle. Idle power is higher; how much has not been measured.
s2idleuses cpuidle states, so suspend will not reach PSCI low-power states with this DT (inference). The suspend test is the trigger to revisit, together with a firmware update or an upstream answer.- Why a core in
retdoes not take a pending SPI is open: the GIC’s wake path, or the PSCI firmware. That is the report’s question.
Update 2026-11-04 (12:57 AM ET): confirmed on the board DTS
Attempt 2026-11-04-4 booted this DT (kernel #21). There was no cpuidle driver, SCMI replied within 0.1 ms (78 of
78, 0 timeouts), and cpufreq came up on all three clusters. With -28, plain WFI is 4 of 4 cold boots without a stall.
Update 2026-11-04 (2:31 AM ET): the tally
scripts/attempt.sh tally (scmi-2026-11-03.md, “the counts from attempt.sh tally”):
domain states present 7 of 10 stalled against no PSCI cpuidle 0 of 4 (p = 0.035); ADR 0008’s DT 3 of 7 against 0 of 4
(p = 0.21, not significant alone). The decision stands on the mechanism plus the first comparison. More boots on this
DT will sharpen the count; record now adds each one to the table.
Update 2026-11-04 (4:40 AM ET): the suspend test, and the tally at ten boots
The suspend test ran: six cold boots, 2026-11-04-5 to -10
(suspend-2026-11-04.md). s2idle entry and the RTC wake work. The resume resets the
SoC every time, in the PCIe controllers’ noirq resume: first the PCIe GDSCs (fixed by local patch 0008, from an
upstream series), then the NVMe endpoint behind PCIe 6a (link kept) and PCIe 4’s host re-init (link shut down). The failure comes before any idle
state matters, so the test neither confirms nor refutes this ADR’s suspend cost (inference). The revisit stays open
until a resume completes.
Those boots also added to the count (scripts/attempt.sh tally with group 2 '^2026-11-03-28$|^2026-11-04-([4-9]|10)$';
kernels #21 to #23 and the no-PCIe-6a diagnostic DT, none of which touches the SCMI probe). No PSCI cpuidle: 0 of 10
stalled. Against domain states present, 7 of 10: p = 0.0015. Against ADR 0008’s DT, 3 of 7: p = 0.051.
Update 2026-11-04 (5:01 AM ET): a complete suspend round trip, and the count against ADR 0008
With local patches 0008 and 0009 (the PCIe GDSCs and the PCIe PHY GDSCs to retention), attempt 2026-11-04-11
completed an s2idle round trip on this DT (suspend-2026-11-04.md, newest update).
The CPUs had no cpuidle driver, so s2idle ran in WFI. Whether the SoC reached a low-power state, and at what power, is
not measured. The revisit trigger is met, and the suspend cost of this ADR is still the open question. Measuring it
needs a power reading that is not charge current.
Count: console boot #11 adds one more clean boot to -10. Group 2 is now '^2026-11-03-28$|^2026-11-04-([4-9]|1[01])$':
0 of 12 stalled. That is p = 0.0007 against domain states present (7 of 10), and p = 0.036 against ADR 0008’s DT
(3 of 7), now under 0.05.
Update 2026-11-04 (5:13 AM ET): suspend 3 of 3, the count at 14
Two more boots of -11’s image completed the round trip too: 3 of 3 (suspend-2026-11-04.md). All were clean for SCMI. The same group 2 regex now gives 0 of 14 stalled: p = 0.0003 against domain states present, and p = 0.026 against ADR 0008’s DT.
Update 2026-11-04 (10:07 AM ET): when the report is sent
The “Decision” line that made sending scmi-cpucp-stall-report.md an owner decision after the suspend test is superseded by ADR 0010. The report waits for milestone M5, like the binding and DTS. The owner reviews and sends it, and an agent never does.
This ADR’s “patch 0009” is the deleted IRQ-to-CPU-1 diagnostic (cee735d). Today’s kernel/patches/0009 is a
different patch, the PCIe PHY GDSCs, added before numbers were retired (ADR 0007
update).
Update 2026-11-04 (11:24 AM ET): the first base image, and the count at 17
Attempt 2026-11-04-12 is the first base image on this DT with patches 0008 and 0009: kernel #26, no command-line or
DT extras. It had 3 cold boots, all clean for SCMI. scripts/attempt.sh tally with group 2
'^2026-11-03-28$|^2026-11-04-([4-9]|1[0-2])$' gives boots=17 known=17 stalled=0 clean=17: no PSCI cpuidle has
0 of 17 stalled.
10. Milestones M0 to M7, a feature status table, and the upstream hold ends at M5
Status
Accepted 2026-11-04 (owner: “all a” to the open questions 1, 7 and 9 of milestones-versioning.md, section 5). Supersedes the line in ADR 0009 “Decision” that made sending the stall report an owner decision after the suspend test.
Context
- The phases and gates (README “Phases and gates”) mark four big steps. Phase 3, from first boot to a daily-drivable install, is a long stretch with no checkpoint in between. Progress was tracked as prose in README “Resume here”.
- Comparable projects track progress per feature and per stage (research note, section 1):
- Asahi’s feature-support tables have a column saying where each feature lives (upstream release, downstream, WIP).
- postmarketOS (now Nura) puts devices into categories with entry criteria.
- The XPS 13 9345’s first upstream DTS went in with the EC, speakers, mics and camera not working, and it listed what worked and what did not.
- The kernel’s process documents favour posting early (
3.Early-stage.rst,5.Posting.rstat the pin). A board DTS needs a documented compatible (bindingssubmitting-patches.rstitem 7), must passdtbs_checkwith no new warnings, and must passdt-check-stylein relaxed mode (maintainer-soc.rst) (research note, section 3). - AI agents must not add
Signed-off-byand must never send anything themselves (coding-assistants.rst:34at the pin). 207 of the repo’s 210 commits had a Claude co-author trailer (git log, 2026-11-04). - On 2026-11-04 the owner put upstream sends (the SCMI stall report, patch 0009) on hold “until the project progresses further”.
Decision
-
The milestone ladder. Each rung fits inside the phases and gates:
M Name Phase / gate Done when M0 Way back to stock Phase 1, Gate 1 recovery media reach the recovery screen and relock is tested (ADR 0001) M1 First light Phase 2, Gate 2 our Linux versionline on the AP console, the panel or the readbackM2 Board on the console Phase 3 every enabled DT node binds; the M2 rows of docs/status.mdread works; every other row is works, or partial or parked with a note; one base image has 3 clean cold boots byscripts/attempt.sh tallyM3 Userspace from USB Phase 3 a rootfs from USB with Mesa on the GPU and Wi-Fi in userspace; the audio state is known; the first release tag (ADR 0011) M4 Daily-drivable Phase 3, Gate 3 installed on the spare SSD (ADR 0002), with dual-boot or a documented return to stock M5 Upstream-ready Phase 4 entry the google,micabinding and the DTS, rebased on the qcom tree, passdt_binding_check,dtbs_check(no new warnings), relaxeddt-check-styleandcheckpatch.pl; a Working / Not working list is written; every local patch has its status line; the owner has reviewed and signed off every patchM6 Upstream Phase 4, Gate 4 the binding and DTS are applied to the qcom tree, then reach a mainline release M7 In a distribution (optional) after Phase 4 a Nura testingdevice package, or an Ubuntu Concept entryThe M2 rows are: USB host, Type-C, PCIe enumeration, Wi-Fi, eDP and backlight, GPU probe, EC keyboard, touchpad, TPM, SCMI cpufreq and an s2idle round trip. A base image is one with no debug command-line or DT extras: its
scripts/attempt.sh stickfirst line readsbase image. -
docs/status.mdis the feature matrix. It has one row per feature and four state words: works, partial, broken, untested. Untested is the default. Each row says where the feature works (mainline at the pin, a local patch, the board DTS, a config fragment), what upstream has or what gbook would have to send, and the recon note behind it. README “Resume here” points to it. Its prose stays as the owner’s dated record. -
The upstream hold ends at M5. Nothing goes to a list before then. At M5 the binding and DTS go out first as a
[PATCH RFC]series, following the kernel’s own versioning (ADR 0011). The stall report and patch 0009 wait for the same bar. The owner reviews, signs and sends every patch. An agent never addsSigned-off-byand never sends. -
The research note’s other rider is the owner’s call. It suggests a recon post in
#aarch64-laptopsbefore M5 (Phase 1 list in the project log). That post is the owner’s to make, and an agent does not post.
Consequences
- Progress has a name between the gates: “M2 nearly done” says more than “Phase 3”.
docs/status.mdhas to be kept current. A recon note that changes a feature’s state also changes its row.- M5 can run in parallel with M3 and M4, because it is about the binding and DTS, not userspace.
- What blocks M5 today (inference, research note section 3): the unverified
arm,max-rx-timeout-ms, ADR 0009’s deleted idle states, the merge-window pin, and the panel entry that lacks the full model name.
Update 2026-11-04 (11:37 AM ET): parking untested rows
Owner, park round (“yes” to all recommendations). A parked row in docs/status.md keeps its state (untested,
partial or broken), and its note says “parked until
- NVMe I/O until the spare SSD (M4);
- the left port’s USB 3 and DP until a session without the SuzyQ;
- idle and suspend power until a battery-only session;
- audio, camera, fingerprint, the rootfs and Mesa until M3;
- the lid switch, ambient-light sensor and Glowbar until a stock-tree or EC lead;
- the crypto engine until upstream drops
BROKEN.
Three rows stay open for M2: wake from s2idle by a key or the power button, deep suspend, and Bluetooth.
Update 2026-11-04 (12:38 PM ET): M2 reached
All four criteria of M2 are met (docs/status.md, “Milestone M2”):
- every enabled DT node binds, by the
unbound=proxy; - the twelve M2 rows work; the backlight was set and read back in 3 of 3 base-image boots;
- every other row works, is partial, or is parked: Bluetooth works (2026-11-04-14), keyboard wake from s2idle is partial (-15), deep suspend failed and is parked (-16), and 16 rows were parked in the park round;
- the base image 2026-11-04-12 has 3 clean cold boots (
tally).
The next milestone is M3, userspace from USB. Its first base image that passes the release bar gets the first tag (ADR 0011).
Update 2026-11-04 (7:11 PM ET): M3’s technical criteria have evidence; the release is pending
Three ROOT boots on 2026-11-04 (attempts -18 to -20; rootfs-2026-11-04.md): a rootfs
from USB with Mesa on the GPU (eglinfo, kmscube, cage and vkcube on the Adreno X1-85), Wi-Fi in userspace (the
supplicant and networkd carried the netlog), and the audio state known (aplay -l: no sound card; the attempt is
ticket 10). The status rows “Rootfs from USB” and “Mesa (freedreno)” read works. M3 closes with the first release tag
(ADR 0011): a snapshot-pinned rootfs build, 3 clean cold boots of one base image, and
the owner’s tag drafted by scripts/release-draft.sh.
Update 2026-11-04 (8:39 PM EST): M3 reached, first release v2026.11.04
The release bar was met the same evening: the rootfs rebuilt from snapshot 20261104T000000Z (321 packages,
4b0e2e027c0b2d74…), written to ROOT and read back equal (attempt.sh root-cmd, root-verify), then three clean cold
boots of base image 9673d6d782ef4cdb (attempts -21 to -23, tally clean=3 stalled=0;
rootfs-2026-11-04.md, “Release boots”). The owner tagged v2026.11.04 from
scripts/release-draft.sh’s message and pushed it to the private remote. M3 is passed. The upstream hold stays until M5; the next
milestone is M4, daily-drivable (Gate 3).
11. Versions: CalVer tags for released images, the gbook version in uname -r
Status
Accepted 2026-11-04 (owner: “all a” to the open questions 2, 3, 4, 6 and 8 of milestones-versioning.md, section 5). Question 5 (patch status lines) is an update to ADR 0007.
Context
- Several things change at different rates: the repo (every commit), the boot image (every
package-image.sh), the kernel base (a re-pin), the local patches, and the boots themselves. Each boot has an attempt IDYYYY-MM-DD-Ninattempts.tsv. The repo has no tags. An image is identified only by its kernel banner (release plus UTS#N). - SemVer requires a declared public API (semver.org 2.0.0, item 1), and gbook has none. CalVer suits projects whose releases are driven by external changes (calver.org, “When to use CalVer?”). Here those changes are the mainline base and the milestones.
- Annotated git tags “are meant for release” (
git-tag(1)), andgit describereads only annotated tags by default. scripts/setlocalversionat the pin appends the contents oflocalversion*files in the object and source dirs to the release.init/Kconfigcaps the release at 64 characters.- The pin
a804c57is a 7.4 merge-window commit, 1703 commits afterv7.3and beforev7.4-rc1(GitHub compare API, research note section 2).
Decision
-
A release is a base image with 3 clean cold boots. A base image has no debug command-line or DT extras (
scripts/attempt.sh stickreadsbase image). The 3 boots of one image hash are counted byscripts/attempt.sh tally, and the image is one the owner would hand on. Every other image is an experiment, named only by its attempt IDs. -
A release gets an annotated tag
vYYYY.0M.0D[.MICRO](CalVer, e.g.v2026.11.20;.1for a second release the same day). The tag goes on the gbook commit that built the image, after its 3 boots. The image must have been built from a clean tree (no+in itsuname -r). No SemVer. -
The tag message is the release note until a tagged image first leaves the build host. From then on,
CHANGELOG.mdin Keep a Changelog 1.1.0 form is the release note. The message contains:- the milestone;
- the kernel base (
a804c57, “v7.3 + 1703, 7.4 merge window”) and the kernel release fromuname -r; - each local patch with its status line;
- the image sha256 as
scripts/attempt.sh stickprints it; - the attempt IDs and the
tallyline that proved it; - what changed since the last tag.
-
uname -rnames the gbook state.scripts/build-kernel.shwriteslocalversion-gbookinto the build directory (theO=dir, never the source tree). Itsgbook_localversion()gives the exact rules, and check 35 tests them:-gbook-plusgit describeagainst the nearest annotatedv[0-9]*tag, without thev(-gbook-2026.11.20-3-g1a2b3c4);-gbook-g<hash>before the first tag;- a trailing
+when the tree differs from the commit (changed tracked files, or untracked ones that are not ignored).
Today that reads
7.3.0-gbook-g6934235-ga804c5742bff-dirty. The kernel tree’s own-dirty(the applied patches, ADR 0007) stays. A released image keeps the-gbook-g<hash>it was built with, because the tag comes after its boots. The tag message maps that hash to the version, and a rebuild at the tag would be a new, unproven image. Thekernelcolumn ofattempts.tsvalready records the release, so each attempt names its gbook commit, and no new column is needed. -
Upstream series use the kernel’s scheme only. That means
[PATCH RFC], then[PATCH vN], with the per-version changelog below---andgit format-patch --base(submitting-patches.rstat the pin). A gbook version never appears in a patch subject. -
The pin moves onto a mainline release tag. The next re-pin is onto
v7.4(or the release current then), after M3 and before M5 in any case (ADR 0010). After that, the pin moves at most once per mainline release. Each re-pin re-checks every local patch (check 20) and the DTS, and the first base image on the new pin needs its own 3 clean cold boots before it can be released.
Consequences
- No tag exists on 2026-11-04. The image on the stick is a test image (
gbook.suspend=1 no_console_suspend), and no base image with patches 0008 and 0009 has 3 clean cold boots yet. - Every gbook commit changes the kernel release, so a rebuild relinks the version objects. The UTS
#Nkeeps counting builds as before. - The release grows by about 20 to 33 characters (worst case
-gbook-2026.11.04.1-12-g1234567+), which stays under the 64-character cap. - A tool to cut a release (check the tally, compose the tag message) is not needed until M3. It is left for then.
- Verified on 2026-11-04: build
#25(kernelImage52734464 bytes) reads7.3.0-gbook-g8493ab4+-ga804c5742bff-dirtyininclude/config/kernel.release, andscripts/image_uts.shreads the same release from theImagebanner. The+was an uncommitted log edit at build time. - The
v7.4re-pin replaces patches 0002 to 0007. Their series has a v2 (2026-10-30) that drops 0003 and adds four patches, and torvalds master’scf27afc3bd6e(“drm/msm: default separate_gpu_kms to auto selection”) conflicts with 0002, 0003 and 0005 to 0007 (patch-status lookup, 2026-11-04). The status lines of ADR 0007’s update record both.
Update 2026-11-04 (2:03 PM ET): a release names the rootfs too
Owner, M3 round (“yes” to question 10 of m3-rootfs.md). From M3 on, a release is two
artifacts: the KERN-A image and the rootfs image (ADR 0012). The tag message adds
the rootfs image’s sha256, its snapshot.debian.org timestamp and the hash of its lock list, next to the KERN-A image
hash that scripts/attempt.sh stick prints.
Update 2026-11-04 (8:39 PM EST): the first release, v2026.11.04
Tagged by the owner from scripts/release-draft.sh M3 '^2026-11-04-2[123]$' at commit 506159b: kernel
7.3.0-gbook-g82c43ed-ga804c5742bff-dirty (patches 0001 to 0009), KERN-A image 9673d6d782ef4cdb, rootfs
4b0e2e027c0b2d74… from snapshot 20261104T000000Z (lock 12f96fa08768262e…, 321 packages), 3 clean cold boots.
Two things the first real run taught the tool: the Attempts line must come from attempts.tsv (tally lists every
id on one line), and git log -n 40 rather than | head, which ended the script by SIGPIPE under pipefail before the
owner’s tag line. The tag also records the rootfs read back from the stick (attempt.sh root-verify, retro-9/01):
release-draft.sh refuses a stick whose ROOT is not the manifest’s rootfs.
Update 2026-11-05 (9:54 AM EST): the second release, v2026.11.05
Tagged by the owner from scripts/release-draft.sh M3 '^2026-11-05-5$' at commit 69edd54: kernel
7.3.0-gbook-2026.11.04-35-g7c6a1aa-ga804c5742bff-dirty (patches 0001 to 0011; the two new ones, 0010 and 0011, keep the four
speakers through the SoundWire masters’ runtime PM wakes), KERN-A image bc62c69ecf560525 (the sound node in the board DTS, no
extras), a rootfs from the same snapshot and lock as v2026.11.04‘s (20261104T000000Z, lock 12f96fa08768262e…, 321 packages),
rebuilt 2026-11-05 as 6fea12f2… (the audio group, the m3-swrm capture; the udev rule tried that night never shipped), 3 clean cold boots (attempt 2026-11-05-5), the four spoken channel names heard on each boot and the
microphones’ capture heard played back. The AP console was absent for the three boots (the SuzyQ was unplugged on the build host’s side);
the bar’s evidence is the netlog, which attempt.sh tally reads. The draft ran once before the final docs commit and again on it,
so the tag file names the tagged commit: run it last.
12. M3 rootfs: Debian sid from a snapshot, on the stick’s partition 2, entered by switch_root
Status
Accepted 2026-11-04 (owner: “yes” to the ten open questions of m3-rootfs.md, section 5). Nothing in it has run on mica yet; section 4 of the note is inference until the first rootfs boot.
Context
- M3 (ADR 0010) needs a rootfs from USB with Mesa on the GPU and Wi-Fi in userspace, a known audio state, and the first release tag (ADR 0011).
- Mesa does not separate the candidates: X1-85 support landed in Mesa 24.2 (MR !30249, merged 2024-07-18), and every candidate ships 26.x. Ghostty 1.4 arm64 exists only in Omarchy’s aarch64 repo (research note, sections 1-2).
- The packaging already runs in a digest-pinned Debian sid container, and Debian can be pinned to one snapshot.debian.org timestamp. Omarchy aarch64 has no dated pin, and its install path is UEFI-only.
- Everything the kernel needs is built in, and
uname -rchanges with every gbook commit (ADR 0011).
Decision
- Debian sid arm64, built by
mmdebstrapin the pinned container from one snapshot.debian.org timestamp, with a lock list and a local.debcache. Omarchy aarch64 (Arch Linux ARM base) is revisited at M4. - Ghostty 1.4.0 is built from its signed source tarball (Zig, as Omarchy’s PKGBUILD does). If that fails, M3 goes without Ghostty until M4.
- ROOT is partition 2 of the same stick, from 256 MiB on. Every image’s GPT declares it, so the per-attempt
256 MiB
dd,verifyandgptstay as they are. The owner writes ROOT only when the rootfs changes. /initfinds ROOT next to the booted kernel partition: the partition whosePARTUUIDequalskern_guid, its disk’s partition 2, which must be labelledgbook-root. It mounts it, thenswitch_roots. No ROOT, or a failed mount, runs today’s capture-and-reboot path. The command line does not change.- The Wi-Fi network block and the stock ADSP/CDSP firmware stay in the initramfs.
/initcopies them to the new root’s/run/gbook/before the hand-off. The rootfs image carries no secret, no vendor blob and no identifier (ADR 0005). - Every driver stays built in. New needs get
kernel/*.configfragments. The rootfs ships no/lib/modules.kernel/mica-rootfs.configturnsFW_LOADER_USER_HELPERoff, as systemd’s README asks. - Audio gets one test attempt: the 9345’s sound node as a
dts/diag/overlay (unverified per ADR 0004), akernel/mica-audio.config, and Debian’s 9345 topology and UCM. Either outcome is the “known state”. - No getty on the AP console, no
sshd, nothing listening. The panel runs Ghostty undercageas an unprivileged user. - The container build tries mmdebstrap’s unshare or fakechroot mode first, and
--cap-add SYS_ADMINonly if those fail. - A release names both images. See the ADR 0011 update of the same date.
Consequences
- The rootfs keeps the netlog format and listener: one shared sender script serves
/initand the rootfs. A base rootfs sends after its checks and then stays up. - The first rootfs boot must check two assumptions: that firmware lookups after
switch_rootresolve against the new root, and that the GPU’s first open there findsgen70500_*. If either fails, the stock files or the command line change, and this ADR gets an update. wpa_supplicant@wlan0needs a drop-in to read/run/gbook/.- The work is tracked in
.scratch/m3-rootfs/.
Update 2026-11-04 (3:08 PM ET): how decision 9 came out
scripts/build-rootfs.sh runs the pinned container rootless with podman (retro-8/05), not through pkexec docker.
Inside it, mmdebstrap’s own unshare mode does not apply: as container root it skips the user namespace, and container
root cannot mount without SYS_ADMIN (probe, 2026-11-04). The script makes the same namespace by hand (unshare --map-root-user --map-auto --mount --pid --fork, a subuid range for root) and runs mmdebstrap’s root mode inside it;
podman’s --security-opt unmask=ALL lets that namespace mount /proc (“Mount too revealing” otherwise). The arm64
essential set built this way in 32 s under qemu binfmt. fakechroot was not tried: mmdebstrap(1) says the mode fails with
a different libc inside the chroot than outside, which a foreign-architecture chroot is. GBOOK_ROOTFS_MODE=sysadmin
keeps the third option (--cap-add SYS_ADMIN, a capability inside podman’s user namespace only, never on the host).
Update 2026-11-04 (3:52 PM ET): the reproducibility contract, and throttling
From rootfs-pinning.md (recommendations 1, 4 and 7), after the first builds of ticket 03:
- Two builds of one snapshot timestamp must agree on
lock.txtand therootfs.tarsha256. Therootfs.ext4sha256 is expected to agree only between builds with the same e2fsprogs version, which the manifest records: the container installs its tools from live sid at build time. The first two builds agreed on all three. - The answer to snapshot.debian.org’s throttling is the
.debcache plus an offline flat repository (GBOOK_ROOTFS_OFFLINE=1, pinned to the previouslock.txt), not a mirror switch: Debian publishes no limit figures,snapshot-cloudflare.debian.orgfronts the same backend (live headers, inference), and no third-party snapshot mirror resolved on 2026-11-04. A build on another mirror (GBOOK_ROOTFS_MIRROR) sayssnapshot=nonein its manifest and is not release material (ADR 0011).
Update 2026-11-04 (6:38 PM ET): decisions 5 and 8 change, sshd for the build host
Owner (“yes” to the ranked options after the second ROOT boot; rootfs-access.md,
recommendations 1 and 2). The rootfs runs sshd, key-only, for the build host alone (AllowUsers gbook@<the build host's LAN address>,
PermitRootLogin no, no passwords), and gbook has sudo for the agent’s reads and unit restarts; writes on the
device still need the owner’s word, as everywhere. The image stays free of keys: the host key and the build host’s authorized
key travel in the initramfs (~/.local/state/gbook/ssh, packaged like the Wi-Fi block) and /init copies them to
/run/gbook/; ssh.service has ConditionPathExists on the host key, Debian’s baked host keys are removed from the
image, and sshd-keygen is masked (an empty machine-id is no first boot, so it would never run, and must not write
keys to ROOT). The AP console stays read-only (ADR 0006); the gettys stay masked. The boot journal also travels as a
section of the rootfs’s netlog, so a Wi-Fi failure still leaves the journal on the stick and a working boot sends it.
Research notes
Where the evidence comes from and how to read each source (blocked sites and their mirrors, the APIs that work):
sources.md.
One file per topic, each claim with its source and the date it was read.
Done:
-
chromeos-boot-path.md(2026-10-21): how Linux can boot through mica’s depthcharge, fastboot, CCD, recovery image -
stock-dtb.md(2026-10-21): the stock DT extracted from the recovery image, its hashes and board devices -
ccd-gsc.md(2026-10-21): SuzyQ on the left port, console map, GSC version and ccdstate, EC log -
9345-dts-diff.md(2026-10-21): node-by-node against mainline 9345; AP consoleuart21; what to copy and what to replace -
el2-handoff.md(2026-10-21): depthcharge enters the kernel at EL2 with no Gunyah; build the mica DTB withx1-el2.dtso -
fit-packaging.md(2026-10-21): mkdepthcharge FIT + dev-key signing for mica, 256 MiB limit, cmdline, cgpt attributes, FIT matching, offline checks, tools on Arch -
console-kernel-config.md(2026-10-21): built-in drivers and boot arguments for a console-only first boot onuart21, earlycon limits, Image size, jhovold/jglathe/Ubuntu Concept configs -
silent-boot-observation.md(2026-10-21): if the AP console is silent: depthcharge ramoops and its readback, the coreboot framebuffer, EC console, firmware log, LED codes,ccdstate -
scmi-cpucp-timeouts.md(2026-11-03): intermittent SCMItimed out in respandscmi-cpufreq -110at boot; upstream fixes (all in the pin), the FastChannel quirk hazard on0x0firmware version,arm,max-rx-timeout-ms, measure-first plan -
milestones-versioning.md(2026-11-04): a milestone ladder M0-M7 over the gates, CalVer image tags, kernel[PATCH vN]rules and patch status words, when the upstream hold could end (AI agents may not addSigned-off-by); 9 open questions for the owner -
m3-rootfs.md(2026-11-04): which rootfs for M3 (Debian sid, Arch Linux ARM + Omarchy, Ubuntu, Fedora, postmarketOS: Mesa for the X1-85, Ghostty 1.4, our kernel, pinning) and how it boots: ROOT on the same stick,/initfinds it bykern_guid,switch_root; 10 open questions for the owner -
debug-tools.md(2026-11-03): the per-boot debug tools: SCMI and I2C trace events, dyndbg, what each boot carries and howattempt.sh tracereads it -
rootfs-pinning.md(2026-11-04): pinning and reproducing the M3 rootfs when snapshot.debian.org throttles -
rootfs-access.md(2026-11-04): reaching the booted rootfs from the build host without a secret in the image (keys in the initramfs, journal upload, ssh) -
asoc-debug-tools.md(2026-11-04): ASoC, SoundWire and audioreach debugging tools; the downstream driver’s enumerator handling (ticket 16) -
scmi-cpucp-stall-report.md(2026-11-04): DRAFT report for linux-arm-msm on the PSCI-retention stall, not sent (M5 hold) -
steamos-arm.md(2026-11-05): what Valve published for the Steam Frame (no ARM SteamOS tree; package repos, repair image, the Deckard DTS, kernel commits through GitHub), five adoption candidates and the owner’s decisions -
m4-recovery-matrix.md(2026-11-05): Valve’s five recovery rows mapped onto mica’s vboot and recovery-stick mechanisms, each labelled proven or inference; the three owner probes that close M4’s return to stock; the slot-policy question -
public-writeup.md(2026-11-05): publishing gbook as a guide: the repo inventory and identifier exposure, Nura and aarch64-laptops conventions, Diátaxis, licences, venues with versions, a redaction gate; finds Anton Bambura’s public Nura port of mica (DTS 2026-10-19, same compatible) -
cloudflare.md(2026-11-05): which Cloudflare products fit the chosen GitHub Pages setup: a DNS-only CNAME and the challenge TXT now, Web Analytics by snippet, R2 or GitHub Releases for the artefacts at the first release that has them, token permission names; Pages, Workers, Turnstile, Tunnel and rate limiting skipped with reasons; 6 open questions for the owner
Reading upstream (learned 2026-11-03, scmi-cpucp-timeouts.md):
- lore.kernel.org, patchwork and git.kernel.org sit behind a bot check (Anubis). Read threads on
lkml.rescloud.iu.eduorlists.infradead.org/pipermail, keep the lore Message-ID, and read torvaldsmasteron the GitHub mirror. - The owner’s web clippings in
~/code/obsidian/the build host/Clippings/may already hold the thread (the X1E CPUFreq series was there).
Queued:
fcc-p212g.md: FCC filings for regulatory model P212G (internal photos, chip markings, SSD placement)
Sources: where the evidence comes from, and how to read it
Compiled 2026-11-04, 1:59 PM ET from the research and recon notes, and from the 2026-11-04 session’s lookups. This is an index: each claim lives in the note that cites it (last column), and a new finding goes there, not here. Primary sources only. News coverage of the Googlebook exists in recon notes, but it is never the authority for a claim (CLAUDE.md, “Docs conventions”).
How to read them
| Source | Works | Blocked or flaky, and the way round |
|---|---|---|
| Kernel source and docs at the pin | the local tree ~/.cache/gbook/linux-a804c57 (cite file:line); github.com/torvalds/linux at a804c5742bff; the GitHub compare API for “is X after the pin” | git.kernel.org sits behind a bot check (Anubis) |
| LKML threads | lkml.rescloud.iu.edu and lists.infradead.org pipermail; keep the lore Message-ID | lore.kernel.org sits behind Anubis (README, “Reading upstream”) |
| Patch state (series, versions, review) | the patchwork REST API: https://patchwork.kernel.org/api/series/<id>/, /api/patches/?msgid=<id> (read 2026-11-04 for 0002-0006 and 0008) | the patchwork web UI sits behind Anubis |
| Mesa | the GitLab API: https://gitlab.freedesktop.org/api/v4/projects/176/merge_requests/<n>; release notes at docs.mesa3d.org/relnotes | |
| Debian packages | qa.debian.org/madison.php, sources.debian.org API, snapshot.debian.org and its API file, metasnap.debian.net; manpages.debian.org and raw files on salsa.debian.org (apt, devscripts) | snapshot.debian.org answered 503/504 three times on 2026-11-04 (m3-rootfs); keep a local .deb cache. At 3:33 PM ET [20:33 UTC] the same day snapshot.debian.org answered and snapshot-cloudflare.debian.org gave 503 through the same Varnish/Fastly chain (rootfs-pinning). Debian publishes no limit figures; snapshot.debian.org/news/ and /doc/api/ are 404 (the front page and the salsa API file hold that content) |
| GNU manuals | gnu.org/software/tar/manual | answered 429 three times on 2026-11-04; tar --help on the build host gives the option semantics (rootfs-pinning) |
| Arch and Omarchy packages | pacman -Si / pacman -Qi live on the build host; pkgs.omarchy.org (aarch64); omacom/omarchy-pkgs PKGBUILDs | no dated archive for Arch Linux ARM (m3-rootfs) |
| GitHub repos and releases | gh api repos/<o>/<r>/releases/latest, gh api repos/<o>/<r> --jq .license.spdx_id | |
| Busybox in the initramfs | Debian’s busybox config: salsa.debian.org installer-team/busybox, debian/config/pkg/static | |
| Valve’s SteamOS ARM (Steam Frame) sources (2026-11-05 lookups) | gitlab.steamos.cloud through its API: https://gitlab.steamos.cloud/api/v4/projects/<id>/repository/{tree,files/<path>/raw,commits} (public projects, no token); the package servers’ directory listings and pacman databases: holo-packages.steamos.cloud/archlinux-deckard/archlinux/main/{core,extra}/os/aarch64/*.db.tar.xz and .files.tar.xz, archlinux-deckard-hotfixes/ (Valve’s own packages, one repo per merge request); steamdeck-images.steamos.cloud/recovery/ (the Frame repair image and the QDL package, 4 GB each, not downloaded); release-note bodies from the Steam news API api.steampowered.com/ISteamNews/GetNewsForApp/v2/?appid=4165890&maxlength=0 (the store pages render by JS); Valve’s private kernel tree’s commits resolve through GitHub’s fork network: gh api repos/torvalds/linux/commits/<sha> and compare/v7.2...<sha>; partner.steamgames.com pages through firecrawl | GitLab blob search needs a token (401); the package servers’ roots answer 401 (go straight to the repo paths); steamdeck-packages is x86_64 only; GitHub’s search/commits?q=hash: does not find fork-network commits; the downloaded atomupd and custom packages carry a CI credential and Valve’s RAUC certificate, so nothing from them goes into the repo (gitleaks passed on the note) |
| Nura (postmarketOS) and its GitLab (2026-11-05 lookups) | gitlab.postmarketos.org redirects (308) to gitlab.nura.eco; pmaports is project 226 there: https://gitlab.nura.eco/api/v4/projects/226/merge_requests/<n>, /repository/files/<path>/raw?ref=; the wiki pages (wiki.postmarketos.org) by title; Anton Bambura’s kernel tree on gitlab.com: https://gitlab.com/api/v4/projects/jenneron%2Flinux/repository/commits/<sha> and /files/<path>/raw?ref=<sha> | projects/postmarketOS%2Fpmaports answers a 301 to the id; use the id |
| The Googlebook itself | live reads through the gbook skill (adb, SuzyQ consoles), dated in docs/recon/ |
By topic
| Topic | Primary sources | Cited in |
|---|---|---|
| Boot chain: depthcharge, FIT, vboot, GPT | chromium.googlesource.com, chromium.org developer library, depthcharge-tools | chromeos-boot-path, fit-packaging, el2-handoff |
| Recovery image, stock OS | dl.google.com/device/recovery, support.google.com/googlebook, source.android.com | stock-dtb, ADR 0001 |
| Hardware and sibling boards | Dell XPS 13 9345 manuals, mainline x1e80100-dell-xps13-9345.dts, Linaro arm64-laptops, aarch64-laptops, jglathe/linux_ms_dev_kit, dwhinham/linux-surface-pro-11 | 9345-dts-diff, console-kernel-config |
| SCMI, CPUCP, idle | the kernel source at the pin; LKML threads by Message-ID (Johan Hovold, Sibi Sankar) | scmi-cpucp-timeouts, scmi-cpucp-stall-report, ADR 0009 |
Qualcomm downstream audio drivers (CodeLinaro audio-kernel-ar) | the GitLab API: https://git.codelinaro.org/api/v4/projects/clo%2Fla%2Fplatform%2Fvendor%2Fqcom%2Fopensource%2Faudio-kernel-ar/repository/files/soc%2F<file>/raw?ref=audio-kernel.lnx.6.0.r67-rel (/repository/tree?path=soc&ref=… lists a folder); save downloads in their own scratch folder and read them with sed -n or rtk rg | the -/raw/ web URLs answered 429 on 2026-11-05 |
| Is there an upstream fix for a driver (quick read) | patchwork’s search: https://patchwork.kernel.org/api/patches/?q=soundwire%3A+qcom&order=-date&per_page=40 (/api/patches/<id>/ holds content and diff); a file’s torvalds history: gh api 'repos/torvalds/linux/commits?path=drivers/soundwire/qcom.c&per_page=10' (read 2026-11-05 for patch 0010) | both miss a fix that is only in a maintainer tree’s next branch (git.kernel.org sits behind Anubis) |
| Local patches’ upstream state | patchwork API; omacom/omarchy-pkgs linux-omarchy-aarch64 | line 1 of each kernel/patches/*.patch, ADR 0007 |
| Kernel process, versions, milestones | Documentation/process/* at the pin (submitting-patches.rst, coding-assistants.rst, maintainer-soc.rst); semver.org, calver.org, keepachangelog.com; Asahi feature support; Nura (postmarketOS) docs | milestones-versioning, ADR 0010, ADR 0011 |
| Rootfs, Mesa, userspace (M3) | Mesa MRs and release notes; Debian, Ubuntu and Fedora package pages; ghostty.org docs, Ghostty release tarballs; Omarchy’s aarch64 repo | m3-rootfs |
| Pinned, reproducible rootfs builds (M3) | snapshot.debian.org and metasnap; the Debian man pages for mmdebstrap, debsnap, debootsnap, debrebuild, apt, dpkg-query, dpkg-scanpackages, apt-ftparchive, diffoscope, mke2fs; the mmdebstrap script; apt and devscripts source on salsa; tytso/e2fsprogs source and release notes; reproducible-builds.org (SOURCE_DATE_EPOCH spec, system images), reproduce.debian.net; Isar, Debusine, live-build, debos, ELBE and meta-debian docs; the isar-users list | rootfs-pinning |
| Reaching the booted rootfs (M3) | the build host’s man pages for openssh 10.6p1 and systemd 262.1 (the sid arm64 versions; Debian’s copies at manpages.debian.org/unstable): sshd_config, sshd, ssh_config, ssh, ssh-keygen, systemd-journal-upload, systemd-journal-remote, journal-upload.conf, journal-remote.conf, journalctl, journald.conf, machine-id, systemd.unit, systemd.socket, systemd-ssh-generator, systemd.system-credentials; Debian packaging on salsa (ssh-team/openssh debian/systemd/*, openssh-server.postinst, rules, patches/debian-config.patch; systemd-team/systemd debian/systemd.postinst); systemd/systemd at v262 (src/ssh-generator/ssh-generator.c, src/core/main.c, src/core/manager.c, src/basic/initrd-util.c) and v259 (src/core/import-creds.c); the mmdebstrap script (cleanup list); madison and the packages.debian.org file lists; the pinned kernel tree (Documentation/usb/gadget_configfs.rst, gadget-testing.rst, bindings/usb/usb-drd.yaml, drivers/usb/dwc3/, x1e001de-devkit.dts) and the built .config; live reads on the build host (ss -ltn, systemctl cat, modinfo) | rootfs-access |
| Terminal tools on the build host (2026-11-04 lookups) | TerminalTextEffects and its docs; Omarchy’s ttfx (omacom/ttfx, pacman -Qi ttfx); gum with colorprofile (NO_COLOR handling); no-color.org; fastfetch; Ghostty about / libghostty; Quickshell and quickshell.org | this file; the decisions are in the running log (the follow colour guard, check 36) |
| SteamOS on ARM, the Steam Frame (ideas for M4 and the rootfs) | Valve’s GitHub and gitlab.steamos.cloud projects, the package servers (pacman databases, ten packages read), the mainline sm8650-valve-deckard.dts, Valve’s kernel commits through GitHub, Steamworks partner docs and Steam Support pages, the Steam news API, Mesa and FEX upstream | steamos-arm.md |
| M4 return to stock, slot policy (planning) | the repo’s own notes at firmware-bluey-16702.B (fit-packaging.md section 5, chromeos-boot-path.md sections 2 to 6), ADRs 0001 to 0003 and 0010, the first-boot runbook, the unlock-day ticket, the project log’s relock and stick-tries rows | m4-recovery-matrix.md |
| Publishing the project (guide, site, wiki) | the repo’s own docs; Nura wiki device-page conventions and pmaports; aarch64-laptops, jhovold and jglathe repos; diataxis.fr; adr.github.io (MADR); creativecommons.org; each tool’s docs and release list (mdBook, MkDocs, Zola, Hugo, Quartz, Wiki.js, GitHub Pages, Forgejo, Codeberg) | public-writeup.md |
| Closed Case Debugging, the SuzyQ consoles | the GSC’s own version and ccdstate reads on the build host, the EC log, ChromiumOS CCD documentation | ccd-gsc.md |
| A silent first boot: ramoops, the firmware framebuffer, EC console, LED codes | depthcharge and coreboot source at their pins, AOSP, the stock DT | silent-boot-observation.md |
Per-boot debug tools: SCMI and I2C trace, dyndbg, attempt.sh trace | the kernel tree at the pin (Documentation/trace/, the drivers’ trace points) | debug-tools.md |
| ASoC, SoundWire and audioreach debugging | the kernel tree at the pin (sound/soc/, drivers/soundwire/), CodeLinaro’s downstream audio-kernel-ar | asoc-debug-tools.md |
| Cloudflare for the public site and release artefacts (2026-11-05 lookups) | developers.cloudflare.com: every page also serves plain Markdown at <page>/index.md, and each product has an index at <product>/llms.txt (/usr/bin/curl into a scratch folder, then rtk rg); the API reference pages under /api/resources/ name the permission groups each endpoint accepts; the Cloudflare blog for dated announcements (2025-04-08 on Pages and Workers, 2020-12-09 on Web Analytics); cloudflare.com/plans/sase-zero-trust for the Zero Trust seats; docs.github.com for the Pages custom-domain, HTTPS, verification, limits and releases pages | cloudflare.md |
Keeping it current
- A new research or recon note that relies on a new kind of source adds one row here, in the same commit.
- A source that becomes blocked, or a new way round, is recorded in “How to read them”, with the date.
How Linux can boot on mica’s depthcharge firmware
Researched 2026-10-21 from firmware source, Google’s docs and a live read of the device. Nothing was changed on the device.
Answer
The Googlebook boots through ChromeOS firmware: coreboot plus the depthcharge bootloader, with Android boot support added on top. Qualcomm ABL is not involved. That changes the plan:
- “Unlock bootloader” is ChromeOS developer mode with new labels. Once unlocked, the firmware boots from external drives and accepts self-signed kernels by default. No
crossystemstep is needed. - The best first-boot route is a ChromeOS kernel partition on a USB drive. It holds a FIT image (kernel plus device trees), signed with the public developer keys. postmarketOS and depthcharge-tools already boot Qualcomm Chromebooks this way. The stock drive stays untouched.
- Firmware fastboot has no
bootcommand, so the plannedfastboot bootRAM-boot test cannot work. Fastboot is also reachable only through a bridge cable (ALink or Prolific PL27A1) or over TCP via a USB-Ethernet adapter. That explains the “specialized cables” report. - Nobody has a device tree for mica. Mainline has
hamoa/x1e80100boards, including the XPS 13 9345, but no Chrome-firmware X1 board. Writing that device tree is the core work, as expected. - Closed Case Debugging exists. GSC consoles are readable while CCD stays locked, so a SuzyQ should give EC (and possibly AP) logs before anything is unlocked.
Items marked inference below are reasoned from source, not tested on the device.
Sources and revisions
| Repo | Ref | Commit |
|---|---|---|
| depthcharge | firmware-bluey-16702.B | 694eabf646855067acd51935059c3191436ccc79 |
| depthcharge | main (fastboot check only) | e91770fa63a431c6a84ff59387a41f7e19222919 |
| vboot_reference | firmware-bluey-16702.B | 34fbbe62cc1832016e956d55d72b4aba606b7e21 |
| ChromiumOS coreboot | firmware-bluey-16702.B | ff3258d575b0ddf4ba0514a2cd03c6a1936dba69 |
| ChromiumOS coreboot | main | 80ab88b8322882ce45c18211c21404500d6f689d |
| ChromiumOS EC | firmware-bluey-16702.B-main | 19b76df3d2722a371141bbac61bf14be79f7a44e |
| ChromiumOS EC (CCD docs) | cr50_stab | ffacd3c99e6a9090a61439d58d46099f0b5ed09e |
| chromiumos-overlay | firmware-bluey-16702.B | 04eaf2635e1fc4533a7ebf32601aa09a4958affb |
| Linux | torvalds master | a804c5742bff224d4cb4d25dd383554d598be32b |
| pmaports / boot-deploy / pmbootstrap | main / 0.25.0 / 3.12.1 | a683af41… / a50452a0… / 7d23010a… |
| depthcharge-tools | v0.6.2 | e62d095bc61a371b69587ba2773e10a5f2e87308 |
Correction 2026-10-21:
e62d095is not v0.6.2. It is themainhead, 107 commits after the tag (git ls-remotereadsrefs/tags/v0.6.2^{}=95d17c444ae3a9dec1c51ead6301ed04777314b9). The citations below point ate62d095. Found by the FIT packaging research (docs/research/fit-packaging.mdon branchresearch/fit-packaging).
Repos: https://chromium.googlesource.com/chromiumos/platform/depthcharge, …/platform/vboot_reference,
…/third_party/coreboot, …/platform/ec, https://github.com/alpernebbi/depthcharge-tools,
https://gitlab.postmarketos.org/postmarketOS/pmaports. These are branch heads read on 2026-10-21. The exact
commits behind the device’s Google_Mica.16702.59.0/Google_Mica.16702.143.0 build are not public. Local copies
are in .firecrawl/ (gitignored).
1. What mica is
| Fact | Evidence |
|---|---|
| mica is a variant of the bluey baseboard | coreboot src/mainboard/google/bluey/Kconfig.name: config BOARD_GOOGLE_MICA "-> Mica". First upstream commit a1048f60938f (CL 90951, merged 2026-01-31) |
| SoC family Hamoa = X1E80100 | Kconfig line 140 (16702.B): select SOC_QUALCOMM_HAMOA. Linux 8c0b058ab598 renames x1e80100 to hamoa. Live read: soc_id 555, X1E80100, 12 CPUs |
| 32 MB AP flash, read over QSPI | BOARD_ROMSIZE_KB_32768. Matches Dell’s “32 MB flash EPROM” |
| USB-C retimer PS8820 (the 9345 uses PS8830) | select MAINBOARD_HAS_PS8820_RETIMER |
| Camera and fingerprint reader on USB | MAINBOARD_HAS_CAMERA_VIA_USB, MAINBOARD_HAS_FINGERPRINT_VIA_USB |
| USB-C debug accessory (CCD) port | HAVE_DEBUG_ACCESS_PORT_SOURCE_SINK, DAP_USE_SMB1. Which physical port is unknown |
| EC: Zephyr on a Nuvoton NPCX9M7FB, partly private | EC zephyr/program/bluey/BUILD.py: register_npcx9_project(project_name="mica", …, extra_modules=["google-private", …]) |
| GSC: Ti50 | MAINBOARD_HAS_GOOGLE_TPM selects TPM_GOOGLE_TI50. depthcharge CONFIG_DRIVER_TPM_GOOGLE_TI50=y |
| depthcharge board | configs/config.mica: BOARD_DIR="bluey", KERNEL_FIT=y, KERNEL_START=0xC7800000, KERNEL_SIZE=0x10000000, KERNEL_FIT_FDT_ADDR=0xDA800000, BOOTCONFIG=y, ANDROID_PVMFW=y, DRIVER_EC_CROS_SPI=y |
config.mica also sets DRIVER_SOC_X1P42100=y. That is the name of the depthcharge SoC driver for the whole
X1 family. coreboot builds mica as Hamoa, and the device reports X1E80100.
Internal disk, live read 2026-10-21 (recon): ChromeOS KERN-A, KERN-B and
ROOT-B sit next to Android A/B partitions: boot, init_boot, vendor_boot, dtb, dtbo, vbmeta,
pvmfw and super.
2. Unlock = developer mode
- The menu item. depthcharge
src/vboot/ui/screens.c:765(16702.B): the Advanced options item.name = "Unlock bootloader"targetsUI_SCREEN_RECOVERY_TO_DEV(“Transition to developer mode”). Once unlocked, the screen reads “Bootloader is unlocked”. Its menu offers Lock bootloader, Boot from internal disk, Boot from external drive (:1801), Select alternate bootloader and Advanced options (which holds “Bootloader fastboot”). - The OEM unlocking toggle. Android’s switch in Developer options maps to the NV bit
VB2_NV_OEM_LOCK. While that bit is set, vboot withholdsDEV_BOOT_ALLOWED(vbootfirmware/2lib/2misc.c~600-613). The firmware then shows “Booting with an unlocked bootloader is disabled… enable OEM unlocking in Developer options”. This matches Google’s steps: turn on OEM unlocking, then Esc + F2 + Power, then Advanced options > Unlock bootloader. - Defaults are already set. vboot
2misc.c:296-303: while in normal mode, the firmware presetsDEV_BOOT_EXTERNAL=1andDEV_ENABLE_FASTBOOT=1, and clearsDEV_BOOT_SIGNED_ONLY. That is the trogdorcrossystem dev_boot_usb=1 dev_boot_signed_only=0step, done ahead of time. This matters because Googlebook OS has nocrossystem(live read: not on PATH). - Self-signed kernels boot.
vb2_need_kernel_verificationis false in dev mode unless FWMP ordev_boot_signed_onlysays otherwise (2misc.c~821-837). On the ChromeOS path, a keyblock whose signature fails is still accepted on its hash (2load_kernel.c~163-253). The body must still match the keyblock’s own key, so the kernel must be self-signed, not unsigned. The public dev keys (/usr/share/vboot/devkeys) work.
3. Boot routes after unlocking
Route A: ChromeOS kernel partition with a FIT image (recommended)
vboot treats a GPT entry as bootable when its type is the ChromeOS kernel GUID fe3a2a5d-4f32-41a7-b725-accc3285a309
or the Android vbmeta GUID (cgptlib_internal.c, 2load_kernel.c:696-733). For ChromeOS entries, depthcharge
loads the body to KERNEL_START and boots it as a FIT (src/arch/arm/fit.c:57-68).
FIT image rules (verified in source):
- Compression: none, lzma, lz4 or legacy-lz4 only (
src/boot/fit.c:291-311). The arm64Imagemagic is checked (boot64.c:188), so a gzipImage.gzfails. - Device-tree choice: depthcharge ranks each FIT configuration’s
compatibleagainstgoogle,mica-rev<board>-sku<sku>, thengoogle,mica-rev<board>,google,mica-sku<sku>andgoogle,mica(fit.c:49-102, name from coreboot’sMAINBOARD_PART_NUMBER“Mica”). With no match it boots/configurations/default. The ordering is documented in the kernel’sDocumentation/arch/arm/google/chromebook-boot-flow.rst. - What depthcharge writes into the DT:
/chosen:bootargs,kaslr-seedandrng-seed.- Memory: replaces every memory node with one
/memoryfrom coreboot’s map and adds/reserved-memoryregions (dt_update.c). - Ramdisk:
linux,initrd-start/end. /firmware/coreboot(withboard-id,sku-id,fw-config),/firmware/chromeos, ramoops, LPDDR info and the pKVM pvmfw and DRNG reservations.- bluey’s
cpumap_fixup.calso marks defective CPU coresstatus = "fail"based on SoC ID.
Tooling: depthcharge-tools mkdepthcharge wraps mkimage and vbutil_kernel and signs with the dev keys. Its
boards.ini has no mica entry; a custom section is needed (inference: values from config.mica):
[boards/custom/mica]
arch = arm64
codename = custom-mica
dt-compatible = google,mica(-rev\d+)?(-sku\d+)?
image-format = fit
image-start-address = 0xC7800000
image-max-size = 256 MiB
Correction 2026-10-21: this section is incomplete.
[boards/arm64]inboards.iniate62d095(lines 3514-3521) setsboots-lz4-kernel,boots-lzma-kernelandloads-fit-ramdisk = True. Per the FIT packaging research, a custom board section does not pick these up, so without themdepthchargectl buildpatches the initramfs into the DTB, pads the kernel and tries only an uncompressed kernel. Add these three lines to the section:loads-fit-ramdisk = True boots-lz4-kernel = True boots-lzma-kernel = TrueA plain
dt-compatible = google,micais enough: depthcharge-tools extends it to the-rev/-skupattern itself.
Disk layout, per the pmbootstrap precedent (pmb/install/partition.py:105-203): a ChromeOS-kernel partition
with cgpt add -t kernel -S 1 -T 5 -P 10, holding the signed image, plus a root partition. depthcharge-tools
adds kern_guid=%U, which the firmware expands to the partition’s PARTUUID, so root=PARTUUID=%U/PARTNROFF=1
finds the next partition.
Route B: modified Android GKI boot image
vboot’s Android path (2load_android_kernel.c:320-468) verifies boot, init_boot, vendor_boot, pvmfw and
dtbo with AVB. In dev mode, verification errors are allowed and the boot state turns orange. The DT comes from
the dtbo partition’s table (android_dtboimg.c). The kernel command line comes only from vendor_boot.
Inference: if no dtbo base entry matches, depthcharge falls back to a FIT inside the boot image. This route
overwrites stock partitions, so it ranks below Route A.
Route C: alternate firmware payload (RW_LEGACY)
coreboot’s chromeos.fmd has an RW_LEGACY region outside write-protected RO. depthcharge runs payloads listed
in altfw/list when DEV_BOOT_ALTFW is allowed (payload.c, screens.c:2041-2063). This is how a UEFI
payload (EDK2 or U-Boot) could boot generic distro images. It needs flashrom with root to write RW_LEGACY,
plus an arm64 Hamoa payload that nobody has built. This is a later option, not the first boot.
4. Firmware fastboot
- Commands (
src/fastboot/cmd.c, 16702.B):continue,upload,download,erase,flash,getvar,oem cmdline|bootconfig add/del/set,oem logs,oem read/write-ufs-descriptor,oem set-successful,oem set-priority,oem sha256,reboot[-…],set_activeandsnapshot-update. main adds onlyfetch. There is noboot, noflashing unlockand nooem unlock. - Transport (
fastboot.c:241-261,usb.c:16-28): depthcharge is the USB host. It looks for a Prolific PL27A1 (067b:27a7) or a Google ALink cable or dongle (18d1:506d/506e/506f/5084), then falls back to TCP port 5554 over DHCP with ASIX, r8152 or smsc95xx USB-Ethernet adapters. bluey registers no USB device-mode controller, so a plain USB-C cable to a PC cannot reach it. - Present in production? It is linked in only with
CONFIG_FASTBOOT_IN_PROD, which the depthcharge ebuild sets whenuse avb. Inference (strong): mica needs AVB for Android boot, so fastboot is present. The board overlay is private. - Use here:
flashwith no allow-list andoem set-priority/set-successfulcan install and select a kernel partition, given a USB-Ethernet adapter on a DHCP network. That makes it an optional install path, not a RAM boot. Android’s userspacefastbootd(reboot-fastboot) is separate.
5. Closed Case Debugging
Live read 2026-10-21 (gsctool -a -I runs as the adb shell user): CCD Locked, no password. The Always
capabilities are:
UartGscRxAPTx(read the AP console)UartGscRxECTx(read the EC console)UartGscRxFpmcuTxFlashReadUnlockNoRebootandUnlockNoShortPP
FlashAP, FlashEC, OverrideWP and OpenNoDevMode are IfOpened, and AllowUnverifiedRo is Never. GSC
firmware: RO 2.0.119, RW 0.33.291.
From docs/case_closed_debugging_gsc.md (EC cr50_stab):
- Consoles: the doc says Ti50 enumerates as
18d1:504a. On mica it is18d1:5066, and Linux exposes the consoles as ttys (ccd-gsc.md), sousb_consoleis not needed.usb_console -d 18d1:504a -i 1gives the AP console and-i 2the EC console. - Opening CCD: needs developer mode, then
gsctool -a -oand repeated power-button presses over about 5 minutes. Opening wipes dev mode. - Flashing over CCD:
flashrom -p raiden_debug_spi:target=APworks once CCD is open (FlashAP). - Caveat: “the AP console itself is disabled for production devices”. So the AP UART may carry nothing, even though the capability allows it.
FlashRead is not a flash dump. The same doc’s capability table says it allows “dumping a hash of the AP or EC
flash”. A full AP flash backup over raiden_debug_spi still needs CCD opened, and opening needs developer mode.
Inference: with the Fyra SuzyQ on the debug-accessory port, the EC console (and maybe firmware logs) can be read now, with no unlock. A flash hash could also be taken now, to compare against later.
6. Recovery image (Gate 1)
https://dl.google.com/device/recovery/mica-user/16471258/recovery.zip answered HEAD with 200, 7,601,764,157
bytes, Last-Modified 2026-10-05 15:44 GMT [11:44 EDT] (found via cros.tech/googlebook/mica, not downloaded).
Saving it covers the log’s “save a copy of the recovery image” item, with one problem: its build number
16471258 is older than the installed build 16560795 (fingerprint …/CL3B.260622.276.R2/16560795). Anti-rollback
may refuse it. Before relying on it for Gate 1, check the build inside the zip and whether the Device Recovery
Utility serves a newer image than this static URL. It should also contain the stock dtb,
dtbo and vendor_boot images, which means the vendor device tree and firmware without unlocking (inference:
check the zip’s layout).
7. Proposed path
Status 2026-10-21: step 1 is done. The recovery zip is extracted (stock-dtb.md) and the EC and GSC consoles were read (ccd-gsc.md). The DTS for step 3 is drafted (dts/). Recorded as ADR 0003.
- Before unlock: save the recovery zip (check the build-number question in section 6) and extract the stock DTB, dtbo and firmware. Read the EC console (and the AP console, if live) with the SuzyQ.
- Unlock (owner, after Gate 1): OEM unlocking, Esc + F2 + Power, Unlock bootloader.
- First boot (Route A):
- Build a mainline kernel with a
google,micaDTS: start fromx1e80100-dell-xps13-9345.dtsand use the stock DTB as the hardware map. - Package it with
mkdepthchargeand dev keys onto a USB drive’s ChromeOS-kernel partition. - Choose Boot from external drive.
- Build a mainline kernel with a
- Console: CCD AP/EC UART if live. Otherwise ramoops (depthcharge always reserves it) read back on the next
stock boot, or the panel through
mdssonce the DTS is good enough.
Not found
- Any
micaorblueydevice tree (mainline,chromeos-6.12, the 16702.B kernel branch, lore linux-arm-msm). - Public board overlays for bluey or mica (to confirm
use avb), and the exact firmware build commits. Which physical USB-C port is the debug-accessory port: the left one works (tested 2026-10-21); the right is untested.- Any public report of Linux or another non-Android OS running on a Googlebook. AskUbuntu 1570130 (2026-10-07) is unanswered.
- Board-id and sku-id values (still unknown):
/proc/device-treereads are denied to adb shell. They are in/firmware/corebootonce booted, or in the stock DTB.
mica’s stock device tree
Extracted 2026-10-21 on the build host from Google’s public recovery image. The device was not touched. The DTBs themselves
stay in dumps/ (gitignored, vendor content). This note keeps how to reproduce them, their hashes and what they
show.
Source
| Item | Value |
|---|---|
| URL | https://dl.google.com/device/recovery/mica-user/16471258/recovery.zip (Last-Modified 2026-10-05 15:44 GMT [11:44 EDT]) |
recovery.zip | 7,601,764,157 bytes, sha256 cb68dd6dbd73e568cfeecca0cbd4ceb23338640814dc3ee52aba5507e5a645d7 |
| Contents | one file, android-desktop_signed_recovery_image.bin (19,230,966,272 bytes, dated 2026-09-29), a GPT disk image |
Build (AVB props in vbmeta_a) | Dell/mica/mica:17/CL3B.260622.273.R1/16471258:user/release-keys, rollback index 1339 (location 0) |
| Installed build (recon 2026-10-21) | …/CL3B.260622.276.R2/16560795: newer than this image |
Reproduce: scripts/extract-stock-dtb.sh dumps/recovery-16471258/recovery.zip. It needs unzip, sfdisk,
lz4 and dtc, and uses scripts/dtbo_split.py.
Partition table (from the image’s GPT)
The image is a full internal-disk layout: 38 partitions, with A/B pairs for most Android partitions. _b twins
are left out below; each has the same size and type as its _a partition. Only vbmeta_b carries attributes,
GUID:48,49,50,51,56 (priority 15, successful). The live device boots slot _b (ro.boot.slot_suffix, recon).
Read with sfdisk -d.
| Partition | Size | Type GUID | Note |
|---|---|---|---|
ota_recovery_a | 128 MiB | Linux data | |
KERN-A | 0 MiB | Linux data | |
KERN-B | 4 MiB | Linux data | |
ROOT-B | 32 MiB | Linux data | |
super | 12288 MiB | Linux data | |
vbmeta_a | 4 MiB | Android vbmeta | |
init_boot_a | 8 MiB | Linux data | |
vendor_boot_a | 32 MiB | Linux data | |
boot_a | 64 MiB | ChromeOS kernel | |
pvmfw_a | 4 MiB | Linux data | |
dtb_a | 2 MiB | Linux data | |
dtbo_a | 8 MiB | Linux data | |
desktop_security_persist | 16 MiB | Linux data | |
desktop_security_storage | 32 MiB | Linux data | |
misc | 4 MiB | Linux data | |
metadata | 64 MiB | Linux data | |
recovery_a | 32 MiB | Linux data | |
frp_persist | 2 MiB | Linux data | |
modem_a | 350 MiB | Linux data | |
bluetooth_a | 8 MiB | Linux data | |
dsp_a | 75 MiB | Linux data | |
keymaster_a | 10 MiB | Linux data | |
persist | 32 MiB | Linux data | |
rawdump | 256 MiB | rawdump | |
userdata | 4154 MiB | Linux data |
Points that matter for the boot path (chromeos-boot-path.md):
boot_a/buse the ChromeOS-kernel type GUID (fe3a2a5d-…).vbmeta_a/buse the Android vbmeta GUID (88434509-…). vboot loads Android kernels through it.KERN-A(1 sector),KERN-B(a 4 MiB squashfs) andROOT-Bare typed as plain Linux data, so vboot does not treat them as kernels.
Where the DT lives
dtb_a(2 MB) is all zeros.dtbo_ais an Android DT table (magicd7b7ab1e, version 1, 3 entries of 32 bytes). Each entry is an LZ4 frame (04 22 4d 18), not a raw DTB.- That matches depthcharge’s Android path, which builds the DT from the
dtbotable (boot path, route B).
| Entry | Decompressed | Kind | Table fields after size/offset | sha256 |
|---|---|---|---|---|
| 0 | 414,268 B | base, root compatible = "qcom,x1e80100", no model | 0x0 0xffff0000 0x3 0x0 0xffffffff 0x0 | 4d019aa922b3d92695bafbc741bd3fd0073454995410a43d98904f4f9dac0f84 |
| 1 | 36,860 B | overlay: the board | 0x0 0xffff0000 0x3 0x0 0xffffffff 0x0 | ae86b73044c9b6bc2af8318291c62d2cbef53bdbe4eea09cbaecafeccb375e84 |
| 2 | 996 B | overlay: keyboard matrix only | 0x0 0xf0001 0x3 0x0 0xffffffff 0x0 | 678a35b9f776a8aa759e542b5f31dc0a2308e940e5d2e3985e69675de9b1c4fd |
dtbo_a.img sha256: a94f74e2b9360351a94618699f5e9362984f41a68ac37e16efb81e5cf01d06ba. The table fields
probably encode board-id and SKU ranges for depthcharge’s matcher (android_dtboimg.c). The decoding is not
checked yet.
What it shows
The base is a Qualcomm vendor tree, not mainline. It has 608 compatible properties (257 distinct). Some
match mainline bindings: qcom,x1e80100-gcc, -dispcc, -adsp-pas, -cdsp-pas, -pcie, qcom,pmic-glink.
Display is downstream (qcom,sde-kms, sde_* labels), and buses use downstream labels (qupv3_seN_i2c).
Remoteproc firmware names are bare (adsp.mdt, cdsp.mdt), loaded from /vendor/firmware. So it is a
hardware map (buses, addresses, GPIOs, regulators) for writing a mainline DTS, not something to boot as is.
Board devices in overlay 1 (bus label as in the stock tree):
| Device | Compatible | Bus / address | Note |
|---|---|---|---|
| Touchpad | hid-over-i2c | qupv3_se0_i2c @ 0x2c, hid-descr-addr 0x20 | vdd from touchpad-vdd-regulator |
| Touchscreen | hid-over-i2c | qupv3_se8_i2c @ 0x10 | LG SW42101M per chromium CL 7743220 |
| GSC (Ti50) | google,cr50 | qupv3_se10_i2c @ 0x50 | mainline tpm_tis_i2c_cr50 binding |
| ChromeOS EC | google,cros-ec-spi | qupv3_se13_spi @ CS0 | plus google,cros-ec-keyb (8 rows x 18 columns, keymap in overlays 1 and 2) |
| USB-C retimers ×2 | parade,ps8830 | qupv3_se3_i2c @ 0x08, qupv3_se7_i2c @ 0x08 | coreboot says MAINBOARD_HAS_PS8820_RETIMER; the DT names the PS8830 binding |
| eUSB2 repeaters ×2 | nxp,ptn3222 | qupv3_se5_i2c @ 0x4f and 0x4b | |
| USB-C ports ×2 | usb-c-connector | under ucsi, connectors 0 and 1 | |
| Fingerprint MCU | usb18d1,5406 | usb_mp port 2 | USB-attached, as coreboot’s FINGERPRINT_VIA_USB says |
| AP flash | jedec,spi-nor | qspi CS0 | the 32 MB firmware flash |
| Regulators | regulator-fixed | pp3300-ucam, dmic-en, fpmcu-vcc, touchpad/touchscreen vdd, rtmr0/1-{1p15,1p8,3p3} |
The overlay also targets display (mdss_*, sde_edp, sde_dp, sde_dp1, eDP HPD on tlmm 119), the battery
charger, the USB multiport PHYs, reserved memory (a splash region, and a pKVM protected region for the GIC ITS at
0x17040000), and it disables seven apps_rsc_drv2 children.
Against the XPS 13 9345 (first pass)
- The 9345 uses
parade,ps8830retimers too. Mica’s two retimers sit on different buses, and mica has no USB4/Thunderbolt in Dell’s spec (10 Gbps). Compare the 9345’sps8830nodes and its USB4 nodes. - The 9345 has a Dell EC (
dell-xps-ecwork); mica replaces it withgoogle,cros-ec-spiandgoogle,cros-ec-keyb. Battery and charging go throughpmic-glinkon both. - The 9345’s TPM is not a GSC; mica adds
google,cr50on I2C. - Camera: the 9345’s camera is the SoC camera-subsystem work in Linaro’s tree (project log); mica’s is USB (
MAINBOARD_HAS_CAMERA_VIA_USB,pp3300-ucamrail), so likely plain UVC.
Follow-up (2026-10-21): the node-by-node comparison is 9345-dts-diff.md, and the draft DTS built from it is in dts/.
Closed Case Debugging on mica with the SuzyQ
Tested 2026-10-21 on the build host with a Fyra Labs SuzyQ Board. CCD stayed Locked, nothing was opened or flashed,
and the only commands sent to the GSC were version and ccdstate. Per-unit identifiers (the Ti50 USB serial
number, chip ID and pcr0) are left out.
Hookup that works
- The SuzyQ’s USB-C plug goes into the Googlebook’s left USB-C port. The board’s own USB-C socket goes to the build host with a data cable. The board is only a pass-through; it needs both connections at once (Fyra docs: “The SuzyQ USB-C connector goes into the CCD port … You must provide a USB cable that can carry data”).
- It enumerated in the first orientation tried. The other orientation and the right port were not tested.
- the build host sees
18d1:5066 Google Inc. Ti50. Linux’susb_serial_simple(“google converter”) creates the ttys. Note the ID: the GSC doc names18d1:504a, but this Ti50 reports5066.
| tty | USB interface | Console | Seen 2026-10-21 |
|---|---|---|---|
ttyUSB0 | 0 Shell | GSC (Ti50) shell | answers commands |
ttyUSB1 | 1 AP | AP UART | silent: the stock kernel has console=null console=ttynull |
ttyUSB2 | 2 EC | EC console | logs events |
ttyUSB3 | 6 FPMCU | fingerprint MCU | silent |
| (none) | 3 Ti50 Update, 4 SPI, 5 I2C | firmware update, flash and I2C bridges | not used |
Use /dev/serial/by-id/usb-Google_Inc._Ti50_*-if0N-port0 instead of the ttyUSBN numbers, which can change.
the build host access
The ttys are root:uucp 0660 by default. Since 2026-10-21, /etc/udev/rules.d/70-ti50-ccd.rules tags
SUBSYSTEM=="tty" with ATTRS{idVendor}=="18d1" and ATTRS{idProduct}=="5066" as uaccess, so the active
desktop session gets an ACL. It sorts before systemd’s 73-seat-late.rules, which applies the tag. Check with
getfacl /dev/ttyUSB0. To undo, delete the file.
Reading safely
Turn off local echo first, or the tty line discipline echoes received bytes back into the console:
stty -F /dev/ttyUSB2 raw -echo -echoe -echok -echoctl -echoke
timeout 60 cat /dev/ttyUSB2 # EC: passive
printf 'ccdstate\r' > /dev/ttyUSB0 # GSC: one command, read the reply from a cat already running on ttyUSB0
What it showed
GSC version:
- RO: A
0.0.113, B2.0.119(active). - RW: A
0.33.270, B0.33.291(active),ti50a_common_mp-16604.B:v0.0.151-54d5cc09, built 2026-06-11. - This matches
gsctool -a -ffrom adb (RO 2.0.119 / RW 0.33.291).
GSC ccdstate:
| Field | Value | Meaning |
|---|---|---|
AP | on | |
Servo | disconnected | |
Rdd | connected | the GSC sees the debug accessory on the left port |
CCD_MODE | asserted | |
| State flags | UARTAP+TX UARTEC UARTFPMCU | the AP UART is bridged both ways; EC and FPMCU are read-only |
| CCD ports blocked | none |
gsctool -a -I from adb shows CCD Locked with UartGscRxAPTx, UartGscRxECTx and UartGscRxFpmcuTx
allowed Always. UartGscTxAPRx shows Y Always too, which explains UARTAP+TX.
EC console (Zephyr logging), charger test:
I: ADSP: Charging from USB0
I: ADSP: Charging from USB1
The EC reports charge-port changes coming from the SoC’s ADSP. That fits the stock DT’s pmic-glink and UCSI
connectors: on Linux, battery and USB-C state go through the ADSP, as on other X1 laptops, and the EC is told.
What it means for bring-up
- A console for the first boot exists. The AP UART is bridged both ways while CCD is locked. A mainline kernel
booted with
console=ttyMSM0,115200(or the right GENI UART) should print tottyUSB1. Which GENI serial engine is wired to the GSC is still to be found in the stock DT (inference to test). - The EC console shows charge and power events during experiments, with no unlock needed.
- Still locked:
FlashAP,FlashEC,OverrideWPandGscFullConsoleneed CCD opened. Opening needs developer mode,gsctool -a -oand power-button presses (boot path, section 5).
Open
- The right port and the other plug orientation (does CCD work only on the left port, which matches coreboot’s
single
DAP_USE_SMB1?). - Whether depthcharge prints to the AP UART during boot. A reboot while
ttyUSB1is being read would show it. That is a harmless test, but it reboots the owner’s device, so it needs the owner’s OK.
mica vs the mainline XPS 13 9345 device tree
Compared 2026-10-21. Both trees were read on the build host; the device was not touched.
- Mainline:
arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dtswithhamoa.dtsiandhamoa-pmics.dtsi, torvalds/linuxa804c5742bff224d4cb4d25dd383554d598be32b. - Stock: the base DTB and board overlay from recovery build 16471258 (stock-dtb.md, overlay 1
sha256
ae86b730…).
Stock labels use Qualcomm’s vendor names (qupv3_seN_i2c). Each was matched to its mainline label by node
address through the base DTB’s __symbols__ (for example qupv3_se0_i2c = i2c@b80000 = mainline &i2c0).
GPIO numbers below are decimal; the stock overlay writes them in hex.
Answer
mica is a close derivative of the 9345 board. The USB-C retimer block (both PS8830s, all six power-rail GPIOs, both reset GPIOs), the touchscreen, the touchpad interrupt line, the PTN3222 reset, Wi-Fi enable, the NVMe and Wi-Fi PCIe controllers, the eDP controller and the debug UART all match the 9345 exactly. The changes cluster in a few places:
- ChromeOS parts replace Dell parts: a cros-ec on SPI (keyboard, and probably lid and battery events), a Ti50 TPM on I2C, the AP firmware flash on QSPI, a USB fingerprint MCU.
- Touchpad moved: it now sits on
i2c0, where the 9345 has its I2C keyboard. The interrupt line is the same. - Backlight is PWM (PMK8550 PWM). The 9345’s panel node has no PWM.
- eUSB2 repeaters: two PTN3222 on
i2c5(0x4f,0x4b) where the 9345 has one oni2c9(0x4f). - Audio: the WCD938x headset codec is disabled. Speakers use the WSA macros, plus 4 DMICs.
So the bring-up DTS should be #include the 9345’s board choices where they match, and replace the
keyboard, EC, backlight and repeater sections. Inference: the 9345 DTS can be copied as the starting file,
not merely used as reference.
AP console: uart21
| 9345 mainline | mica stock | |
|---|---|---|
| Alias | serial0 = &uart21 | serial0 = …/qcom,qup_uart@894000 (qupv3_se21_2uart, qcom,geni-debug-uart) |
| Address | serial@894000 | @894000 |
| Settings | stdout-path = "serial0:115200n8" |
Inference (strong): the GSC’s AP console (ttyUSB1 on the build host, ccd-gsc.md) is wired to uart21.
A mainline kernel with &uart21 { status = "okay"; }, stdout-path = "serial0:115200n8" and console=ttyMSM0,115200
should print there. It is the first thing to check at first boot.
Node by node
| Function | 9345 mainline | mica stock (mainline label) | Same? |
|---|---|---|---|
| Debug UART | &uart21 | &uart21 @ 894000 | yes |
| Bluetooth UART | &uart14, qcom,wcn7850-bt, bt-enable TLMM 116 | &uart14 @ a98000 (msm-geni-serial-hs), bt_en TLMM 116, qcom,kiwi | yes (WCN7850 = “kiwi”) |
| Wi-Fi | &pcie4, pci17cb,1107, wlan-enable TLMM 117 | pcie@1c08000 = &pcie4, wlan-en-gpio TLMM 117 | yes |
| NVMe | &pcie6a | pcie@1bf8000 = &pcie6a, qcom,no-l0s-supported | yes; note no-L0s |
| Keyboard | &i2c0 keyboard@5 hid-over-i2c, int TLMM 67 | cros-ec-keyb under the EC, 8 rows x 18 columns | no |
| EC | Dell EC (not in this DTS) | &spi13 ec@0 google,cros-ec-spi, 3 MHz, int TLMM 66 | no |
| TPM | none | &i2c10 tpm@50 google,cr50, int TLMM 91 | new |
| AP flash | none | &qspi flash@0 jedec,spi-nor, write-protect TLMM 19 | new |
| Touchpad | &i2c17 touchpad@2c, int TLMM 3 | &i2c0 touchpad@2c, hid-descr-addr 0x20, int TLMM 3, vdd PM “humu” L12, vddl TLMM 36 | bus differs |
| Touchscreen | &i2c8 touchscreen@10, int TLMM 51 | &i2c8 touchscreen@10, int TLMM 51, vdd “humu” L15 | yes (LG SW42101M) |
| Retimer 0 | &i2c3 typec-mux@8 parade,ps8830, reset PM8550 GPIO 10 | &i2c3 typec-mux@8 parade,ps8830, reset PM8550 GPIO 10 | yes |
| Retimer 1 | &i2c7 typec-mux@8, reset TLMM 176 | &i2c7 typec-mux@8, reset TLMM 176 | yes |
| Retimer rails | RTMR0: PMC8380-5 GPIO 8, PM8550VE-9 GPIO 8, PM8550 GPIO 11. RTMR1: TLMM 188, 175, 186 | identical (PMC8380 “F” = SID 5, PM8550VE “J” = SID 9) | yes |
| eUSB2 repeater | &i2c9 redriver@4f nxp,ptn3222, reset TLMM 184 | &i2c5 redriver@4f (reset TLMM 184) and redriver@4b (reset TLMM 10) | bus differs, one extra |
| USB-C ports | usb_1_ss0/ss1 + pmic_glink connectors | usb0/usb1 = &usb_1_ss0/ss1, role switch, retimers in the graph, UCSI connectors 0 and 1 | yes |
| USB multiport | &usb_mp, both HS and QMP PHYs on | &usb_mp, HS PHYs on, QMP PHYs disabled, fingerprint at port 2 (usb18d1,5406) | partly |
| Third USB controller | none | usb4 = &usb_2 (a200000) okay, HS PHY &usb_2_hsphy behind PTN3222 0x4b | new |
| Panel | &mdss_dp3 eDP, edp-panel on aux-bus, enable TLMM 74 | sde_edp = &mdss_dp3, HPD TLMM 119 | yes, same controller |
| Backlight | no PWM in the panel node | PWM pmk8550_pwm channel 0, period 270000 ns; enable PMC8380-D GPIO 4, power PMC8380-D GPIO 10 | no |
| DP alt mode | &mdss_dp0, &mdss_dp1 | sde_dp, sde_dp1 = &mdss_dp0/dp1 | yes |
| Speakers | WSA8845 via the WSA macros | wsa_macro, wsa2_macro tuned (bat-cfgs, rloads, gains) | likely same (both list 4x WSA8845) |
| Headset codec | (check the 9345 sound node) | WCD938x disabled, qcom,wcd-disabled | to compare |
| Mics | DMIC0-3, regulator-dmic-en on TLMM 69 (stock overlay entry01-overlay.dts 1389: EN_PP3300_MIC, fixed, always-on; the base tree has none) | dts/diag/sound.dtsi carries it as the VA macro’s vdd-micb-supply (2026-11-04, ticket 10); in the board DTS since 2026-11-05 | |
| Charger PMICs | &smb2360_0, &smb2360_1 | the same two; smb2360_2/3 disabled | yes |
| GIC ITS | enabled | gic_its disabled, region reserved as pkvm,protected-region | stock pKVM detail; ignore for Linux at EL2 |
| Thermal | sys-therm-6 passive trip | port later |
Not compared yet: the camera (USB per coreboot, so probably UVC on usb_mp or usb_2), the ambient-light
sensor, the Glowbar, the lid switch, and the two fans. None of them appear in the board overlay. Inference: they
sit behind the EC or on USB.
Plan for dts/x1e80100-google-mica.dts
Status 2026-10-21: done as a draft (dts/x1e80100-google-mica.dts, ADR 0004). Two changes from this plan: the QSPI flash was left out because mainline hamoa has no QSPI node, and the touchpad reset is held by pinctrl because generic hid-over-i2c has no reset property. The dts/README.md table supersedes this list.
- Copy
x1e80100-dell-xps13-9345.dtsata804c57. Setcompatible = "google,mica", "qcom,x1e80100"so depthcharge’s FIT match (google,mica…) selects it, and set the model. - Keep the matching blocks:
uart21,uart14with BT,pcie4with Wi-Fi,pcie6awith NVMe, both retimers and their rails, the touchscreen,mdss_dp0/1/3,usb_1_ss0/ss1,smb2360_0/1, the remoteprocs. - Replace:
i2c0keyboard → the touchpad (fromi2c17);- drop the Dell EC;
- add
&spi13 { cros_ec: ec@0 { compatible = "google,cros-ec-spi"; … } }withcros-ec-keyb(keymap from overlays 1 and 2); - add
&i2c10 tpm@50 google,cr50; i2c9PTN3222 →i2c5at0x4fand0x4b, and enableusb_2;- backlight →
pwm-backlighton the PMK8550 PWM.
- Start minimal for the first boot: UART, NVMe or USB storage, simple-framebuffer if depthcharge leaves one (unknown), then add the rest.
- Firmware names: the stock tree loads bare
adsp.mdtandcdsp.mdtfrom/vendor/firmware. Mainline expectsqcom/x1e80100/<vendor>/<board>/…; this needs aqcom/x1e80100/google/mica/path and the blobs pulled fromvendorinsuper.
Caveats
- The stock tree is a vendor tree. Only addresses, GPIOs, I2C addresses and regulators carry over. Property
names (
qcom,…) generally do not. parade,ps8830appears in the stock DT while coreboot selectsMAINBOARD_HAS_PS8820_RETIMER. The Linux driver binding may cover both. Checkdrivers/usb/typec/mux/ps883x.cbefore the first USB-C test.- The 9345 mainline DTS was read at one commit. Recheck against the tag the kernel is built from.
Exception level at kernel entry on mica, and x1-el2.dtso
Researched 2026-10-21 from firmware and kernel source at pinned commits, the stock DT extracted from the recovery image, and the existing recon. The device was not touched. Resolves ticket 02.
Answer
- mica’s firmware enters Linux at EL2. coreboot asks BL31 (TF-A) to start the payload at EL2. depthcharge
(built on libpayload) runs at EL2 and jumps to the kernel with a plain branch, with no
eret. The Android and the FIT routes both use that same jump. The bluey firmware has no Gunyah or other Qualcomm hypervisor: on the application cores it starts only BL31 (EL3), QTEE (secure EL1) and depthcharge. AOP, CPUCP, SHRM and ADSP-lite firmware go to other processors. On stock, EL2 belongs to the Android kernel’s pKVM (kvm-arm.mode=protected). - Apply
x1-el2.dtso. Either buildx1e80100-google-mica-el2.dtbfrommica.dtb+x1-el2.dtbo(the way the Makefile does it for the 9345), or put the same changes in the mica DTS directly, as the sc7180/sc7280 Chromebook trees do. mica has no Gunyah variant, so the plain-EL1 DTB has nothing to boot on. The overlay applies cleanly to the current draft and passesdt-validate(section 5). - What breaks without it (inference from the overlay’s comments and the drivers):
- First-boot risk:
sbsa_watchdogstays enabled. Its comment says it “is implemented in software in Gunyah and can’t be used when running in EL2”. IfCONFIG_ARM_SBSA_WATCHDOGprobes a watchdog that does not exist, the result is unknown. Separately,apss_watchdog(the real hardware watchdog) staysreserved. - As soon as NVMe or Wi-Fi is on:
pcie_smmustaysreservedandpcie4/pcie6aget noiommu-map. Under Gunyah, firmware programs that SMMU; at EL2 nobody does. - Phase 3: the GPU tries to load a zap shader through PAS, IRIS tries PAS boot (broken at EL2, per commit
c0f045e), and the ADSP/CDSP lack theiommusthat the PAS driver needs to map their firmware.
- First-boot risk:
- Applying it wrongly means applying it under a hypervisor that owns the PCIe SMMU and APSS watchdog. Linux would then fight that hypervisor for both. That cannot happen on this firmware.
1. Firmware chain: who runs at which EL
Sources: ChromiumOS coreboot firmware-bluey-16702.B at ff3258d575b0ddf4ba0514a2cd03c6a1936dba69 and depthcharge
firmware-bluey-16702.B at 694eabf646855067acd51935059c3191436ccc79
(pinned in chromeos-boot-path.md). Read 2026-10-21.
| Step | Evidence |
|---|---|
| coreboot starts at EL3 | src/arch/arm64/Kconfig:26-28: ARM64_CURRENT_EL defaults to 3. Neither src/soc/qualcomm/x1p42100/Kconfig nor src/mainboard/google/bluey/Kconfig nor src/soc/qualcomm/common/Kconfig overrides it (no CURRENT_EL in any of the three) |
| It uses TF-A, which needs EL3 | src/soc/qualcomm/x1p42100/Kconfig:11 select ARM64_USE_ARM_TRUSTED_FIRMWARE; src/arch/arm64/Kconfig:44-47 depends on … ARM64_CURRENT_EL = 3 |
| The payload is requested at EL2 | src/arch/arm64/boot.c:17-20: payload_spsr = get_eret_el(EL2, SPSR_USE_L), passed to run_bl31() |
| BL31 gets that SPSR for BL33 | src/arch/arm64/bl31.c:108-110: bl33_ep_info.spsr = payload_spsr |
| The secure OS runs at secure EL1 | bl31.c:88-104: BL32 loaded from CBFS secure_os, spsr = get_eret_el(EL1, …). Hamoa selects ARM64_USE_SECURE_OS (x1p42100/Kconfig:48-52) |
| depthcharge’s runtime runs at EL2 | libpayload payloads/libpayload/arch/arm64/head.S:38 writes sctlr_el2 and :42 writes hcr_el2 (HCR_TGE) at _entry. Those writes are undefined below EL2 (Arm ARM DDI 0487, MSR to an *_EL2 register from EL1 is UNDEFINED), and depthcharge visibly runs on mica. Also arch/arm64/mmu.c:84 (“We should be in EL2…”) and exception_asm.S:97-126 (spsr_el2, elr_el2) |
| depthcharge does not change EL before the kernel | depthcharge src/arch/arm/boot64_asm.S:38-46: TLB invalidate, MMU off, x0 = fdt, then br x25. No eret, no SPSR write |
| Both boot routes use that jump | src/arch/arm/fit.c:57-124: boot() takes bi->dt (the Android dtbo route) or fit_load() (the ChromeOS FIT route), then boot_arm_linux(). That is boot64.c:166-229, ending in boot_arm64_linux_jump() |
No hypervisor image in the bluey build. src/soc/qualcomm/x1p42100/Makefile.mk:88-431 lists every Qualcomm blob
put into CBFS: QcLib, DCB, pre-DDR DTB, CPR, QUP firmware, AOP, CPUCP, ADSP-lite, SHRM, the QTEE configs
tzoem_cfg/tzqti_cfg, the access-control configs tzac_cfg/hypac_cfg, APDP and ramdump. TF-A (bl31) and
QTEE (secure_os) come from the arch Kconfig. No hypervisor image (hyp.mbn, Gunyah, QHEE) is there.
src/soc/qualcomm/x1p42100/soc.c:115-147 loads the four configs, then preloads bl31 and secure_os, and nothing
else.
DRAM map agrees. src/soc/qualcomm/x1p42100/memlayout.ld:267-268 puts coreboot’s own post-RAM stack at
0x80000000. On Windows-firmware boards, mainline reserves that address as gunyah_hyp_mem (hamoa.dtsi:482-485
at a804c57). TZ is at 0xD8000000, with BL31 right after it (:293-294). Inference: the mainline gunyah_hyp_mem
reservation wastes 8 MiB on mica and does no harm.
Stock agrees. Recon 2026-10-21 (recon-2026-10-21.md rows 19 and 21):
- the command line has
kvm-arm.mode=protectedandid_aa64mmfr1.vh=0, pluskvm-arm.protected_modules=withpkvm-smc-filter.koandqcom_smmu_v2_v3_dispatcher.ko; ro.boot.hypervisor.protected_vm.supported=1.
Protected KVM installs its hypervisor at EL2 from the kernel, and the kernel can only do that if it was entered at
EL2. depthcharge also loads pvmfw and adds its reserved-memory node for pKVM (src/arch/arm/fit.c:79-97,
CONFIG_ANDROID_PVMFW=y in configs/config.mica). Inference: on stock, pKVM plus its Qualcomm SMMU and SMC
modules fill the role that Gunyah fills on Windows laptops.
Linux allows either level: “The CPU must be in non-secure state, either in EL2 (RECOMMENDED in order to have access
to the virtualisation extensions), or in EL1.” (Documentation/arch/arm64/booting.rst:174-175 at a804c57)
2. What x1-el2.dtso changes, and why
File: arch/arm64/boot/dts/qcom/x1-el2.dtso at torvalds/linux a804c5742bff. History read through the GitHub
commits API for that path on 2026-10-21 (four commits):
| Commit (author date) | Change | Reason given |
|---|---|---|
e01acd8f3cc1 (2025-05-03) “x1e/x1p: Add EL2 overlay for WoA devices” | creates the overlay and the -el2.dtb Makefile targets | “WoA devices using x1e/x1p use android firmware to boot, which notably includes Gunyah hypervisor … Linux-based OS could only boot in EL1.” With slbounce Linux can take EL2, and “some modifications to the DT are necessary to facilitate the absence of Gunyah services” |
c0f045e303e0 (2025-09-15) “x1-el2: Disable IRIS for now” | &iris { status = "disabled" } | Reset and IOMMU handling for IRIS live in the hypervisor on X1E. At EL2 “the PAS interface normally used by IRIS to boot the video firmware is not working” |
f010e0b99703 (2025-12-24) “x1-el2: Enable the APSS watchdog” | &apss_watchdog { status = "okay" } | “In EL2, the APSS watchdog is available” |
47c88db49f6c (2026-02-03) “hamoa: Add remoteproc IOMMUS in EL2 device trees” | iommus for remoteproc_adsp (0x1000 0x80) and remoteproc_cdsp (0x0c00 0x0) | “without Gunyah, the IOMMU is managed by the consumer of this DeviceTree. Therefore, describe the firmware streams” |
The whole overlay (x1-el2.dtso:10-69):
apss_watchdogokay;gpu_zap_shaderdisabled (“linux can zap the gpu on it’s own”);irisdisabled;pcie3,pcie4,pcie5,pcie6agetiommu-mapontopcie_smmu(stream bases0x30000,0x40000,0x50000,0x60000);pcie3andpcie5also getmsi-mapontogic_its(“ITS emulation in Gunyah is broken”);pcie_smmuokay;- the remoteproc
iommusabove; sbsa_watchdogdisabled.
The mainline defaults it overrides are in hamoa.dtsi (a804c57):
pcie_smmuisstatus = "reserved"; /* Controlled by Gunyah. */(:8520-8532);apss_watchdogis"reserved"; /* Reserved by Gunyah */(:8560-8566);sbsa_watchdoghas no status, so it is enabled (:8777-8782);pcie4andpcie6aalready havemsi-map(:3721,:3441), so mica’s two PCIe controllers have MSIs either way.
The Makefile builds x1e80100-dell-xps13-9345-el2.dtb := x1e80100-dell-xps13-9345.dtb x1-el2.dtbo (:440-441).
That is the board mica’s DTS is drafted from (ADR 0004).
3. Chromebook precedent: Qualcomm Chrome trees carry the same changes
Older Qualcomm Chromebooks boot the same way: coreboot, TF-A and depthcharge, with no QHEE. Their mainline trees already contain what the WoA EL2 overlays add:
sc7180-el2.dtso:15: “Venus can be used in EL2 if booted similarly to ChromeOS devices”. Commit0d95f64be417(2025-05-03): WoA sc7180 “includes QHEE hypervisor”, hence EL1.sc7180-trogdor.dtsi:44/delete-node/ &gpu_zap_shader;and:45/delete-node/ &hyp_mem;;:1005-1008addsvideo-firmware { iommus = … }to Venus. That is the same zap and video changesc7180-el2.dtsomakes.sc7280-chrome-common.dtsi:3-9(“memory protections put into place by the Chrome bootloader … different than … Qualcomm’s typical bootloader”) deletesgpu_zap_shaderandhyp_mem(:23-24) and sets&watchdog { status = "okay"; }(:143-145). In the base tree that watchdog isstatus = "reserved"; /* Owned by Gunyah hyp */(kodiak.dtsi:6918-6923).
Inference: mica’s bluey firmware is the same design one generation later, so it needs the same DT treatment. For
X1 that treatment is already packaged as x1-el2.dtso.
4. Against the stock mica tree
Derived facts from the recovery-image base DTB (dumps/recovery-16471258/dtbo/entry00-base.dts, gitignored;
reproduce with scripts/extract-stock-dtb.sh, see stock-dtb.md). Line numbers refer to that
decompiled file.
| Overlay item | Stock mica | Fits EL2 without Gunyah? |
|---|---|---|
pcie_smmu (arm,smmu-v3 at 0x15400000) | present with no status, so enabled (:13161-13171) | yes: Android owns it (through pKVM’s SMMU module) |
pcie4 / pcie6a iommu-map | pcie4 maps onto pcie_smmu at 0x40000, pcie6 at 0x60000 (:14378, :14288) | yes: the same stream bases as the overlay |
APSS watchdog at 0x17410000 | qcom,msm-watchdog, enabled (:12829-12839) | yes: HLOS owns it, as at EL2 |
sbsa_watchdog (arm,sbsa-gwdt at 0x1c840000) | present, enabled (:10597-10601) | counter-signal, see below |
GPU zap-shader | a zap-shader child exists under the GPU (:11731) | counter-signal, see below |
ADSP/CDSP iommus | none on qcom,x1e80100-adsp-pas (:5512-5537) | neutral: stock PAS goes through pKVM and the vendor driver |
| PCIe MSI | msi-parent points at qcom,pci-msi controllers (:14324-14331); the GIC ITS is a pkvm,protected-region (overlay 1) | neutral: vendor pKVM detail (9345-dts-diff.md) |
Counter-signals, and why the overlay is still right:
hypac_cfg. coreboot loads a “hyp access-control” config (hyp_ac_config.elf,Makefile.mk:371-376,soc.c:135-138). Which component reads it is not public. Nothing besides BL33 is ever entered at EL2 (section 1), so it does not mean there is a hypervisor. Inference: QTEE or TF-A applies it as a policy.- Stock enables the SBSA watchdog. That cuts against the overlay’s comment that Gunyah emulates it. The stock
tree is a Qualcomm vendor base, and enabling a node does not show anything exists at
0x1c840000. Disabling it is safe either way. Leaving it enabled is the only one of the two choices that can go wrong at first boot. - Stock keeps a zap-shader node. ChromeOS precedent goes the other way (section 3: trogdor and chrome-common
delete it). Inference: the stock node serves Android’s pKVM and vendor-driver path, not mainline’s.
adreno_gpu.c:49-52ata804c57treats a missing or disabledzap-shaderas “zap not available” (-ENODEV). It then takes the zap-less path. The overlay’s own comment says that path works at EL2: “linux can zap the gpu on it’s own” (x1-el2.dtso:14).
5. Check: the overlay applied to the draft DTS
Run 2026-10-21 on the build host in a scratch directory (nothing committed):
scripts/build-dtb.shcompiles withdtc -@(line 17), so the base keeps__symbols__.dtc -@ -I dts -O dtb x1-el2.dtsoandfdtoverlay -i mica.dtb -o mica-el2.dtb x1-el2.dtboapplied with no errors.dt-validate -s schema-a804c57.json mica-el2.dtbreported only the known pendinggoogle,micaroot-compatible finding, the onescripts/check.shalready allows.fdtgetbefore → after:iommu@15400000: reserved → okay;watchdog@17410000: reserved → okay;watchdog@1c840000: (enabled) → disabled;video-codec@aa00000: okay → disabled;pcie@1c08000iommu-map=0 <pcie_smmu> 0x40000 0x10000;pcie@1bf8000=… 0x60000 0x10000.
The draft dts/x1e80100-google-mica.dts sets &gpu_zap_shader { firmware-name } (:748-750) and
&iris { status = "okay" } (:959-962), following the 9345. The overlay disables both, which is intended at EL2.
Packaging note for the FIT ticket: depthcharge can apply overlays itself. A FIT configuration’s fdt property may
list a base followed by overlays (src/boot/fit.c:115-134, :177-178), applied with dt_apply_overlay()
(:464-479). The configuration also needs a compatible (:261-263). Applying it at build time is simpler and
matches the kernel Makefile.
Watchdog left armed by firmware? coreboot’s common Qualcomm watchdog.c (src/soc/qualcomm/common/watchdog.c:10-16,
linked in romstage at x1p42100/Makefile.mk:34) only reads the AOSS reset status to record a tombstone. It does not
arm a watchdog. Inference: a kernel without apss_watchdog will not be reset by a firmware-armed watchdog. The
depthcharge watchdog code was not read (only part of depthcharge is cached).
Not found
- TF-A’s Qualcomm platform code that reads
bl33_ep_info.spsr. coreboot uses a prebuilt BL31 pluslibqtisec.a(Makefile.mk:99-104). That BL31 honours the requested EL2 is inferred from the stock pKVM evidence; its source was not read. - Who reads
hypac_cfg, and what policy it applies. - What is at
0x1c840000on mica, and whether the SBSA watchdog exists there without Gunyah. - Whether mica’s QTEE supports the PAS resource-table SCM call. With
iommuspresent,qcom_q6v5_pas.ctakes that path (:862has_iommu,:475-500qcom_pas_get_rsc_table(),:927use_tzmem). It decides whether ADSP/CDSP boot at EL2 (Phase 3). - The state firmware leaves the PCIe SMMU in (enabled, bypass or abort) when Linux leaves it alone, which decides how NVMe fails without the overlay.
- Anything on exception levels in
chromebook-boot-flow.rst. Ata804c57(Documentation/arch/arm/google/chromebook-boot-flow.rst) it covers FIT compatible matching only. - A boot log showing
CPU: All CPU(s) started at EL2. It would confirm this answer, and the first boot (or ramoops) will show it. - The full depthcharge tree at
694eabf: only the files under.firecrawl/src-depthcharge-…were read.
Packaging and signing a mica FIT, and checking it offline
Researched 2026-10-21 on the build host from upstream source at pinned commits, distro package metadata and a read of the stock
dtbo table in dumps/ (read only). The Googlebook was not touched. Nothing was installed. Items marked inference
are reasoned from source, not tested.
Ticket: 03-fit-packaging. Decision context:
ADR 0003, chromeos-boot-path.md.
Answer
A mica kernel partition is a vboot kernel blob (64 KiB vblock + body) whose body is a FIT with one kernel, one DTB and
one ramdisk. mkdepthcharge builds it in one command. Everything except the actual boot can be checked on the build host.
Step list
-
Tools. Run the toolchain in a Debian sid container (all four packages are official there), or install
extra/uboot-toolsplus AURvboot-utilsand run depthcharge-tools from a git checkout (see 8). Docker is active on the build host, butthe owneris not in thedockergroup (live read 2026-10-21), so the container runs throughpkexec:pkexec docker run --rm -it -v /home/the owner/.cache/gbook/out:/out debian:sid bash # inside: apt-get update && apt-get install -y depthcharge-tools vboot-kernel-utils vboot-utils cgpt u-boot-tools device-tree-compiler sha256sum /usr/share/vboot/devkeys/{kernel.keyblock,kernel_data_key.vbprivk,kernel_subkey.vbpubk}The three hashes must equal the pinned
tests/devkeysvalues in 4. -
Inputs.
Image: the plain arm64Image, notImage.gz. depthcharge checks the arm64 magic0x644d5241after its own decompression step and knows only none/lz4/lzma.mica.dtb: fromscripts/build-dtb.sh. Its rootcompatiblemust contain the exact stringgoogle,mica. The current draft does:google,mica qcom,x1e80100(live read of the built DTB, 2026-10-21).initramfs.cpio.gz: any cpio the kernel can unpack. Gzip inside the cpio is fine. FIT-level ramdisk compression must benone, and mkimage writes none.
-
Build and sign (keys default to
/usr/share/vboot/devkeys):mkdepthcharge -o /out/mica.kpart --tmpdir /out/mkdc-tmp -v \ -A arm64 --format fit -C none \ -c "console=ttyMSM0,115200n8 earlycon=qcom_geni,0x894000 ignore_loglevel" \ -d /out/Image -i /out/initramfs.cpio.gz -b /out/mica.dtb- mkdepthcharge prepends
kern_guid=%U, so the stored cmdline iskern_guid=%U console=ttyMSM0,115200n8 earlycon=qcom_geni,0x894000 ignore_loglevel. - At boot depthcharge replaces
%Uwith the kernel partition’s PARTUUID (lowercase) and appendscros_secure. It also appendscpufreq.default_governor=powersave, but only when booting with no battery. - It runs
mkimage -f auto -A arm64 -T kernel -O linux -C none -n … -i initramfs -b mica.dtb -d Image, thenfdtput … type kernel_noload, thenvbutil_kernel --version 1 --arch aarch64 --vmlinuz depthcharge.fit --config kernel.args --bootloader empty.bin --keyblock … --signprivate … --pack, thenvbutil_kernel --verify. --tmpdirkeepsdepthcharge.fitfor the checks below.- Leave out
root=for the first boot. The initramfs is the whole userspace. --kernel-startis not needed. On arm64 it only feeds the 32-bit initramfs hacks, which stay off.
- mkdepthcharge prepends
-
Offline checks (all on the build host, no device):
fdtget mica.dtb / compatible # contains google,mica dumpimage -l mkdc-tmp/depthcharge.fit # kernel-1, fdt-1, ramdisk-1, conf-1 fdtget mkdc-tmp/depthcharge.fit /configurations default # conf-1 (one DTB keeps the default) fdtget mkdc-tmp/depthcharge.fit /images/fdt-1 compression # none fdtget mkdc-tmp/depthcharge.fit /images/kernel-1 compression # none | lz4 | lzma fdtget mkdc-tmp/depthcharge.fit /images/kernel-1 data-size # must FAIL: data is embedded, not external vbutil_kernel --verify mica.kpart --signpubkey /usr/share/vboot/devkeys/kernel_subkey.vbpubk --verbose vbutil_kernel --verify mica.kpart # hash-only path, same as dev-mode firmware futility show mica.kpart stat -c %s mica.kpart # body must be <= 0x10000000 (256 MiB)Expected
--verifyoutput: keyblock flags23(0x17), “Body verification succeeded”, and a Config that echoes the cmdline with a literal%U. -
Disk (build as a file on the build host, then copy it to the USB drive as an owner step):
truncate -s 2G mica-usb.img cgpt create mica-usb.img cgpt boot -p mica-usb.img # protective MBR, for Linux later cgpt add -i 1 -t kernel -b 8192 -s 262144 -l KERN-A -S 0 -T 3 -P 15 mica-usb.img # 128 MiB cgpt add -i 2 -t data -b 270336 -s 3923935 -l ROOT mica-usb.img # optional rootfs for later; fits the 2 GiB file dd if=mica.kpart of=mica-usb.img bs=512 seek=8192 conv=notrunc cgpt show mica-usb.img cgpt show -i 1 -P mica-usb.img; cgpt show -i 1 -T mica-usb.img; cgpt show -i 1 -S mica-usb.img-t kernelgives typefe3a2a5d-4f32-41a7-b725-accc3285a309.-S 0 -T 3 -P 15means “try three times, then fall back”. vboot spends a try on each attempt whilesuccessfulis 0.- pmbootstrap’s
-S 1 -T 5 -P 10is for an installed system. Withsuccessful=1the tries count is ignored, so a kernel that panics is picked again on every boot. - Sizes are in device blocks. The numbers above assume 512-byte sectors.
-
FIT matching is not a blocker. depthcharge prefers
google,mica-rev<R>-sku<S>, thengoogle,mica-rev<R>,google,mica-sku<S>andgoogle,mica. A DTB whose root compatible holdsgoogle,micamatches whatever board-id and sku-id the EC reports. With one DTB, mkdepthcharge also keepsdefault = "conf-1", which boots even with no match. That is a safety net, and a trap: a wrong DTB still boots.
What cannot be checked offline:
- depthcharge’s own config choice. There is no upstream unit test for
fit.cat 694eabf; the logic is small enough to check by reading. - The real board-id and sku-id.
- Whether depthcharge’s DT fixups (
dt_apply_fixups) succeed on a mainline-shaped DT. - Whether earlycon prints anything.
On the AP console, depthcharge prints Compat preference: … and Choosing best match … (fit.c:384-387, 439-440),
which reveals board-id and sku-id on the first boot.
Follow-ups for other files (not edited here):
- chromeos-boot-path.md labels
e62d095as depthcharge-tools v0.6.2. It isv0.6.2-107-ge62d095(2026-10-19); the tag is95d17c4(2023-06-30). - Its draft
[boards/custom/mica]section lacksloads-fit-ramdisk,boots-lz4-kernelandboots-lzma-kernel. See 1. - The draft DTS has no
/chosen/stdout-path(fdtget→FDT_ERR_NOTFOUND, 2026-10-21), so a bareearlyconwould do nothing. Hence the explicitearlycon=qcom_geni,0x894000above.
Update 2026-10-21: all three follow-ups are applied on main in commit
709212e: dated corrections inchromeos-boot-path.md, and/chosen/stdout-pathin the DTS.
Sources and revisions
| Repo | Ref | Commit |
|---|---|---|
| depthcharge | firmware-bluey-16702.B | 694eabf646855067acd51935059c3191436ccc79 |
| vboot_reference | firmware-bluey-16702.B | 34fbbe62cc1832016e956d55d72b4aba606b7e21 |
| ChromiumOS coreboot | firmware-bluey-16702.B | ff3258d575b0ddf4ba0514a2cd03c6a1936dba69 |
| depthcharge-tools | master (v0.6.2-107) | e62d095bc61a371b69587ba2773e10a5f2e87308 (tag v0.6.2 = 95d17c444ae3a9dec1c51ead6301ed04777314b9) |
| pmaports | master | a683af41125794aeeba199cd53928332df4e6256 |
| pmbootstrap | 3.12.1 | 7d23010a125ca7386fec4e390d14128b6c24d266 |
| boot-deploy | master | a50452a054233c2d7bf6142cbc332c94c79cc18f |
U-Boot (tools/fit_image.c) | tag v2026.07 (Arch uboot-tools 2026.07-2) | tag |
| Linux | torvalds master | a804c5742bff224d4cb4d25dd383554d598be32b |
All read 2026-10-21. Paths below are relative to each repo. Line numbers refer to these commits.
1. mkdepthcharge for a custom board
mkdepthcharge is board-agnostic. It takes the arch, format, compression, cmdline, keys and files
(depthcharge_tools/mkdepthcharge.py:597-978). For fit it runs:
mkimage -f auto -A <arch> -T kernel -O linux -C <compress> -n <name> [-i initramfs] -b <dtb>… -d <vmlinuz>(:1190-1202);fdtput <fit> <kernel node> type kernel_noload(:1214-1219);fdtput -d /configurations default, only when there is more than one DTB (:1221-1227);vbutil_kernel --version <n> --arch <vboot arch> --vmlinuz <fit> --config <cmdline file> --bootloader <512-byte empty file> --keyblock … --signprivate … --pack <out>(:1240-1250), thenvbutil_kernel --verify(:1401-1410).
For arm64 the vboot arch string is aarch64 (utils/platform.py:536-545), which futility maps to ARCH_ARM
(vboot futility/cmd_vbutil_kernel.c:290-291). --kernel-start is used only by the --patch-dtbs and
--pad-vmlinuz hacks (mkdepthcharge.py:1059-1188). The manual says arm64 depthcharge decompresses the kernel to a
safe place itself, so the hacks are for 32-bit boards (mkdepthcharge.rst:151-169).
The board section only matters for depthchargectl build/check. boards.ini tells users to add “fully specified
sections with a unique codename” (depthcharge_tools/boards.ini:7-29). Sections inherit only from their /-separated
parents (utils/collections.py:11-42). So [boards/custom/mica] inherits nothing from [boards/arm64]
(boards.ini:3514-3521, which sets boots-lz4-kernel, boots-lzma-kernel and loads-fit-ramdisk).
Those keys default to False (depthchargectl/__init__.py:123-137). With loads-fit-ramdisk False, depthchargectl build turns on patch_dtbs (_build.py:549). That adds /chosen/linux,initrd-* to the DTB and pads the kernel.
Without the two boots-* keys, only none compression is tried (_build.py:434-442).
A complete section:
[depthchargectl]
board = custom-mica
[boards/custom/mica]
arch = arm64
codename = custom-mica
name = Dell XPS Googlebook (mica, custom)
dt-compatible = google,mica
image-format = fit
image-max-size = 256 MiB
image-start-address = 0xC7800000
loads-fit-ramdisk = True
boots-lz4-kernel = True
boots-lzma-kernel = True
- A plain
dt-compatible = google,micais extended togoogle,mica(-rev\d+)?(-sku\d+)?automatically (utils/string.py:112-131). It is used only to pick DTBs from an fdtdir (_build.py:238-292). image-start-addressis informational on arm64 (see above).image-max-sizemakesdepthchargectl buildreject a too-large image (_build.py:589).
Version differences. e62d095 is 107 commits past the v0.6.2 tag. For a one-DTB FIT build, the relevant code is
the same in both, apart from the new default removal for multi-DTB FITs (git diff v0.6.2 e62d095 -- depthcharge_tools/mkdepthcharge.py).
Precedent.
- pmaports trogdor:
deviceinfo_depthcharge_board="qc7180",deviceinfo_depthcharge_compression="lzma", a 128 MiB kpart at sector 8192 (device/community/device-google-trogdor/deviceinfo:21-34). - boot-deploy runs
depthchargectl build --root none --board … --kernel … --kernel-cmdline … --initramfs … --fdtdir … --compress … --output …(boot-deploy-functions.sh:1078-1108).
2. Compression and the 256 MiB limit
Allowed formats.
- depthcharge reads a FIT image node’s
compressionas none, lzma or lz4. Anything else is invalid (src/boot/fit.c:140-165), and decompression supports exactly those (:291-311). - mkdepthcharge offers
none,lz4andlzmaand passes-C noneexplicitly, because mkimage otherwise assumes gzip (mkdepthcharge.py:685-699, 993-1003). - The FDT image node must be
nonewhen the config has nocompatible(fit.c:254-259), and mkimage writesnonefor every-bfile (U-Boottools/fit_image.c:392-393). - The FIT ramdisk must be
none(fit.c:484-488); a ramdisk node with nocompressionproperty counts as none (fit.c:143).
Embedded data only. depthcharge reads only the data property (fit.c:149-151). A FIT built with external data
(mkimage -E) would load empty images. mkimage -f auto embeds the data (fit_image.c:368).
Kernel placement.
- depthcharge partly decompresses the kernel to read the arm64 header, checks magic
0x644d5241(boot64.c:44, 178-192) and takesimage_sizefrom it (:194-197). - It picks a KASLR slot outside
[min(BASE_ADDRESS, KERNEL_START, FIT_FDT_ADDR), 4 GiB)(boot64.c:129-133) and decompresses or copies the kernel there (:199-210). - So the decompressed kernel never has to fit the 256 MiB buffer. Only the packed body does.
The 256 MiB limit.
config.micasetsCONFIG_KERNEL_START=0xC7800000andCONFIG_KERNEL_SIZE=0x10000000.- The linker script turns those into
_kernel_start/_kernel_end(src/image/depthcharge.ldscript.S:38-39), and vboot getskernel_buffer_size = _kernel_end - _kernel_start(src/vboot/stages.c:162-163). - vboot rejects a body larger than the buffer (“Kernel body doesn’t fit in memory”, vboot
firmware/2lib/2load_kernel.c:389-392). It requires the body to start within the first 64 KiB of the partition (:378-382; futility’s default--padis 65536,cmd_vbutil_kernel.c:33). - The body is
roundup(FIT, 4K) + 4 KiB cmdline + 4 KiB params + roundup(bootloader, 4K)(futility/vb1_helper.c:668-685, sizes infutility/kernel_blob.h:15-21). So the FIT may be up to about 256 MiB − 12 KiB. - The partition must hold 64 KiB + body.
The DT limit. The flattened DT after depthcharge’s edits must fit the 8 MiB FDT buffer at 0xDA800000
(depthcharge.ldscript.S:40-41, src/arch/arm/fit.c:105-111). The draft DTB is 215,695 bytes.
Ramdisk placement. The ramdisk stays inside the FIT in the kernel buffer, below 4 GiB, as fit_add_ramdisk
assumes (fit.c:335-348).
Choice.
- Inference: use
-C nonefor the first boot. It has the fewest moving parts, and an arm64 Image plus a small initramfs is far below 256 MiB. lz4is the fallback if USB read time or partition size matters. lzma is the slowest to decompress in firmware.- depthchargectl itself tries none, then lz4, then lzma, and keeps the first that fits (
_build.py:434-442, 562-597).
3. Kernel command line
- Storage. mkdepthcharge writes the cmdline to a file for
vbutil_kernel --config. An empty cmdline becomes--, because vbutil_kernel rejects an empty one (mkdepthcharge.py:876-881). The config slot is 4096 bytes including the NUL (kernel_blob.h:15,vb1_helper.c:73-75). kern_guid=%U. mkdepthcharge prepends it unless--no-kern-guidis given (mkdepthcharge.py:885-905,mkdepthcharge.rst:236-249).%Uexpansion.- vboot copies the chosen entry’s unique GUID into
params->partition_guid(2load_kernel.c:761-762). - depthcharge passes it to
commandline_subst(src/vboot/stages.c:307-312), which replaces each%Uwith the GUID in lowercase (src/boot/commandline.c:79-85). - It then appends every queued parameter with a leading space (
:94-98):cros_secureon the ChromeOS path (src/vboot/boot_info.c:56);cpufreq.default_governor=powersaveonly whenboard_boot_in_no_battery_mode()(src/board/bluey/board.c:161-163).
- The result goes into
/chosen/bootargs(src/boot/dt_update.c:38). The working buffer is 2 × 4096 bytes (stages.c:293,src/vboot/boot_info.h:43).
- vboot copies the chosen entry’s unique GUID into
root=PARTUUID=%U/PARTNROFF=1. The kernel resolves this form itself (block/early-lookup.c:41-61, 226-246at a804c57). It matters only when the kernel mounts root, that is with no initramfs/init.- Inference: an initramfs’s own init would have to parse
PARTNROFFitself. - For the first boot, leave
root=out. Keepkern_guid=%U: pmOS’s boot-deploy uses it to find the partition to write the next image to (boot-deploy-functions.sh:1121-1144).
- Inference: an initramfs’s own init would have to parse
- Console.
- uart21 is
serial@894000(arch/arm64/boot/dts/qcom/hamoa.dtsi:1296-1298at a804c57). The draft DTS setscompatible = "qcom,geni-debug-uart"on it andserial0 = &uart21. - The GENI driver registers earlycon
qcom_genifor that compatible and names its ttysttyMSM(drivers/tty/serial/qcom_geni_serial.c:1755, 1769, 1785). earlycon=qcom_geni,<addr>“must already be setup and configured” (Documentation/admin-guide/ kernel-parameters.txt:1515-1519). Bareearlyconuses/chosen/stdout-path(:1398-1403), which the draft DTS lacks.- So:
console=ttyMSM0,115200n8 earlycon=qcom_geni,0x894000. - Inference: if the firmware leaves uart21’s clocks off, earlycon stays silent while
ttyMSM0still works once the driver probes. The kernel needsCONFIG_SERIAL_QCOM_GENI_CONSOLE=y.
- uart21 is
4. Dev keys
-
What vboot checks in developer mode.
need_keyblock_validis false (2load_kernel.c:143; chromeos-boot-path.md section 2).- When the keyblock signature fails against mica’s kernel subkey, vboot accepts the keyblock on its SHA-512 hash
(
:164-181). - Keyblock flag mismatches only clear
keyblock_valid(:184-211). - The body must still verify against the keyblock’s own data key (
:433-438). - So any self-consistent key pair boots. The public dev keys are the convention.
-
Where they come from. vboot_reference ships them in
tests/devkeys/.make installcopies them to/usr/share/vboot/devkeys(Makefile:30, 448-453). At 34fbbe6, sha256:File sha256 kernel.keyblockca2feab9d94eae768fea8616e52cda6cb5576b3d45cd8f7f8af0ce14cb9ce252kernel_data_key.vbprivkbc9e707a86e55a93f423e7bcdae4a25fd470b868e53829b91bbe2ccfbc6da27bkernel_subkey.vbpubk36b9c5fa6f5d0432b9acbe8e2b7da2e602162a87a425575c6d7ba975a0440708kernel.keyblockdecodes to header 2.1, size 1208 bytes and flags0x17= DEVELOPER_0 | DEVELOPER_1 | RECOVERY_0 | MINIOS_0 (offsets fromfirmware/2lib/include/2struct.h:371-418, read on the build host 2026-10-21). That matchesKERNEL_KEYBLOCK_MODE(scripts/keygeneration/common.sh:87) and passes in dev mode without recovery and without miniOS.key.versionssays kernel key version 1 and kernel version 1. mkdepthcharge’s default--tpm-kernel-versionis 1 (mkdepthcharge.py:966-978). Rollback checks do not apply whenneed_keyblock_validis false (2load_kernel.c:313-323).
-
On the build host. mkdepthcharge searches, in order:
--keydir, or the directories of any given key;/etc/depthcharge-tools;/usr/share/vboot/devkeys;/usr/local/share/vboot/devkeys(utils/platform.py:166-198,mkdepthcharge.rst:257-268).
Sources:
- AUR
vboot-utilscopiestests/devkeysto/usr/share/vboot/devkeys(its PKGBUILDpackage()). - Debian’s
vboot-kernel-utilsships the same file names there (packages.debian.org sid file list, read 2026-10-21). - With no install at all,
--keydir <vboot_reference checkout>/tests/devkeysworks.
Check any copy against the table above.
5. Partition type and attributes
- Type. vboot boots GPT entries whose type is the ChromeOS kernel GUID
fe3a2a5d-4f32-41a7-b725-accc3285a309or the Android vbmeta GUID (firmware/include/gpt.h:46-47,firmware/lib/cgptlib/cgptlib_internal.c:157-170). cgpt’s alias for the first iskernel(cgpt/cgpt_common.c:653);datais Linux data (:656). - Attribute bits. Priority sits in bits 48-51, tries in 52-55 and successful in 56
(
cgptlib_internal.h:40-53).cgpt add -S/-T/-Pset them (cgpt/cmd_add.c:16-33).cgpt show -i N -S|-T|-P|-Areads them back (cgpt/cmd_show.c:19-41). - Selection.
GptNextKernelEntryskips entries with neithersuccessfulnortries, and picks the highest priority > 0 (firmware/lib/cgptlib/cgptlib.c:44-120).- A booted entry with
successful=0gets a try deducted (2load_kernel.c:768-779). Withsuccessful=1, tries are ignored (cgptlib.c:135-144). - A failing entry is marked bad and the next one is tried (
2load_kernel.c:742-743).
- Precedent. pmbootstrap uses
cgpt add -i <kernel> -t kernel -b <start> -s <size> -l pmOS_kernel -S 1 -T 5 -P 10, then anefiboot partition and adataroot (pmb/install/partition.py:107-204). It writes the image withdd(pmb/install/_install.py:651-663). - First-boot recommendation. Use
-S 0 -T 3 -P 15instead (inference from the selection code). That matches ADR 0003’s “a bad image falls back”. depthchargectl’sbless --oneshotsetssuccessful=0,tries=1and the highest priority (depthchargectl.rst:247-252). - Stock disk, for contrast (stock-dtb.md). The stock
boot_ahas the ChromeOS-kernel type but holds a plain Android boot image (magicANDROID!at offset 0, read 2026-10-21) and has priority 0. So vboot’s ChromeOS path never picks it, and the Android path goes throughvbmeta.
6. FIT configuration matching, and what the stock dtbo table says
The match list.
- depthcharge builds the compatibles from coreboot’s mainboard part string, lowercased with
_→-(src/boot/fit.c:49-102). For mica that string is"Mica"(corebootsrc/mainboard/google/bluey/Kconfig:317-325at ff3258d). - The list is
google,mica-rev<board_id>-sku<sku_id>,google,mica-rev<board_id>,google,mica-sku<sku_id>andgoogle,mica. Entries whose id is undefined are skipped (fit.c:80-101). - Nothing in the bluey board code adds compatibles:
fit_add_compatis called only fromfit.c. - The ordering is documented in
Documentation/arch/arm/google/chromebook-boot-flow.rst:21-49.
How a config is ranked.
- Each FIT config is ranked by the best (lowest-index) list entry that equals any of its compatible strings
(
fit.c:228-289; exact string match). The best-ranked config wins; with no match,/configurations/defaultboots (fit.c:436-449). mkimage -f autowritesconf-N { kernel, fdt, loadables, ramdisk }with nocompatible(fit_image.c:482-531).- So depthcharge takes the “legacy FIT” branch and reads the first
compatibleproperty of the DTB’s root node. That requires an uncompressed FDT and no overlays (fit.c:248-274). - The draft DTB’s root
compatible = "google,mica", "qcom,x1e80100"therefore matches rank 3 on any board-id and sku-id.
Where board-id and sku-id come from.
- mica selects
MAINBOARD_HAS_CHROME_EC(bluey Kconfig:79-92), which selectsEC_GOOGLE_CHROMEEC(:282-287). - So coreboot reads board_id from the EC board version and sku_id from the EC’s SKU (
bluey/boardid.c:21-24, 41-42). - The values are not public. depthcharge prints them on the AP console (
fit.c:384-387) and writes them to/firmware/corebootin the booted DT (chromeos-boot-path.md section 3).
Stock dtbo table (dumps/recovery-16471258/parts/dtbo_a.img, header and entries decoded 2026-10-21; version 1,
3 entries of 32 bytes). depthcharge parses v1 entries as dt_size, dt_offset, id, rev, flags, custom[3]
(src/boot/android_dt_table.h:43-53). It treats rev as min_rev = rev & 0xffff, max_rev = rev >> 16; 0..0xffff
means “any” (android_dtboimg.c:82-92). custom[0] & 0xff == 0 makes custom[1] a SKU, where 0xffffffff means
any (:139-145, 228-229, 259-263). Compression is flags & 0xf (android_dt_table.h:35-41).
| Entry | id | rev | min..max board_id | flags | SKU (custom[1]) | Meaning |
|---|---|---|---|---|---|---|
| 0 base | 0 | 0xffff0000 | 0..0xffff (any, no rev check) | 3 = LZ4 | 0xffffffff (any) | every board |
| 1 board overlay | 0 | 0xffff0000 | any | 3 = LZ4 | any | every board |
| 2 keyboard overlay | 0 | 0x000f0001 | 1..15 | 3 = LZ4 | any | board_id 1-15 only |
idis unused (0) and only echoed as an index. The table does not encode a SKU at all.- The only board-id information is that the keyboard-matrix overlay applies to board revisions 1-15.
- Inference: production mica reports a board_id between 1 and 15, and rev 0 (early proto) boards had a different keyboard. The real value stays unknown until the first boot.
- The mainline DTB does not depend on it, as shown above. A rev- or sku-specific DTB can be added later with
google,mica-rev<N>compatibles.
7. What can be verified offline
| Check | Command | Proves | Source |
|---|---|---|---|
| DTB root compatible | fdtget mica.dtb / compatible | contains google,mica → matches on any rev/sku | fit.c:49-102, 248-289 |
| FIT layout | dumpimage -l depthcharge.fit (or mkimage -l) | one kernel, one fdt, one ramdisk, conf-1 | fit_image.c:339-531 |
| FIT details | fdtget on /configurations default, /images/*/compression, absence of data-size/data-offset | default kept, FDT and ramdisk uncompressed, embedded data | fit.c:140-165, 254-259, 484-488 |
| Kernel magic | od -A d -t x4 -j 56 -N 4 Image → 644d5241 | depthcharge’s header check passes | boot64.c:43-44, 188-192 |
| Signature chain | vbutil_kernel --verify mica.kpart --signpubkey …/kernel_subkey.vbpubk --verbose | keyblock signed by the dev subkey, body signed by the data key | vb1_helper.c:495-640 |
| Dev-mode path | vbutil_kernel --verify mica.kpart (no key) | keyblock hash + body, which is what dev-mode firmware checks | vb1_helper.c:510-528; 2load_kernel.c:164-181 |
| Summary | futility show mica.kpart | keyblock flags, versions, body size, cmdline | futility/cmd_show.c:454-, file_type.inc:34 |
| Size | body size from --verify ≤ 0x10000000 | fits KERNEL_SIZE | 2load_kernel.c:389-392 |
| Board rules | depthchargectl check --config mica.ini --board custom-mica mica.kpart (mica.ini holds the section from 1; depthchargectl.rst:191-194, _check.py:223-224) | exit 3 size, 4 unreadable, 5 signature, 6 format, 7 missing DTB | depthchargectl.rst:415-432 |
| Disk | cgpt show [-i 1 -t|-P|-T|-S] mica-usb.img | type GUID and attributes | cgpt/cmd_show.c:19-41 |
Cannot be exercised without the device:
- depthcharge’s FIT selection.
tests/boot/at 694eabf has tests for android_mte, bootconfig, commandline, memchipinfo and payload, but none for fit (gitiles tree listing, 2026-10-21). The%Usubstitution does havetests/boot/commandline-test.c. - The real board-id and sku-id.
- depthcharge’s DT fixups on a mainline DT.
dt_apply_fixupsfailing aborts the boot (src/arch/arm/fit.c:99-100). - Console output.
8. Installing the tools on Arch
| Tool | Arch official | AUR | Debian sid (container) |
|---|---|---|---|
mkimage, dumpimage | extra/uboot-tools 2026.07-2 (pacman -Si, 2026-10-21) | u-boot-tools 2025.01-3.2 | |
vbutil_kernel, futility, dev keys | none (pacman -Ss vboot: no match) | vboot-utils 154.16805-1, last modified 2026-10-05 00:45 EDT [04:45 UTC] | vboot-kernel-utils 0~R156-16836.B+dfsg-1 |
cgpt | none | in vboot-utils | cgpt 0~R156-16836.B+dfsg-1 |
mkdepthcharge, depthchargectl | none | depthcharge-tools 0.6.2-1, flagged out of date 2026-05-06 14:36 EDT [18:36 UTC], last modified 2024-01-05 07:01 EST [12:01 UTC] | depthcharge-tools 0.6.2-5 |
dtc/fdtget, lz4, xz | installed (dtc 1.8.1, lz4 1.10.0, xz 5.8.4; pacman -Q) | device-tree-compiler, lz4, xz-utils |
Sources: pacman -Si/-Ss/-Q on the build host; AUR RPC /rpc/v5/search and the PKGBUILDs at aur.archlinux.org (read
2026-10-21); qa.debian.org/madison.php and packages.debian.org file lists (read 2026-10-21). Debian sid
depthcharge-tools depends on cgpt, vboot-kernel-utils, vboot-utils, u-boot-tools, device-tree-compiler,
lz4, xz-utils, zstd and others.
AUR packages.
- AUR
vboot-utilsbuildsrelease-R154-16805.Band runsmake install … MINIMAL=1, then copiestests/devkeys. It depends on AURcbfstoolandchromeos-flashrom-git, which is heavy for three binaries. - AUR
depthcharge-toolsbuilds the 2023 v0.6.2 tag withpython setup.py. It declares onlypythonas a dependency, somkimage,vbutil_kernelandfdtputmust come from elsewhere.
Options, in order of preference.
- Container.
pkexec docker run … debian:sidwith the packages in the Answer. No host install, all official packages. vboot is R156 rather than the firmware’s branch.- Inference: the keyblock and preamble formats (2.1/2.2) have not changed between them, and
--verifyuses the same verification code the firmware links. - A pinned image digest or a Dockerfile in
scripts/would make it reproducible (not done here).
- Inference: the keyblock and preamble formats (2.1/2.2) have not changed between them, and
- Arch host.
pacman -S uboot-tools, AURvboot-utils, and depthcharge-tools run from the pinned checkout.- For example
PYTHONPATH=<checkout> python3 -m depthcharge_tools.mkdepthcharge …; the module has a__main__guard (mkdepthcharge.py:1415). Inference: untested. - That needs an owner OK for the installs.
- For example
- Scratch build. Build
futilandcgptfrom the vboot_reference checkout at 34fbbe6 (Makefile:360-361definesfutil). Inference: untested; it needs OpenSSL and libuuid headers.
Not found
- Board-id and sku-id values. Neither public source nor the stock dtbo reveals them; only the keyboard overlay’s
board_id range 1-15. The EC console’s
cbicommand would show them, but sending it needs the owner’s OK under ADR 0006. - A depthcharge unit test for
fit.cat 694eabf, so FIT selection can only be checked by reading the code. - Whether
dt_apply_fixups, including bluey’s CPU-map fixup, succeeds on a mainline-shaped DT. The fixup source was not located undersrc/board/bluey/at 694eabf. - Whether depthcharge leaves uart21 clocked, so that
earlycon=qcom_geniprints before the GENI driver probes. - Measured image sizes. There is no mica kernel build yet, so the 256 MiB margin is not measured.
- A tested run of the container or AUR route on the build host. Nothing was installed for this note.
Console-only kernel config and boot arguments for the first mica boot
Researched 2026-10-21 for ticket 04. Sources were read on the build host. Nothing was built, and the Googlebook was not touched. Items marked inference are reasoned from source, not tested.
Answer
Derive the config with plain make defconfig. Do not trim it. At torvalds a804c57 the arm64 defconfig
already builds in every driver on uart21’s supplier chain. An arm64 defconfig Image is about 50 MiB, so the
256 MiB KERNEL_SIZE gives no reason to trim. After make defconfig, check the list below in .config before
building, so that a later tree or fragment cannot quietly switch one of them to =m.
Must be =y (built in). These are needed for ttyMSM0 on uart21 with no initramfs and no module loading:
| Option(s) | What needs it | Kconfig default | defconfig @ a804c57 |
|---|---|---|---|
SERIAL_QCOM_GENI, SERIAL_QCOM_GENI_CONSOLE | the qcom,geni-debug-uart driver, the ttyMSM console and the qcom_geni earlycon | none | =y (:585-586) |
QCOM_GENI_SE | the qupv3_2 wrapper (qcom,geni-se-qup); it creates the uart21 child only once it probes | ARCH_QCOM | not in the file, so default y |
PINCTRL_MSM, PINCTRL_X1E80100 | TLMM: qup_uart21_default (gpio86/87, qup2_se5) | ARCH_QCOM | not in the file, so default y |
QCOM_PDC | TLMM’s wakeup-parent = <&pdc>; TLMM probe defers until PDC exists | none | =y (:1746) |
COMMON_CLK_QCOM, CLK_X1E80100_GCC | GCC_QUPV3_WRAP2_S5_CLK (“se”) and the wrapper’s m-ahb/s-ahb clocks | GCC: ARCH_QCOM | =y (:1472, :1487) |
QCOM_RPMH, QCOM_COMMAND_DB, QCOM_CLK_RPMH, QCOM_RPMHPD | rpmhpd CX powers both GCC and uart21, and the UART’s OPP table requires rpmhpd levels. rpmhcc and the BCM voter sit under apps_rsc | RPMHPD: none | =y (:1660, :1654, :1496, :1678) |
INTERCONNECT, INTERCONNECT_QCOM, INTERCONNECT_QCOM_X1E80100 | the qup-core/qup-config paths on clk_virt, gem_noc and config_noc | none | =y (:1865, :1871, :1901) |
ARM_SMMU (+ ARM_SMMU_QCOM, def_tristate y) | iommus = <&apps_smmu 0x423 0x0> on qupv3_2 | none | =y (:1617) |
ARM_PSCI_FW (selected by arm64), ARM_PSCI_CPUIDLE, ARM_PSCI_CPUIDLE_DOMAIN | apps_rsc has power-domains = <&system_pd>, a PSCI domain. Without its provider, RPMh never probes, and nothing downstream does either | DOMAIN: y | ARM_PSCI_CPUIDLE=y (:95); DOMAIN by default |
Three entries are missing from the defconfig file only because their Kconfig default (ARCH_QCOM) already makes
them y: PINCTRL_MSM, PINCTRL_X1E80100 and QCOM_GENI_SE. Do not “fix” the file by adding them. No firmware
file is needed. coreboot loads the UART protocol into QUP2 SE5, and the kernel loads GENI firmware only when the
SE reports no protocol.
Boot arguments (they go into the vbutil_kernel --config command line, which depthcharge writes to
/chosen/bootargs. defconfig sets no CONFIG_CMDLINE):
console=ttyMSM0,115200n8 earlycon=qcom_geni,0x894000 clk_ignore_unused pd_ignore_unused
Optional for the first attempt: ignore_loglevel, and keep_bootcon (it keeps earlycon after ttyMSM0 takes
over, so lines print twice). root= and init= belong to the FIT/initramfs tickets.
- Use the explicit
earlycon=qcom_geni,0x894000. A bareearlyconreads/chosen/stdout-path, anddts/x1e80100-google-mica.dtshas no/chosennode yet.hamoa.dtsi:34ischosen { };, and the 9345 file sets none either. ADR 0006 recordsstdout-path = "serial0:115200n8"as decided, so the DTS needs that line. That is a DTS-ticket item; this note does not edit the DTS. Update 2026-10-21: the DTS now setsstdout-path = "serial0:115200n8"(commit709212e). - Earlycon does not wait for clocks. It registers from
early_parambefore any driver probes, and its setup writes only SE registers. It sets no clock, pinmux, interconnect, OPP or baud rate. So it prints only if firmware left SE5 clocked, loaded with UART firmware, pinmuxed and running at 115200. coreboot always loads the UART firmware, but it sets the baud divider and pinmux only when its own serial console is built in. Whether mica’s production firmware does that is not known (see “Not found”). ttyMSM0does not depend on firmware state. The real console sets its own clocks, pins and 115200 baud when it probes. That happens once the chain above is up, and printk then replays the log buffer.clk_ignore_unused pd_ignore_unused: the stock Googlebook kernel boots with both, and so does Ubuntu Concept’s x1e GRUB config. They are not needed forttyMSM0itself, because the driver claims its clocks when it probes. They matter in the failure case: if the real driver never probes,clk_disable_unusedwould gate the QUP clocks atlate_initcall_sync(they carry noCLK_IGNORE_UNUSED) and silence the earlycon (inference).
Image size against KERNEL_SIZE. KernelCI’s arm64 defconfig build of mainline b88c841d (v7.3-rc6+,
clang-21, 2026-10-11) gives:
Image: 52,271,616 bytes (49.9 MiB);- header
image_size: 53,084,160 bytes (50.6 MiB); Image.gz: 14.6 MiB.
depthcharge’s kernel buffer is KERNEL_SIZE = 256 MiB. It holds the whole loaded body: the FIT with its kernel,
DTB and any ramdisk. An uncompressed Image uses about a fifth of it. That build is close to a804c57, not
a804c57 itself.
Other trees, as facts for the kernel-tree decision:
| Tree, pin | Config used | GENI serial | Rest of the console chain | Notable differences |
|---|---|---|---|---|
jhovold wip/x1e80100-6.16 @ a9cd6cda (2025-07-28) | arch/arm64/configs/johan_defconfig, 429 lines | =y, console =y | all =y | Qualcomm-only, a trimmed defconfig. No ACPI, NVMe =m, PCIE_QCOM=m, DRM =m. Newest x1e branch on GitHub; repo last pushed 2025-09-19 |
jglathe jg/ubuntu-qcom-x1e-7.3.y @ 0151068a (2026-10-20) | Ubuntu packaging, debian.qcom-x1e/config/annotations | =m, console =y | all =y | Ubuntu distro config: ACPI, EFI_ZBOOT, CMDLINE="console=ttyAMA0", NVMe =m, CROS_EC_SPI=m, TCG_TIS_I2C_CR50=m |
Ubuntu Concept linux-qcom-x1e 7.0.0-22.22, qcom-x1e-7.0 @ d997b73b (2026-03-26) | debian.qcom-x1e/config/annotations (flavour arm64-qcom-x1e) | =m, console =y | all =y | ACPI, EFI_ZBOOT, NVMe =m, PCIE_QCOM=y, PHY_QCOM_QMP_PCIE=y. Its resolute-x1e branch uses the generic Ubuntu config, where GCC, x1e TLMM and x1e interconnect are =m |
Both Ubuntu-based configs load the GENI serial driver from an initramfs. So a first boot without an initramfs would
print nothing on ttyMSM0 from them, and earlycon would be missing too (inference). For a console-only first
boot, use defconfig or johan_defconfig, or add SERIAL_QCOM_GENI=y to one of the Ubuntu configs.
Sources and revisions
| Source | Ref | Commit |
|---|---|---|
| torvalds/linux | master | a804c5742bff224d4cb4d25dd383554d598be32b (Makefile reads 7.3.0) |
| depthcharge | firmware-bluey-16702.B | 694eabf646855067acd51935059c3191436ccc79 |
| ChromiumOS coreboot | firmware-bluey-16702.B | ff3258d575b0ddf4ba0514a2cd03c6a1936dba69 |
| jhovold/linux (GitHub) | wip/x1e80100-6.16 | a9cd6cda46ab81b1ec1d687d40ff0933dc6e6915 |
| jglathe/linux_ms_dev_kit (GitHub) | jg/ubuntu-qcom-x1e-7.3.y | 0151068ae447fd8f687b57837dd4be857cff7ca8 |
Launchpad ~ubuntu-concept/ubuntu/+source/linux/+git/resolute | qcom-x1e-7.0 | d997b73be317eed5dd6e617028769fb48addc3db |
| same repo | resolute-x1e | ba0042b4d0be94e27274fc336655f21fa0146027 |
Launchpad ~ubuntu-concept/ubuntu/+source/ubuntu-x1e-settings | default HEAD / ubuntu/resolute | c38933d09985f3eae7f8f3982ac9d666fee101a2 / d40028294dfd621a701e67ff79aff7df011b7e79 |
| KernelCI | kbuild node 6acbaef4a3f195e1aa6556c8 | mainline b88c841d11641923fa3a4da011d7202cd89df68a |
The kernel files came from raw.githubusercontent.com/torvalds/linux/<commit>/…, because the local sparse
checkout holds only DTS and bindings. The Ubuntu trees were read from shallow sparse clones. Paths below without a
repo prefix are in torvalds/linux at a804c57.
Evidence
1. The probe chain from uart21 to its suppliers (DT)
uart21: serial@894000(hamoa.dtsi:1296-1318):clocks = <&gcc GCC_QUPV3_WRAP2_S5_CLK>;interconnectsonclk_virt(MASTER_QUP_CORE_2),gem_nocandconfig_noc(SLAVE_QUP_2);power-domains = <&rpmhpd RPMHPD_CX>andoperating-points-v2 = <&qup_opp_table_100mhz>, whose OPPs requirerpmhpd_opp_low_svs/svs(:671-681);pinctrl-0 = <&qup_uart21_default>: gpio86 TX and gpio87 RX, functionqup2_se5(:6726-6737).
- mica sets
compatible = "qcom,geni-debug-uart"andserial0 = &uart21(dts/x1e80100-google-mica.dts:32,1552-1555). That is the same as the 9345, CRD and devkit. - The parent
qupv3_2: geniqup@8c0000(hamoa.dtsi:847-862) has the clocksGCC_QUPV3_WRAP_2_M_AHB_CLKand_S_AHB_CLK, andiommus = <&apps_smmu 0x423 0x0>. gcc(:759-807) haspower-domains = <&rpmhpd RPMHPD_CX>(:804).apps_rsc: rsc@17500000(:8575-8607) haspower-domains = <&system_pd>(:8591). Its children areapps_bcm_voter,rpmhccandrpmhpd.system_pdis a child ofpsci { compatible = "arm,psci-1.0"; }(:377-474).cmd-dbis a reserved-memory node (:517-518).tlmm(:6095-6108) haswakeup-parent = <&pdc>.
Code that turns these links into hard dependencies:
- The wrapper probe gets its clocks, then
devm_of_platform_populate(). The UART child exists only after that (drivers/soc/qcom/qcom-geni-se.c:1603-1650). geni_se_resources_init()gets theseclock, the OPP table and the interconnect paths (qcom-geni-se.c:1196-1226,geni_icc_getat:1220). The serial probe calls it throughresources_init(drivers/tty/serial/qcom_geni_serial.c:1921).- TLMM returns
-EPROBE_DEFERwhile the PDC wakeup domain is missing (drivers/pinctrl/qcom/pinctrl-msm.c:1471-1477). - A missing PM-domain provider sends genpd to
driver_deferred_probe_check_state()(drivers/pmdomain/core.c:3362-3383). That defers, then returns-ETIMEDOUToncedeferred_probe_timeoutexpires (drivers/base/dd.c:285-298). The default is 10 s whenMODULES=y(drivers/base/Kconfig:76-79). rpmh-rsc usespdev->dev.pm_domain(drivers/soc/qcom/rpmh-rsc.c:1124-1128). So without the PSCI domain driver,apps_rscnever probes. The domain driver matchesarm,psci-1.0and registers atcore_initcall(drivers/cpuidle/cpuidle-psci-domain.c:126,192). - Init order when everything is built in: the RPMh core and the x1e interconnect at
core_initcall(rpmh-rsc.c:1177,drivers/interconnect/qcom/x1e80100.c:1915), TLMM atarch_initcall(pinctrl-x1e80100.c:1866), GCC atsubsys_initcall(gcc-x1e80100.c:7506), and the GENI wrapper as a module init (qcom-geni-se.c:1689). - Inference:
QCOM_SCM(selected byPINCTRL_MSMandARM_SMMU_QCOM), AOSS QMP and IPCC are not on this chain.
2. Kconfig and defconfig
SERIAL_QCOM_GENIis a tristate that depends onQCOM_GENI_SE.SERIAL_QCOM_GENI_CONSOLEis a bool that selectsSERIAL_EARLYCON(drivers/tty/serial/Kconfig:915-929).- The
qcom,geni-debug-uartmatch entry exists only with the console option (qcom_geni_serial.c:2191-2199). - The earlycon is
OF_EARLYCON_DECLARE(qcom_geni, "qcom,geni-debug-uart", …)(:1755-1756). - Inference: with the driver
=m, the earlycon table entry sits in the module and is never seen at early boot. - Defaults:
QCOM_GENI_SE,QCOM_COMMAND_DBandQCOM_RPMH:default ARCH_QCOM(drivers/soc/qcom/Kconfig:30-43,155-158).PINCTRL_MSM:default ARCH_QCOM. The SoC drivers sit insideif PINCTRL_MSM, andPINCTRL_X1E80100hasdefault ARCH_QCOM(drivers/pinctrl/qcom/Kconfig:2-17,Kconfig.msm:2,582-591).CLK_X1E80100_GCCandQCOM_CLK_RPMH:default ARCH_QCOM(drivers/clk/qcom/Kconfig:267-276,416-419).QCOM_RPMHPDhas no default (drivers/pmdomain/qcom/Kconfig:20-26). Neither doesINTERCONNECT_QCOM_X1E80100(drivers/interconnect/qcom/Kconfig:461-469),ARM_SMMU(drivers/iommu/arm/Kconfig:3-15) orQCOM_PDC(drivers/irqchip/Kconfig:523-529).ARM_SMMU_QCOMisdef_tristate yand depends onARCH_QCOM(drivers/iommu/arm/Kconfig:53-59).ARM_PSCI_FWis selected byARM64(arch/arm64/Kconfig:110).ARM_PSCI_CPUIDLE_DOMAINis a bool withdefault y(drivers/cpuidle/Kconfig.arm:17-44).
- Also in
defconfig, outside the console chain:CROS_EC=yandCROS_EC_SPI=y:1427-1430,TCG_TIS_I2C_CR50=m:611,BLK_DEV_NVME=m:309,PSTORE_RAM=m:1928.PSTORE_RAMmatters for the ramoops fallback in ADR 0006, which the observation ticket covers.
3. Earlycon and console mechanics
earlycon=with no options takes the console fromstdout-path.qcom_geni,<addr>starts a polled console; “the serial port must already be setup and configured. Options are not yet supported” (Documentation/admin-guide/kernel-parameters.txt:1398-1402,1515-1518).- Parsing:
setup_earlycon()tries names with an empty compatible first, thenqcom_geni(drivers/tty/serial/earlycon.c:176-214).0x<addr>parses as MMIO (drivers/tty/serial/serial_core.c:2175-2176).- The register page goes through the fixmap (
earlycon.c:39-48;FIX_EARLYCON_MEMisdef_bool y,arch/arm64/Kconfig:364-365). - The parameter is an
early_param(earlycon.c:242).
- A bare
earlyconon a DT boot with ACPI support goes throughearly_init_dt_scan_chosen_stdout(). That returns-ENOENTwithoutstdout-path(earlycon.c:223-241,arch/arm64/kernel/acpi.c:231-233,drivers/of/fdt.c:950-969). arm64 selectsACPI_SPCR_TABLEwhen ACPI is on (arch/arm64/Kconfig:12), anddefconfighasACPI=y(:116). qcom_geni_serial_earlycon_setup()(qcom_geni_serial.c:1702-1754):- It returns
-ENXIOunless the SE already reports the UART protocol. - It writes only packing, FIFO and frame registers in the SE. It touches no clock, pinctrl, ICC or baud divider.
- It returns
- The real console:
- It is named
ttyMSM, and its index comes from theserialalias (:1768-1787,:1895-1897). - Only index 0 is valid (
GENI_UART_CONS_PORTS 1at:88, check at:1615). - Setup defaults to 115200 and parses
console=options (:1605-1649).
- It is named
- If the SE has no protocol, the driver tries
geni_load_se_firmware()(:1358-1367). That needs afirmware-nameproperty on the wrapper (qcom-geni-se.c:1565-1567), whichhamoa.dtsidoes not set. - Boot consoles are dropped when the preferred real console registers, unless
keep_bootcon(kernel/printk/printk.c:3868-3878,4211-4220).
4. What firmware leaves on SE5 (coreboot ff3258d)
UART_FOR_CONSOLEdefaults to 21 (src/soc/qualcomm/x1p42100/Kconfig:80-82). That is QUP2 SE5, the same engine as Linuxuart21.load_qc_se_firmware_late()loads UART firmware intoQUPV3_2_SE5“even if coreboot’s serial output is disabled” whenCONSOLE_SERIALis off (src/mainboard/google/bluey/mainboard.c:290-297). It runs frommainboard_late_init(:320-322).qupv3_se_fw_load_and_init()loads the firmware and sets up the SE (src/soc/qualcomm/common/qupv3_config.c:14-80).- The baud divider (from XO), the TX/RX pinmux and the watermarks are set only in
uart_init()(src/soc/qualcomm/common/qupv3_uart.c:47-90). That is coreboot’s serial-console path. qupv3_fw_init()turns on all QUP clocks (qupv3_config.c:255-262). Where bluey calls it was not traced.
Inference: with production firmware (serial console presumably off), the SE reports UART, so earlycon
registers. Whether its output appears at 115200 depends on divider and pinmux state that coreboot does not set. The
real ttyMSM0 console does not depend on any of this.
5. clk_ignore_unused and pd_ignore_unused
- Both are documented as debug/development aids that “should not be needed on a platform with proper driver
support” (
kernel-parameters.txt:788-798,5279-5283). - Their code:
clk_disable_unused()runs atlate_initcall_sync(drivers/clk/clk.c:1580-1627), andgenpd_power_off_unused()also atlate_initcall_sync(drivers/pmdomain/core.c:1356-1387). - The QUP clocks have no
CLK_IGNORE_UNUSED.gcc_qupv3_wrap2_s5_clkhas onlyCLK_SET_RATE_PARENT(gcc-x1e80100.c:4900-4912). The wrapper AHB clocks are at:5014-5035. - Who passes them:
- Stock Googlebook kernel: its live
/proc/cmdlinecontainsclk_ignore_unused pd_ignore_unused(recon,/proc/cmdlinerow). - Ubuntu Concept x1e:
grub.d/ubuntu-x1e-settings.cfgsetsGRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX} clk_ignore_unused pd_ignore_unused cma=128M efi=noruntime", both atc38933d0and onubuntu/resolute@d4002829. The resolute copy also setsGRUB_BADRAM. The repo log has “Add efi=noruntime as recommended by jhovold” (2025-08-12). - Inference:
efi=noruntimeand theGRUB_BADRAMworkaround are for UEFI boots, so they do not apply to a depthcharge DT boot.
- Stock Googlebook kernel: its live
6. Image size and depthcharge limits
config.mica:KERNEL_START=0xC7800000,KERNEL_SIZE=0x10000000,KERNEL_FIT_FDT_ADDR=0xDA800000,BASE_ADDRESS=0xDB000000.- The ldscript defines
_kernel_end = CONFIG_KERNEL_START + CONFIG_KERNEL_SIZE(src/image/depthcharge.ldscript.S:38-39). - vboot loads into
kernel_buffer = _kernel_start, size_kernel_end - _kernel_start(src/vboot/stages.c:161-162). So 256 MiB bounds the loaded partition body. boot_arm_linux()reads the arm64 header and needs a contiguous free range of headerimage_sizebytes. It picks a KASLR slot outside[min(BASE_ADDRESS, KERNEL_START, FDT_ADDR), 4 GiB)and reserved memory, then decompresses into it (src/arch/arm/boot64.c:113-210). TheImagetherefore runs from RAM above 4 GiB (or below0xC7800000), not from the 256 MiB buffer.- FIT compression: none, lz4 or lzma (chromeos-boot-path.md).
- Measurement (2026-10-21): KernelCI node
6acbaef4a3f195e1aa6556c8,defconfig, clang-21, artifactfiles.kernelci.org/kbuild-clang-21-arm64-mainline-6acbaef4a3f195e1aa6556c8/Image.gz, created 2026-10-11 11:44 EDT [15:44 UTC]. Results:Image.gz: 15,284,553 bytes;Image: 52,271,616 bytes;- header
text_offset: 0; - header
image_size: 53,084,160; - magic
ARM\x64.
7. Other trees
jhovold:
johan_defconfig@a9cd6cdasets:CPU_IDLE=y:47,ARM_PSCI_CPUIDLE=y:48;SERIAL_QCOM_GENI(_CONSOLE)=y:177-178,PINCTRL_MSM/X1E80100=y:188-190;COMMON_CLK_QCOM=y:316,QCOM_CLK_RPMH=y:321,ARM_SMMU=y:335;QCOM_COMMAND_DB/GENI_SE/RPMH/RPMHPD/PDC=y:348-363,INTERCONNECT_QCOM_X1E80100=y:381;- GCC by default;
PCIE_QCOM=m:115,BLK_DEV_NVME=m:129.
- It has no
ACPIline, andACPIdefaults toyonly on x86 (drivers/acpi/Kconfig:9-17). - Branch head commits: “johan_defconfig: add custom config” and “arm64: defconfig: build Qualcomm PCIe driver as a module”.
- GitHub API, 2026-10-21: the newest
wip/x1e80100-*branch is 6.16 (head 2025-07-28), and the repo was last pushed 2025-09-19. kernel.orgjohan/linux.gitonly hasmasterat 6.5-rc6.
jglathe:
debian/debian.envisDEBIAN=debian.qcom-x1e, andrebuild.shrunsdpkg-buildpackage.debian.qcom-x1e/config/annotationssets:SERIAL_QCOM_GENIm:10798, consoley:10799;CLK_X1E80100_GCCy:2359,PINCTRL_MSM/X1E80100y:8781/:8973;INTERCONNECT_QCOM_X1E80100y:5729,ARM_SMMUy:1396, PSCI cpuidley:1360-1361;QCOM_RPMH/RPMHPDy:9297-9298,QCOM_PDCy:9280;EFI_ZBOOTy:168,CMDLINE"console=ttyAMA0":2384,DEBUG_INFO_NONEy:3171;BLK_DEV_NVMEm:51,PCIE_QCOMy:8421;CROS_EC_SPIm:2859,TCG_TIS_I2C_CR50m:12233,EC_QCOM_HAMOAn:4046.
- Its dev-build
arch/arm64/configs/ubuntu_x1e_defconfig(used bydevbuildconfig.sh) also hasSERIAL_QCOM_GENI=m:2290 andCMDLINE="console=ttyAMA0":130.
Ubuntu Concept:
debian.qcom-x1e/config/annotations@d997b73b(headerFLAVOUR: arm64-qcom-x1e) sets:SERIAL_QCOM_GENIm:10416, consoley:10417;CLK_X1E80100_GCCy:2139,PINCTRL_X1E80100y:8625;INTERCONNECT_QCOM_X1E80100y:5471,ARM_SMMUy:1203, PSCI cpuidley:1169-1170;QCOM_RPMH/RPMHPDy:8940-8941,QCOM_PDCy:8922;ACPIy:502,EFI_ZBOOTy:156,CMDLINE"console=ttyAMA0":2161;BLK_DEV_NVMEm:48,PCIE_QCOMy:8144,PHY_QCOM_QMP_PCIEy:8340.
resolute-x1e@ba0042b4builds fromdebian.master(generic arm64), where these arem:CLK_X1E80100_GCC:3033,PINCTRL_X1E80100:10897 andINTERCONNECT_QCOM_X1E80100:7167.- With a
CONFIG_CMDLINEset, the bootloader’s command line still wins under the defaultCMDLINE_FROM_BOOTLOADER(inference, from Kconfig defaults; not read for this note).
Not found
- mica firmware serial state. Whether mica’s production coreboot is built with
CONSOLE_SERIALis unknown. So is whether depthcharge or anything after coreboot leaves SE5’s divider, pinmux and clocks usable. Without that, nobody can say whether earlycon output will be readable at 115200. A read ofttyUSB1across a reboot answers it (silent-boot observation ticket). Where bluey callsqupv3_fw_init()was not traced. - No
stdout-pathin the mica DTS.dts/x1e80100-google-mica.dtshas no/chosen/stdout-path, although ADR 0006 decidedserial0:115200n8. That belongs to a DTS change, not this ticket. - No
Imagebuilt froma804c57itself. The closest public measurement isb88c841d(v7.3-rc6+). Adefconfigbuild of the chosen tree should record its ownImagesize. - No current jhovold x1e tree past 6.16 on GitHub or kernel.org as of 2026-10-21.
- Launchpad cgit for the Ubuntu Concept kernel repos returns HTTP 403 for log/tree pages. Branch heads came
from
code.launchpad.netref pages, and the configs from shallow clones. - Interconnect lookup when the x1e provider is a module.
of_icc_get()’s behaviour was not read; the “defers” claim in the table rests on the DT link plusfw_devlink, inference.
Observing a silent first boot on mica
Researched 2026-10-21 from firmware and kernel source at pinned commits, AOSP source, and the stock DT already extracted from the recovery image (stock-dtb.md). Nothing was run on the Googlebook and no SuzyQ tty was opened. Items marked inference are reasoned from source, not tested on the device.
Ticket: 05-silent-boot-observation.
Answer
If the AP console (uart21 through the SuzyQ -if01) stays silent, these channels show how far the kernel got.
They are ranked by how much they tell and how little they need from the new kernel.
- ramoops: the kernel log of the failed boot, read on the next boot. This is the best channel.
- depthcharge always adds
/reserved-memory/ramoops(compatible = "ramoops") on mica. The buffer is the coreboot CBMEM entryRAMOOPS, 1 MiB by default, and it sits in CBMEM just below0x866C0000. - The exact address is decided at boot, not fixed in source. Inference: it lies between
0x85F80000and0x866C0000. - Layout: 256 KiB console, 128 KiB ftrace, 128 KiB pmsg and two 256 KiB dump records.
max-reason = 2(oops and panic). - Mainline
a804c57picks the node up unchanged. The kernel needsCONFIG_PSTORE=y,CONFIG_PSTORE_CONSOLE=yandCONFIG_PSTORE_RAM=ybuilt in. arm64defconfighasPSTORE_RAM=mand noPSTORE_CONSOLE. - What to read:
console-ramoops-0holds everyprintkof the previous boot, up to the hang. Admesg-ramoops-Nrecord appears only after an oops or panic. - Readback:
- (a) Stock OS, after the next boot:
adb shell cat /sys/fs/pstore/console-ramoops-0.adb bugreportalso carries it as “LAST KMSG”. AOSP grants the shell user both the file mode and the SELinux rule. Inference to test: the stock kernel has pstore enabled. - (b) The next mainline boot:
/sys/fs/pstore/. - Update 2026-10-21 (live read): on stock build 16560795,
/sys/fs/pstoreis denied to adb shell, so (a) as written fails.adb shell dumpsys dropbox --print SYSTEM_LAST_KMSGreturns the last 64 KiB instead (stock pstore ticket).
- (a) Stock OS, after the next boot:
- Conditions:
- DRAM must survive the reset. Let the kernel reboot itself (
panic=5, plussoftlockup_panic=1orhung_task_panic=1for hangs). Do not hold the power button: that is an EC power-off. - The next boot must run the same firmware.
- DRAM must survive the reset. Let the kernel reboot itself (
- depthcharge always adds
- The panel, through the firmware framebuffer.
- coreboot brings up eDP and draws its splash into a 32 bpp linear framebuffer at
0xE4800000(region size0x2200000). It reports that framebuffer in the coreboot table. - depthcharge adds no
simple-framebuffernode and does not reserve the region. At handoff it clears the screen to black. bluey registers no display driver, so depthcharge has no way to stop the scan-out. Inference: the panel is still scanning out when the kernel starts. - Mainline can use it with
COREBOOT_TABLE,COREBOOT_FRAMEBUFFER,DRM_SIMPLEDRMand fbcon. The driver registers asimple-framebufferdevice from the coreboot table, so no width, height or stride needs to go into the DTS. - The DTS must add a
no-mapreservation for0xE4800000+0x2200000; the stock overlay does the same with itscont_splash_region. Without it simpledrm cannot probe: arm64ioremaprefuses memory in the linear map.- Correction 2026-10-21: the stock
splash_region(entry01-overlay.dts:40-42) has the same address, but its size is0x21c0000and it has nono-map. The first-boot DTB uses coreboot’s0x2200000withno-map(decision).
- Correction 2026-10-21: the stock
- Keep the lid open: coreboot skips display init when the lid is closed.
- Inference: add
clk_ignore_unused pd_ignore_unused, and do not let the DT turn off the eDP rails, or the kernel cuts the display’s clocks and power.
- coreboot brings up eDP and draws its splash into a 32 bpp linear framebuffer at
- EC console (SuzyQ
-if02), passive.- It logs every AP power-state change (
power state N = S0…) and AP resets (AP_RST_Ltransitions). It also logsPOWER_GOODloss and the 8 s long-press power-off. - It does not log kernel progress. Host-command tracing is off by default and cannot be turned on through a read-only console.
- Use it as a heartbeat: a kernel that reaches
panic=5, or an initramfs that runsreboot -forpoweroff -fon purpose, shows up as a reset or an S0→S5 transition.
- It logs every AP power-state change (
- Firmware log of the previous boot (
/sys/firmware/log).- coreboot’s CBMEM console keeps appending across warm reboots. The next boot’s log therefore holds the failed
boot’s firmware lines, including depthcharge’s
Choosing best match …andjumping to kernel. - It proves depthcharge picked our FIT configuration and jumped. It holds no kernel output.
- It is readable on a mainline boot with
COREBOOT_MEMCONSOLE. adb shell on the stock OS is denied (recon).
- coreboot’s CBMEM console keeps appending across warm reboots. The next boot’s log therefore holds the failed
boot’s firmware lines, including depthcharge’s
- Glowbar / service-LED codes: firmware stage only.
- 4,3 matches coreboot’s mica-only LCD check,
MAINBOARD_LCD_FAIL_DETECT. When eDP EDID/DPCD read or link training fails in coreboot, it sends the EC lightbar sequence 101 (inference that 101 is the 4,3 pattern). - 3,5 is the EC’s power-on timeout. Inference: it is the
POWER_GOOD not seen in timepath. - A ramdump pattern (sequence 55) exists, but mainline leaves Qualcomm download mode off.
- None of these reports kernel progress. The pattern table is in the EC’s private module.
- 4,3 matches coreboot’s mica-only LCD check,
- GSC
ccdstate: coarse. TheAP:line says whether the AP is powered and out of reset. In cr50 source it followsTPM_RST_L; Ti50 is closed (inference that Ti50 behaves the same). It cannot tell a hung kernel from a running one. Its output also printspcr0, which must be redacted before anything is saved.
For the runbook (inference, for the console-config and failure-playbook tickets):
- Kernel config:
PSTORE,PSTORE_CONSOLE,PSTORE_RAM,COREBOOT_FIRMWARE(the menu that gates the next three),COREBOOT_TABLE,COREBOOT_MEMCONSOLE,COREBOOT_FRAMEBUFFER,DRM_SIMPLEDRMandFRAMEBUFFER_CONSOLE, all=y. - Command line:
panic=5 softlockup_panic=1, plusclk_ignore_unused pd_ignore_unusedwhile the framebuffer is the display. - DTS: a
no-mapregion at0xE4800000. - After a silent boot: watch the EC console for the reset, let the device fall through to the stock OS, and run
adb shell cat /sys/fs/pstore/console-ramoops-0.- Pull the USB drive first, or make sure the internal disk is the default: another mainline boot overwrites the zone.
- Read it before any further reboot: the stock kernel’s own log replaces it on the next boot.
Sources and revisions
| Repo | Commit | Read how |
|---|---|---|
depthcharge firmware-bluey-16702.B | 694eabf646855067acd51935059c3191436ccc79 | blobless clone, 2026-10-21 |
ChromiumOS coreboot firmware-bluey-16702.B | ff3258d575b0ddf4ba0514a2cd03c6a1936dba69 | blobless sparse clone |
ChromiumOS EC firmware-bluey-16702.B-main | 19b76df3d2722a371141bbac61bf14be79f7a44e | blobless sparse clone |
ChromiumOS EC cr50_stab (GSC analogue) | ffacd3c99e6a9090a61439d58d46099f0b5ed09e | gitiles |
| Linux (torvalds) | a804c5742bff224d4cb4d25dd383554d598be32b | raw files at the commit |
AOSP platform/system/core main | a3b721a32242006b59cb12bd62c9133632af3a2d | gitiles |
AOSP platform/system/sepolicy main | 4571ddd9440721fec583c906a337de949a77749e | gitiles |
AOSP platform/packages/modules/adb main | 1cf2f017d312f73b3dc53bda85ef2610e35a80e9 | gitiles |
AOSP platform/frameworks/native main | 4f463a6b1de9198963dc6aff74154a504ba3f8f6 | gitiles |
The 16702.B commits are the ones pinned in chromeos-boot-path.md. The AOSP commits are main-branch heads on 2026-10-21. The Googlebook OS build is Android 17 based and its exact AOSP revision is not public, so AOSP facts are inference for the device until read live.
1. ramoops
Where depthcharge puts it
- The node.
ramoops_fixupfirst deletes any existing node withcompatible = "ramoops"(depthchargesrc/boot/ramoops.c:47-50). It then callsdt_add_reserved_memory_region(tree, "ramoops", "ramoops", start, size, false)(:52-53). - Its shape. The helper creates
/reserved-memory/ramoopswith acompatiblestring andreg, and addsno-maponly when asked (corebootsrc/commonlib/device_tree.c:2171-2198). Here it is not asked, so the node has no unit address and nono-map. - Properties (
ramoops.c:36-40, 59-68):record-size = 0x40000,console-size = 0x40000,ftrace-size = 0x20000,pmsg-size = 0x20000;max-reason = CONFIG_RAMOOPS_DUMP_REASON;- an empty
dump-oops.
max-reason.RAMOOPS_DUMP_REASONdefaults to 2, or 4 withRAMOOPS_DUMP_REASON_MAX(src/boot/Kconfig:124-135).configs/config.micasets neither, so mica gets 2 =KMSG_DUMP_OOPS(kernelinclude/linux/kmsg_dump.h:23-30), unless the private board overlay addsRAMOOPS_DUMP_REASON_MAX.- Address and size come from coreboot’s table:
lib_sysinfo.ramoops_bufferandramoops_buffer_size(ramoops.c:76-77). The region is marked used (:87), so depthcharge’s memory wipe skips it. The wipe runs on every non-normal boot, developer mode included (src/vboot/stages.c:66-71), and it subtracts used ranges first (src/vboot/util/memory.c:98-100). - Built for mica, on both boot paths.
ramoops.cis built whenKERNEL_FIT=y(src/boot/Makefile.inc:22), andconfig.micasets it.boot()applies the DT fixups both for an Android DT (bi->dt) and for a FIT (src/arch/arm/fit.c:57-99). So the stock Android kernel gets the same node as ours. - The buffer is also covered by a CBMEM reservation.
dt_update_memoryadds every CBMEM range as a mappedregion@<addr>reservation (src/boot/dt_update.c:183-193, 219-220).
coreboot side:
ramoops_allocdoescbmem_add(CBMEM_ID_RAM_OOPS, CONFIG_CHROMEOS_RAMOOPS_RAM_SIZE)atBS_DEV_INIT_CHIPS.lb_ramoopsthen publishes it asLB_TAG_RAM_OOPS(src/vendorcode/google/chromeos/ramoops.c:12-45), called fromsrc/lib/coreboot_table.c:571-572.CHROMEOS_RAMOOPSdefaults to y and its size to0x00100000(src/vendorcode/google/chromeos/Kconfig:25-32). bluey’s Kconfig does not override it (src/mainboard/google/bluey/Kconfig). The board’s coreboot defconfig is in the private overlay, so 1 MiB is inference (the default).- Where CBMEM sits.
cbmem_top_chipset()returns_dram_pil(src/soc/qualcomm/x1p42100/cbmem.c:7-10), which is0x866C0000(memlayout.ld:289). CBMEM sits below that. - The address is stable, not fixed. coreboot itself notes that entries created early are “more likely to stay
in the same place” across boots (
src/lib/cbmem_console.c:169-173). Inference: the same firmware build gives the same ramoops address on every boot. - A bound from the stock DT. The stock base DT reserves
wlan-msa@85380000with size0xc00000(ending at0x85F80000) andadsp-boot-dtb@866c0000. Inference: the stock kernel would collide with CBMEM if CBMEM reached below0x85F80000, so ramoops lies in[0x85F80000, 0x866C0000). - Mainline overlaps. Mainline
hamoa.dtsihas nono-mapregion in that window. Its nearest arespu-secure-shared-memory@85300000(size0x80000) andadsp-boot-dtb@866c0000(hamoa.dtsireserved-memory, read from.firecrawl/torvalds-a804c57-hamoa.dtsi).
Does mainline pick it up unchanged?
Yes, with the right config.
- Binding.
reserved-memory/ramoops.yamlwants a/reserved-memorychild withcompatible = "ramoops"andreg, plus at least one size (:9-13, 33-62, 113-121).max-reasondefaults to 2 (:76-86).dump-oopsis not in the binding (unevaluatedProperties: false,:111). depthcharge adds it at boot, so it never reaches ourdt-validaterun.ram.cignores it; it reads only the deprecatedno-dump-oops(fs/pstore/ram.c:672-678).
- Device creation.
of_platform_default_populate_initcreates platform devices for/reserved-memorychildren that match"ramoops"(drivers/of/platform.c:490-499, 567-571). It is anarch_initcall_sync(:604). The driver matches"ramoops"(fs/pstore/ram.c:898). - Parsing.
ramoops_parse_dttakes theregresource and reads each size andmax-reason(ram.c:646-697). Zones are laid out as dump records first, then console, pmsg and ftrace (ram.c:784-812). With depthcharge’s sizes, that leaves0x80000for dumps, two 256 KiB records. On probe it printsramoops: using 0x…@0x…, ecc: 0(:869), which gives the live address on the first mainline boot. - Old contents survive probe. A zone whose header carries the
DBGCsignature is kept as the previous boot’s record (fs/pstore/ram_core.c:39, 526-557). The files are named<type>-ramoops-<id>(fs/pstore/inode.c:365-368). - The console zone gets everything. The pstore console registers with
CON_PRINTBUFFER(fs/pstore/platform.c:423), so it replays the log buffer from boot. A hang before the ramoops probe (arch_initcall_sync) leaves the zone with the previous run’s content. That stage needs the AP console or earlycon. - Config.
PSTORE,PSTORE_CONSOLEandPSTORE_RAMall default to off (fs/pstore/Kconfig:2-4, 37-39, 68-70).- arm64
defconfighas onlyCONFIG_PSTORE_RAM=m(arch/arm64/configs/defconfig:1928). A module loads too late for early hangs, so the runbook config should set all three=y.
Will DRAM survive the reset?
- The platform keeps DDR across a warm reset. bluey’s romstage checks the download-mode cookie in shared IMEM
and, when set, loads Qualcomm’s XBL ramdump image to dump memory (
src/mainboard/google/bluey/romstage.c:411-426, 471-486;src/soc/qualcomm/common/qclib.c:566-585). That only works if DDR contents survive a warm reset into coreboot. coreboot also tells warm from cold resets (romstage.c:459-462). - EC-initiated resets are cold on bluey.
PLATFORM_EC_POWERSEQ_QC_EXP_WARM_RESET=nwith the comment “Bypassing warm reset as it’s currently unreliable” (ECzephyr/program/bluey/program.conf:42-43).warm_reset_seq()then logsWarm reset is disabled, falling back to cold reset(power/qcom_exp.c:1041-1084). A long press powers the AP off (qcom_exp.c:11-23). - An AP-initiated reset does not go through that path. The EC only counts
AP_RST_Ltransitions (qcom_exp.c:262-288). - What the kernel sends. It issues PSCI
SYSTEM_RESET, orSYSTEM_RESET2(warm) withreboot=warmwhen the firmware supports it (drivers/firmware/psci/psci.c:309-325). - Inference: a kernel-initiated reboot (
panic=N) keeps ramoops. A power-button hold or an ECapresetmay not. Whether PSCISYSTEM_RESETis warm here is decided in closed Qualcomm firmware. Test it on the first boot that reaches a shell: write a marker to/dev/kmsg,reboot, and look for it.
Reading it back
| Where | How | Status |
|---|---|---|
| Stock OS after the failed boot | adb shell cat /sys/fs/pstore/console-ramoops-0, or adb bugreport (“LAST KMSG”) | Inference to test |
| Next mainline boot | cat /sys/fs/pstore/console-ramoops-0, dmesg-ramoops-* | Source-verified, needs config above |
| Firmware console log | Not possible: the kernel log never goes into the CBMEM console | n/a |
Stock-OS evidence (AOSP):
- Mount and file modes.
init.rcmounts pstore at/sys/fs/pstoreassystem:log 0550. It makesconsole-ramoops,console-ramoops-0andpmsg-ramoops-0system:log 0440(system/core rootdir/init.rc:396-405). - Group. adbd runs the shell with supplementary group
AID_LOG(adb daemon/main.cpp:103, 115). - SELinux. sepolicy labels pstore
pstorefs(private/genfs_contexts:333) and allowsshell pstorefs:dir searchandshell pstorefs:file r_file_perms(private/shell.te:296-299). A directcatof the file should work.lsof the directory may be denied, since the shell has onlysearch. - bugreport. dumpstate dumps
console-ramoopsorconsole-ramoops-0as “LAST KMSG” (cmds/dumpstate/dumpstate.cpp:172-173, 994-1001). Its domain has the same read rule (private/dumpstate.te:213-215). - Unknown: whether the stock kernel builds
PSTORE_RAMandPSTORE_CONSOLE. A one-line live check after any stock reboot answers it:adb shell 'wc -c < /sys/fs/pstore/console-ramoops-0'. It is a read; it is not run here. - One shot. Only the first boot after the failure holds the mainline log. Each kernel saves the old zone at
probe and then writes its own console over it (
ram_core.c:526-557). So the next boot must be the stock OS (pull the USB drive or keep the internal disk as the default), and the file must be read before another reboot. - Unlocking wipes data. Unlocking wipes user data (ADR 0001), so adb has to be paired again before this channel works.
- Address. The ramoops node has no unit address, so even a listing of
/sys/firmware/devicetree/base/reserved-memorywould not give the address. The recon already shows the stock shell is denied DT property reads (recon).
2. Framebuffer
- coreboot sets it up.
- x1p42100 selects
HAVE_LINEAR_FRAMEBUFFER,MAINBOARD_HAS_NATIVE_VGA_INITandMAINBOARD_FORCE_NATIVE_VGA_INIT(src/soc/qualcomm/x1p42100/Kconfig:22-26). - The memory layout places
FRAMEBUFFER(0xE4800000, 0x2200000)(memlayout.ld:296). - bluey’s
display_startup()(src/mainboard/google/bluey/display.c:158-185) brings up MDSS and eDP. It reads EDID, sets 32 bpp and registers the framebuffer for the coreboot table (fb_new_framebuffer_info_from_edid,:179-180). It then renders the splash and turns on the backlight (:131-156). - It runs on a normal or no-battery boot (
mainboard.c:332-333). vboot always requests display on this board (VBOOT_ALWAYS_ENABLE_DISPLAY, blueyKconfig:307-311;src/security/vboot/vboot_logic.c:311-313). - It is skipped when the lid is closed (
display.c:160-163).
- x1p42100 selects
- It is not reserved.
soc_read_resourceslists the firmware carve-outs as reserved RAM, and the framebuffer is not among them (src/soc/qualcomm/x1p42100/soc.c:74-114). So depthcharge’s/memorycovers it as ordinary RAM. The stock overlay reservessplash_regionat0xe4800000, size0x21c0000, labelledcont_splash_region(stock DT overlay entry 1,fragment@1), which is the same buffer. - depthcharge adds no framebuffer node.
- A search of the whole depthcharge tree at
694eabffinds nosimple-framebufferorsimplefb. - Only MediaTek
rauruadds a framebuffer reservation (src/board/rauru/board.c:33-59). bluey’sboard.cregisters no display ops, andsrc/drivers/video/has no Qualcomm X1 driver. - At handoff,
display_cleanupcallsui_display_clear(), which paints black (src/vboot/ui/display.c:372-376). It then callsdisplay_stop(), which with no ops only logs “not implemented” (src/drivers/video/display.c:27-33, 82-108). - Inference: the MDP keeps scanning out the black framebuffer with the backlight on when the kernel starts.
- A search of the whole depthcharge tree at
- Mainline can reuse it without a DT node.
- depthcharge writes
/firmware/coreboot(compatible = "coreboot",reg= coreboot table and CBMEM) (src/boot/coreboot.c:30-70). Mainline’scoreboot_tabledriver matches it (drivers/firmware/coreboot/coreboot_table.c:261-262). COREBOOT_FRAMEBUFFER(drivers/firmware/coreboot/Kconfig:83-91) registers asimple-framebufferplatform device fromLB_TAG_FRAMEBUFFER, orcoreboot-framebufferwithDRM_COREBOOTDRM(framebuffer-coreboot.c:107-155).DRM_SIMPLEDRMbinds it (drivers/gpu/drm/sysfb/Kconfig:57-64).- arm64
defconfigbuilds the coreboot table only as a module (defconfig:270-272).
- depthcharge writes
- The
no-mapreservation is required, not optional.- A device from the coreboot table carries only a memory resource. simpledrm therefore maps it with
devm_ioremap_wcand fails the probe when that returns NULL (drivers/gpu/drm/sysfb/simpledrm.c:775-810). - arm64
ioremapreturns NULL for any page in the linear map (“ioremap attempted on RAM pfn”,arch/arm64/mm/ioremap.c:26-29). Since depthcharge leaves0xE4800000as plain RAM, the DTS must carve it out withno-map.
- A device from the coreboot table carries only a memory resource. simpledrm therefore maps it with
- A hand-written
simple-framebuffernode is the alternative. The binding needsreg,width,height,strideandformat. It acceptsmemory-region,clocks,power-domainsand*-supply(Documentation/devicetree/bindings/display/simple-framebuffer.yaml:58-152).- Width and height come from the panel EDID at boot. They are not in source, so the coreboot-table route needs fewer guesses.
- Keeping it alive (inference). Unused-clock and power-domain cleanup and the regulator core can switch off
the display path the firmware left on. Use
clk_ignore_unused pd_ignore_unusedand do not declare the eDP rails as switchable regulators with no consumer.DRM_MSMis a module indefconfig(:1012), so it will not take over unless loaded.
3. EC console
Source: EC 19b76df (power/qcom_exp.c, power/common.c). bluey sets CONFIG_AP_ARM_QUALCOMM_QC_EXP=y
(zephyr/program/bluey/program.conf:34).
| EC message | Meaning | Source |
|---|---|---|
power state N = S0, in 0x… (and S5, G3) | every chipset state change | power/common.c:696 |
POWER_GOOD seen / POWER_GOOD not seen in time | AP power-on succeeded / timed out | qcom_exp.c:874-878 |
AP running ... | power-on sequence done | qcom_exp.c:1221 |
AP_RST_L transitions not expected: N | an AP reset with an unusual pulse count | qcom_exp.c:252 |
power waiting for long press …, power off after long press … | the 8 s power-button off | qcom_exp.c:976-987 |
POWER_GOOD is lost | the AP dropped its power-good: shutdown | qcom_exp.c:995 |
chipset_reset(N) then Warm reset is disabled, falling back to cold reset | EC-initiated reset | qcom_exp.c:1081, 1107 |
- Host-command tracing is off by default.
HCDEBUG_OFFis the default unlessPLATFORM_EC_BRINGUP(zephyr/Kconfig:557-562), and turning it on needs a console write. The SuzyQ EC console is receive-only while CCD is locked (ccd-gsc.md:UARTECwithout+TX).- So the EC console cannot show the kernel’s
cros-ectraffic. - It does show a kernel that reboots or powers off on purpose.
- So the EC console cannot show the kernel’s
- The stock log seen 2026-10-21 (
I: ADSP: Charging from USB0) comes from the ADSP, which coreboot starts. It says nothing about the kernel.
4. Firmware console log
- It persists. The CBMEM console “will persist across multiple boots and append to the previous log” when the
storage allows (
src/lib/cbmem_console.c:10-17).- A valid header found at the new CBMEM console address is kept, not reset (
:58-79). - The console is created early “so that the console region … [is] more likely to stay in the same place”
(
:159-173). - The same DRAM-retention condition as ramoops applies.
- A valid header found at the new CBMEM console address is kept, not reset (
- What it shows of the failed boot. depthcharge prints
Choosing best match <config> for compat <string>.(src/boot/fit.c:439) andjumping to kernel(src/arch/arm/boot64.c:215). - Where to read it. Mainline exposes it as
/sys/firmware/logwithCOREBOOT_MEMCONSOLE(drivers/firmware/coreboot/Kconfig:93-101;memconsole.c:29-42). The stock adb shell is denied that file (recon).- Firmware fastboot’s
oem logsis another reader, but it needs the USB-Ethernet route (boot path, section 4).
- Firmware fastboot’s
- On the AP UART. coreboot loads the console UART’s QUP firmware “even if coreboot’s serial output is disabled”
(
src/mainboard/google/bluey/mainboard.c:290-297,QUPV3_2_SE5). That isuart21(ADR 0006). Whether production firmware prints there (CONSOLE_SERIAL) is in the private defconfig. The stock-boot capture in ccd-gsc.md answers it.
5. Glowbar and service-LED codes
Dell’s codes (project log, Service Manual page 64): 3,5 power-rail failure (an SoC power-on timeout seen by the EC); 4,3 LCD failure (EDID/DPCD read or eDP link training failed; it boots on after 10 minutes).
- mica’s LED setup. mica selects
EC_GOOGLE_CHROMEEC_LED_CONTROL,MAINBOARD_LCD_FAIL_DETECTandMAINBOARD_RAMDUMP_LED_NOTIFICATION(blueyKconfig:79-92). - The LCD check. It is documented as reading EDID/DPCD and running link training in the preboot phase, then
telling the EC (
Kconfig:209-216). Its sequence number isMAINBOARD_LIGHTBAR_CMD_SEQ_DIAG_LCD, 101 on mica (:218-224).qcom_mdss_edp_initsends it whenedp_ctrl_initfails (display.c:72-83).- The call is
EC_CMD_LIGHTBAR_CMD/LIGHTBAR_CMD_SEQ(src/ec/google/chromeec/ec.c:2266-2284). - Inference: sequence 101 is Dell’s 4,3. It reflects coreboot’s eDP bring-up, before any kernel runs.
- The ramdump pattern. On download-mode entry coreboot sends sequence 55 (
MAINBOARD_LIGHTBAR_CMD_SEQ_RAMDUMP,Kconfig:387-402;romstage.c:476-482). Mainline’sqcom_scm.download_modedefaults to 0 (drivers/firmware/qcom/qcom_scm.c:41, 2749-2750), so a mainline panic should not trigger it. - 3,5 lives in the EC. The public lightbar sequence list stops at 12 (
include/lightbar_msg_list.h:9-22). mica builds withCONFIG_GOOGLE_PRIVATE=y(zephyr/program/bluey/mica/project.conf:9), and sequences 55 and 101 are not public. Inference: 3,5 is raised from the EC power sequencer’sPOWER_GOOD not seen in timepath (qcom_exp.c:874). That would be a firmware/PMIC-stage failure, not a kernel one. - A deliberate beacon (inference, untested). Mainline’s
cros-ec-lightbaris added when the EC reportsEC_FEATURE_LIGHTBAR(drivers/mfd/cros_ec_dev.c:299-311). Itssequencesysfs file accepts a number (drivers/platform/chrome/cros_ec_lightbar.c:433-470). If mica’s EC advertises the feature, an initramfs could flash the Glowbar at a checkpoint. That needs a workingcros-ec-spi, so it is a late-stage signal.
6. GSC ccdstate
- What
ccdstateprints. In cr50 (the public GSC firmware),ccdstateprints the AP state, the AP UART state,pcr0, the EC, servo and Rdd states,CCD_MODEand the state flags (board/cr50/rdd.c:473-490). - How the AP state is tracked. It follows
TPM_RST_L:AP onwhen it deasserts,AP offafter it has been asserted for a while (board/cr50/ap_state.c:119-132, 186-187, 240). TheAP:line addsKorFfor kernel or firmware (:24-29). - Ti50. Ti50’s source is not public. The 2026-10-21 read showed
AP on(ccd-gsc.md). Inference: Ti50 uses the same signal. - Use.
- It shows whether the AP is held in reset or powered off. It cannot show where a running kernel stopped.
- The command is on ADR 0006’s allowed list.
ccdstateoutput includespcr0, a per-unit value: redact it before saving.
Not found
- The live ramoops address and size. CBMEM allocation is dynamic. The first mainline boot prints it
(
ramoops: using …). The stock shell cannot read the DT. - mica’s coreboot defconfig (private overlay):
CHROMEOS_RAMOOPS_RAM_SIZE,CONSOLE_SERIAL,CONSOLE_CBMEM_BUFFER_SIZEandBMP_LOGO. - Whether the stock Googlebook kernel builds
PSTORE_RAM/PSTORE_CONSOLEand mounts pstore. AOSP policy allows the read; the kernel config is not public. - Whether PSCI
SYSTEM_RESETon this firmware keeps DDR. It is decided in Qualcomm firmware outside coreboot’s public tree. Only an on-device test answers it. - The EC Glowbar pattern table (sequences 55, 101 and the 3,5 trigger). It is in the EC
google-privatemodule. - Whether mica’s EC advertises
EC_FEATURE_LIGHTBAR. - Ti50
ccdstatesemantics. No public Ti50 source; cr50 is used as the analogue. - What writes the 64 KiB
dram_dc_logregion at0x81CE4000. coreboot reserves it (src/soc/qualcomm/x1p42100/memlayout.ld:281,soc.c:97), and the stock DT names itdepthcharge-log(no-map). Mainlinehamoa.dtsicalls the same addressuefi-log. No writer exists in depthcharge694eabfor corebootff3258d.
SCMI over the CPUCP mailbox: intermittent timeouts at boot
Researched 2026-11-03 from the kernel source at the pin (a804c5742bff, Makefile says 7.3.0; it sits between
v7.3 and v7.4-rc1), torvalds master via the GitHub mirror (head 05441d697f7f, 2026-11-03), the upstream
review threads, and the attempt -8/-9 logs. lore.kernel.org, patchwork and git.kernel.org sit behind a bot check
(Anubis), so threads were read on the LKML mirror lkml.rescloud.iu.edu and on lists.infradead.org; lore
Message-IDs are given where the mail quotes them. Mirror dates are US Eastern. Read 2026-11-03. The device was not
touched.
Summary
- Attempt -9 shows a slow CPUCP, not lost interrupts. Replies came later than the 30 ms timeout between 0.37 s
and 0.81 s (how much later is not in the log), while some messages in the same window succeeded, and the TX channel was still held when the next request
was due (section 1). Inference from the log and
shmem.c:88-119. - Every upstream x1e80100 SCMI/CPUCP fix is already at the pin. The one later mailbox fix (
readq(), v7.4-rc1) does not touch the SCMI reply path. No upstream patch to backport was found (section 2). - No upstream x1e board overrides the
scminode. Nor does the stock mica tree (section 3). - Upstream never reported this boot-time stall. Their x1e timeouts were on resume (IRQ disabled, fixed) and on
PERF_LEVEL_GET by regular message (firmware crash, fixed by a quirk). Both fixes are at the pin (section 4). No
primary report of an intermittent boot-time
-110on X1 laptops was found (section 5). - Hazard: a bad boot reads the firmware version as
0x0. The FastChannel quirk then stays off, and PERF_LEVEL_GET falls back to the regular message that Qualcomm says crashes this firmware (section 4).
Ranked recommendation
- Measure first. Boot the same image several more times to get a failure rate. Add
CONFIG_ARM_SCMI_DEBUG_COUNTERS=yand thescmitrace events so each failure shows which message and how late the reply was. - Then set
arm,max-rx-timeout-mson mica’sscminode, sized from the measured latency (a bounded value such as 100-250 ms, not i.MX’s 5000). The property is in the binding and driver at the pin. A boot passes only if it prints the newSCMI max-rx-timeout: <N>ms(driver.c:3205-3208),Firmware version 0x20000andEnabling SCMI Quirk [quirk_perf_level_get_fc_force]. - Guard against the hazard: treat
Firmware version 0x0or a missing FC quirk line as a failed boot, and never rebindscmi-cpufreqin such a boot. A localkernel/patches/file that matches the quirk without the version range is optional (section 6). - One-off diagnostics, not configs to keep:
arm,no-completion-irq(polling) andcpuidle.off=1A/B boots.
1. What -8 and -9 show
Source: dumps/firstboot/2026-11-03-netlog.txt (gitignored), the scmi-dmesg capture of each boot. Same config;
-9 adds only kernel/patches/0001 (panel-edp). Kernel CONFIG_HZ=250 (build-a804c57/.config:483).
| -8 (good) | -9 (bad) | |
|---|---|---|
Using scmi_mailbox_transport | 0.358 s | 0.367 s |
| First timeout | none | 0.437 s, then every ~36 ms (30 ms timeout rounded up to 8 jiffies at 250 Hz, plus one) |
| Base protocol line | 0.361 s, Firmware version 0x20000 | 0.541 s, Firmware version 0x0, after 3 timeouts |
| Quirks enabled | perf_level_get_fc_force, clock_rates_triplet_out_of_spec | clock_rates_triplet_out_of_spec only |
| Late replies | none | Message for 7 at 0.579 s, 9 at 0.650 s, 32 at 0.809 s |
| Perf protocol | normal | Failed to query supported version for protocol 0x13 (0.613 s) |
scmi-cpufreq | 3 policies | -110 at 0.649 s, never retried |
Initialized 3 performance domains | 0.376 s | 0.806 s |
What the log and the source say:
- The boot waited on SCMI. In -9 the only other kernel line between 0.369 s and 0.805 s is
Freeing initrd memory, andsimpledrmcomes at 0.807 s (0.377 s in -8). All SCMI work took 18 ms in -8. - The firmware held the channel. Two timeouts are 68 ms apart (0.473 s to 0.541 s), not 36 ms.
shmem_tx_prepare()busy-waits up to 2 × the timeout for the platform to setCHANNEL_FREEbefore it writes a new request (drivers/firmware/arm_scmi/shmem.c:94-119). It did not give up (noTimeout waiting for a free TX channel !). Inference: CPUCP was still busy with the request that had timed out, so the next request waited about 32 ms. The same gap fits one slow reply (~32 ms, under the timeout) between two timeouts; both readings mean a slow firmware. The first gap (0.369 s to 0.437 s) is the same shape. - The interrupt path works. The late replies are reported through the IRQ path (
transports/mailbox.c:54-78todriver.c:1012-1023). Messages in the same window also succeeded: the OPP lines at 0.656-0.660 s need several PERF replies. A lost or disabled IRQ would fail every message. - “Message for N type 0 is not expected!” means a reply came for a token the kernel had already given up on
(
scmi_xfer_command_acquire(),driver.c:1012-1023; type 0 = command reply). The source describes this case: “a spurious IRQ from a previous timed-out reply which arrived late” (transports/mailbox.c:58-66). - Why it can happen this early (inference): on mica, SCMI is built in and binds at 0.36 s, right after depthcharge, which loaded CPUCP through coreboot (el2-handoff.md). On the UEFI devkit upstream tested, SCMI bound at 7.9 s (Marc Zyngier’s dmesg, section 4), so upstream may never run in this early window.
2. Upstream fixes and whether the pin has them
Presence was read in the pinned source; the landing release is the first tag that contains the commit (GitHub
compare API against torvalds tags).
| Commit | Title | Release | At a804c57 |
|---|---|---|---|
d2fab3fc27cb | mailbox: qcom-cpucp: Mark the irq with IRQF_NO_SUSPEND flag (Sibi Sankar, 2024-10-30; fixes the x1e resume timeout) | v6.12 | yes, drivers/mailbox/qcom-cpucp-mbox.c:176 |
da1642bc97c4 | firmware: arm_scmi: Queue in scmi layer for mailbox implementation (timeouts no longer start while queued) | v6.12 | yes, transports/mailbox.c:288-305 |
a0a18e91eb3a | firmware: arm_scmi: Reject clear channel request on A2P | v6.12 | yes, driver.c:1067-1077 |
1780e411ef94 + 54962707f8b8 | Use max-rx-timeout-ms from devicetree; Use vendor string in max-rx-timeout-ms | v6.12 | yes, driver.c:3187-3190; binding Documentation/devicetree/bindings/firmware/arm,scmi.yaml:127-132 |
5d8a76622658 + e2261bb81e0d | Skip opp duplicates; Report duplicate opps as firmware bugs (the [Firmware Bug] … opps_by_lvl lines) | v6.12 | yes (perf.c, the -8 log lines) |
c23c03bf1faa | Fix timeout checks on polling path | v6.15 | yes (polling path only) |
487c407d57d6, 94a263f981a3, 397f802d06c4 | quirk framework; Ensure that the message-id supports fastchannel; quirk: Force perf level get fastchannel (Johan Hovold) | v6.16 | yes, quirks.c:173-174, driver.c:1924-1951 |
06e3c7ec8045 + 892c83aa39cb | arm64: dts: qcom: x1e80100: Add cpucp mailbox and sram nodes; Enable cpufreq | v6.16 | yes, hamoa.dtsi:338-351, :8568-8573, :8758-8776 |
0fac05fdd9af | Rework protocol version negotiation logic (source of Trying version 0x40000) | v7.0 | yes, driver.c:2169-2211 |
d8283ac2c8fb | Implement arm,no-completion-irq property | v7.1 | yes, driver.c:3202-3203, binding :149-154 |
3690aaa6d18f + fc4f2f995302 | qcom-cpucp: fix PREEMPT_RT self-deadlock in IRQ handler; handle NULL data in send_data callback | v7.3 | yes, qcom-cpucp-mbox.c:69-84, :120-126 |
74f1cf02d9fe | mailbox: qcom-cpucp: read mbox hardware register using readq() | v7.4-rc1 | no: qcom-cpucp-mbox.c:64 still reads 32 bits at +0x4 |
Notes:
74f1cf02d9fedoes not affect SCMI. It changes only the value handed to the client. The SCMI mailbox client ignores that value: itsrx_callback()reads the header from shared memory (transports/mailbox.c:54-78).- No newer change in master. The other
arm_scmicommits between the pin and master add ACPI support and fix raw mode (e.g.f35ee1a26795,4f5f291a5704,0543fc1443de). None touches timeouts or the mailbox path. linux-next could not be read (git.kernel.org bot check), so changes queued for 7.5 are unknown. - The default timeout is 30 ms for the mailbox transport (
transports/mailbox.c:383, “We may increase this if required”), with no Qualcomm-specific value anywhere.
3. Device trees: does anyone set something different?
- The upstream SoC node is
hamoa.dtsi:338-351:mboxes = <&cpucp_mbox 0>, <&cpucp_mbox 2>, shmemcpu_scp_lpri0/1insram@18b4e000, onlyprotocol@13. The node has no label. - No x1e/x1p board file and no
x1-el2.dtsomentionsscmiorcpucp(rgoverarch/arm64/boot/dts/qcom/x1*andpurwa*). - The other Qualcomm SCMI nodes at the pin (
sm8750.dtsi:225-238,glymur.dtsi:409-422,kaanapali.dtsi:230-243) set no timeout, noatomic-threshold-usand noarm,no-completion-irq. - The only users of the properties in arm64 are the i.MX SoCs with
arm,max-rx-timeout-ms = <5000>(freescale/imx95.dtsi:346,imx94.dtsi:84,imx952.dtsi:216) and one Renesas board witharm,no-completion-irq(renesas/r8a78000-ironhide.dts:31). - Stock mica (
dumps/recovery-16471258/dtbo/entry00-base.dts:384-403) uses the same mailbox channels (<cpucp 0 cpucp 2>) and addsprotocol@80. It has no timeout property. The mailbox isqcom,cpucp-v2, IRQ SPI 28 level-high (:10535-10540), the same ashamoa.dtsi:8571. - Consequence for a DT override: mica’s DTS includes
hamoa.dtsi(dts/x1e80100-google-mica.dts:22), so the override has to use a path reference,&{/firmware/scmi} { … };. Per ADR 0004 it is not from the stock tree, so mark it “unverified” indts/README.md.
4. What the maintainers said
The CPUFreq series review (“[PATCH V7 0/2] qcom: x1e80100: Enable CPUFreq”, lore):
- Johan Hovold, 2024-11-01: grepping cpufreq sysfs on the CRD reset the machine after
arm-scmi arm-scmi.0.auto: timed out in resp(caller: do_xfer+0x164/0x564)(2411.0/00567). - Marc Zyngier, 2024-11-01: “I’m seeing similar things indeed. Randomly grepping in cpufreq/policy* results in
hard resets”. His dmesg has
SCMI max-rx-timeout: 30msand the SCMI bind at 7.93 s (00663). - Johan, 2024-11-05: the ThinkPad T14s “hard resets when accessing the cpufreq sysfs attributes as well” (05166).
- Sibi Sankar (Qualcomm), 2024-12-05, root cause: “The PERF_LEVEL_GET implementation in the SCP firmware side is the reason for the crash … the crash happens when idle states are enabled and a regular LEVEL_GET message is triggered from the kernel. This was fixed a while back but it will take a while to flow back to all the devices” (2412.0/06550).
- Marc, 2024-12-05: “this fix will never reach some platforms … How do we distinguish between good and bad firmware versions?” (06821).
- Sibi, 2025-01-06: “The perf protocol version won’t have any changes so detecting it isn’t possible”, and “the X1E firmware supports fast channel level get but when queried it says it doesn’t support it … The PERF_LEVEL_GET regular messaging which gets used as a fallback has a bug which causes the device to crash” (2501.0/03550).
- Johan, 2025-01-10, the mitigation: “Why can’t you add a quirk for x1e platforms that makes sure that the driver always uses fastchannel level get?” (2501.1/03949).
- The outcome: that quirk is
397f802d06c4(lore). Its message says the firmware “does not set the FastChannel supported attribute bit for PERF_LEVEL_GET but crashes when falling back to regular messaging”, and that an upper version bound can be added “when we learn which version has a fix”. Johan asked to merge the DT “With the SCMI perf quirk for the Qualcomm firmware merged for 6.16” (2505.1/00578). Bjorn Andersson applied both DT patches on 2025-05-17 (2505.2/01472).
The earlier resume timeout (“Ensure that the message-id supports fastchannel”, 2024-09):
- Johan: the patch “breaks resume from suspend on the x1e80100 crd …
arm-scmi firmware:scmi: timed out in resp(caller: do_xfer+0x164/0x568)” (958170, lore). - Konrad Dybcio: “it looks like PERF_LEVEL_GET (msgid 8) requires the use of FC, but the firmware fails to inform us about it through BIT(0) in attrs” (958413).
- Sudeep Holla asked why the firmware would not implement PERF_LEVEL_GET on the normal channel (“I am very much interested to know the reason”) (958443).
- Sibi, 2024-10-06, root cause: “the SCP was servicing the request but mailbox had the interrupt disabled during
suspend which caused the timeout”. The fix is
IRQF_NO_SUSPEND(d2fab3fc27cb, at the pin) (966573).
Late replies in general: Cristian Marussi (Arm) explains the agent’s per-message state machine and why a late immediate reply is “discarded” (2501.2/08228, 2025-01-23). That mail is about out-of-order delayed responses. -9’s lines are type 0 (plain replies), which the source comments above cover.
What this means for -9 (inference):
- The quirk failed for a known reason.
perf_level_get_fc_forceis declared for vendor"Qualcomm"with implementation range"0x20000-"(quirks.c:174), and it is enabled only ifimpl >= start_range(quirks.c:302-305). The version query timed out, so the version read as0x0and the quirk stayed off. - The crash path was not taken, by luck.
scmi-cpufreqfailed, so nothing sent PERF_LEVEL_GET. Had cpufreq bound in that boot, it would have used the regular-message fallback that Qualcomm says crashes the SCP. A partly failed boot is therefore riskier than a fully failed one. - No maintainer has spoken to this case. None of the threads read here discusses SCMI timeouts at boot with an otherwise healthy firmware.
5. Reports from other X1 machines
- Searched: GitHub issues (“timed out in resp”, “scmi” + “x1e”) and web search for
scmi-cpufreq … -110on Snapdragon X. - No primary report found of an intermittent boot-time SCMI timeout or
scmi-cpufreq -110on X1 laptops. - The known x1e reports are the resume timeout and the sysfs hard reset (section 4), both fixed at the pin.
- Unrelated:
dwhinham/archiso-aarch64-sp11#36(X1P64100 OPPs flagged turbo, a different firmware bug) andSky1-Linux/linux-sky1#25(CIX SoC).
6. Options, ranked
| # | Option | Evidence | Cost and risk |
|---|---|---|---|
| 1 | Measure: more boots of one image. Add CONFIG_ARM_SCMI_DEBUG_COUNTERS=y (Kconfig:58-70; debugfs scmi/0/transport/counters/, e.g. xfers_response_timeout, err_msg_unexpected, driver.c:3002-3046) and tracing for the scmi_xfer_begin/scmi_xfer_end/scmi_rx_done events (include/trace/events/scmi.h:37-145). Read both in /init’s SCMI capture | One good and one bad boot give no rate. The counters and trace show which protocol/message waits and for how long. CONFIG_FTRACE is unset and the counters are off (build-a804c57/.config:2028) | Config fragment only. Tracing makes the image bigger |
| 2 | arm,max-rx-timeout-ms on &{/firmware/scmi}, sized from option 1 | In the binding (arm,scmi.yaml:127-132) and read at probe (driver.c:3187). Upstream allows larger values (i.MX: 5000). -9’s late replies came within the ~0.44 s window | If CPUCP is really dead, every message waits up to 3 × the value (shmem.c:107-119 spins 2 ×, then the wait). At 200 ms and -9’s 10 timeouts, about 6 s of boot. Not from the stock tree |
| 3 | Pass criteria and a guard in /init/attempt.sh: fail the boot on Firmware version 0x0 or a missing quirk_perf_level_get_fc_force line; never rebind scmi-cpufreq in such a boot | Section 4: without the quirk, PERF_LEVEL_GET falls back to the message that crashes the firmware | None |
| 3b | Optional local patch (kernel/patches/, ADR 0007): declare perf_level_get_fc_force with no version range, or with a machine compatible (quirks.c:307-309 supports compats) | Johan’s own commit names the SoC-compatible match as the fallback design | One-line patch, local only; it hides the version-read failure but not the timeouts |
| 4 | A/B diagnostics: arm,no-completion-irq (polling, driver.c:3202); cpuidle.off=1 | Polling separates IRQ delivery from firmware latency; section 1 already points at the firmware. Sibi says the firmware bug depends on idle states (2412.0/06550); whether that also affects reply latency is inference | One boot each; polling busy-waits, cpuidle off costs power. Not configs to keep |
| 5 | Backport an upstream fix | None found (section 2); the only fix newer than the pin (readq()) does not touch the SCMI path | n/a |
Sources
- Pinned tree
~/.cache/gbook/linux-a804c57ata804c5742bff:drivers/mailbox/qcom-cpucp-mbox.cdrivers/firmware/arm_scmi/{driver,shmem,quirks,base}.candtransports/mailbox.cDocumentation/devicetree/bindings/firmware/arm,scmi.yamlarch/arm64/boot/dts/qcom/hamoa.dtsi
- Commit metadata: GitHub API on
torvalds/linux(commits by path since 2024-06;compareagainst thev6.11-v7.4-rc1tags). - Review threads: the LKML mirror and infradead pipermail URLs above. The owner’s lore search clipping lists the
full V3-V7 thread (
~/code/obsidian/the build host/Clippings/sx1e80100 Enable CPUFreq.md). - Logs: attempts -8 and -9 in
dumps/firstboot/2026-11-03-netlog.txt(gitignored). Summary indocs/recon/scmi-2026-11-03.md. - Stock tree:
dumps/recovery-16471258/dtbo/entry00-base.dts(stock-dtb.md).
Update: the cause narrowed, the board DTS changed (2026-11-03, 11:43 PM ET)
The stalls follow the PSCI domain idle states (attempts -27 to -34, scmi-2026-11-03.md).
Options 3-4 were run: cpuidle.off=1 (-28) and arm,no-completion-irq (-29) each gave 3 of 3 clean cold boots, and
so did removing every domain-idle-states (-34). The board DTS now does the last of these
(ADR 0008) and keeps the 1000 ms window. An upstream report draft is
in scmi-cpucp-stall-report.md (not sent).
Milestones, version numbers and maturing the project
Researched 2026-11-04 from the kernel’s own process documents at the pin (~/.cache/gbook/linux-a804c57,
a804c5742bff), the GitHub mirror of torvalds/linux, and project-owned pages: semver.org, calver.org,
keepachangelog.com, asahilinux.org, docs.nura.eco and wiki.nura.eco (postmarketOS), discourse.ubuntu.com (Canonical),
chromium.org and gitlab.com/Linaro. Raw pages are cached in .firecrawl/milestones/ (gitignored). The device was not
touched. Inference marks what the source does not state; the Recommendation sections are this note’s own proposals.
Summary and recommendation
Decided 2026-11-04: the owner answered “all a” to section 5. The decisions are recorded in ADR 0010, ADR 0011 and the ADR 0007 update.
| # | Recommendation | Why, in one line |
|---|---|---|
| 1 | A milestone ladder M0 to M7 that slots rungs between the existing gates (table in section 4) | Gate 2 to Gate 3 is a long stretch; Asahi, Nura and the 9345’s own upstream commit all measure progress per feature |
| 2 | A feature matrix (docs/status.md): one row per feature, a state and an upstream column | Asahi’s tables and Nura’s device pages both do this; the README’s “Resume here” paragraph does it as prose |
| 3 | CalVer YYYY.0M.0D[.MICRO] for a known-good image, as an annotated git tag, also visible in uname -r | SemVer needs a public API, which gbook does not have; the upstream kernel drives the releases |
| 4 | The kernel’s own [PATCH vN] rules for anything sent upstream; a status word on every kernel/patches/ header; never reuse a patch number | One scheme per audience: lists read vN, the repo reads dates |
| 5 | Keep holding upstream sends, but end the hold on a checkable bar (M5: binding + DTS pass the upstream checks) rather than on “mature” | The kernel docs favour posting early; the 9345 DTS landed with EC, speakers, mics and camera not working |
1. Milestones: how comparable projects define them
Asahi Linux (Apple Silicon): a per-feature table with an upstream column
- One table per SoC generation, with rows for SoC blocks and per-device features, and columns per device (M1 Series Feature Support, read 2026-11-04; index at Feature Support Overview).
- Each cell says where the feature lives, not just whether it works (same page, legend):
- a kernel release, e.g.
6.0: “incorporated upstream as of this release”; linux-asahi (kernel release): stable, in Fedora Asahi Remix, “should be upstream by the release indicated”;linux-asahi: “(mostly) stable and available for use”;WIP: actively progressing, “not yet ready for wider testing”;TBA: no active work.
- a kernel release, e.g.
- No dates are promised: “If a feature is not ready, then there is no estimation on when it will be ready” (same page, intro).
- Progress reports follow the upstream kernel: the blog titles them “Progress Report: Linux 7.0”, “7.1”, “7.2” (Asahi blog index, read 2026-11-04).
- “Officially supported” means the installer offers it, with listed exceptions. M3 support “has now been merged into the installer”, “almost everything supported on the M1 and M2 series machines just works”, with DCP, GPU and sleep listed as known limitations, at first behind the installer’s Expert mode (M2: Episode 1, 2026-09-06, read 2026-11-04).
postmarketOS, now Nura: device categories with entry criteria
wiki.postmarketos.org now redirects to wiki.nura.eco, and the categorization page moved to docs.nura.eco (live read
2026-11-04). The categories (Device Categorization,
read 2026-11-04) give the clearest written criteria for “this device reached level X”:
| Category | Requirements (abridged from the page) |
|---|---|
| Testing | close-to-mainline kernel as new as the oldest supported LTS; compiled with LLVM; “Port and dependencies build”; “The device boots” |
| Community | install instructions on the wiki; close-to-mainline kernel; pmbootstrap kconfig check --community; automatic kernel upgrades work; maintainer joins release testing; kernel “upgraded through 3 kernel releases”; kernel not older than 6 months |
| Main | team endorsement; at least 5 maintainers; hardware CI in two locations for at least 3 devices; UEFI boot; generic kernel; a maintained working-features file |
| Downstream / Archived | vendor kernels as a step toward mainline; unmaintained or superseded ports |
- Demotion is part of the scheme: maintainers get two weeks (up to one more month on request) to meet the criteria again before a move down (same page, “Moving to a lower category”).
- Per-device feature matrix: each device page lists features as Works / Broken / Untested. The Lenovo Yoga Slim 7x
(x1e80100, category
testing) lists, among others, Battery, Screen, Keyboard, Touchpad, 3D Acceleration, Audio, Wi-Fi and FDE as Works; Camera, Bluetooth and GPS as Broken; USB OTG as Untested (Lenovo Yoga Slim 7x (lenovo-yoga-slim7x), served from wiki.nura.eco, read 2026-11-04). No Nura page exists for the Dell XPS 13 9345 (live read 2026-11-04).
The sibling laptop’s own bar: the 9345 initial DTS in mainline
The commit that added the XPS 13 9345 board file (f5b788d0e8cd, “arm64: dts: qcom: Add support for X1-based Dell XPS
13 9345”, authored 2024-10-03, applied by the qcom maintainer;
commit, read 2026-11-04) states its
level in three lists:
- Working: touchpad, keyboard (after a suspend/resume, with an i2c-hid patch), touchscreen, eDP with brightness, NVMe, USB-C at USB 2/3 in one orientation, Wi-Fi, GPU/aDSP/cDSP firmware loading, lid switch, sleep/suspend.
- Not working: speakers, microphones, fingerprint reader, USB as DP/USB 3 (retimer), camera, EC over I2C.
- Untested: the higher-resolution panel.
Its binding went in as a separate patch, 1aa50217d7fd “dt-bindings: arm: qcom: Add Dell XPS 13 9345” (GitHub commit
list for qcom.yaml, read 2026-11-04). Retimer support followed in a later commit (bd2dbbb1f35a, 2024-11-12). So an
x1e80100 laptop DTS was accepted upstream as “initial support” with audio, camera and EC missing.
At the pin, mainline carries 28 x1*/hamoa* board .dts files: laptops, reference and dev kits, and one desktop
(ls arch/arm64/boot/dts/qcom/).
Distributions for the same SoC
- Ubuntu Concept (Canonical) lists x1e laptops in two groups, “Devices we have seen successful installations on” (the XPS 13 9345 among them) and “Currently not (yet) supported”, with one Launchpad bug per device as its tracker (Ubuntu Concept ♥️ Snapdragon X Elite, section “What works”, read 2026-11-04).
- Linaro’s arm64-laptops kernel names its integration branches by upstream base and series version, e.g.
arm64-laptops-v7.3-rc1-camss-v13,qcom-laptops-v6.18-rc4-camss-next(gitlab.com/Linaro/arm64-laptops/linux, branch list, read 2026-11-04). No first-party per-laptop status page was found there; the repository page carries the kernel README.
ChromiumOS: status words for downstream kernel patches
There is no first-party “mainline on Chromebooks” status page that this search found. What ChromiumOS does publish is a vocabulary for how far each downstream patch is from upstream (Kernel Development, section “UPSTREAM, BACKPORT, FROMLIST, and you”, read 2026-11-04):
UPSTREAM:“exclusively for patches that have actually landed in Linus’ tree”;BACKPORT:same, with changes, which must be documented;FROMGIT:cherry-picks from a maintainer tree, naming tree and branch;FROMLIST:“sent to a public mailing list for review, but hasn’t yet been merged”, with a link;CHROMIUM:changes “which cannot be submitted upstream”; a rejected list patch kept anyway isCHROMIUM: FROMLIST:with the reason.
The older design page says the kernel is rebased onto new upstream releases “approximately every 3-6 months, and approximately every other kernel versions” (Chromium OS Kernel, “Upgrades”, read 2026-11-04; the page’s examples are 2.6.32 and 3.x era, so treat it as dated).
What the sources agree on (inference)
- A milestone is a set of features at a stated location (works locally, sent, in a release), not a date.
- Levels are defined by checkable entry criteria (Nura) or a Working / Not working list (the 9345 commit, Asahi’s M3 post), and both allow named exceptions.
- Upstream releases are the clock for reports and rebases (Asahi reports, ChromiumOS rebases, Linaro branch names).
2. Version numbers
What there is to version in gbook
| Object | Today | Changes when |
|---|---|---|
| The repo | 209 commits, no tags (git tag, 2026-11-04) | every unit of work |
| The boot image | ~/.cache/gbook/out/mica-usb.img; its identity is the kernel banner (release + #<n> UTS build number, scripts/image_uts.sh) | each package-image.sh |
| The kernel base | torvalds a804c57 (ADR 0004) | a re-pin |
| Local patches | kernel/patches/NNNN-*.patch, header says source and upstream state (ADR 0007) | per patch |
| The DTS | dts/x1e80100-google-mica.dts, history in git | per commit |
| A boot | attempt ID YYYY-MM-DD-N in gitignored dumps/firstboot/attempts.tsv (scripts/attempt.sh) | every boot |
| An upstream series | none sent yet | per posting |
Facts about the base (live reads 2026-11-04): the pin’s Makefile reads 7.3.0 with an empty EXTRAVERSION; the
GitHub compare API puts a804c5742bff 1703 commits after v7.3 (tagged 2026-10-18 7:14 PM ET [23:14 UTC]) and before
v7.4-rc1 (tagged 2026-11-01 4:26 PM ET [21:26 UTC]); the pin was committed 2026-10-21 2:04 AM ET [06:04 UTC]. So
the pin is a 7.4 merge-window commit. No kernel/*.config sets CONFIG_LOCALVERSION (grep, 2026-11-04).
Two local patch numbers have been reused: 0008 named a GENI I2C timing patch (commit 16a2828) and now names the
PCIe GDSC patch (5272658), and 0009 named the IRQ-to-CPU-1 diagnostic (ADR 0009, “Decision”:
deleted) and now names the PCIe PHY GDSC patch (running log, 2026-11-04 rows).
The schemes
- SemVer 2.0.0 (semver.org/spec/v2.0.0, read 2026-11-04):
- item 1: “Software using Semantic Versioning MUST declare a public API”;
- item 4: “Major version zero (0.y.z) is for initial development. Anything MAY change at any time”;
- item 10: build metadata after
+(e.g.1.0.0-beta+exp.sha.5114f85) is ignored for precedence; - FAQ “How do I know when to release 1.0.0?”: when it is used in production or users depend on a stable API;
- FAQ “Is v1.2.3 a semantic version?”: no;
v1.2.3is a tag name, the version is1.2.3.
- CalVer (calver.org, read 2026-11-04):
- conventions
YYYY,0M,0D,MICRO; multiple releases a day append a micro segment,2026.9.11.1(“Multiple releases per day?”); - “When to use CalVer?”: a large or changing scope, or “Do other external changes drive new project releases?”;
- “CalVer deliberately does not attempt to communicate breaking changes … There is no substitute for a changelog”;
- the same section opens “If third parties use your project, then use a serious version.”
- conventions
- Kernel patch series (
Documentation/process/submitting-patches.rstat the pin, GitHub):- “The canonical patch format”, “Subject Line”: “v1, v2, v3” for versions sent in response to comments, “RFC” for a
request for comments,
[PATCH v2 M/N]; - “Commentary”: the per-version changelog goes below the
---line, with links to earlier versions on lore; - “Describe your changes”: each version must be self-contained, “Don’t just say that this is version N”;
- “Don’t get discouraged - or impatient”:
[PATCH Vx RESEND]only for an unmodified resend; - “Providing base tree information”:
git format-patch --base, and the base “in an official maintainer/mainline tree”; - “Tooling”: b4 automates dependency tracking, checkpatch and sending.
- “The canonical patch format”, “Subject Line”: “v1, v2, v3” for versions sent in response to comments, “RFC” for a
request for comments,
- Git tags (
git-tag(1), local man page, read 2026-11-04): “Annotated tags are meant for release while lightweight tags are meant for private or temporary object labels”;git describeignores lightweight tags by default. - Keep a Changelog 1.1.0 (keepachangelog.com/en/1.1.0, read 2026-11-04):
for humans; an entry per version, newest first, with its date;
Added,Changed,Deprecated,Removed,Fixed,Security; anUnreleasedsection on top; “Using commit log diffs as changelogs is a bad idea”. - Making it visible on the device:
CONFIG_LOCALVERSIONappends a string to the kernel release, after “any files with a filename matching localversion* in your object and source tree”; the total is at most 64 characters (init/Kconfigat the pin,config LOCALVERSION).
Recommendation
- Do not use SemVer for the repo or the image. gbook declares no public API (item 1), and the thing others will
depend on, the
google,micabinding and DTS, is versioned by upstream once merged (inference). - Use CalVer
YYYY.0M.0D[.MICRO]for a released image, tagged as an annotated tagv2026.11.04(vas the tag prefix, per the SemVer FAQ’s convention). A release is an image you would hand to someone else: one that passed a stated number of cold boots, shown byattempt.sh tally(CLAUDE.md docs conventions). The upstream base and the milestone are the reasons a new one is cut, which is CalVer’s “external changes drive releases” case. - One tag message carries the links. It names the kernel base (
a804c57, “v7.3 + 1703, 7.4 merge window”), the patch list with status words, the milestone, and the attempt IDs that proved it. - Version vs attempt ID: a version names an image, and an attempt ID names one boot of an image. Several attempts
boot one version; an attempt of an untagged image stays an experiment. Add the version to the attempt’s row when
attempt.sh newsees a tagged commit (inference: the column does not exist yet). - Show the version on the device: have
build-kernel.shwrite alocalversion-gbookfile into the build directory (e.g.-gbook2026.11.04, or-gbook<git describe>for an untagged build), souname -rand the netlog’sLinux versionline name the gbook state. The kernel tree’s own-dirtysuffix stays as it is. - Upstream series use the kernel’s scheme only:
[PATCH RFC], then[PATCH v2]and so on, changelog below---,--base, b4 if adopted. The repo version never appears in a patch subject. - Local patches: add one status line to each header, using the ChromiumOS words plus one of ours:
LOCAL(gbook only, likeCHROMIUM:),FROMLIST <lore Message-ID>,FROMGIT <tree branch>,UPSTREAM <tag>. Today that reads 0001 LOCAL, 0002-0006 and 0008 FROMLIST, 0007 Omarchy’s fix-up (LOCAL, third-party), 0009 LOCAL (inference from the headers and ADR 0007). Numbers only grow: a deleted patch’s number is retired. - Changelog: the running log stays the history. Start
CHANGELOG.md(Keep a Changelog) only when a tagged image first leaves the build host; until then the annotated tag message is the release note.
3. Maturing the project
When to start upstreaming: what the kernel docs say
-
Post early. “posting your plans during the early stages can only be helpful” (
Documentation/process/3.Early-stage.rst, “When to post?”). For complex work “there is a lot to be gained by getting feedback from the community before the work is complete”, and code not ready for inclusion should say so and list the known problems (5.Posting.rst, “When to post”). -
The merge window lasts about two weeks; afterwards only fixes go in for six to ten weeks until the release (
2.Process.rst, “The big picture”). New board files reach the merge window through the platform tree: for qcom theT:entry isgit.kernel.org/pub/scm/linux/kernel/git/qcom/linux.gitwith maintainers Bjorn Andersson and Konrad Dybcio, listlinux-arm-msm@vger.kernel.org, IRC#linux-msmon OFTC (MAINTAINERSat the pin, “ARM/QUALCOMM SUPPORT”). Patches should be prepared against that tree, not mainline (submitting-patches.rst, “Obtain a current source tree”). -
Board DTS rules (
maintainer-soc.rst, “Validating Devicetree Files”;maintainer-soc-clean-dts.rst;maintainer-devicetree.rst):make dtbs_checkadds no new warnings;- no
scripts/dtc/dt-check-stylewarnings in relaxed mode, and most strict-mode ones addressed (the script exists at the pin); - DTS go through the SoC platform tree, separate from driver patches.
-
Binding rules (
Documentation/devicetree/bindings/submitting-patches.rst, items 1, 5, 6, 7):- the binding is a separate patch,
dt-bindings: <dir>: …; - it comes before its users in the series;
- every compatible in a board DTS “must be previously documented”, which for mica means a
google,micaentry inarm/qcom.yaml; - DTS patches go last or in their own series.
qcom.yamlalready describes depthcharge boards (“many devices … use the open source depthcharge bootloader”) withgoogle,<board>[-revN][-skuN]entries such asgoogle,coachz-rev1. The match order they rely on is documented inDocumentation/arch/arm/google/chromebook-boot-flow.rst; it falls back to plaingoogle,$(BOARD), which fits ADR 0004’sgoogle,mica. - the binding is a separate patch,
-
AI-assisted work (
coding-assistants.rst,generated-content.rst):- “AI agents MUST NOT add Signed-off-by tags. Only humans can legally certify the Developer Certificate of Origin”;
- the human submitter reviews all AI-generated code and takes responsibility;
- an AI assistant “must never send anything itself”;
- contributions should be transparent about which tools were used, on which parts, and how it was tested.
207 of gbook’s 210 commits carry a
Co-Authored-By: Claudetrailer (git log, 2026-11-04), so this is a hard prerequisite for any send. -
DT ABI: “a stable binding means that a newer kernel will not break on an older device tree” (
Documentation/devicetree/bindings/ABI.rst). Inference: oncegoogle,micais upstream, changes to it carry that constraint, which is one reason to post the binding only when the board’s compatible scheme is settled.
What blocks a clean upstream DTS today (inference, from repo files)
- Unverified items:
arm,max-rx-timeout-ms = <1000>is marked unverified (README “Resume here”), and other unverified items are listed indts/README.md. - No idle states: ADR 0009 deletes
domain-idle-statesfrom every CPU domain. Upstream may want the SoC problem fixed or explained first; the unsent stall report is the natural RFC for that. - Base tree: the pin is a merge-window commit; a series has to be rebased onto the qcom tree.
- Kernel-side support: the panel entry (0001) needs the full panel name, and the GMU and GDSC patches are other people’s series in flight.
Release cadence, roadmap, CI
- Cadence (recommendation): event-driven, not calendar-driven. Cut a tagged image when a milestone is reached or
the pin moves. Re-pin at most once per mainline release, onto a release tag such as
v7.4; ChromiumOS rebases every other release, and Asahi reports once per release. - Roadmap: the ladder in section 4, kept in the README’s phase table, with a
.scratch/<milestone>/map.mdper active milestone. The repo already uses wayfinder maps (.scratch/phase-2-first-boot/map.md, issue tracker). - Status:
docs/status.mdin the Asahi/Nura form. Rows: feature; state (works / partial / broken / untested); where it works (mainline at pin / local patch NNNN / board DTS only); upstream (no / sent vN / in 7.x); evidence (recon note). It replaces the feature prose in “Resume here”. - CI:
scripts/check.shthrough lefthook already runs on every commit. Add upstream’s own checks when M5 starts:make dtbs_check,dt-check-style,checkpatch.plon exported patches. Server-side CI on the the private remote and hardware CI (a Nura “main” requirement) are out of proportion for one owner and one device. - Outside contact before upstream: the plan already lists joining
#aarch64-laptops(project log, Phase 1). Posting the recon there is “posting your plans” at no ABI cost.
4. Proposed milestone ladder for gbook
Inference throughout: rung names, criteria and states are proposals. “Borrowed from” names the outside criterion each rung copies. The state column is from the README’s “Resume here” and the recon notes.
| M | Name | Phase / gate | Done when | Borrowed from | State 2026-11-04 |
|---|---|---|---|---|---|
| M0 | Way back to stock | Phase 1, Gate 1 | recovery media reach the recovery screen; relock tested (ADR 0001) | (gbook) | passed 2026-11-02 |
| M1 | First light | Phase 2, Gate 2 | our Linux version line on the AP console, panel or readback (CONTEXT.md) | Nura testing: “The device boots” | passed 2026-11-02 |
| M2 | Board on the console | Phase 3, rung 1 | every enabled DT node binds; USB, PCIe, Wi-Fi, display, GPU probe, EC keyboard, touchpad, TPM, suspend round trip; N clean cold boots by tally | the 9345 commit’s Working list | nearly: all of these since 2026-11-04 (unbound, suspend); touchscreen parked (touch-tpm) |
| M3 | Userspace from USB | Phase 3, rung 2 | a rootfs from USB with Mesa on the GPU, networked Wi-Fi, audio state known; first tagged image | Nura device page rows (3D, Wi-Fi, Audio, Battery) | not started |
| M4 | Daily-drivable | Phase 3, Gate 3 | install on internal storage (spare SSD, ADR 0002), dual-boot or documented return to stock | README Gate 3; Asahi “official”: the installer offers it | not started |
| M5 | Upstream-ready | Phase 4 entry | binding and DTS on the qcom tree pass dt_binding_check, dtbs_check (no new warnings), dt-check-style relaxed, checkpatch; Working / Not working list written; every local patch labelled; a human signs off | maintainer-soc, bindings/submitting-patches, coding-assistants | not started; can run in parallel with M3/M4 |
| M6 | Upstream | Phase 4, Gate 4 | google,mica binding and DTS applied to the qcom tree, then in a mainline release; firmware question settled | README Gate 4; Asahi’s “kernel release” cell | not started |
| M7 | In a distribution (optional) | after Phase 4 | a Nura testing device package, or a device entry in Ubuntu Concept | Nura categories; Ubuntu Concept list | not started |
5. Open questions for the owner
Reply in the usual form (“1a 2 ok”); each has a recommended answer.
- The ladder. a) adopt M0-M7 and add the rungs to the README phase table; b) keep phases and gates only. Recommended: a.
- Version scheme for images. a) CalVer
YYYY.0M.0D[.MICRO]annotated tags for released images; b) SemVer0.y.z, one minor per milestone; c) no versions until upstream. Recommended: a. - What counts as a release. a) an image that passed 3 clean cold boots by
tallyand is the one you would hand on; b) every image that reaches a new milestone, regardless of boot count. Recommended: a. - Version on the device. a)
build-kernel.shwrites alocalversion-gbookfile, souname -rnames the gbook state; b) leave the kernel release as is. Recommended: a. - Patch headers. a) add a status line (
LOCAL/FROMLIST/FROMGIT/UPSTREAM) to everykernel/patches/header and retire deleted numbers; b) leave the free-text headers. Recommended: a, as an ADR 0007 update. - Changelog. a) the annotated tag message is the release note until an image leaves the build host, then
CHANGELOG.md(Keep a Changelog); b) startCHANGELOG.mdnow. Recommended: a. - When the upstream hold ends. a) hold until M5’s checks pass, then send the binding and DTS as
[PATCH RFC]; b) post an RFC of the binding and current DTS after M2; c) hold until M4. Recommended: a, plus the IRC post from the Phase 1 list now, which costs no ABI. - Re-pin policy. a) re-pin onto the next mainline release tag (
v7.4) after M3, and before M5 in any case; b) re-pin only when a needed fix lands. Recommended: a. - Status matrix. a) a new
docs/status.md, with “Resume here” pointing to it; b) a table in the README. Recommended: a.
Sources
All read 2026-11-04. Kernel documents at the pin a804c5742bff (local tree ~/.cache/gbook/linux-a804c57, mirrored at
https://github.com/torvalds/linux/tree/a804c5742bff).
- Kernel:
Documentation/process/submitting-patches.rst(sections named inline),2.Process.rst,3.Early-stage.rst,5.Posting.rst,maintainer-soc.rst,maintainer-soc-clean-dts.rst,maintainer-devicetree.rst,coding-assistants.rst,generated-content.rst;Documentation/devicetree/bindings/submitting-patches.rst,bindings/ABI.rst,bindings/arm/qcom.yaml;Documentation/arch/arm/google/chromebook-boot-flow.rst;MAINTAINERS(“ARM/QUALCOMM SUPPORT”);init/Kconfig(LOCALVERSION);Makefile. - torvalds/linux on GitHub: f5b788d0e8cd
(9345 DTS), commit list for
qcom.yaml(1aa50217d7fd) and the 9345 DTS (bd2dbbb1f35a); tagsv7.3,v7.4-rc1and comparev7.3...a804c5742bffviagh api. - SemVer 2.0.0: https://semver.org/spec/v2.0.0.html
- CalVer: https://calver.org/
- Keep a Changelog 1.1.0: https://keepachangelog.com/en/1.1.0/
git-tag(1), DESCRIPTION (local man page).- Asahi Linux: https://asahilinux.org/docs/platform/feature-support/overview/ , https://asahilinux.org/docs/platform/feature-support/m1/ , https://asahilinux.org/blog/ , https://asahilinux.org/2026/09/m2-episode-1/
- Nura (postmarketOS): https://docs.nura.eco/pmaports/main/packaging/device-categorization.html , https://wiki.postmarketos.org/wiki/Lenovo_Yoga_Slim_7x_(lenovo-yoga-slim7x) (served from wiki.nura.eco)
- Ubuntu Concept: https://discourse.ubuntu.com/t/ubuntu-concept-snapdragon-x-elite/48800
- Linaro: https://gitlab.com/Linaro/arm64-laptops/linux
- ChromiumOS: https://www.chromium.org/chromium-os/developer-library/guides/kernel/kernel-development/ , https://www.chromium.org/chromium-os/chromiumos-design-docs/chromium-os-kernel/
M3 rootfs: which distribution, and how it boots from the stick
Researched 2026-11-04 (written 1:03 PM ET) on the build host. Sources: distro package indexes and package pages, Mesa and Ghostty
upstream at named commits and tags, the Omarchy repositories at named commits, the kernel tree at the pin
(~/.cache/gbook/linux-a804c57, a804c5742bff, with local patches applied in drivers/gpu/drm/msm and elsewhere,
not in block/, fs/ or init/), the cached vboot source in .firecrawl/, and the built kernel’s
~/.cache/gbook/build-a804c57/.config. The Googlebook was not touched: no adb, no gbook, no SuzyQ. Nothing was
installed. Inference marks what no source states. All package versions are as read on 2026-11-04 and will move.
Context: ADR 0010 sets M3 as “a rootfs from USB with Mesa on the GPU
and Wi-Fi in userspace; the audio state is known; the first release tag” (ADR 0011).
The boot path is the dev-signed FIT on KERN-A (ADR 0003,
fit-packaging.md), and the kernel stays ours: torvalds a804c57 plus kernel/patches/, every
needed driver built in.
Answer
- Debian sid arm64 for M3, built by
mmdebstrapin the same digest-pinned sid container that packages the image, pinned to one snapshot.debian.org timestamp, with a lock list and a local.debcache (snapshot answered 503 today). - Mesa does not decide it. Every candidate ships Mesa 26.x; the Adreno X1-85 is supported since Mesa 24.2.0, and
Mesa’s kmsro path should drive our split
msm-kms+msmdevices (inference from source). - Ghostty decides the runner-up. Only Omarchy’s aarch64 repo has a 1.4.0 binary; Debian has no Ghostty at all. Recommended: build 1.4.0 from its signed source in the container, as Omarchy’s PKGBUILD does (owner question 2).
- Boot (inference): ROOT is partition 2 on the same stick from 256 MiB on, so the per-attempt 256 MiB
ddnever touches it;/initfinds ROOT through depthcharge’skern_guid=%U, copies its log, Wi-Fi block and stock firmware into the new root’s/run, andswitch_roots. No ROOT, and it falls back to today’s capture-and-reboot. - Omarchy aarch64 (Arch Linux ARM base) is the natural M4 base: Ghostty 1.4.0, Quickshell and Hyprland are in its repo, and the owner runs Omarchy on the build host. It lacks a dated pin, which matters for M3’s release tag.
| Debian sid | Arch Linux ARM + Omarchy aarch64 | Ubuntu 26.04 / 26.10 | Fedora 44 / 45 | postmarketOS (Alpine edge) | |
|---|---|---|---|---|---|
| Mesa (X1-85 since 24.2.0) | 26.2.6 | 26.2.6 (ALARM), 26.2.5 (Omarchy’s mirror) | 26.0.8 / 26.2.4 | 26.2.6 / 26.2.3 | 26.2.6 |
| GTK 4 / libadwaita (Ghostty 1.4 needs 4.18 / 1.7) | 4.24.1 / 1.10.0 | 4.24.1 / 1.10.0 | 4.22.4 / 1.9.1; 4.24.1 / 1.10.0 | 4.22.5 / 1.9.4; 4.24.1 / 1.10.0 | 4.24.1 / 1.10.0 |
| Ghostty arm64 | none (community 1.3.1 .deb) | 1.4.0 (Omarchy repo; not in ALARM) | 1.3.0 (universe) | none (COPR, Terra) | 1.3.1 git, testing only, musl |
| Dated pin | snapshot.debian.org | none found | archive only; ports not confirmed | none found (koji NVRs) | none found |
| Built in the existing sid container | yes (mmdebstrap 1.6.0-5) | possible (pacman 7.1.0, arch-install-scripts 32 in sid; keyrings are not) | likely (inference) | possible (mkosi 27.1, dnf 4.24 in sid; inference) | possible (pmbootstrap 3.12.1 in sid) |
Takes our kernel and /init | yes | yes | yes | yes | only by bypassing its own initramfs (inference) |
1. What every candidate shares
Mesa and the Adreno X1-85
- Support landed in Mesa 24.2.0. Rob Clark’s “freedreno: Enable the X1-85” (
b0d22461b945, MR !30249, merged 2024-07-18) is first contained in tagmesa-24.2.0(GitLab APIcommits/<sha>/refs?type=tag, read 2026-11-04). - Mesa 24.3.0 is the practical floor. “freedreno: Add compute constlen quirk for X1-85” (
3c8190e8b221, MR !31764) halves the compute constlen, because without it “there are sporadic hangs in dEQP-VK.robustness.robustness2.*” on the GPU “in x1e laptops”. It is first inmesa-24.3.0. - The device entry. Mesa main
32f5c9344041hasGPUId(chip_id=0xffff43050c01, name="Adreno X1-85")in the a7xx “gen2” group with the a740 (src/freedreno/common/freedreno_devices.py:1242; atmesa-26.2.0, line 1262). The kernel bindsqcom,adreno-43050c01and loadsgen70500_sqe.fwandgen70500_gmu.bin(gpu-2026-11-03.md). - GL and Vulkan. The freedreno page says the gallium driver “implements up to OpenGL ES 3.2 and desktop OpenGL 4.5”,
and Turnip “is a Vulkan 1.3 driver for Adreno 6xx GPUs” (docs.mesa3d.org/drivers/freedreno.html,
read 2026-11-04; it names no a7xx part). At
mesa-26.2.0,freedreno_screen.c:518-526sets GLSL feature level 460 for everyis_a6xx()screen except a702, andis_a6xx()isscreen->gen >= 6(freedreno_screen.h:275-278), which includes a7xx. - Every candidate clears both floors (versions in the table above). So Mesa does not decide the distribution.
Mesa on our split DRM devices (inference from source, untested on mica)
msm.separate_gpu_kms=1 is on the base command line (scripts/package-in-container.sh). At the pin, the display then
registers as DRM driver msm-kms and the GPU as msm (drivers/gpu/drm/msm/msm_drv.c:885, 904); on mica they were
card2 and renderD128 (gpu-2026-11-03.md).
- Mesa’s pipe loader has no descriptor named
msm-kms, so it falls back to kmsro: “kmsro supports lots of drivers, try as a fallback” (src/gallium/auxiliary/pipe-loader/pipe_loader_drm.c:181-183atmesa-26.2.0,:174atmesa-26.0.0). - kmsro opens a render node, and when its kernel driver is
msmit creates a freedreno renderonly screen (src/gallium/winsys/kmsro/drm/kmsro_drm_winsys.c:100-103atmesa-26.2.0;:99atmesa-26.0.0). - Inference: a compositor on
card2renders on the X1-85 through kmsro. M3’s first rootfs boot checks this before anything else (4.4).
Ghostty’s floor
- GTK and libadwaita. “1.4.x | 4.18 | 1.7”; 1.3.x needs GTK 4.14 and libadwaita 1.5. The rule is “the minimum version that is available in the most recent LTS releases of Debian, Fedora, and Ubuntu” (ghostty.org/docs/linux, read 2026-11-04).
- OpenGL. “We require at least OpenGL 4.3” (
src/renderer/OpenGL.zig:38-40at tagv1.4.0). Inference: freedreno’s GLSL 460 level and “up to OpenGL 4.5” clear it.eglinfoon the device settles it. - Release. Tag
v1.4.0is dated 2026-10-27 (GitHub tag objectce787fc18a77). Ghostty is MIT-licensed (Omarchy’s PKGBUILDlicense=(MIT)). - Building it. Omarchy’s PKGBUILD builds 1.4.0 from
https://release.files.ghostty.org/1.4.0/ghostty-1.4.0.tar.gzwith a pinned upstream Zig: “Ghostty 1.4.x requires Zig 0.17.x”, fetched fromziglang.orgfor x86_64 and aarch64 with sha256 sums (pkgbuilds/ghostty/PKGBUILDat omarchy-pkgs66368e24e900). Ghostty’s build page lists the Debian build dependencies (libgtk-4-dev libgtk4-layer-shell-dev libadwaita-1-dev gettext libxml2-utils), says release tarballs carry minisign signatures, and does not mention cross-compiling. Its Zig table stops at 1.3.x (ghostty.org/docs/install/build, read 2026-11-04). Debian sid has nozigpackage (madison, read 2026-11-04).
Our kernel under a systemd userspace
Every candidate except postmarketOS boots systemd; Debian sid and ALARM both ship 262.1. systemd v262’s README
“REQUIREMENTS” lists the kernel options it needs. The built config (read 2026-11-04) has DEVTMPFS, CGROUPS,
INOTIFY_USER, SIGNALFD, TIMERFD, EPOLL, UNIX, SYSFS, PROC_FS, FHANDLE, DMIID, BLK_DEV_BSG, NET_NS,
USER_NS, IPV6, AUTOFS_FS, TMPFS_XATTR, SECCOMP, SECCOMP_FILTER, KCMP, CGROUP_BPF, BPF_JIT and
EFI_PARTITION all =y, and UEVENT_HELPER off.
- One mismatch:
CONFIG_FW_LOADER_USER_HELPER=y(from defconfig), where the README says “Userspace firmware loading is not supported and should be disabled in the kernel”.FW_LOADER_USER_HELPER_FALLBACKis off. Inference: harmless in practice, but a one-line fragment turns it off. - Root filesystem and stick.
EXT4_FS=yandUSB_STORAGE=y, butUSB_UASis not set, so the stick runs over bulk-only transport. Inference: slower than UAS, not wrong. - Modules.
MODULES=y, with 1450 symbols=m, but the initramfs loads none (kernel/*.configheader, status legend). ADR 0011 changesuname -rwith every gbook commit, so modules baked into a write-once rootfs would stop matching after the next KERN-A-only rewrite. Inference: keep the all-built-in rule and add fragments (audio below) rather than ship/lib/modules.
Firmware
- The stock ADSP and CDSP images (
qcom/x1e80100/google/mica/) are vendor content: never in git and never in an image that leaves the build host (ADR 0005). - Everything else the boot loads is redistributable: ath12k, Bluetooth, the regulatory database, and the GPU pair.
Debian’s
firmware-qcom-soc20260916-1 (sid, non-free-firmware) carriesqcom/gen70500_sqe.fw,qcom/gen70500_gmu.binandqcom/x1e80100/X1E80100-Dell-XPS-13-9345-tplg.bin(packages.debian.org file list, read 2026-11-04). That is the same linux-firmware tag asscripts/stage-firmware.sh’s pinnedlinux-firmware-qcom-20260916-1. - The firmware loader searches
firmware_class.path, then/lib/firmware/updates/…and/lib/firmware/…. The path can also be set at run time through/sys/module/firmware_class/parameters/path(Documentation/driver-api/firmware/fw_search_path.rstat the pin).
2. The candidates
Debian sid arm64 (mmdebstrap)
- Mesa:
mesa26.2.6-1 andmesa-vulkan-drivers26.2.6-1 (arm64) in sid (qa.debian.org madison, read 2026-11-04; madison also still lists 26.2.5-1 for sid). Forky has 26.1.6-1. - Ghostty: no package. madison and sources.debian.org return nothing for
ghostty. GTK 4.24.1+ds-1 andlibadwaita-11.10.0-2 exceed 1.4’s floor. There are two routes:- Build 1.4.0 from the signed tarball with Zig 0.17, as Omarchy’s PKGBUILD does. Under qemu-user in the container it is slow, and the time is unmeasured. Cross-compiling is undocumented.
- A community
.deb:ghostty_1.3.1-0.ppa2_arm64_forky.deb(also trixie and Ubuntu builds) from mkasberg/ghostty-ubuntu release1.3.1-0-ppa2, which ghostty.org lists as “Community”.
- Our kernel: yes. A Debian rootfs needs no Debian kernel package;
systemd262.1-1 (sid) meets the requirements above. - Firmware and audio data:
firmware-qcom-soc(above),alsa-ucm-conf1.2.16.1-1. Upstream alsa-ucm-confv1.2.16.1hasucm2/Qualcomm/x1e80100/Dell-Xps-9345.confandXps9345-HiFi.conf(GitHub, read 2026-11-04). - Pinning: snapshot.debian.org takes
archive/debian/<yyyymmddThhmmssZ>/URLs, and apt needs[check-valid-until=no]for snapshots older than about twelve days (snapshot.debian.org, read 2026-11-04).- mmdebstrap: with
SOURCE_DATE_EPOCHset, “the result will be reproducible across multiple runs with the same options and mirror content”. For ext4 output, “the filesystem UUID and hash_seed will be set to a UUID derived from SOURCE_DATE_EPOCH” (mmdebstrap(1), 1.6.0-5, read 2026-11-04). - Availability:
snapshot.debian.org/archive/debian/2026110…/dists/sid/…answered HTTP 504 and 503 on three tries on 2026-11-04. So the non-free-firmware component under snapshot is unconfirmed, and a local cache of the exact.debfiles is needed regardless. The project already does this for firmware packages (~/.cache/gbook/pkgs).
- mmdebstrap: with
- Building on the build host:
mmdebstraplists “foreign architecture chroots using qemu-user” and checks the arch “using qemu binfmt_misc support”.- the build host registers
qemu-aarch64with interpreter/usr/bin/qemu-aarch64-staticand flagsPF(/proc/sys/fs/binfmt_misc/qemu-aarch64, read 2026-11-04). TheFflag opens the interpreter at registration, so it also works inside a container (inference, from binfmt_misc semantics). - mmdebstrap’s root mode “needs to be able to mount and thus requires CAP_SYS_ADMIN”. Inference: the container then
needs
--cap-add SYS_ADMIN(or--privileged), whichpackage-image.shdoes not pass today. That is an owner-OK change. - Output can be an
.ext4image directly. Offline rebuilds need the cached.debset served as a local mirror (inference; not tried).
- Licence: main plus non-free-firmware (redistributable firmware). No stock blob goes in the image.
- Size: not measured. The stick is “4 GB+” (README owner tasks). Step 1 of the plan measures it (4.7).
Arch Linux ARM + Omarchy aarch64
- What Omarchy on aarch64 is. Its aarch64
pacman-stable.confenables[omarchy](https://pkgs.omarchy.org/stable/$arch) ahead of[core],[extra]and[alarm]. Those three come from “Omarchy’s mirror of Arch Linux ARM”,https://arm-stable-mirror.omarchy.org/$arch/$repo(default/pacman/aarch64/at omarchy84b3f2872b50, branchquattro). The Snapdragon set (install/omarchy-aarch64-qualcomm.packages) addslinux-firmware-qcom,qcom-firmware-extract,systemd-ukify,vulkan-freedrenoandpipewire-libcamera. - Mesa:
- ALARM’s mirror has
mesaandvulkan-freedreno1:26.2.6-1, while Omarchy’s ALARM mirror has 1:26.2.5-1 (extra.dbof both, read 2026-11-04). - Omarchy’s
mesa-aurorais the Asahi driver only (“Aurora Asahi Gallium, Zink and Honeykrisp”). - Inference: the lag between the two mirrors is Omarchy’s curated “stable” channel.
- ALARM’s mirror has
- Ghostty: 1.4.0-1 for aarch64 in Omarchy’s stable repo (
omarchy.db, 187 packages, read 2026-11-04), built by the PKGBUILD above. ALARM’s ownextrahas noghostty. GTK 1:4.24.1-1 and libadwaita 1:1.10.0-1 are in ALARMextra. - M4 continuity:
quickshell0.3.2,hyprland0.56.2 andqcom-firmware-extractare in the same aarch64 repo. Omarchy’s Snapdragon install builds a UKI “with the device trees systemd-stub picks one from”, which is a UEFI path. mica’s depthcharge boots FIT kernel partitions, not UKIs, so the Omarchy installer does not fit as is. A pacstrapped rootfs with Omarchy packages would (inference). - Our kernel: yes. ALARM’s
linux-aarch64(7.3.1-2) and Omarchy’slinux-omarchy(7.2.8-13 in the aarch64 repo) are simply not installed. - Pinning: none found. Arch’s dated archive covers x86_64 only, and no official ALARM archive was found. The stable mirror is curated, not addressed by date. Inference: superseded packages leave the mirrors, so the local cache plus a lock list would be the only pin.
- Building on the build host: Debian sid ships
pacman-package-manager7.1.0-3 andarch-install-scripts32-1. The ALARM and Omarchy keyrings are not in Debian. Package hooks run in the arm64 chroot under the same qemu binfmt (inference; not tried). - Licence: free packages from ALARM. The Omarchy repo also carries proprietary apps, so only named packages are pulled.
Ubuntu 26.04 “resolute” and 26.10 “stonking”
- Releases: 26.10 is the “Current Stable Release” (released 2026-10-15) and 26.04 is supported (Launchpad API
ubuntu/series, read 2026-11-04). - Mesa: 26.0.8-1ubuntu0.3 (resolute-updates) and 26.2.4-1ubuntu1 (stonking) (Ubuntu madison, read 2026-11-04).
- Ghostty:
ghostty1.3.0~us1-0ubuntu1.1 in resolute-updates/universe and 1.3.0~us1-0ubuntu4 in stonking, both with arm64 builds. ghostty.org lists Ubuntu as “Official, from 26.04”. GTK is 4.22.4 and libadwaita 1.9.1 on 26.04, 4.24.1 and 1.10.0 on 26.10, so either could run 1.4 if built. - Concept images for X1 laptops: UEFI ISOs (
questing-desktop-arm64+x1e.iso) that use SMBIOS “to load the correct dtb automatically”. The page as served shows Mesa 24.2.3 and replies from November 2024, so it reads stale (discourse.ubuntu.com/t/48800, read 2026-11-04). mica has no UEFI, so only Ubuntu’s userspace would apply. - Our kernel: yes, as for Debian.
- Pinning: snapshot.ubuntu.com covers the archive from 2023-03-01 (snapshot.ubuntu.com,
read 2026-11-04). arm64 lives on ports.ubuntu.com, whose
resoluteRelease has noSnapshots:field, andsnapshot.ubuntu.com/ubuntu-ports/answered 404 (read 2026-11-04). So no dated arm64 pin is confirmed. - Building on the build host: inference: mmdebstrap can bootstrap Ubuntu suites with the Ubuntu keyring; not checked.
Fedora 44 and 45
- Mesa: 26.2.6-1.fc44 (F44 updates) and 26.2.3-1.fc45 (mdapi
srcpkg/mesa, read 2026-11-04). - GTK and libadwaita: 4.22.5 / 1.9.4 on F44, 4.24.1 / 1.10.0 on F45.
- Ghostty: none in Fedora (mdapi returns nothing for rawhide, F44 or F45). ghostty.org lists COPR
scottames/ghosttyand Terra, both community; their arm64 builds were not checked. - X1E: the Fedora wiki covers the “aarch64 Fedora Workstation 44 Live ISO” on X1E/X1P laptops with
clk_ignore_unused pd_ignore_unused systemd.tpm2_wait=0 modprobe.blacklist=qcom_q6v5_pas, an EFI firmware path (Snapdragon WoA Laptop Install, oldid 775997, read 2026-11-04). - Our kernel: yes. A Fedora rootfs without
kernel-coreis possible (inference). - Pinning: no dated snapshot mirror found. Koji keeps every build by NVR.
- Building on the build host:
mkosi27.1-1 anddnf4.24.0-1 are in sid. Whether either builds Fedora 45 arm64 there was not checked.
postmarketOS (now Nura) on Alpine edge
- Mesa: Alpine edge
main/aarch64hasmesa,mesa-dri-galliumandmesa-vulkan-freedreno26.2.6-r0 (APKINDEX, read 2026-11-04). Inference: pmOS edge uses Alpine edge’s Mesa (wiki.nura.eco was behind a bot check). - Ghostty: 1.3.1_git20260908-r2 in
testing/aarch64only (edge, not in a stable branch), built against musl.gtk4.04.24.1-r0,libadwaita1.10.0-r0,linux-firmware-qcom20260916-r0. - Our kernel: pmOS’s strength is the boot side: pmbootstrap already builds depthcharge images
(
deviceinfo_generate_depthcharge_image, cgpt layout, fit-packaging.md section 5). x1e80100 laptops are in pmaports (Yoga Slim 7x intesting, milestones-versioning.md section 1). But a pmOS device brings its own kernel package and its own initramfs. Inference: keeping our/initand its netlog means working around pmOS rather than with it. - Pinning: none found for edge.
pmbootstrap3.12.1-1 is in sid.
3. Recommendation, and why
- For M3, Debian sid. It is the only candidate with a citable dated pin, and it builds in the container that already packages every image (same tool, same base). Its Mesa, GTK and libadwaita are as new as anyone’s, and its non-free-firmware carries the GPU pair and the 9345 topology. The cost is Ghostty: build 1.4.0 (question 2).
- For M4, revisit with Omarchy aarch64. Ghostty 1.4.0, Quickshell and Hyprland come as binaries from the owner’s own distro. The boot design in section 4 is distro-neutral: only the rootfs build script changes.
- Not Ubuntu, Fedora or pmOS for M3. Ubuntu adds Ghostty 1.3.0 but no confirmed arm64 pin. Fedora has no Ghostty
and no pin. pmOS fights our
/initand uses musl.
4. Boot design (inference throughout; no part of it has run on mica)
4.1 Stick layout
The owner rewrites the whole 256 MiB mica-usb.img for every attempt, GPT included (scripts/attempt.sh dd-cmd,
scripts/package-in-container.sh), and attempt.sh verify reads the same 256 MiB back.
- ROOT starts at 256 MiB. fit-packaging’s sketch put ROOT at sector 270336, right after KERN-A
(fit-packaging.md, step 5); a 256 MiB
ddwould overwrite its start. Start ROOT at sector 524288 (256 MiB) and run it to the end of a declared sizeD. - The image’s GPT declares ROOT. Otherwise each kernel rewrite deletes the entry.
cgpt createruns on a sparse file of sizeD,cgpt add -i 2 -t data -l gbook-rootadds ROOT, and the first 256 MiB is cut out asmica-usb.img. The attemptdd,verifyandgptstay as they are. The container’s offlinecgpt showattribute checks andcgpt.txtthen run on the full sparse file before the cut, because the cut file’s backup header is not at its end (inference: cgpt’s handling of an invalid backup header was not read). Dmust not exceed the stick. vboot rejects a primary header whoselast_usable_lbalies beyond the drive (firmware/lib/cgptlib/cgptlib_internal.c:144-146at vboot34fbbe6). A backup header not at the drive’s end fails its check (:119-121), andGptRepairrebuilds it in memory from the primary (:337-358).WriteAndFreeGptDatathen runs on every boot (2load_kernel.c:783). Whether it writes the repaired backup was not read (gpt_misc.cis not in the cache).- Today’s image already has its backup header at 256 MiB, not at the stick’s end, and it boots. So this case is tested in practice.
- Linux warns “GPT:Alternate GPT header not at the end of the disk” and carries on with the primary
(
block/partitions/efi.c:552at the pin).
- depthcharge ignores ROOT. vboot considers only ChromeOS-kernel and Android-vbmeta type entries (fit-packaging.md section 5).
- Writing ROOT is a rare owner step:
ddofrootfs.ext4to sector 524288 (or to partition 2), named first and run with!like the kernel write. It is done again only when the rootfs changes, not per kernel.
4.2 How /init finds and mounts ROOT
- Use
kern_guid, notroot=. depthcharge already replaces%Uin the storedkern_guid=%Uwith KERN-A’s PARTUUID (fit-packaging.md section 3). The kernel’s ownroot=PARTUUID=%U/PARTNROFF=1form only matters when the kernel mounts root itself, which it never does with an initramfs/init. So the command line and check 27’s stored-line compare stay unchanged. - The lookup is plain busybox. At the pin,
part_ueventexportsPARTN,PARTNAMEandPARTUUID(block/partitions/core.c:258-262)./initwaits for USB storage (as its USB stage already does, up to 20 s), finds the/sys/class/block/*/ueventwhosePARTUUIDequalskern_guid, takes its parent disk, and requires that disk’s partition 2 to carryPARTNAME=gbook-root. - Mount and hand off:
fsckis optional (e2fsprogs is not in the initramfs). The ext4 journal covers an unclean stop.- Mount ROOT read-write on
/newroot, mount a tmpfs on/newroot/run, and copy the hand-off files there (4.3). - Move
/dev,/procand/sysover, thenexec switch_root /newroot /sbin/init.
- Fallback: no ROOT (or a failed mount) within the USB wait runs today’s path unchanged: captures, netlog, reboot. A broken rootfs still yields a netlog.
4.3 What stays in the initramfs
- Everything that probes before root: busybox,
/init,iw/wpa_supplicantand the whole staged firmware tree. Built-in drivers load firmware at probe time (ath12k, the ADSP and CDSP remoteprocs), before ROOT can be mounted. - Copied into the new root’s
/run, never into the rootfs image:/run/gbook/gbook.log: theGBOOK:lines so far (switch_rootfrees the initramfs);/run/gbook/wpa_supplicant.conf, mode 600: the owner’s network block, as/inithandles it today;/run/gbook/firmware/: the stockqcom/x1e80100/google/mica/files only, for a remoteproc restart after the hand-off./initwrites that path to/sys/module/firmware_class/parameters/pathbeforeswitch_root(fw_search_path.rst).- The rootfs then holds only redistributable firmware, from Debian’s
firmware-qcom-socandfirmware-atheros20260916-1. The latter carriesath12k/WCN7850/hw2.0/{amss,m3,board-2}.binandqca/hmtbtfw20.tlv(packages.debian.org file list, read 2026-11-04).
- Two to verify on the first rootfs boot: that firmware lookups after
switch_rootresolve against the new root (kernel_read_file_from_path_initnsopens the path under the init task’s filesystem,fs/kernel_read_file.c:147-166; not traced further), and that the GPU’s first open in userspace loadsgen70500_*from the rootfs. - The rootfs image stays shareable: no Wi-Fi secret, no stock blob, no per-unit identifier. That is what ADR 0011’s “image leaves the build host” case needs.
4.4 Showing the M3 criteria
A gbook-m3.service in the rootfs runs once after network-online.target. It prints GBOOK: lines to /dev/console
(the AP console, still read-only to us) and appends them to the netlog. Its checks:
- Mesa on the GPU:
eglinfo -Bnames “Adreno X1-85” and freedreno;vulkaninfo --summarynames Turnip.kmscube(orglmark2-es2-drm) oncard2proves the kmsro path, and the owner sees it on the panel.- Then
cageruns Ghostty as an unprivileged user, a real OpenGL 4.3 app on the panel. - Capture
GL_RENDERER, the GL version and any GPU fault lines from dmesg.
- Wi-Fi in userspace:
wpa_supplicant@wlan0reads/run/gbook/wpa_supplicant.confthrough a unit drop-in (inference: the stock unit reads a file under/etc/wpa_supplicant/), andsystemd-networkdtakes DHCP.- Debian sid has every tool named here (madison, read 2026-11-04):
mesa-utilsandmesa-utils-bin9.0.0-2+b3 (eglinfo),vulkan-tools1.4.341.0,kmscube0.0.0~git20260822-1,glmark22023.01,cage0.3.1-1 andwpasupplicant2:2.11-7. - Proof:
networkctl status wlan0(redacted),iw dev wlan0 link(signal and frequency only), a gateway ping, and the netlog itself sent over that link from userspace.
- Audio state known: the status rows read “no sound node in the DTS”.
- Minimum: record what
aplay -lshows (expected: no card) as the known state. - Better: one diagnostic attempt with the 9345’s sound node as a
dts/diag/*.dtsi(marked unverified per ADR 0004), akernel/mica-audio.configbuildingSND_SOC_X1E80100,SND_SOC_WSA884X, the LPASS macros,SOUNDWIRE_QCOM,QCOM_APRandSND_SOC_QDSP6in (all=mtoday), and Debian’s 9345 topology and UCM. - mica’s stock ADSP image may not match the 9345’s topology, so either outcome counts as “known”.
- Minimum: record what
- No listener, no getty on the AP console: mask
serial-getty@ttyMSM0(ADR 0006;/inithas “No interactive shell”). Install nosshd; today’s/initsays “Nothing listens on the network”. A getty or an autologin session on the panel (tty1) is the owner’s to allow.
4.5 Keeping the attempt and netlog tooling
- What the tooling parses:
watch,followandcomparereadGBOOK:lines;boot_health.pyandtallyread the netlog’s dmesg and SCMI trace sections;image-checkcompares the booted kernel with the verified stick. - One sender for both: move
/init’s netlog block (redact, sections,--- end of netlog) into one script inkernel/initramfs/, used by/initand shipped into the rootfs. Same format, same listener (attempt.sh netlog start). - The rootfs sends after its checks and then stays up. A base image cannot depend on a
gbook.*debug argument (ADR 0011), and an M3 release is a usable system, not a capture-and-reboot. Test rootfs builds may reboot like today. - Release identity: a release names two artifacts. Inference/proposal: the tag message adds the rootfs image’s
sha256, the snapshot timestamp and the lock file’s hash, next to the KERN-A image hash
attempt.sh stickprints.
4.6 Kernel-side follow-ups
kernel/mica-rootfs.config:# CONFIG_FW_LOADER_USER_HELPER is not set(systemd README).USB_UAS=yis optional.kernel/mica-audio.configfor the audio attempt above.- Omarchy’s
linux-omarchy-aarch64patches 2002 and 2003 are kernel patches, not Mesa or rootfs patches (README “Resume here” lists “a rootfs with Mesa” as a lead; a handoff paired it with them).2002-power-supply-qcom_battmgr-Notify-charger-supplies-of-online-changes.patch(Brandon Williams, 2026-10-13): without it “UPower keeps the state it read at startup”.2003-wifi-ath12k-don-t-count-a-vdev-restart-as-a-started-.patch(Birk Skyum, 2026-10-20): stops 3 stime out while waiting for fw stats donestalls after an AP changes channel or bandwidth.- Both matter once UPower and a long-lived Wi-Fi link exist. They would enter as
kernel/patches/with status lines (ADR 0007). Their upstream status was not checked.
4.7 Steps
- Measure: an
mmdebstrap --simulate(orapt-get install --dry-run) of the package list in the container, to size the rootfs and to chooseDfor the owner’s stick. scripts/build-rootfs.sh: snapshot pin, lock list,.debcache,SOURCE_DATE_EPOCH, ext4 output, a manifest with sha256. It runs throughpkexec docker runwith the extra capability.package-in-container.sh: the two-entry GPT (4.1);/init’s ROOT lookup and hand-off (4.2-4.3); the shared netlog sender; a check inscripts/check.shfor the lookup against synthetic uevent files.- Owner: write ROOT once, then attempts as today. First boot:
eglinfoandkmscube, then Wi-Fi, then Ghostty, then audio. - An ADR for the rootfs choice and the boot design, once the owner answers section 5.
5. Open questions for the owner
Reply in the usual form (“1a 2 ok”); each has a recommended answer.
- Distribution for M3. a) Debian sid arm64 via
mmdebstrap, snapshot-pinned, in the existing container; b) Arch Linux ARM + Omarchy’s aarch64 repo now (Ghostty 1.4.0 binary, no dated pin); c) Ubuntu 26.10; d) postmarketOS. Recommended: a, with b reconsidered for M4. - Ghostty at M3. a) build 1.4.0 from the minisign-checked tarball with Zig 0.17 in the container (first-party
sources, build time unmeasured); b) the community 1.3.1 arm64
.deb(fast, third-party binary, not in a release image); c) none until M4, withkmscube/glmark2as the GL proof. Recommended: a, falling back to c if the emulated build proves impractical. - Stick layout. a) ROOT as partition 2 on the same stick from 256 MiB, declared in every image’s GPT, so the
per-attempt
ddis unchanged; b) a second stick through a hub. Recommended: a. - Finding ROOT. a)
/initmatcheskern_guidto aPARTUUIDand mounts partition 2 labelledgbook-rooton the same disk; b) a fixedroot=PARTUUID=…of ROOT on the command line. Recommended: a (no stick-specific value in the image, command line unchanged). - Secrets and vendor firmware. a) the Wi-Fi block and the stock ADSP/CDSP files stay in the initramfs and are
copied to the new root’s
/run; the rootfs carries only redistributable firmware; b) put both into the rootfs image (it then never leaves the build host). Recommended: a. - Modules. a) keep everything built in, adding
mica-rootfs.configandmica-audio.config; b) ship/lib/modulesin the rootfs (stale after each kernel-only rewrite). Recommended: a. - Audio for M3. a) one diagnostic attempt with the 9345 sound node,
mica-audio.configand Debian’s 9345 topology and UCM, recording whatever happens; b) record “no sound node” as the known state. Recommended: a. - Console and network policy. a) mask
serial-getty@ttyMSM0, nosshd, nothing listening; an autologincagesession with Ghostty on the panel only; b) also a getty ontty1. Recommended: a. - Container capability. a) let
build-rootfs.shrun its container with--cap-add SYS_ADMIN(mmdebstrap’s root mode mounts); b) try--mode=unshareorfakechrootwithout it first. Recommended: b, then a if it fails. - Release identity. a) an M3 tag message also names the rootfs sha256, snapshot timestamp and lock hash (an ADR 0011 update); b) tag the kernel image only. Recommended: a.
Sources
| Source | Ref | Commit or version | Read |
|---|---|---|---|
| omacom/omarchy-pkgs | master | 66368e24e900bef4baa6d136c1254bc66daaef0d | 2026-11-04 |
| omacom/omarchy | quattro | 84b3f2872b5023c13967543ed4a98b08fcac5b21 | 2026-11-04 |
| Mesa (gitlab.freedesktop.org API) | main; tags mesa-24.2.0, mesa-24.3.0, mesa-26.0.0, mesa-26.2.0 | 32f5c9344041e53c3bff89c6fc5b107c8b7ed471 | 2026-11-04 |
| Ghostty | tag v1.4.0 | tag object ce787fc18a7782e81585beb63f0d905ccaf69284 | 2026-11-04 |
| systemd | tag v262, README | tag | 2026-11-04 |
| alsa-ucm-conf | tag v1.2.16.1 | tag | 2026-11-04 |
| Linux | torvalds | a804c5742bff (local tree, patches applied outside block/, fs/, init/) | 2026-11-04 |
| vboot_reference | firmware-bluey-16702.B | 34fbbe62cc18 (cached in .firecrawl/) | 2026-11-04 |
Package indexes, all read 2026-11-04:
- Debian:
https://qa.debian.org/madison.php,https://packages.debian.org/sid/all/firmware-qcom-soc/filelist,https://sources.debian.org/api/src/ghostty/(404). - Ubuntu:
https://people.canonical.com/~ubuntu-archive/madison.cgi,https://api.launchpad.net/devel/ubuntu/series,http://ports.ubuntu.com/ubuntu-ports/dists/resolute/Release. - Fedora:
https://mdapi.fedoraproject.org/<release>/srcpkg/<name>,https://bodhi.fedoraproject.org/releases/. - Alpine edge:
https://dl-cdn.alpinelinux.org/alpine/edge/{main,community,testing}/aarch64/APKINDEX.tar.gz. - Arch Linux ARM:
http://mirror.archlinuxarm.org/aarch64/{core,extra}/*.db. - Omarchy:
https://pkgs.omarchy.org/stable/aarch64/omarchy.db,https://arm-stable-mirror.omarchy.org/aarch64/extra/extra.db. - Pages: ghostty.org
docs/linux,docs/install/binary,docs/install/build; docs.mesa3d.orgdrivers/freedreno.html; manpages.debian.orgmmdebstrap(1)(1.6.0-5); snapshot.debian.org; snapshot.ubuntu.com; discourse.ubuntu.comt/48800; fedoraproject.org wikiSnapdragon_WoA_Laptop_Install; github.com/mkasberg/ghostty-ubuntu releases. - Local reads on the build host:
/proc/sys/fs/binfmt_misc/qemu-aarch64;~/.cache/gbook/build-a804c57/.config.
Not found
- A Ghostty package in Debian (any suite) or Fedora; a Ghostty arm64 binary of 1.4 anywhere but Omarchy’s repo.
- A dated archive for Arch Linux ARM or Alpine edge; a confirmed snapshot service for Ubuntu ports (arm64).
- Whether snapshot.debian.org serves
non-free-firmwarefor sid: the service answered 503/504 on 2026-11-04. - The Zig version for Ghostty 1.4 on ghostty.org (its table stops at 1.3.x); the 0.17 figure is from Omarchy’s PKGBUILD.
- Whether vboot writes a repaired backup GPT back to the stick (
gpt_misc.cnot read). - The rootfs size; the build time of Ghostty under qemu-user; whether mmdebstrap runs without
CAP_SYS_ADMINin the container. - The upstream status of Omarchy patches 2002 and 2003; whether mica’s speaker path matches the 9345’s.
- A current postmarketOS page on its edge base (wiki.nura.eco served a bot check).
Debug tools for the next boots: SCMI timeouts and the touchscreen 0xff reads
Researched 2026-11-03 from the kernel tree at the pin (~/.cache/gbook/linux-a804c57, a804c5742), the build
config ~/.cache/gbook/build-a804c57/.config (kernel #13 era), the pinned Debian busybox-static, the netlog
dumps/firstboot/2026-11-03-netlog.txt (gitignored) and the GitHub page of morluto/rea. The device was not
touched. Line numbers are at the pin unless a repo path is given. Inference marks what the source does not state.
Ranked recommendation for the next boot
| # | Add | Problem | Cost |
|---|---|---|---|
| 1 | Event tracing: two config lines, trace_event= on the command line, trace dump into the netlog | both | one kernel rebuild (~5 min); 0 owner time per boot |
| 2 | log_buf_len=4M | both | none |
| 3 | IRQ counts, pin and regulator state in /init | both | none (sysfs, debugfs, /proc reads) |
| 4 | Raw i2ctransfer reads of the touchscreen’s HID descriptor | touch | none; needs the owner’s OK (I2C writes of a register pointer) |
| 5 | dyndbg on the silent i2c-hid and GENI error lines | touch | none (GBOOK_CMDLINE_EXTRA) |
1. Kernel fragment (a new kernel/mica-debug.config; it sorts after mica-firstboot.config):
# Event tracing (tracefs, trace_event=). arm64 defconfig:1970 sets "# CONFIG_FTRACE is not set".
CONFIG_FTRACE=y
CONFIG_ENABLE_DEFAULT_TRACERS=y
# require: EVENT_TRACING
olddefconfig on a scratch copy of the build config with these two lines adds only TRACING, EVENT_TRACING,
RING_BUFFER, TRACEPOINTS, NOP_TRACER, CONTEXT_SWITCH_TRACER, TRACEFS_AUTOMOUNT_DEPRECATED,
BRANCH_PROFILE_NONE, UPROBES, UPROBE_EVENTS, EPROBE_EVENTS, BPF_EVENTS, DYNAMIC_EVENTS, PROBE_EVENTS
and the module-only IWLWIFI_DEVICE_TRACING. No FUNCTION_TRACER, so no per-function patch sites. The probe-event
extras can be pinned off with # CONFIG_UPROBE_EVENTS is not set (and EPROBE_EVENTS, BPF_EVENTS) for a leaner
image. EVENT_TRACE_STARTUP_TEST stays off.
Command line (GBOOK_CMDLINE_EXTRA, one line; the -18 boot proved a quoted dyndbg= survives packaging):
log_buf_len=4M trace_event=scmi:scmi_xfer_begin,scmi:scmi_xfer_response_wait,scmi:scmi_rx_done,scmi:scmi_xfer_end,scmi:scmi_msg_dump,i2c:*,qcom_geni_i2c:* dyndbg="file i2c-hid-core.c line 1055 +p; file i2c-qcom-geni.c line 235-306 +p"
/init additions (sketch; the SCMI awk was tested on a synthetic trace only):
# After the sysfs mount:
mount -t tracefs tracefs /sys/kernel/tracing 2>/dev/null
T=/sys/kernel/tracing
# Before the touch window (after touch-input). Adapter numbers follow probe order, so resolve them.
i2c_bus() { for b in /sys/bus/i2c/devices/i2c-*; do case "$(readlink -f "$b")" in */"$1"/i2c-*) echo "${b##*i2c-}"; return ;; esac; done; }
ts_bus=$(i2c_bus a80000.i2c); tp_bus=$(i2c_bus b80000.i2c)
cap i2c-bus "touchscreen=i2c-${ts_bus:-none} touchpad=i2c-${tp_bus:-none}"
cap irq-counts "$(grep -E 'apss_cpucp_mbox|a80000\.i2c|b80000\.i2c|hid-over-i2c' /proc/interrupts | tr -s ' ')"
cap ts-pins "$(grep -E '^ gpio(32|33|48|51|88|89) +:' /sys/kernel/debug/gpio 2>/dev/null)"
for r in /sys/class/regulator/*; do
[ "$(cat "$r/name" 2>/dev/null)" = vreg_touchscreen_3p3 ] && cap ts-vreg "state=$(cat "$r/state") users=$(cat "$r/num_users")"
done
# (item 4 goes here, before the snapshot, so its transfers are in the trace too)
# Keep SCMI, the two touch buses and their GENI controllers; drop i2c10 (TPM) and the other buses.
trace_keep() { grep -E "entries-in-buffer|scmi_|geni_i2c_[a-z_]+: (a80000|b80000)\.i2c|i2c_[a-z]+: i2c-(${ts_bus:-x}|${tp_bus:-x}) " "$T/trace" | redact; }
trace_keep >/tmp/trace-probe.txt # before the touch window floods the ring
cap trace "$(head -n 1 /tmp/trace-probe.txt) kept=$(wc -l </tmp/trace-probe.txt)"
cap geni-setup "$(grep -o 'geni_i2c_bus_setup: .*' /tmp/trace-probe.txt | sort -u)"
cap geni-err "$(grep -o 'geni_i2c_err: .*' /tmp/trace-probe.txt | sort | uniq -c)"
cap scmi-latency "$(awk '{t=""; s=""; for (i = 1; i <= NF; i++) { if ($i ~ /^[0-9]+\.[0-9]+:$/) t = substr($i, 1, length($i) - 1); if ($i ~ /^seq=/) s = $i }
/scmi_xfer_begin:/ { b[s] = t }
/scmi_rx_done:/ && (s in b) { d = (t - b[s]) * 1000; n++; if (d > m) m = d }
/scmi_xfer_end:/ && / s=-110 / { to++ }
/t=!RESP/ && (s in b) { late = late sprintf(" %s:%.0fms", s, (t - b[s]) * 1000) }
END { printf "replies=%d max=%.1fms timeouts=%d late=%s\n", n, m, to, (late == "" ? " none" : late) }' /tmp/trace-probe.txt)"
# In the netlog block, after the dmesg section (never through say/cap: the console runs at 115200 baud):
# echo "--- trace at probe"; cat /tmp/trace-probe.txt; echo "--- trace at end"; trace_keep
What one boot then answers:
- SCMI: the reply latency of every message (
scmi_xfer_begintoscmi_rx_done), the arrival time of each late reply (!RESPdump), and with theapss_cpucp_mboxIRQ count next to thesent_ok/response_okcounters already in/init, whether the interrupts arrive late or not at all. That sizesarm,max-rx-timeout-msfrom data (scmi-cpucp-timeouts.md, ranked step 1-2). - Touch: the bytes on the wire per read (
i2c_reply), the GENI status of every interrupt during it (geni_i2c_irq:m_statandrx_st, whose low bits are the RX FIFO word count), any controller error (geni_i2c_err), and the SCL counters actually programmed (geni_i2c_bus_setup).
Not for the next boot: tp_printk, ftrace_dump_on_oops, initcall_debug, the function tracer, SCMI raw mode,
i2cdetect/i2cdump/i2cget (reasons below). Housekeeping: softlockup_panic=1 on the base command line does
nothing in this build (section 6).
1. Event tracing from an initramfs
State today. CONFIG_FTRACE is not set (.config:12029; arch/arm64/configs/defconfig:1970), so there is
no tracefs and no trace event in the current kernel. FTRACE defaults to y if DEBUG_KERNEL
(kernel/trace/Kconfig:204-207), but defconfig turns it off explicitly. ENABLE_DEFAULT_TRACERS selects TRACING
(:588-591), and TRACING selects EVENT_TRACING, RING_BUFFER and TRACEPOINTS (:179-188).
Command-line parameters (all [FTRACE], Documentation/admin-guide/kernel-parameters.txt:7628-7700):
| Parameter | Handler | Use here |
|---|---|---|
trace_event=<list> | trace_events.c:4659 | the capture. Comma-separated system:event, system:* |
trace_buf_size=nn[KMG] | trace.c:1035 | per CPU; default 1 441 792 bytes (trace.c:740). Default is enough with the snapshot before the touch window |
tp_printk | trace.c:354 | events also to printk. Not now: see below |
tp_printk_stop_on_boot | trace.c:361 | stops tp_printk at late_initcall_sync |
ftrace_dump_on_oops[=orig_cpu] | trace.c:296 | dumps the ring to the console on an oops. Not now |
trace_trigger=, trace_instance= | trace_events.c:3854, trace.c:320 | not needed |
Armed before the SCMI probe. start_kernel calls trace_init() (init/main.c:1086), which runs
trace_event_init() (trace_events.c:4987) and event_trace_enable(), which enables the trace_event= list
(:4884-4912) and again at early_initcall (:4932-4946). arm-scmi binds at about 0.36 s, so its first message is
in the ring.
Events at the pin (all built in, so trace_event= reaches them):
scmi(include/trace/events/scmi.h):scmi_xfer_begin(:37, calleddriver.c:1429),scmi_xfer_response_wait(:65,driver.c:1349, prints the timeout in ms),scmi_rx_done(:121,driver.c:1164for replies),scmi_xfer_end(:93,driver.c:1467, prints the return code),scmi_msg_dump(:147, with payload; a reply for an xfer that already timed out is dumped as!RESPthroughscmi_bad_message_trace,driver.c:867-891, from the “Message for %d … is not expected” path,:1015-1021). Leave outscmi_fc_call(:12): it fires on every FastChannel perf level set (perf.c:708), each cpufreq change.i2c(include/trace/events/i2c.h):i2c_read,i2c_write,i2c_reply,i2c_result, emitted around every__i2c_transfer(i2c-core-base.c:2283-2307) once the event’s static key is on.i2c_replyrecords the bytes read; the text output shows the first 64 (%*phD, capped inlib/vsprintf.c:1205): the whole 30-byte HID descriptor, the first 64 of the 935-byte report descriptor.smbus(include/trace/events/smbus.h): only SMBus calls; i2c-hid usesi2c_transfer, so not needed.qcom_geni_i2c(include/trace/events/qcom_geni_i2c.h, created ini2c-qcom-geni.c:4-5):geni_i2c_bus_setup(i2c-qcom-geni.c:263),geni_i2c_irq(:344, every interrupt),geni_i2c_err(:299, every error, including NACKs and timeouts thatdev_errskips).qcom_geni_se:geni_se_regsdumps 50 SE registers, but onlyspi-geni-qcom.cemits it, not the I2C driver.hid,i2c-hid: no trace events at the pin.- GPI DMA: no events; and
QCOM_GPI_DMA=m(.config:8490), so these GENI I2C buses run FIFO / SE DMA.
Why not tp_printk. It copies every enabled event to printk; with ignore_loglevel they go to the 115200-baud
console. The kernel doc warns it can live-lock with high-frequency events (kernel-parameters.txt:7644-7648), and
the touchpad alone gave 841 to 4055 reports per window (attempts.tsv, -18, -19). Console output inside the SCMI
window could also change the timing under study (inference). Fallback only for a boot that never reaches the
netlog, and then with trace_event=scmi:* alone plus tp_printk_stop_on_boot. ftrace_dump_on_oops writes the
whole ring to the same console: neither problem is an oops, so skip it.
Timestamps. The default local trace clock is sched_clock() (trace_clock.c:37-42, trace.c:1077), so trace
times line up with the dmesg times (inference: arm64 printk also uses sched_clock).
Later, if useful. BOOTTIME_TRACING (Kconfig:226) adds per-event filters at boot through a bootconfig in the
initrd. A persistent ring buffer (reserve_mem=… trace_instance=…@…, kernel-parameters.txt:7734-7758) survives a
warm reset, but the next boot is usually stock, so it is of little use here.
2. The printk ring wraps
CONFIG_LOG_BUF_SHIFT=17 (.config:162) gives a 128 KiB ring. In the netlog, two of the last six boots’ dmesg
sections hold about 126 KB of text and start mid-boot (first line GICv3: … and KASLR enabled, not
Booting Linux): the earliest lines were lost. Any added dyndbg makes this worse. log_buf_len= is an
early_param (kernel/printk/printk.c:1071); 4M costs 4 MiB of RAM.
3. Read-only /init captures (no rebuild)
/proc/interrupts. The CPUCP mailbox IRQ isapss_cpucp_mbox(qcom-cpucp-mbox.c:175-176); each GENI I2C IRQ carries its device name,a80000.i2cfor the touchscreen bus (i2c-qcom-geni.c:1201-1202). The SCMI research doc could not tell lost interrupts from slow firmware (its section 1); an IRQ count againstsent_ok/response_okand the late replies does.- Pins,
/sys/kernel/debug/gpio.msm_gpio_dbg_show_oneprints direction, level, function, drive and pull per pin (pinctrl-msm.c:654-716) and skipsgpio-reserved-ranges(:683), so reading it does not touch secure pins. It reads TLMM registers only. Pins: SDA/SCLgpio32/gpio33(hamoa.dtsi:6180-6186, 2 mA,bias-pull-up), IRQgpio51, reset-ngpio48and report-enablegpio89(bothoutput-low, as in the stock overlayentry01-overlay.dts:1085-1094), VDD enablegpio88. - Regulator.
vreg_touchscreen_3p3isregulator-boot-on, notalways-on(dts/x1e80100-google-mica.dts:336-352). Its state tells whether the panel is powered when the raw read runs. - Clock table.
geni_i2c_bus_setupprintsclk_div: 2 means the 19.2 MHz table{2, 5, 11, 22}(i2c-qcom-geni.c:188), 4 the 32 MHz table{4, 3, 9, 19}(:196), which the driver avoids unless the clock already runs at 32 MHz, because it “breaks I2C transfers on X1E80100 CRD” (:207-218). The formula comment (:174-185) gives, for 19.2 MHz at 400 kHz: t_high = 0.52 µs, t_low = 1.15 µs, SCL ≈ 436 kHz. The comment says the firmware adds cycles beyond that formula. Inference: by the formula alone, both periods sit under the Fast-mode minimums (t_low 1.3 µs, t_high 0.6 µs, NXP UM10204; values from memory, not re-read today). The stock side (Qualcomm’s vendor driver) needs a device-side read; that is out of scope here.
4. Busybox I2C applets and a raw descriptor read
The applets are there. The image’s busybox is Debian busybox-static:arm64 1:1.38.0-3+b1
(scripts/package-in-container.sh:9). ~/.cache/gbook/out/ held no initramfs during this research, so the same
package was fetched from deb.debian.org (sha256 968d1aa8f579fa1ac59c26afa365454369e13cf29848e6400b50028fed0ffda0;
compare with manifest.txt on the next packaging) and listed under qemu-aarch64-static: i2cdetect i2cdump i2cget i2cset i2ctransfer, plus devmem, xxd, hexdump, nc, timeout. CONFIG_I2C_CHARDEV=y (.config:3896) and
devtmpfs give /dev/i2c-N.
Which applet. Only i2ctransfer (i2ctransfer [-fay] I2CBUS {rLEN[@ADDR] | wLEN[@ADDR] DATA…}…) can issue
HID-over-I2C’s 16-bit register write and read in one transfer with a repeated start, as
i2c_hid_read_register does (i2c-hid-core.c:200-243). i2cget and i2cdump use 8-bit SMBus commands, wrong
for this device. i2cdetect probes every address with SMBus quick writes by default (-q), and i2cset writes;
use neither. i2cdetect -l (list adapters, no bus traffic) is safe.
When the read is meaningful. If i2c-hid’s own probe fails, it powers the panel down (i2c-hid-core.c:1320-1324),
and the regulator turns off when its last user disables it (regulator/core.c:3269-3270): a raw read would then
only NACK. In -17 and -18 the failure (usage index exceeded, hid-core.c:266) came later, in the HID driver’s
report parse; i2c-hid stayed bound, its IRQ kept counting, so the panel stayed powered (inference from -17’s IRQ
count 1 to 730). So run it only while /sys/bus/i2c/devices/<bus>-0010/driver exists, with -f (the address is
busy). i2c_transfer holds the adapter lock for the whole transfer (i2c-core-base.c:2346-2352), so it cannot
interleave with an i2c-hid read mid-transfer.
ts_dev=/sys/bus/i2c/devices/${ts_bus}-0010
if [ -n "$ts_bus" ] && [ -e "$ts_dev/driver" ] && [ -c "/dev/i2c-$ts_bus" ]; then
for n in 30 30 16 20 24 32; do # 30: FIFO path, as at probe; 32 and up: SE DMA (i2c-qcom-geni.c:528, i2c-core-base.c:2684)
cap ts-raw "desc r$n $(i2ctransfer -f -y "$ts_bus" w2@0x10 0x00 0x00 r$n 2>&1 | tr '\n' ' ')"
done
cap ts-raw "rdesc r64 $(i2ctransfer -f -y "$ts_bus" w2@0x10 0x21 0x00 r64 2>&1 | tr '\n' ' ')" # wReportDescRegister 0x21
else
cap ts-raw "skipped: bus=${ts_bus:-none} driver=$(basename "$(readlink "$ts_dev/driver" 2>/dev/null)" 2>/dev/null)"
fi
Safety (inference from the HID-over-I2C read path above): the two written bytes only set the register pointer, as
i2c-hid’s own descriptor fetch does; register 0x0000 here is the HID descriptor and 0x0021 the report
descriptor (the -18 dump), both read-only. Still an I2C write by an agent’s script, so it needs the owner’s OK
(CLAUDE.md). What it separates: a cut at byte 20 for every length points at the device or the bus; a cut that moves
with the length, or only on one path (FIFO vs DMA), points at the controller.
5. GENI error prints and dyndbg
geni_i2c_err(i2c-qcom-geni.c:291-313) logsdev_erronly for bus-protocol, arbitration, overrun, illegal command and the GP_IRQ errors, followed bygeni_i2c_err_misc’s register dump (:269-289:m_cmd,geni_status,geni_ios, FIFO/DMA status), whose lines aredev_dbg. NACK, abort and timeout aredev_dbgonly (:302-307) and skip the register dump even withdyndbgon. The error table is at:152-164(-ENXIOaddress NACK,-EPROTObus protocol,-EAGAINarbitration,-ETIMEDOUT).dyndbgline range235-306covers “I2C clk selected” (:235, theclk_idx) and every error line, but not:368, which prints on every SE DMA interrupt (the touchpad’s 47-byte reports) and would flood the console. Line ranges are in thedyndbggrammar (Documentation/admin-guide/dynamic-debug-howto.rst:131-142).i2c-hid-core.c:1055is the silent “nothing at this address” that -19 could not see.- Probe-time
dev_errs inqcom-geni-se.ccover clocks, interconnect, OPP and SE firmware load (:932-1649); none is a transfer diagnostic. There is no GENI debugfs at the pin.
6. Other per-boot aids
softlockup_panic=1is dead.SOFTLOCKUP_DETECTOR,HARDLOCKUP_DETECTOR,DETECT_HUNG_TASKandWQ_WATCHDOGare all off (.config:11947-11951).kernel/watchdog.c, which owns the parameter (:428), is built only withLOCKUP_DETECTOR(kernel/Makefile:105). Every netlog boot logs it under “Unknown kernel command line parameters”. Either drop it frompackage-in-container.sh:10or addCONFIG_SOFTLOCKUP_DETECTOR=y(thenDETECT_HUNG_TASKdefaults on,lib/Kconfig.debug:1229, 120 s). Neither open problem is a hang, so low priority.initcall_debug(init/main.c:795) prints two lines per initcall to the console. The same data costs nothing on the console asinitcall:initcall_start,initcall:initcall_finish(include/trace/events/initcall.h:27,48) intrace_event=. Useful only for the probe-order question from -19.- pstore/ramoops: already built in and mounted by
/init; it covers the panic and console of a failed boot. - SCMI raw mode (
ARM_SCMI_RAW_MODE_SUPPORT,arm_scmi/Kconfig:33-46) injects messages to the firmware and inhibits the normal stack: a write path, not a diagnostic for this project. GENERIC_IRQ_DEBUGFS(off,.config:75) would show per-IRQ trigger and state;/proc/interruptscovers the question for now.
7. morluto/rea: no
Fetched 2026-11-03 (GitHub page and raw README.md, HTTP 200). REA (“Reverse Engineer Anything”) is an MCP server
and CLI that gives a coding agent reverse-engineering tools: native binaries through an existing Hopper, Ghidra, IDA
or Kuna, JavaScript/Electron, .NET, Android APKs over adb, websites, packet captures, firmware images through Binwalk
or Unblob. README: “REA connects your agent to tools for inspecting native binaries, JavaScript and Electron apps,
.NET assemblies, and websites.” It is a Node.js package (rea-agents, Node 22.x/24.x/26.12.1+), MIT, run on the
host (npx rea-agents setup). The last-commit date did not render in the fetch.
Fit with this setup:
- It runs on a workstation, not on the target: it needs Node.js and npm, which an aarch64 busybox initramfs has
not, and it has no I2C, SCMI, device-tree, serial-console or kernel-tracing feature. Its only Linux-kernel feature
is static inspection of
.komodules; this kernel builds none. - Both open problems sit in the running kernel’s view of hardware (reply latency, bytes on the wire); a static analyser of binaries does not observe either.
- Where it could help at all: reading a stock vendor binary on the build host (for example the vendor GENI I2C driver’s timing table) through Ghidra. That runs on the build host, and ADR 0005 keeps vendor content out of git.
Verdict: no for the boot loop. A maybe, later and off-device, only if the project decides to reverse-engineer a stock vendor binary; nothing in its README shows aarch64 kernel-module support.
Update: the wake trace (2026-11-03, 10:50 PM ET)
For the late SCMI completion IRQ (scmi-2026-11-03.md, attempts -28 to -30):
GBOOK_CMDLINE_EXTRA="trace_event=irq:irq_handler_entry,power:cpu_idle,timer:hrtimer_expire_entry". A second
trace_event= appends to the first (setup_trace_event(), trace_events.c:4644-4649 at a804c57). When
power:cpu_idle is on the command line, /init’s probe-time capture also keeps CPU 0’s cpu_idle,
irq_handler_entry and hrtimer_expire_entry lines (the end copy does not, to keep the netlog short).
attempt.sh trace wake [ms] prints, per slow reply, CPU 0’s idle state before the apss_cpucp_mbox interrupt and what
woke it. cpu_idle state= is the cpuidle state index on CPU 0, not the cluster or system domain state.
- Attempt -31 (10:58 PM ET): with the default ring (
TRACE_BUF_SIZE_DEFAULT1441792 bytes per CPU,trace.c:740) the probe snapshot kept 604908 of 1255636 events and lost the SCMI probe. The wake trace needstrace_buf_size=8Mtoo (per CPU,set_buf_size(),trace.c:1020; 96 MB over 12 CPUs).
Update: the fast sweep (2026-11-04, 9:21 PM EST)
A boot whose stick carries ROOT takes /init’s fast sweep (task #24, .scratch/m3-rootfs/issues/14-fast-sweep.md). It
keeps the trace: the same trace_event= ring, the probe copy from trace_snapshot (now at about 32 s, 586 lines kept
on 2026-11-04-24, since no touch window floods the ring) and the end copy in the netlog, so attempt.sh trace scmi and
trace i2c read a fast boot as before. Gone on the fast sweep: the *-dmesg captures (the console has the printk
lines live, the netlog’s dmesg section has them all), the prompts and settles. GBOOK_CMDLINE_EXTRA=gbook.sweep=full
brings the M2 sweep back on a ROOT stick.
Debugging the sound card: ASoC, SoundWire and audioreach tools
Research pass by an agent on 2026-11-04 (11:55 PM EST) (from date in the writing command), for M3 ticket 16 (the left amplifiers never
enumerate; docs/recon/audio-2026-11-04.md). Sources are cited per item: the kernel at a804c57 (K), the alsa-utils
1.2.16 man pages, alsa-ucm-conf master. Items marked inferred or unverified are that. The first lead (a UCM PA Volume 12
cset above the driver’s cap) was checked on boot -28: every PA Volume read 6 and alsaucm exited 0, so it is not the
cause of the silent pair; the enumerator registers are (the recon note, “The speaker test”).
Sources: kernel /home/the owner/.cache/gbook/linux-a804c57 (K), alsa-utils 1.2.16 man pages, alsa-ucm-conf master (installed 1.2.16.1 may differ; check /usr/share/alsa/ucm2 on the device).
Lead to run first (inferred, verify on device)
- Master
ucm2/codecs/wsa884x/four-speakers/SpeakerSeq.confsetscset "name='WooferLeft PA Volume' 12"(and the other three), plus DAC/COMP/BOOST/PBR=1, VISENSE=0, WSA MODE=0 per amp.K/sound/soc/qcom/x1e80100.c:46-51caps all six* PA Volumeat 6 (0 dB onpa_gain-9 dB/1.5 dB steps, wsa884x.c:1732) andWSA[2] WSA_RX0/1 Digital Volumeat 81 (-3 dB).SOC_SINGLE_RANGE_TLVputs throughsnd_soc_put_volsw(soc.h:94), whosesoc_mixer_valid_ctl(soc-ops.c:155) returns -EINVAL aboveplatform_max, so a cset of 12 fails. What alsaucm does after a failed cset, and the installed file’s ordering (per amp would leave later amps withoutDAC Switch; per control would not) are unverified. Check:grep -rn 'PA Volume' /usr/share/alsa/ucm2/codecs/wsa884x/(value and order);alsaucm -n -b -withopen <name from alsaucm listcards>,set _verb HiFi,set _enadev Speaker,list1 _enadevs, watching stderr;amixer -c0 contents | grep -A2 -E 'PA Volume|DAC Switch'for per-amp values and the reported max.
ASoC debugfs (K/sound/soc/soc-core.c:135-216, soc-dapm.c:2470-2600, soc-pcm.c:275-300)
/sys/kernel/debug/asoc/components,.../dais: every registered component and DAI name./sys/kernel/debug/asoc/<card>/<component>/dapm/<widget>:<name>: On|Off [(forced)] in N out N(w->power; N = connected endpoints),stream <name> active|inactive,widget-type, then onein|out "<path>" "<peer widget>" "<peer component>"per path withconnecttrue: a missing expected line means a disconnected route..../dapm/bias_levelper component and card. Diff an audible amp’s widgets against a silent one’s (prefix from DTsound-name-prefix, soc-core.c:1418)./sys/kernel/debug/asoc/<card>/<FE dai_link>/state: DPCM state with each connected BE’s negotiated Hardware Params (channels, rate): the only view of whatWSA_CODEC_DMA_RX_0runs under theMultiMedia2FE./proc/asound/card0/pcm1p/sub0/hw_paramsis the FE side only (designs/procfile.rst:130)./sys/kernel/debug/asoc/dapm_pop_time(ms, soc-core.c:216): non-zero logs every DAPM widget power event to dmesg (soc-dapm.c:173), cheaper than ftrace./sys/kernel/debug/regmap/<sdw dev>/registers,cache_only: one per amp (devm_regmap_init_sdw, wsa884x.c:2103); diff a silent vs audible amp.cache_onlyis 1 until ATTACHED and again on runtime suspend (:1548-1556, :2139), so read it during playback.
SoundWire (K/drivers/soundwire/debugfs.c, sysfs_slave.c:222-246, qcom.c)
/sys/kernel/debug/soundwire/master-<ctrl>-<link>/<sdw:…>/registers:Register Valuetable (DP0, SCP, DP1..14 banks 0/1;XX= read failed; the read resumes the device). Compare DPn CHANNELEN of quiet amps./sys/bus/soundwire/devices/sdw:*/status(Unattached|Attached|Alert) anddevice_number. Caveat (2026-11-04/05): that status follows the driver’s last enumeration event, not the hardware; it readUNATTACHEDwhile two units played andAttachedafter they were lost. The masters’ enumerator registers decoded byscripts/swrm_enum.py(orscripts/audio-state.sh) are the truth.no slave alert found.spurious interrupt(qcom.c:739):SLAVE_PEND_IRQfired but no device inSWRM_MCP_SLV_STATUSis in Alert; ratelimited, harmless by itself.SWR bus clsh detected(qcom.c:760):MASTER_CLASH_DET; the driver masks that interrupt after the first hit, so it logs once; follow up withstatus/registers.qcom,port-mapping(bindings/sound/qcom,wsa8840.yaml:35, wsa884x.c:2089, qcom.c:1179): 6 u32s in slave port order (DAC, COMP, BOOST, PBR, VISENSE, CPS; wsa884x.c:798-805), each the controller port whoseqcom,ports-*config that codec port takes; absent = positional fallback. A wrong map silences amps without errors.
Tracing and dyndbg (K/include/trace/events/asoc.h, Documentation/trace/events.rst:45-56, admin-guide/dynamic-debug-howto.rst:249-269, kernel-parameters.txt:7684)
- Boot:
trace_event=asoc:*(snd_soc_dapm_widget_power,snd_soc_dapm_path,snd_soc_dapm_connected, …); runtimeecho 'asoc:*' > /sys/kernel/tracing/set_event, read/sys/kernel/tracing/trace.snd_pcm:*needsCONFIG_SND_DEBUG(designs/tracepoints.rst). SoundWire has no tracepoints. dyndbg="file drivers/soundwire/bus.c +p; file drivers/soundwire/qcom.c +p; file sound/soc/codecs/wsa884x.c +p"on the command line, or the same query into/proc/dynamic_debug/control. Yield at a804c57:bus.cis rich (enumeration, dev_num, PING status, clock stop);qcom.chas 3 sites,wsa884x.c2;lpass-wsa-macro.c,q6apm.c,topology.c,audioreach.chave nodev_dbg, so dyndbg there shows nothing.
Topology (K/sound/soc/qcom/qdsp6/topology.c:1415-1445)
- Loaded from
qcom/<card->driver_name>/<card->name>-tplg.bin(q6apm.c:841); no debugfs of its own; its widgets/routes appear in the q6apm component’sdapm/. Decode offline:alsatplg -d <bin> -o <out.conf>.
alsa-utils (man pages 1.2.16)
speaker-test -D hw:0,1 -c 4 -t wav -s Nfor N 1-4 (-sone channel;-t wavspeaks the channel name) and-m FL,FR,...; the card’s 4-channel map is FL, LB, FR, RB by index (x1e80100.c:110-115, DSP positions; physical amp per index unverified). Read the pattern: both silent amps on one bus (WSAvsWSA2) points at bus config (ports, port-mapping, BE params); one woofer plus one tweeter points at per-amp control state.aplay -vprints the hw/sw params actually set;--dump-hw-paramslists the device capabilities.amixer -c0 contents(all controls, values, limits),cget; from a script, address a control whose name has spaces bynumid=N(amixer cset name="WSA WSA_RX0 Digital Volume" 0found nothing on 2026-11-04 and the mute test was void;numid=21worked);alsaucm -c <name> list _verbs,list _devices/HiFi,list1 _enadevs,set _enadev Speaker(batch-n -b -)./usr/sbin/alsa-info --no-upload --stdout --with-amixer(Debian path): one-shot snapshot of cards, mixer, dmesg,/proc/asound.
WSA884x specifics (K/sound/soc/codecs/wsa884x.c)
- hwmon
wsa884xper amp (/sys/class/hwmon/hwmon*/name,temp1_inputmilli-degrees, :2114); measured only with the PA off, cached value while playing (:1873): four instances prove enumeration, not playback. - Controls:
PA Volume(0-31 raw, capped 6),WSA MODE(Speaker/Receiver),DAC|COMP|BOOST|PBR|VISENSE|CPS Switch(:1740-1750, each enables one SoundWire port; VISENSE = V/I sense). DAPM map: onlySPKR <- IN(:1754).
The downstream driver (research pass, 2026-11-05 (12:27 AM EST))
Source: CodeLinaro audio-kernel-ar, branch audio-kernel.lnx.6.0.r67-rel (no x1e80100 branch exists there; 6.12.r4 has the
same probe logic): https://git.codelinaro.org/clo/la/platform/vendor/qcom/opensource/audio-kernel-ar/-/blob/audio-kernel.lnx.6.0.r67-rel/<path>.
Read for ticket 16 against the stock log dumps/firstboot/2026-11-05-stock-lastkmsg-swr.txt.
wsa884x_swr_probe(asoc/codecs/wsa884x/wsa884x.c:1989-2045): supplies on, thespkr-sd-n-nodeactive state, a 5 ms sleep “for soundwire auto enumeration”, thenswr_get_logical_dev_num(); a failure isdev_dbgonly and the probe defers.VPHX EN Status(wsa884x_validate_dt_configuration_params, :1130-1138) is a codec register read over SoundWire (WSA884X_VPHX_SYS_EN_STATUS,wsa884x-registers.h:323) from the ASoC component probe, which runs only after the device number was assigned: a VPHX line for0220proves that amplifier enumerated on stock; its “devnum assigned” line fell outside the pstore window.swrm_get_logical_dev_num(soc/swr-mstr-ctrl.c:2860-2931): reads the hardware enumerator table (0x530/0x534 + 8*i) fori = 1..qcom,swr-num-dev(2 per bus on stock mica,entry00-base.dts:17647,17716), matchesaddrexactly, requires theSWRM_MCP_SLV_STATUSslot to read attached or alert; no retry inside; “device 0x%llx is not ready” on failure (dev_err).AUTO_ENUM_FAILED(swr-mstr-ctrl.c:2675-2687): the downstream IRQ handler writesSWRM_ENUMERATOR_CFG0, waits forSWRM_ENUMERATOR_STATUSidle, writes 1 (re-arms the hardware enumerator). Mainlinedrivers/soundwire/qcom.cata804c57only defines the bit (:62).CHANGE_ENUM_SLAVE_STATUSgoes toswrm_process_change_enum_slave_status(:2431-2500). Bus clash handling matches mainline (mask the interrupt after the first hit).- Unique id: downstream
reg = <0x4 0x2170220>/0x2170221is the same 48-bit id mainline byte-swaps out of the two registers; the last digit isunique_id0 or 1 (speaker@0,0/@0,1). Neither driver handles two slaves with equal ids; noSWR_IDstrap code exists inwsa884x.c. Qualcomm’s pineapple reference pairs the shutdown pin by unique id (wsa_spkr_en02for both0220s,wsa_spkr_en13for both0221s;audio-devicetreepineapple-audio-overlay.dtsi:218/237/296/315), where stock mica pairs it by bus (entry00-base.dts:17662/17679/17731/17748), asdts/diag/sound.dtsidoes. A disabledswr_haptics@f0170220sits on stock swr3 (:17615-17621).
Pinning and reproducing the M3 rootfs when snapshot.debian.org throttles
Researched 2026-11-04 (written 3:42 PM ET) on the build host. Sources: the snapshot.debian.org front page and the API file in its
salsa repository, metasnap.debian.net, the Debian unstable man pages (mmdebstrap, debsnap, debootsnap, debrebuild,
apt-get, apt.conf, apt_preferences, sources.list, dpkg-query, dpkg-scanpackages, apt-ftparchive, diffoscope, mke2fs),
the mmdebstrap script at gitlab.mister-muffin.de, apt and devscripts source on salsa, e2fsprogs source (the
tytso/e2fsprogs GitHub mirror) and release notes, reproducible-builds.org, the Isar, Debusine, live-build, debos, ELBE
and meta-debian documentation, the isar-users and debian-devel lists, two live HTTP reads at 3:33 PM ET [20:33 UTC],
and tar --help, strings and getent on the build host. Nothing was installed, no container ran, the Googlebook was not
touched. Inference marks what no source states. Script line numbers are from scripts/build-rootfs-in-container.sh
as read while this note was written; the file was being edited during this research, so re-check them before quoting.
Context: ADR 0012 decision 1 builds Debian sid arm64 “from one snapshot.debian.org
timestamp, with a lock list and a local .deb cache”. scripts/build-rootfs.sh and scripts/build-rootfs-in-container.sh
implement it: mmdebstrap --variant=apt against http://snapshot.debian.org/archive/debian/$ts/ (line 14), a .deb
cache synced in and out of /var/cache/apt/archives (lines 33, 50), lock.txt as pkg:arch=version (line 35), and
mke2fs -d rootfs.tar with -U and hash_seed from the epoch (lines 73-74). On 2026-11-04 the owner saw both
snapshot hosts answer 503/504 and deliver 17 packages in 14 minutes (owner’s report, this session). This note asks what
the primary sources say about the service’s limits, the alternatives, lock lists, and the reproducibility of the output.
1. snapshot.debian.org: limits, the Cloudflare name, other services, debsnap
What Debian publishes. The front page (snapshot.debian.org, read 2026-11-04) gives
the usage (deb [check-valid-until=no] https://snapshot.debian.org/archive/debian/<yyyymmddThhmmssZ>/ <suite> main;
“Valid date formats are yyyymmddThhmmssZ or simply yyyymmdd”) and, in its 2024-10-08 news entry, the only
statement on throttling: “The Netfilter rate limiting has been disabled.” “It will be replaced by rate limiting on the
HTTP layer, which should be easier for clients to deal with.” The same entry says the HTTP interface is served from the
new Glasklar Teknik server and a LeaseWeb VM, and that the archive holds 172 TB. No number, window or policy is
published; there is no FAQ; bugs go to the snapshot.debian.org pseudo-package and questions to the
debian-snapshot list. The machine interface
(API file, read 2026-11-04) documents
/mr/package/<src>/<ver>/binfiles/<bin>/<binver>, /mr/binary/<bin>/, /file/<hash> and /mr/timestamp and states
no usage policy. The repository README says of the
web application: “It’s not really built to scale well, but for now it’s one way to access the data.”
The CDN. Philipp Kern, replying on debian-devel to a “503 Response object too large” report (lists.debian.org/debian-devel/2025/06/msg00147.html, quoted in Christof Warlich’s message of 2025-06-11): “Generally Fastly requires to turn on segmented caching to serve larger objects”, “our account still supports 2 GB for requests without Streaming Miss (snapshot.d.o) and 5 GB for requests with Streaming Miss (deb.d.o)”. So snapshot.debian.org is served through Fastly.
snapshot-cloudflare.debian.org. No Debian document read for this note names it: not the front page, the API
file, the README, nor the debootsnap and debrebuild scripts. Live read at 3:33 PM ET [20:33 UTC], one GET per host of
/archive/debian/20261104T000000Z/dists/sid/Release:
| Host (DNS at 3:28 PM ET) | Answer | Headers |
|---|---|---|
| snapshot.debian.org (151.101.2.132, .66.132, .130.132, .194.132) | 302 to /file/8528f033…/Release | server: Apache, via: 1.1 varnish (Varnish/7.1), 1.1 varnish, x-served-by: cache-mia-kfll1870075-MIA, x-cache: MISS |
| snapshot-cloudflare.debian.org (104.20.45.122, 172.66.150.28) | 503 | server: cloudflare, cf-cache-status: BYPASS, retry-after: 0, and also via: 1.1 varnish, x-served-by: cache-mia-kmia1760085-MIA, x-cache: MISS |
Inference from the headers: the Cloudflare name is a second CDN front that forwards to the same Varnish/Fastly chain
(the x-served-by cache node naming is identical), not a separate copy of the archive. Its 503 came from behind
Cloudflare, so switching hosts gains nothing when the backend is the bottleneck. The only statement on its history is
third-party: Felix Moessbauer (Siemens) on isar-users, 2024-09-03: “Please note, that snapshot-cloudflare stopped
mirroring in June.” and “there we have massive problems with rate limiting”
(groups.google.com/g/isar-users/c/LSwbPYxSNCE).
An isar-cip-core patch of April 2024 still used a snapshot-cloudflare.debian.org/archive/debian/20240411T000000Z URL
(lore.kernel.org). Note that at 3:33 PM
the main host answered while the owner’s reads earlier in the day failed: the state changes within a day.
How apt retries. apt’s acquire-worker.cc (salsa main, read 2026-11-04,
source): Acquire::Retries defaults to
3 (acquire-item.cc), Acquire::Retries::Delay defaults to true, the delay is 1 << Iter seconds capped by
Acquire::Retries::Delay::Maximum (default 30), and Retry-After is honoured only when the method reports
TooManyRequests (HTTP 429) and Acquire::Retries::HandleRetryAfter is true. The code comments: “Usually, all
requests run into the rate limit at the same time. Distribute the retries to avoid hitting the limit again.” A 503 gets
the plain backoff, and today’s 503 carried retry-after: 0 anyway. apt.conf(5)
documents only Acquire::Retries (“Number of retries to perform. If this is non-zero APT will retry failed files the
given number of times.”); the Delay options are undocumented there. Isar’s response to the same problem
(isar-users patch, Felix Moessbauer, 2024-11-06):
“The snapshot mirrors apply strict rate limiting, making it impossible to use them with a default apt configuration.”
It sets Acquire::Retries to 10 and Acquire::Retries::Delay::Maximum to 600 whenever ISAR_USE_APT_SNAPSHOT is on,
3 and unset otherwise. debrebuild sets Acquire::Retries "5" and Acquire::http::Dl-Limit "1000" and notes “Release
files from snapshots.d.o have often expired by the time we fetch them”
(debrebuild.pl).
Other services.
snapshot.notset.franddebian.notset.frresolve to no address today (getent ahostsv4, 3:28 PM ET). The software behind them, fepitre/snapshot-mirror, calls itself a “Debian snapshot service written from scratch” that “currently usessnapshot.debian.orgset of timestamps and data for provisioning the service”, serves “A partial timestamps set” atsnapshot.notset.fr, and limits clients in nginx (limit_conn conn_limit_per_ip 20; limit_rate 10m;) “for allowing every Debian rebuilder infrastructure to scale their actual builders”. The repository page shows no archive notice and no commit date (read 2026-11-04).snapshot-mlm-01.debian.orgwas offered as a workaround endpoint during the June 2024 hardware migration (bug #1072675: “We’re currently migrating to new hardware. Not losing any data.”, Linus Nordberg, 2024-06-12). It is a Debian machine, not documented for general use (inference: not a pin target).- metasnap.debian.net “is an addition to snapshot.d.o”: for each suite, architecture
and binary package it records “which version of it was seen in which snapshot.d.o timestamps in that suite”; its
pkgs=endpoint takesname=versionpairs and returns a minimal set of timestamps covering them (POST for lists over 32768 bytes); timestamps are indexed only once they are 12 hours old. Run by Johannes Schauer Marin Rodrigues. - Debusine serves its own repositories at
/{scope}/{workspace}/{timestamp}, “compatible with snapshot.debian.org and snapshot.ubuntu.com” (explanation/package-repositories). That is Freexian’s archive, not a mirror of Debian’s.
debsnap (debsnap(1)) “is a tool to help with
retrieving snapshots of old packages from a daily archive repository”: by default every version of one source package,
--binary and -a <arch> for binaries, --first/--last for a range, DEBSNAP_BASE_URL defaulting to
https://snapshot.debian.org, exit status 2 when some files “are no longer available on the snapshot mirror”. It is a
per-package fetcher against the same service, so it fills a cache but does not avoid the limit.
debootsnap (debootsnap(1) and the
script) “Combines debootstrap and
snapshot.debian.org to create a chroot tarball with exact package versions from the past”. Its input is a list
pkg1:arch=ver1,pkg2:arch=ver2 (or --buildinfo), produced on the source machine with
dpkg-query --showformat '${binary:Package}=${Version}\n' --show. The script asks metasnap for the timestamps, writes
one deb line per timestamp, downloads each name:arch=version with apt-get download through a local caching proxy
(devscripts.proxy.setupcache, http_proxy=http://127.0.0.1:<port>, Acquire::Retries "5"), builds a local flat
repository served as deb [trusted=yes] http://localhost:<port>/ ./ with a dummy package whose Depends: names every
pkg:arch (= ver), and runs mmdebstrap --variant=essential --include=debootsnap-dummy --format=tar. This is the
pattern reproduce.debian.net runs at scale: “rebuilderd calls debrebuild that calls
debootsnap, mmdebstrap and finally sbuild”.
2. Local mirrors and caches; how other projects pin
mmdebstrap’s own tools (mmdebstrap(1), 1.6.0-5, read 2026-11-04):
sync-in <outside> <inside>: “Recursively copy everything inside pathoutside into pathinside inside the chroot” “without deleting anything from pathinside but overwriting content as necessary”;sync-outthe reverse. EXAMPLES: “If, for some reason, you cannot use a caching proxy like apt-cacher or apt-cacher-ng, you can use the sync-in and sync-out special hooks to synchronize a directory outside the chroot with /var/cache/apt/archives inside the chroot”, and “Instead of copying potentially large amounts of data with sync-in you can also use a bind-mount in combination with a file:// mirror”.- The essential step “Removes all packages downloaded in the download step, except those which were present in
/var/cache/apt/archives/ before (if any). This can be disabled using –skip=essential/unlink.” The script’s
--skip=essential/unlink(line 53) therefore keeps the newly downloaded.debs for thesync-out(inference from the two sentences; the man page does not spell out the combination). --hook-dir=/usr/share/mmdebstrap/hooks/file-mirror-automount: “A hook that automatically makes the contents of file:// mirrors as well as .deb packages given with –include available inside the chroot”; “This hook takes care of copying all relevant file to their correct locations and cleans up those files at the end.”--includeaccepts “apt features like pkg/suite, pkg=version, pkg-” and.debpaths, with the caveat “Since apt (outside the chroot) passes paths to dpkg (on the inside) verbatim, you have to make the .deb package available under the same path inside the chroot as well”.copy://is not mentioned.--aptoptvalues are “permanently added to /etc/apt/apt.conf.d/99mmdebstrap inside the chroot”; the man page’s own example is--aptopt='Acquire::Check-Valid-Until "false"', “necessary for allowing old timestamps from snapshot.debian.org”. So line 53’s option, and any retry option added the same way, stays in the rootfs.--simulate: “Only the package cache is initialized but no binary packages are downloaded or installed”, “No hooks are executed”, and for a tarball “no output is produced” (ticket 02 hit this).
What the cache does not cover. sync-in fills /var/cache/apt/archives, apt’s download cache
(apt.conf(5): Dir::Cache::archives is “the location to
place downloaded archives”). apt still needs the mirror’s InRelease and Packages indexes, so a snapshot that answers
503 blocks the build even with every .deb cached (inference: the man page describes only what the hooks copy). The
way round, which debootsnap takes, is a local flat repository:
- DebianRepository/Format: “A flat repository does not use the dists
hierarchy of directories” and “places meta index and indices directly into the archive root”; the sources line is
deb uri directory/. sources.list(5): the “suite must end with a slash (/)”; “The file scheme allows an arbitrary directory in the file system to be considered an archive”;copyis the same but copies into the cache;trusted=yesmakes apt accept the source without authentication, which the page says to use only in local, trusted contexts. - dpkg-scanpackages(1) “creates a Packages
file”;
-mincludes every version found; “apt(8) ignores uncompressed Packages files except on local access (i.e. file:// sources)”. apt-ftparchive(1) is “a superset of the dpkg-scanpackages(1) program”; itsreleasecommand writes a Release file “containing (by default) an MD5, SHA1, SHA256 and SHA512 digest” withAPT::FTPArchive::Release::{Origin,Label,Suite,Codename,Valid-Until, Acquire-By-Hash,...}. Neither page covers signing.
apt-cacher-ng (user manual, sections as numbered
there): “a caching proxy for software packages which are downloaded by Unix/Linux system distribution mechanisms from
mirror servers accessible via HTTP”; clients use Acquire::http::proxy "http://CacheServerIp:3142"; or put the mirror
host after the cache address in the URL (3.2); Remap-debrep merges mirrors that are “considered equal” (4.3); “The
internal cache directory tree follows the URL requests from the clients unless modified by Remapping rules” (4.3.2),
so each snapshot timestamp is its own tree (inference); existing files go into the _import directory (8.1);
expiration removes files that are “not referenced in any index file” after a grace period (ExTreshold, 7.1, 9.3).
An Offlinemode setting was not found in the pages read (unverified). Isar users measured “~12min” to “~7min” for
four images with it (Alexander Heinisch, isar-users, 2024-08-22, thread above). Trade-off: a daemon and its expiration
rules, for a benefit the sync-in cache already gives a single-image project.
aptly (overview, mirror create):
“aptly’s goal is to establish repeatability and controlled changes in package environments”; a mirror “consists of
metadata, list of packages and package files”, a snapshot is an “immutable list of packages”;
aptly mirror create <name> <archive url> <distribution> [<component1> ...] with -architectures, -filter=<query>
and -filter-with-deps; flat repositories take ./ as distribution. A mirror of one snapshot timestamp filtered to
rootfs/packages.txt with dependencies would be a self-hosted pin, at the cost of a Go tool and its database.
reprepro (reprepro(1)) “is a tool to manage a
repository of Debian packages”, filled by includedeb or by update from conf/updates rules (Method is an apt
URI, FilterFormula selects packages). Whether current reprepro keeps more than one version per package per
distribution (a Limit field) was not found in the man page text read: unverified.
How established projects pin (each from its own documentation, read 2026-11-04):
- Debian reproducible builds: the debootsnap chain above;
.buildinfofiles from buildinfos.debian.net, exact versions from metasnap and snapshot (reproduce.debian.net). - Debusine: timestamp URLs as above; “by-hash files under dists/, and all files resulting from a timestamp-specific
query are immutable: they may be removed, but they won’t be replaced with different contents at the same URL”, served
with
Cache-Control: max-age=31536000; clients pin by putting the snapshot ID in the repository URL, and aSnapshotsfield in generated Release files is planned. Its MmDebstrap task turnsbootstrap_repositoriesinto a sources.list “fed to mmdebstrap as input”,extra_packagesinto--include. - live-build (manual 8.1-8.4):
--mirror-bootstrap,--mirror-chroot,--mirror-binary*; pinning throughconfig/archives/*.prefandconfig/apt/preferences; local.debs inconfig/packages.chroot/(“automatically installed into the live system during build”);--apt-optionspasses options through. - debos (actions): the
debootstrapaction hassuite,mirror,variant,keyring-package,keyring-file,check-gpg,merged-usr; theaptactionpackages,recommends,unauthenticated,update. No snapshot, pin or cache field. - ELBE (schema reference):
mirrorwith aprimary_hostand aurl-listof extra repositories; eachpkghasversion,pin(“prefer the defined version of the debian package”) andmd5/sha256attributes. No snapshot mention. - Isar (user manual,
debian-common.conf):
ISAR_USE_APT_SNAPSHOT“Use a frozen apt snapshot instead of the live mirror”,ISAR_APT_SNAPSHOT_DATEin20240702T082400Zform, the timestamp “Automatically derived from SOURCE_DATE_EPOCH if not overwritten”; the premirror rewritesdeb.debian.org/(.*)tosnapshot.debian.org/archive/\1/<date>; base-apt caches “upstream debian packages to reduce time for further downloads and to be able to work offline” (ISAR_USE_CACHED_BASE_REPO; “By default, the local caching repo is not gpg signed”); retries 10 / 600 s when snapshots are on. - meta-debian (README): poky recipes for “cross-building GNU/Linux images using Debian source packages”; a source build, not a binary pin; no snapshot text.
3. Lock lists
pkg=version. apt-get(8): “A specific version of a
package can be selected for installation by following the package name with an equals and the version of the package to
select.” “Both of the version selection mechanisms can downgrade packages and must be used with care.” apt parses the
suffix at the last / or = (cacheset.cc,
find_last_of("/=")) and fails with Version '%s' for '%s' was not found (canNotGetVerFromVersionNumber) or
Release '%s' for '%s' was not found. Against a snapshot timestamp the index does not move (inference from the service’s
purpose; the front page’s news entries describe imports of the archive at each timestamp), so the version is there as
long as the service answers; against GBOOK_ROOTFS_MIRROR=http://deb.debian.org/debian (build-rootfs.sh line 10) sid
moves daily (live read: sid’s Release is dated Wed, 04 Nov 2026 14:14:42 UTC, Valid-Until seven days later) and
“was not found” is the failure mode for any pinned version that has been superseded. mmdebstrap passes pkg=version
through --include (section 2), and debootsnap avoids the solver moving a version by making every entry a
pkg:arch (= ver) dependency of one dummy package.
apt preferences (apt_preferences(5)):
P >= 1000 “causes a version to be installed even if this constitutes a downgrade of the package”, 500 <= P < 990
“unless there is a version available belonging to the target release”, P < 0 “prevents the version from being
installed”, P = 0 “has undefined behaviour”; Pin: version 5.32* with Pin-Priority: 1001 is the page’s example;
“the first specific-form record matching an available package version determines the priority”, so “always list
wild-card pins first, so later specific pins override it”. A 300-stanza preferences file is a second way to lock; the
--include list is shorter and is what debootsnap uses.
apt’s Snapshot: option. sources.list(5):
Snapshot “allows selecting an earlier version of the archive from the snapshot service”; IDs “are usually timestamps in
the form of YYYYMMDDTHHMMSSZ”; apt.conf(5): “snapshots
can only be acquired if a URI is known from where to get them”. Live read of deb.debian.org/debian/dists/sid/Release:
the fields present are Suite, Codename, Date, Valid-Until, Acquire-By-Hash; no Snapshots field. The
timestamp in the mirror URL, as the scripts do it, stays the working form.
Verifying a rebuild. dpkg-query(1): -W lists
installed packages, default format ${binary:Package}\t${Version}\n, where binary:Package carries an architecture
qualifier only for Multi-Arch same or foreign packages; the man page states no output order. The script’s
${Package}:${Architecture}=${Version} (line 35) qualifies every line and matches debootsnap’s pkg:arch=ver input
format, so lock.txt can be fed straight back to debootsnap or, comma-joined, to --include. Two locks compare with
comm -3 after LC_ALL=C sort (which lock_check already enforces, build-rootfs.sh line 25); the manifest’s
lock_sha256 is the one-line form. metasnap’s pkgs= endpoint (section 1) can confirm which timestamps hold a lock,
once the timestamp is 12 hours old.
4. Reproducibility of the output
The specification. SOURCE_DATE_EPOCH is “A UNIX timestamp, defined as the number of seconds, excluding leap seconds, since 01 Jan 1970 00:00:00 UTC”; tools embedding a time “MUST use a timestamp no later than the value of this variable” (clamping); “Build processes MUST NOT unset this variable for child processes if it is already present”.
mmdebstrap (man page and
script, main, read 2026-11-04):
- “By setting SOURCE_DATE_EPOCH the result will be reproducible across multiple runs with the same options”; the output
“will differ if it is run on machines with differing /etc/resolv.conf and /etc/hostname contents” (copied in for
debootstrap compatibility). The script’s line 34 overwrites
/etc/hostname; nothing in the script writes or removes/etc/resolv.conf, so the tar carries the podman container’s copy, a reproducibility input the build does not control and possibly a LAN resolver address (whether mmdebstrap’s cleanup removes it was not verified). Cleanup will “Remove files that make the result unreproducible and write the empty string to /etc/machine-id”; “A tarball created in this mode [unshare] will be bit-by-bit identical to a tarball created with the root mode”. - The script extracts the essential packages chrootless “because otherwise directory creation timestamps will differ”
since “we want to create bit-by-bit identical tar output” (
setup()), and builds the tar with--sort=name,--mtime=@$mtime,--clamp-mtime,--numeric-owner,--pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime, plus--xattrsfor tar output;$mtimeis$ENV{SOURCE_DATE_EPOCH} + 0. - ext4 output: “If SOURCE_DATE_EPOCH is set, the filesystem UUID and hash_seed will be set to a UUID derived from
SOURCE_DATE_EPOCH to create reproducible images” (man page). The script derives
$uuid = create_v5_uuid(create_v5_uuid($UUID_NS_DNS, "mister-muffin.de"), $mtime)(a name-based RFC 4122 version-5 UUID, SHA-1 over namespace and name;$UUID_NS_DNS = 6ba7b810-9dad-11d1-80b4-00c04fd430c8) and runsmke2fs -q -F -o Linux -T ext4 -U $uuid -E hash_seed=$uuid -b 4096 -d - <target> <numblocks>, the block count from itsapprox_disk_usage. It sets noE2FSPROGS_FAKE_TIME: it relies on mke2fs readingSOURCE_DATE_EPOCHitself.
e2fsprogs. Release notes: 1.47.1 (2024-05-20) “Mke2fs -d
can now support an input tar file if the libarchive library is available.” and “Add support for SOURCE_DATE_EPOCH
environment variable per the specification https://reproducible-builds.org/specs/source-date-epoch”; 1.46.6
(2023-02-01) “Fix resize2fs to honor the E2FSPROGS_FAKE_TIME environment variable … to create reproducible images”;
1.47.2 (2025-01-01) gnu.translator xattrs in tar input and “Allow building e2fsprogs without libarchive-dev installed”;
1.47.3 (2025-07-08) -d copies fs-verity metadata and chattr flags; 1.47.4 (2026-03-06) “Fix mke2fs -d so it can handle
copying a file larger than 2 GiB.” mke2fs(8): “Tarball
input is only available if mke2fs was compiled with libarchive support enabled and if the libarchive shared library is
available at run-time”; -E hash_seed=: “Use the specified UUID as the seed for hashing, rather than generating a
random seed each time. Intended for use with reproducible builds.”; -U takes a UUID or clear, random, time;
-E root_owner sets the root directory’s uid:gid. The source (tytso/e2fsprogs master, read 2026-11-04):
lib/ext2fs/initialize.c:SOURCE_DATE_EPOCHis read first and setsfs->nowplusEXT2_FLAG2_USE_FAKE_TIME;E2FSPROGS_FAKE_TIMEis read only whenSOURCE_DATE_EPOCHis unset and setsfs->nowwithout the flag;s_mkfs_timeands_lastcheckcome fromext2fsP_get_time(fs).misc/create_inode.c:clamped_timereturnsfs->nowonlyif ((fs->flags2 & EXT2_FLAG2_USE_FAKE_TIME) && t > fs->now); file times are set withext2fs_inode_xtime_set(&inode, i_mtime, clamped_time(fs, st->st_mtime)); a directory input is walked withscandir(".", &dent, NULL, alphasort); hard links are matched by device and inode and added withadd_link.misc/create_inode_libarchive.c: libarchive isdlopened aslibarchive.so.13(“you need libarchive to be able to process tarballs” when missing); entries are applied in archive order (archive_read_next_headerloop), metadata througharchive_entry_statandset_inode_extra, hard links throughadd_link. So the tar’s own order and mtimes decide inode allocation and timestamps, which is why mmdebstrap sorts and clamps the tar first.misc/mke2fs.c: without-U,uuid_generate(fs->super->s_uuid); with an all-zero hash seed,uuid_generateons_hash_seed;hash_seed=is parsed withuuid_parse(“Invalid hash seed” otherwise).- Live read on the build host:
stringsofmke2fs 1.47.4 (6-Mar-2025)andlibext2fs.so(e2fsprogs 1.47.4-1) contain bothSOURCE_DATE_EPOCHandE2FSPROGS_FAKE_TIME.
What this means for lines 16 and 73-74. SOURCE_DATE_EPOCH is exported (line 16), so the
E2FSPROGS_FAKE_TIME=$epoch prefix on line 74 is never consulted (initialize.c reads SOURCE_DATE_EPOCH first); on
its own it would fix the superblock times but not clamp file mtimes (no EXT2_FLAG2_USE_FAKE_TIME). The comment above
line 73 says the UUID is derived “as mmdebstrap’s own ext4 output does”: the -U/hash_seed pairing matches, the
derivation does not (sha256 of the epoch cut into UUID shape here, a version-5 UUID over mister-muffin.de there).
Both are deterministic for one epoch; only the comment is wrong.
Tool versions float. Line 23 installs mmdebstrap and e2fsprogs from live sid at build time, and the manifest
records the versions (lines 83-84). Inference: two builds of one timestamp on different days may run different mke2fs
versions and lay the image out differently while the tar is byte-identical, so the tar is the stronger contract.
e2fsck -fn (line 76) checks consistency, not identity.
What the reproducible-builds documentation says about images
(system-images): “It is also possible to produce bit-reproducible
filesystems with recent mkfs.ext2 from e2fsprogs-1.47.1”, with -E hash_seed=<uuid> and -U <uuid> in its example; an
image that “has been created by mkfs.ext4, mounted, modified and saved as an image … would not be reproducible: the
allocation of the inodes is undefined”; general problems are filesystem timestamps and “UUID or a label which are not
set explicitly”; “clamp mtimes of files in the root filesystem to $SOURCE_DATE_EPOCH before generating the system
image”, with tar --sort=name --mtime=@$SOURCE_DATE_EPOCH --clamp-mtime in its tool table. The GNU tar manual’s
Reproducibility page answered 429 three times today; tar --help (GNU tar 1.35 on the build host) gives the semantics:
--clamp-mtime “only set time when the file is more recent than what was given with –mtime”, --sort=ORDER
“directory sorting order: none (default), name or inode”.
What to compare between two builds. In order of cost: lock.txt (comm -3), then sha256 of rootfs.tar
(content, ownership, modes, mtimes, xattrs), then sha256 of rootfs.ext4 (layout, which also depends on the
e2fsprogs version). To explain a mismatch, diffoscope
“will recursively unpack archives of many kinds”, lists “ext2/ext3/ext4/btrfs/fat filesystems” and “tape archives
(.tar)” among its formats, and --exclude-directory-metadata ignores “permissions, timestamps, xattrs etc.” when only
content matters.
Recommendations
- Add an offline mode to
scripts/build-rootfs-in-container.shthat builds a flat repository from/work/cache(apt-ftparchive packagesandrelease, ordpkg-scanpackages -m), passesdeb [trusted=yes] file:///work/cache ./as the mirror with--hook-dir=/usr/share/mmdebstrap/hooks/file-mirror-automount, and sets--includefrom the previouslock.txt(pkg:arch=versionis apt syntax). Why:sync-inspares the.debdownloads but apt still needs the snapshot’s indexes, so a 503 stops the build today even with a full cache; this is the debootsnap pattern. Trade-off: the flat repository is unsigned (the.debs were verified when first fetched;lock_sha256and the cache’s sha256s are the chain of custody), and a package missing from the cache fails with “Version … was not found”, which is the wanted behaviour. - Raise apt’s retries for the snapshot mirror with
--aptopt='Acquire::Retries "10"'and--aptopt='Acquire::Retries::Delay::Maximum "600"'(Isar’s values), only when the mirror is a snapshot host. Why: apt’s defaults are 3 retries capped at 30 s of backoff andRetry-Aftercounts only for 429, while snapshot answers 503 withretry-after: 0. Trade-off: a dead mirror takes longer to fail, and the options land in the rootfs’s/etc/apt/apt.conf.d/99mmdebstrap(harmless for a rootfs that never runs apt; a customize hook can delete the file). - Fix lines 73-74: drop
E2FSPROGS_FAKE_TIME=(dead withSOURCE_DATE_EPOCHexported) and reword the comment:-Uandhash_seedfollow mmdebstrap, the derivation is this script’s own sha256 form. Adde2fsprogs >= 1.47.1as a stated floor (tar input andSOURCE_DATE_EPOCH; 1.47.4 for files over 2 GiB). Trade-off: none. - State the reproducibility contract in ADR 0012: two builds of one timestamp must agree on
lock.txtand therootfs.tarsha256; therootfs.ext4sha256 is expected to agree only with the same e2fsprogs version (recorded in the manifest). Why: the container’s tools float with sid (line 23). Alternative: install the tools from the same snapshot timestamp. Trade-off: that puts the tool install behind the throttled service too; recording versions and comparing tars costs nothing. - When
GBOOK_ROOTFS_MIRRORpoints at deb.debian.org, feed--includefrom the lastlock.txtso drift fails loudly instead of silently moving versions, or have the manifest carry alock_changed=Nline fromcomm -3against the previous lock. Why: sid moves daily and the manifest’smirror=line alone does not say what changed. Trade-off: a pipeline test against a moved sid fails until the lock is refreshed on purpose. - Add the lock comparison to the normal build: keep the previous
lock.txtaslock.prev.txtand writelock_changed=Ninto the manifest. Why: it is the cheapest reproducibility check and the first thing to read when sha256s differ. Trade-off: none. - Record in ADR 0012’s next update that
snapshot-cloudflare.debian.orgfronts the same backend (live read, section 1), that Debian publishes no limit figures, and that the project’s answer to throttling is the cache plus the offline flat repository rather than a mirror switch; note metasnap’s 12-hour rule if debootsnap or metasnap are ever used to find timestamps. Trade-off: none. - Write a fixed
/etc/resolv.confin the customize hook, or remove it and let systemd-resolved or networkd manage it at boot. Why: mmdebstrap copies the host’s file in, the man page names it as a reason outputs differ, and the script does not touch it, so the container’s resolver address ends up in the tar. Trade-off: none for a rootfs that gets its network from/run/gbook/and DHCP. - Do not add apt-cacher-ng or aptly for this. Why: the
sync-incache plus recommendation 1 covers a single-image project; both tools add a daemon or a database. Reconsider only if several images or hosts share the cache.
Sources
- snapshot.debian.org: front page (usage, 2024-10-08 news),
API, README; bug #1072675; debian-devel 2025-06 (Fastly limits); debian-devel 2024-04 (snapshot state); live reads 2026-11-04 at 3:28 PM ET (DNS) and 3:33 PM ET [20:33 UTC] (HTTP). - metasnap.debian.net; reproduce.debian.net; fepitre/snapshot-mirror.
- isar-users: apt-cacher-ng thread (2024-07/09), retry patch (2024-11-06); isar-cip-core patch (2024-04).
- Man pages (Debian unstable): mmdebstrap(1), debsnap(1), debootsnap(1), debrebuild(1), apt-get(8), apt.conf(5), apt_preferences(5), sources.list(5), dpkg-query(1), dpkg-scanpackages(1), apt-ftparchive(1), reprepro(1), diffoscope(1), mke2fs(8).
- Source: mmdebstrap script; devscripts debootsnap, debrebuild.pl; apt cacheset.cc, acquire-worker.cc, acquire-item.cc; e2fsprogs release notes, initialize.c, create_inode.c, create_inode_libarchive.c, mke2fs.c.
- reproducible-builds.org: SOURCE_DATE_EPOCH, system images.
- Project docs: Debusine package repositories and MmDebstrap task; live-manual 8; debos actions; ELBE schema; Isar user manual, debian-common.conf; meta-debian; apt-cacher-ng manual; aptly overview, aptly mirror create; DebianRepository/Format.
- Not readable today: the GNU tar manual’s Reproducibility page (HTTP 429, three attempts);
wiki.debian.org/snapshot.debian.organdsnapshot.debian.org/news/,/doc/api/(404: the front page and the salsaAPIfile hold that content).
M4 recovery matrix: Valve’s five rows mapped onto mica’s ChromeOS mechanisms
Written 2026-11-05, 10:58 AM EST [15:58 UTC], task #32 (owner decision 2026-11-05 on
steamos-arm.md, “Decisions”, 1). Planning material for M4 (daily-drivable, Gate 3:
ADR 0010: “installed on the spare SSD, with dual-boot or a documented
return to stock”). Nothing here was run on the device; no ADR is written yet (the slot policy is an open owner
decision below). Every row carries one of three labels: proven on mica (an attempt id or log row), proven in
source (a research note’s file:line at its pin), or inference / untested. The vboot and depthcharge citations
come from fit-packaging.md and chromeos-boot-path.md, both pinned at
firmware-bluey-16702.B; this note does not reopen the source.
Answer in short
M4’s “documented return to stock” is already proven except for the physical swap. ADR 0002
puts M4 on a spare M.2 2230 SSD with the stock drive in a drawer, and ADR 0003
forbids writing the stock drive, so dual-boot on the stock SSD is excluded; “dual-boot” today is stick in, stick out
(status row “Reboot back to stock”: stock boots once the stick is out, proven 2026-11-02-2 and
2026-11-03-5). Return at M4 is therefore: power off, put the stock SSD back, power on, Boot from internal disk, and
Lock bootloader if a locked device is wanted. Lock bootloader wiping the data and relocking without the recovery
stick is proven (project log 2026-11-02, “Unlock day, relock test passed”; recon.sh read green, flash.locked=1,
locked). What is untested is what ADR 0002 already lists: does the firmware accept an unlisted drive, does a blank
drive still reach recovery and fastboot, and is the drive bound to the device. That turns M4’s gate from “design a
recovery system” into three owner-run probes (section “Test plan before M4”).
The Frame’s five rows and mica’s answers:
| Need | Steam Frame (first-party recipe) | mica today (M3, stick) | mica at M4 (spare SSD) | Label |
|---|---|---|---|---|
| Boot the previous OS, keep data | AUX + power, “Previous B” in the U-Boot menu; splctl set-state <slot> good after a verified boot | no slot picker in the dev-mode menu (Lock bootloader, Boot from internal disk, Boot from external drive); vboot picks the highest-priority kernel entry with tries or successful, deducts a try from a successful=0 entry, marks a failing entry 0/0 | the same vboot rules on the SSD: two kernel partitions, “previous” is either the automatic fallback when the new one’s tries run out, or a cgpt write from the running OS before reboot | proven in source (vboot selection, fit-packaging.md section 5); the count-down proven on mica (retro-7/08: 3 tries ran out after 3 boots, so packaging sets 15); the two-slot layout untested |
| Reset the OS, keep user data | “Repair Steam Installation” menu entry; steamos-reset-tool from Settings | none: the stick’s ROOT is rewritten whole with attempt.sh root-cmd | rewrite the inactive root partition from the build host or from the running OS, user data on its own partition | inference (nothing exists) |
| Wipe user data without booting the OS | “Erase User Data” menu entry (/efi/steamos/factory-reset) | none | a mke2fs of the data partition from /init behind a command-line flag (proposal; the M4 shape of #36) | inference (nothing exists) |
| Reinstall from USB | boot menu, USB repair stick, “Wipe Device & Install SteamOS” | the Gate 1 recovery stick: Esc + F2, then Use USB drive; erases the disk, installs 16560795 (the installed build) | the same stick; with the spare SSD installed it would install stock onto the spare drive, untested | proven on mica to the recovery screen (Gate 1, 2026-11-02, ADR 0001 update); the install itself never run; the firmware rewrite it may do is inference (runbook section 9) |
| Unbrick the boot firmware | a QDL (EDL) package, 4.1 GB | no EDL path documented; ChromeOS recovery mode is the only path, CCD stays locked, no agent writes firmware (ADR 0001, ADR 0006) | the same | proven in source that depthcharge’s fastboot has no boot and no unlock command (chromeos-boot-path.md section 4); “no EDL path” is the absence of evidence, stated as such |
| Know the state | “Purple, solid: in bootloader” (LED) | the AP console and the EC’s power lines over SuzyQ (attempt.sh watch classifies them); LED codes 3,5 and 4,3 (runbook section 8); Advanced options > Firmware log; the dev-mode warning screen | the same | proven on mica (every boot since 2026-11-02; the three release boots of 2026-11-05 had no AP console because the SuzyQ was unplugged on the build host’s side, and the netlog covered only the post-Wi-Fi window) |
1. Previous slot: what vboot does, and what the owner can do
- Selection (fit-packaging.md section 5, vboot at
firmware-bluey-16702.B):GptNextKernelEntryskips entries with neithersuccessfulnortriesand picks the highest priority (cgptlib.c:44-120); a booted entry withsuccessful=0loses a try (2load_kernel.c:768-779); withsuccessful=1tries are ignored (cgptlib.c:135-144); a failing entry is marked bad, 0/0, and the next one is tried (2load_kernel.c:742-743). - On mica today: the stick’s KERN-A is written
-S 0 -T 15 -P 15(scripts/package-in-container.sh, retro-7/08): depthcharge takes one try per boot, nothing marks the kernel successful, andsuccessful=0keeps the 0/0 mark a rejected kernel gets (“No valid image detected”, runbook section 8).attempt.sh stickprints the tries left andnewwarns at 0. Proven on mica: 3 tries ran out after 3 boots on 2026-11-04. - The Frame’s
set-state goodmaps tocgpt add -i <N> -S 1on the boot disk from the running OS after a verified boot (cgpt add -S/-T/-P,cmd_add.c:16-33). Two consequences:- The rootfs has no
cgpttoday (rootfs/packages.txt, read 2026-11-05); Debian ships it incgpt(sourcevboot-utils): a package to add for M4, to be checked withscripts/size-rootfs.shfirst. - It is a GPT write to the boot disk: M4 work under the owner’s OK, never in the stick era, where the stick is the only disk gbook writes and the build host writes it.
- The rootfs has no
- “Previous” without a menu: with two kernel partitions on the SSD (
KERN-A,KERN-B), “boot the previous OS” is either the automatic fallback (the new slot’s tries run out, or it is rejected and marked 0/0, and vboot takes the other) or an explicitcgpt add -i <old> -P <higher>before reboot. Neither is a key press at power-on. The firmware-fastbootoem set-priorityandoem set-successfulexist (chromeos-boot-path.md section 4) but need an ALink or PL27A1 bridge or a USB-Ethernet adapter over DHCP: not available with the current hardware. - The policy decision (open, grilling question 1): keep the stick’s count-down (
-S 0 -T n, a bad kernel marks itself 0/0, a good one still runs out of tries unless something writessuccessful), pmbootstrap’s installed-system values (-S 1 -T 5 -P 10, unlimited boots, no 0/0 signal), or two slots with the OS flippingsuccessfulafter a verified boot (Valve’s shape; the “verified” test is gbook’s:m3-endreached, the names heard, the audio state read).
2. Reset the OS, keep user data
Nothing exists. Today the whole ROOT partition is rewritten from the build host (attempt.sh root-cmd, root-verify), which is
the stick’s “reinstall”. At M4 the analogue is a root partition per slot (ROOT-A, ROOT-B) and a separate data
partition (home, or Valve’s var + home split), so that a root rewrite leaves the data. Valve’s update discipline
(write the inactive slot, read back, compare, only then switch) is the one attempt.sh verify already follows for the
stick; keep the ordering when KERN-B comes (steamos-arm.md, section 5, second proposal). Inference throughout; no
partition layout is decided.
3. Wipe user data without booting the OS
Nothing exists. Proposal: /init takes a command-line flag (gbook.wipe=data, name open) and runs mke2fs on the
data partition, after printing what it is about to erase on the AP console and the panel, with a countdown. It needs
mke2fs in the initramfs (busybox has mkfs.ext2 only; check before relying on it) and a way to set the flag without
a rootfs: on the stick era that is a repackage; at M4, cgpt can only set attributes, not the command line, so the
flag would live in the FIT’s command line of a dedicated “wipe” kernel entry (inference), or the wipe stays a the build host-side
operation on the swapped-out drive. Task #36 covers the read-only lock in /init; this flag is its M4 sibling.
4. Reinstall from USB: the recovery stick
- Proven: Device Recovery Utility media reaches the recovery screen with Esc + F2 and serves
16560795, the installed build (Gate 1, 2026-11-02; ADR 0001 update). A full copy is indumps/gate1/. - Never run: Use USB drive. Runbook section 9: it erases the disk and installs
16560795; the stick’s ROOT-B carrieschromeos-firmwareupdate, so a recovery may rewrite AP and EC firmware (inference). - Two hazards Valve’s recipe has no analogue for:
- the recovery keys with the image stick inserted mark KERN-A bad (runbook section 8; proven on mica, wifi-2026-11-03.md: the owner powered on with the recovery keys and the stick was rejected), so the image stick and the recovery stick are different Lexar drives and the recovery one stays out unless a recovery is intended;
- with the spare SSD installed, Use USB drive would install stock onto the spare drive (inference), which is the “unlisted drive” probe’s second half: does it install, and does the result boot.
5. Unbrick the boot firmware
No EDL path is documented for mica. The boot firmware is depthcharge (Google_Mica.16702.*); CCD stays Locked and
the agent rules forbid every firmware write (ADR 0001, ADR 0006, CLAUDE.md). ChromeOS recovery mode plus the recovery
stick’s firmware updater (inference, section 4) is the only path on record. Valve’s QDL package is an answer for a
Qualcomm device whose PBL exposes EDL; whether mica’s does is unknown and stays untested: an EDL probe would need a
cable in the data lines at power-on and is outside every gate.
6. Know the state
| Signal | What it shows | Where |
|---|---|---|
AP console over SuzyQ (-if01) | depthcharge’s screens as text, the kernel’s early log, every GBOOK: capture | attempt.sh watch, attempt.sh follow |
EC console (-if02) | the power sequence: EC: POWER ON (S5->S3), powered off (S5->G3), soft-off | attempt.sh watch (scripts/ec_power.awk) |
| LED codes | 3,5 = EC power-on timeout; 4,3 = LCD fail at the firmware stage | runbook section 8 |
| The dev-mode warning screen | the device is unlocked; its menu has the three entries | depthcharge screens.c:1801 (unlock-day ticket) |
| Advanced options > Firmware log | why depthcharge refused an image | runbook section 8 |
scripts/recon.sh on stock | verifiedbootstate, flash.locked, vbmeta.device_state | the relock test |
| The netlog | the kernel log after Wi-Fi joins, the rootfs journal | attempt.sh netlog |
The netlog is the fallback when the SuzyQ is out, not a replacement: it starts after Wi-Fi joins (about 35 s) and shows
nothing of depthcharge. The three release boots of 2026-11-05 ran that way (log row “Release bar met for
v2026.11.05”).
Test plan before M4 (owner only, no agent writes)
Each step opens the case: ADR 0002’s prerequisite (disconnect the battery-cable bracket, hold power 20 s, Service Manual) and the warranty note apply. In order, each one a dated recon note:
- Blank-SSD probe (project log Phase 3 list): the spare SSD, empty, installed; power on with no keys. Learns whether recovery and fastboot live in the 32 MB firmware flash (fastbootd lives on the SSD), and what the firmware shows with no bootable disk (the dev-mode menu, or recovery). The SuzyQ records it.
- Unlisted-drive probe (ADR 0002): the spare SSD with a gbook install written on the build host (the stick’s layout: KERN-A
plus ROOT,
cgptattributes as decided in grilling question 1). Boot from internal disk. Pass =m3-endon the netlog and the names heard. Dell lists “Google Qualified” drives; this is the test of whether an unlisted one is accepted at all. - Swap-back probe: the stock SSD returns; power on; Boot from internal disk; stock boots (
recon.shreadsorange, unlocked, the installed build). Then, if the owner wants the locked state: Lock bootloader (wipes; proven to relock without the recovery stick on 2026-11-02), stock boots,recon.shreadsgreen,flash.locked=1,locked. This probe is the one that closes M4’s “documented return to stock”.
Optional, after 2: the recovery stick with the spare SSD installed (section 4’s second hazard), only if the owner wants stock on the spare drive too.
Commands (named, not run)
| Purpose | Command | When |
|---|---|---|
| Read a disk’s kernel entries | cgpt show -i <N> -P / -T / -S (or -A) <disk> | any time on the build host against the stick (attempt.sh gpt wraps it) |
Mark the booted kernel good (Valve’s set-state good) | cgpt add -i <N> -S 1 <disk> | M4, from the running OS, owner’s OK, after the verified-boot test |
| Prefer the other slot before a reboot (Valve’s “Previous”) | cgpt add -i <other> -P 15 <disk> (and -T 1 -S 0 to try it once) | M4, owner’s OK |
| Re-arm a rejected entry | rewrite the image (attempt.sh dd-cmd); cgpt add -i 1 -T 15 -P 15 alone would re-arm a kernel the firmware already refused | after “No valid image detected” |
| Return to stock, stick era | pull the stick, power on | proven |
| Return to stock, M4 | swap the stock SSD back, Boot from internal disk, Lock bootloader if wanted | the swap-back probe |
Decisions (owner, 2026-11-05, 11:02 AM EST)
Grilling round; the owner took every recommendation:
- Slot policy: two kernel partitions on the SSD; the OS marks the booted one
successfulafter the verified-boot test (m3-endreached, the names heard), so a bad update falls back by itself.cgptjoins the rootfs for M4. - The stick stays as a second recovery path at M4.
- The data-wipe flag in
/initcomes at M4, when a data partition exists (not with task #36). - The M4 ADR is written after the three probes, with their results.
Open questions (grilling round, as asked)
- Slot policy for the SSD install (section 1): count-down as the stick, installed-system values, or two slots with the
OS flipping
successfulafter a verified boot. - Whether M4 keeps the stick as a second recovery path (the proven one; it costs nothing).
- The data-wipe flag in
/init(section 3): with #36 now, or at M4. - The ADR: now, or after the three probes.
Sources
| Source | What | Read |
|---|---|---|
| steamos-arm.md section 5 | the Frame’s boot menu, splctl, the first-party recovery matrix (Steam Support pages 1B71-EDF2-EB6D-2BB3 and 65B4-2AA3-5F37-4227), the update discipline | 2026-11-05 |
| fit-packaging.md section 5 | vboot selection and attribute bits at firmware-bluey-16702.B (cgptlib.c, 2load_kernel.c, cmd_add.c, cmd_show.c); pmbootstrap’s values; the stock boot_a with priority 0 | 2026-10-21 note, re-read 2026-11-05 |
| chromeos-boot-path.md sections 2, 3, 4, 6 | developer mode, Route A, firmware fastboot’s commands and transports, the recovery image | 2026-10-21 note, re-read 2026-11-05 |
| ADR 0001, updates 2026-10-21 and 2026-11-02 | Gate 1 passed; the recovery combo; the relock test’s place | 2026-11-05 |
| ADR 0002 | the spare SSD, the three open probes, the case-opening prerequisite | 2026-11-05 |
| ADR 0003 | the stock drive is never written; the ChromeOS kernel partition route | 2026-11-05 |
| ADR 0010 | M4’s “done when” | 2026-11-05 |
| runbook, first boot sections 5, 8, 9 | unlock day and the relock test; the failure playbook (0/0 mark, Firmware log, LED codes); back to stock | 2026-11-05 |
| unlock-day ticket | the dev-mode menu’s three entries (depthcharge screens.c:1801); the relock pass criteria | 2026-11-05 |
| project log, 2026-11-02 “Unlock day, relock test passed”; 2026-11-04 “Retro-7/08: stick boot tries” | the relock proven without the recovery stick; the count-down proven, 15 tries | 2026-11-05 |
| status.md rows “Boot from USB” and “Reboot back to stock” | stock boots once the stick is out | 2026-11-05 |
rootfs/packages.txt | no cgpt in the rootfs | 2026-11-05 |
scripts/package-in-container.sh (the GPT comment), scripts/attempt.sh (stick_tries) | the stick’s -S 0 -T 15 -P 15 and the tries bookkeeping | 2026-11-05 |
SteamOS on ARM: what Valve has published for the Steam Frame, and what gbook can take from it
Researched 2026-11-05 (written 10:41 AM EST [15:41 UTC]) on the build host, task #30 (owner’s idea 2026-11-05). First-party sources only:
Valve’s GitHub organisation and gitlab.steamos.cloud (through their APIs), Valve’s package servers (directory listings
and the pacman databases, plus ten packages downloaded into the scratchpad and read, never installed), the
Steam store, Steamworks partner documentation and Steam Support pages, the Steam news API, Valve-authored commits
reachable through GitHub’s torvalds/linux commit endpoint, Mesa and FEX upstream, and the kernel tree at gbook’s pin
(~/.cache/gbook/linux-a804c57). The Googlebook was not touched. Nothing was installed. Inference marks what no
source states. Package versions are as listed on 2026-11-05 and will move. Proprietary packages (licence field
Proprietary) are described; only identifying strings (menu entries, messages, a comment fragment, USB IDs) are
quoted from them, never code. One configuration file carries a shared CI credential, which is not copied.
Context: gbook builds its rootfs with scripts/build-rootfs.sh (Debian sid, snapshot-pinned, mmdebstrap in a rootless
podman container, ADR 0012) and its boot image with scripts/package-image.sh
(FIT image on the ChromeOS KERN-A partition, dev-key signed, ADR 0003);
the kernel is torvalds a804c57 plus kernel/patches/0001-0011 (ADR 0007) and
the kernel/*.config fragments; firmware is staged by scripts/stage-firmware.sh (stock modem_a plus distro
packages, hashes in kernel/firmware.sha256). Milestone M4 needs a documented return to stock
(ADR 0010).
Answer in short
- Valve has not published an ARM SteamOS source tree or image-build pipeline. What is public is the Steam Frame’s
package repositories (Arch Linux rebuilt for aarch64 plus Valve’s own
deckard-*andsteamos-*packages), a repair image and a QDL flash package (about 4 GB each), the mainlinesm8650-valve-deckard.dts, Lepton (the Android compatibility tool), an Arch aarch64 “preview” package-source mirror, and developer documentation. - The Frame’s kernel is Linux 7.2 plus 1,178 commits (a 6.18 branch carries 1,215) from a private GitLab tree
whose commits resolve through GitHub. None of gbook’s eleven patches is in it; Valve touches the same SoundWire,
WSA884x and GMU code with different, mostly
HACK:-prefixed changes, and carries no x1e80100, SCMI or panel-edp work. - The OS layout is Steam Deck’s, ported: RAUC A/B raw root slots named by “partsets”, read-only root,
/etcoverlay on a per-slotvar, sharedhome, casync/desync delta updates, U-Boot with a boot menu,splctlfor the boot-firmware slot. The best gbook candidates are the recovery matrix (section 5), aramoopsregion, the ath12k IOMMU and IRQ workarounds, and the WirePlumber card rules (section 6).
What is public
| Where | What | Pin read 2026-11-05 | Licence |
|---|---|---|---|
| github.com/ValveSoftware (55 repos listed by the API) | Nothing ARM. steamos_kernel (“SteamOS kernel branches”, default branch brewmaster-4.11, last push 2023-05-22), steamos_mesa (last push 2018-10-13), linux (Debian kernel-packaging fork, 2018-01-11), SteamOS (issue tracker), gamescope (fork) | repo list | NOASSERTION on the kernel repos |
gitlab.steamos.cloud (46 public projects listed by /api/v4/projects) | holo/holo-core-aarch64-preview, frame-public/lepton, frame-public/frame-developer-tools, frame-public/android-vulkan-headers, two Waydroid forks, SteamHardware/SteamFrame, holo/steamos-manager, holo/fakemachine, devkit/steamos-devkit, the Steam Runtime (steamrt/*) and Proton SDK groups | see rows below | per project |
holo/holo-core-aarch64-preview (project 1471) | Read-only mirror of the package sources (PKGBUILDs) behind the “preview Arch Linux aarch64 port”, built by Collabora from Arch’s packaging state of 2025-11-18 (97c0a0b4), with aarch64 build fixes and licence fixes (Arch RFC 40). Binary repo at holo-packages.steamos.cloud/holo-core-aarch64-preview/mash-20251118{,.2,.3}/{core,extra}/os/aarch64/, a system.rootfs.zst (384,971,555 bytes, not downloaded), pacman.conf with SigLevel = Optional. Its linux is Arch’s 6.17.8.arch1-1 PKGBUILD, but the config in that tree is the x86 one (# Linux/x86 6.17.8-arch1 Kernel Configuration); its mesa 1:25.2.7-1 enables freedreno in both gallium-drivers and vulkan-drivers. “Technology preview … not intended for use as a permanent operating system” (readme) | main = 67f0d559 (2026-06-10) | no licence file; the PKGBUILDs carry Arch’s |
frame-public/lepton (project 1518) | Lepton, “a tool for use with the Steam client which allows games which are exclusive to Android to run on the Linux operating system”: a Waydroid/Anbox/Halium-derived Android container image (image/) and a non-root compat tool (compat_tool/) that mounts the host’s Mesa and Zink into the container. Built in a podman container (image/do_build.sh) | main = 5964972a (2026-11-03), tag v3.0.10 | MIT (tool), GPL-3.0 (image) per LICENSE.md |
frame-public/frame-developer-tools (project 1324) | perfetto, gpuvis and gfxreconstruct wrappers, gamescope helpers, and pacman/deckard-pacman-src, a 50-line script that maps a package’s repo URL to its PKGBUILD location: holo-packages.steamos.cloud/archlinux-deckard/archlinux//main/{core,extra}/os/aarch64 → gitlab.steamos.cloud/potato/mash/monorepo, .../core-bootstrap/... → potato/mash/bootstrap, archlinux-deckard/bootstraps/ → potato/smash/holo, archlinux-deckard-hotfixes/ → deckard/deckardos/holo. Every one of those GitLab paths answers 404 unauthenticated | main = 16d1942e (2026-09-02) | none stated |
SteamHardware/SteamFrame (project 1521) | CAD of the core module, head strap and controllers, expansion-port documents, two USB-hub reference designs | master = bdbb41ba (2026-10-13) | CC-BY-NC-SA-4.0 (license.txt) |
holo/steamos-manager (project 1288) | The D-Bus daemon behind Steam’s system settings; data/devices/steam-frame.toml identifies the Frame by dt.compatible = "qcom,sm8650" and board-info/board_revision, names it device = "steam_frame", variant = "Deckard", and sets GPU devfreq limits (/sys/devices/platform/soc@0/3d00000.gpu/devfreq/3d00000.gpu, manual minimum 500, powersave maximum 629) and per-profile cpufreq caps for policies 0, 2, 5 and 7 (Performance 2,265,600 / 3,148,800 / 2,956,800 / 3,052,800 kHz) | main = a3b20137 (2026-10-24) | MIT |
holo/fakemachine (project 1315) | A fork of Collabora’s fakemachine (the VM runner used by debos); last commit 2021-05-31 | master = 74dd229b | Apache-2.0 |
steamdeck-packages.steamos.cloud/archlinux-mirror/ | The Steam Deck and Steam Machine repos (jupiter-*, holo-*, 217 folders). x86_64 only: every */os/ folder lists x86_64/ alone; holo-main/os/aarch64/, jupiter-main/os/aarch64/ and guessed frame-* names answer 404 | listing | |
holo-packages.steamos.cloud/archlinux-deckard/archlinux/main/{core,extra}/os/aarch64/ | The Frame’s base repos: Arch Linux rebuilt for aarch64 by “GitLab CI Package Builder ci-package-builder-1@steamos.cloud” (core.db). core: 272 packages, extra: 4,114, dated 2025-12-01 to 2026-10-30, each with a .sig. archlinux-deckard/archlinux/ also lists snapshot folders mash-20240428 … mash-20260528 (some .pvt), archives/, builds/, dev/, pipeline/. The server root answers 401 | listing and core.db.tar.xz | Arch’s per package |
holo-packages.steamos.cloud/archlinux-deckard-hotfixes/ | Valve’s own Frame packages: 1,978 package files, 497 non-debug package bases, dated 2025-08-06 to 2026-11-04, plus 160 subfolders (goldmaster/, mr-1020/ … mr-1133/, one pacman repo per merge request) and create_new_release_from_main.sh/create_staging.sh. Database deckard-arch-hotfixes.db.tar.xz. Package URLs name private GitLab projects under deckard/deckardos/ (all 404) | listing, .db and .files | per package (see below) |
steamdeck-images.steamos.cloud/recovery/ | steamframe-oobe-repair-20260922.5153644-0.3.0.img.bz2 (4,068,109,407 bytes, Last-Modified 2026-09-22 22:57 UTC) and .zip; steamframe-oobe-repair-qdl-20260922.5153644-0.3.0.tar.gz (4,100,462,821 bytes) and .zip; the store’s “download SteamOS for Frame” link (store.steampowered.com/steamos/download/?ver=steamframe) points at steamframe-repair-latest.img.bz2, which redirects to the first file. Not downloaded (see “Not found”) | HEAD | not stated |
torvalds arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts | “Add initial DeviceTree for the Valve “Steam Frame” codename Deckard … based on the Qualcomm Snapdragon 8 Gen 3 SoC (SM8650) … The bootloader starts Linux in EL2, thus a bunch of EL1 & DSP related nodes needs to be removed“ (commit message). Author Neil Armstrong (Linaro), Copyright (c) 2026, Valve Software; binding abbbaa99ac66; merged through qcom-arm64-for-7.4-2 and soc-dt-7.4 (7b9a0e857b42, 2026-10-22). Not in gbook’s pin: compare a804c5742bff...f905cb6e600a says diverged, 135 commits ahead. Follow-ups on linux-arm-msm patchwork: chassis-type (2026-09-24), watchdog (2026-10-02), camera sensors v2 (Vladimir Zapolskiy, 2026-10-02) | f905cb6e600a (2026-09-23) | BSD-3-Clause |
Steamworks partner docs, partner.steamgames.com/doc/steamhardware/steamframe{,/setup,/debugging,/loadgames,/compat,/engines/custom} | “The Steam Frame runs on SteamOS, which is a gaming focused Linux distribution based on Arch”; Developer Mode gives ssh, adb and RDP (user steamos, XRDP Xorg session); “By default the filesystem is read-only … sudo steamos-readonly disable”; sudo pacman -S; coredumpctl; runtimes offered by the devkit client: Android (APK through Lepton), “Steam Linux Runtime 3.0 ARM64 (Sniper)” for native arm64 builds, “Steam Play (Proton)” for Windows builds; custom engines should target “Android 10 or Linux Arm64”; System Update Channel Beta | read 2026-11-05 | Valve |
| Steam Support: SteamOS Recovery and Troubleshooting, SteamOS Installation and Repair, Steam Frame guide, LEDs and button chords | Rollback and USB-repair steps for the Frame (section 5); “Purple, solid: Steam Frame is in bootloader” | read 2026-11-05 | Valve |
Steam news for app 4165890 (api.steampowered.com/ISteamNews/GetNewsForApp/v2/) | “SteamOS 0.3.0 for Steam Frame” (2026-09-17), 0.4 (2026-10-08), “0.5.7 Beta” (2026-11-04). The version line is 0.x (for Steam Frame), separate from the Deck’s 3.x; the store page lists “SteamOS 3”. No kernel versions in any note | read 2026-11-05 | Valve |
| Valve’s kernel tree, through GitHub | linux-72-deckard 7.2.0+g344f27110f05-1 and linux-618-deckard 6.18.0+gee93c0d9394b-1 name gitlab.steamos.cloud/deckard/deckardos/linux-valve (404). Both SHAs resolve at api.github.com/repos/torvalds/linux/commits/<sha> because GitHub serves objects from the whole fork network; search/commits?q=hash: finds no repository, so there is no named public branch (section 3) | 344f27110f05 (2026-11-03), ee93c0d9394b (2026-10-23) | GPL-2.0-only |
| FEX | FEX-Emu/FEX (MIT, release FEX-2610 2026-10-07; the GitHub login Plagman, git@plagman.net, has 44 commits); fex-emu.com “The scourge of x86 emulation” names “the platform we’re testing on, which is of course the Valve Steam Frame”, the kernel patch bylaws/linux 7ae989a43ae7 (Billy Laws, 2025-08-06, arch/arm64/kernel/unaligned_atomic.c) and the Turnip MR !41323 (Ryan Houdek, merged 2026-05-08) | read 2026-11-05 | MIT / GPL-2.0 |
| Mesa upstream (GitLab API) | Adreno 750 (the Frame’s GPU) merge requests that name Valve content: !44840 “tu: Set SP_CHICKEN_BITS_2 bit 30 on A750 for HL: Alyx” (Dhruv Mark Collins, Igalia, merged 2026-10-06), !44348 SUBPASS_FENCE on a7xx+ (Connor Abbott, 2026-09-14), !45615 a7xx gen1/gen2 quirk (Danylo Piliaiev, Igalia, 2026-10-27). The MRs do not state who funds the work | read 2026-11-05 | MIT |
1. Repos, packages and trees, with licences and pins
The table above pins each source. Licences of the Frame packages, from the desc entries in deckard-arch-hotfixes.db
(read 2026-11-05):
| Package (newest in the hotfixes repo) | What | Licence field | Source URL in the package |
|---|---|---|---|
linux-72-deckard 7.2.0+g344f27110f05-1 (built 2026-11-04 18:15 UTC); linux-618-deckard 6.18.0+gee93c0d9394b-1 | the kernel, Image + modules + every qcom/*.dtb | GPL-2.0-only | deckard/deckardos/linux-valve (404) |
linux-firmware-deckard 20261023.1+git594f649d-1 | Valve’s firmware set (section 4) | custom | deckard/deckardos/linux-firmware (404) |
linux-firmware-qcom 20260519-1 (core) | Arch’s split of kernel.org linux-firmware | LicenseRef-WHENCE | kernel.org |
deckard-boot-firmware 20261021.1.uboot20261019.1-3, deckard-boot-luns, deckard-boot-images, deckard-hw-support 20261103.1-1 | U-Boot build, Qualcomm boot firmware, LUN GPT images, boot splash, hardware-support scripts and initramfs hooks | Proprietary (deckard-boot-images also OFL-1.1) | deckard/deckardos/u-boot, .../deckard-hw-support (404) |
deckard-uboot-splctl 20260128.3-6 | “Deckard second-stage bootloader controller” (/usr/bin/splctl) | GPL-2.0+ | deckard/deckardos/deckard-uboot-splctl (404) |
steamos-efi 20260122.2-3 | “Stage 1 bootloader for SteamOS 3+”, here an aarch64 build (/usr/lib/steamos-efi/aarch64-efi/steamcl.efi) | GPL2 | deckard/deckardos/steamos-efi (404) |
steamos-customizations-deckard 20261002.1-4 | “SteamOS customizations (Deckard fork)”: RAUC and atomupd configuration, partsets, mounts, reset tools, udev rules (section 2, 5) | LGPLv2+ | repo.steampowered.com |
deckard-atomupd-config 20250820.1-1, steamos-atomupd-client 0.20260331.1-4, atomupd-daemon 0.20260810.0-4, steamos-reset 20241008.1-6 | the update and reset stack | MIT, LGPL2.1, MIT, GPL | holo/* (404) |
deckard-audio-config 20261027.2-1 | PipeWire, WirePlumber, UCM2 and Valve’s LV2/LADSPA plugins (section 6) | MIT | deckard/deckardos/deckard-audio-config (404) |
deckard-mesa-linux-aarch64, -x86_64, -android-aarch64 27.0.0_devel+gitb826d29e-1 | Valve’s Mesa builds (section 6); b826d29e is not in mesa/mesa (404 Commit Not Found), so a Valve branch (inference) | custom | mesa3d.org |
fex-wtf 2606-5 | “Tool for seeing FEX emulator stats” | MIT | github.com/Sonicadvance1/what-the-FEX |
deckard-steam-rel, deckard-steamvr-rel, deckard-steamvr-session, lepton-compat | the Steam client, SteamVR, their session, Lepton | not read (Steam client) |
The hotfixes repo is also where Arch packages get Valve rebuilds with sub-release numbers (bluez 5.79-1.6, chromium 129.0.6668.58-1.6, busybox 1.37.0-1.5); the mr-NNNN/ folders hold one package each (mr-1133/ has
deckard-steamvr-session-1.7.0-1), so a merge request becomes a pacman repo before it reaches goldmaster/.
Observed licence discrepancy. deckard-hw-support is marked Proprietary, yet its initramfs hooks
(usr/lib/initcpio/hooks/{partsets,blockdev_ro,etc_overlay,factory_reset}) carry SPDX-License-Identifier: GPL-2.0-only
headers, and deckard-boot-firmware (also Proprietary) bundles a U-Boot binary (U-Boot 2025.07-rc3-00656-gfbfd7e7ef9a0 for deckard board, GPL-2.0-or-later upstream) with Qualcomm blobs. This note treats both packages as proprietary and
quotes nothing from them.
What gbook could adopt (proposals)
- Proposal: add one
sources.mdrow for the four new source kinds this note used: the gitlab.steamos.cloud API, the holo-packages listings and.db.tar.xz, the ISteamNews API, and GitHub’s fork-network SHA resolution (this note did not editsources.md). - Proposal: none of the Frame’s
deckard-*source trees is public, so gbook cannot depend on them; the Arch-rebuilt repos are the only reproducible part, and even they float (mash-*snapshots are listed but their contents were not compared).
2. Rootfs and image build
How the Frame image is assembled is not public. The observable pieces:
- Packages. Arch Linux rebuilt for aarch64 at a “mash” snapshot (the preview readme credits Collabora for “the build
infrastructure, developed the tooling required to build and publish packages”), plus Valve’s packages built by GitLab
CI (
builddir = /builds/deckard/deckardos/holo/deckard/linux-72-deckardin the kernel’s.BUILDINFO;makepkg 7.1.0). The pinned state is a snapshot name (mash-20251118), not a dated archive like snapshot.debian.org. - Image.
holo/fakemachine(debos’ VM runner) is the only build-tool clue; that Valve builds images with debos is inference. The RAUCsystem.conf(LGPL,steamos-customizations-deckard) says:compatible=steamos-aarch64,bootloader=custom,max-bundle-download-size=268435456,activate-installed=false, two slotsrootfs.0(bootnameA,/dev/disk/by-partsets/A/rootfs,type=raw) androotfs.1(B), a[casync]section withuse-desync=trueand--seed /var/lib/steamos-atomupd/rootfs.caibx --in-place --error-retry 35, and custom handlers (bootloader-custom-backend.sh, which creates theby-partsetslinks on old layouts and then calls/usr/bin/splctl). So a root slot is one raw image, delivered as a casync index and chunks, written in place. - Immutability. The root is read-only (partner docs:
steamos-readonly disable);/etcis an overlay whose upper directory lives in the slot’svarpartition (var-A/var-B, mounted by theetc_overlayinitramfs hook);/homeis shared and holds “offload” bind mounts for/root,/var/log,/var/lib/flatpak,/var/cache/pacmanand others (*.mountunits in the customizations package);usr/lib/repart.d/90-home.conf(Type=home) lets systemd-repart grow it.atomic-update-keep.conflists the/etcpaths that survive an update. - Update channels.
etc/steamos-atomupd/client.conf:ImagesUrl = https://steamdeck-images.steamos.cloud/,MetaUrl = https://steamdeck-atomupd.steamos.cloud/meta,Variants = vr,Branches = stable;rc;beta;bc;preview;pc;main. The same file carries a sharedUsername/Passwordfor the image server; not reproduced here. - Signing. RAUC bundles verify against
/etc/rauc/trusted_keys/(plus adevelopment_keys/directory); the olderdeckard-atomupd-configships one self-signed certificate, subject and issuerO=Valve, CN=Valve,notBefore2023-05-24 22:10:05 GMT,notAfter2027-03-24 22:10:05 GMT (openssl x509 -noout -subject -issuer -dateson the extracted file, 2026-11-05; the certificate itself is not copied). Packages carry.sigfiles; the preview’spacman.confsetsSigLevel = Optional. - Initramfs. mkinitcpio (
dracut 111-1is also in extra).etc/mkinitcpio.conf.d/steamos.confadds the hooksblockdev_ro partsets initrd_shell factory_reset etc_overlay deckard_fw, the binariessplctl fw_printenv fw_setenv bash strace zstd lsblk, andregulatory.db. The post hookusr/lib/initcpio/post/deckard-uimage(LGPL) wraps the result withmkimage -A arm64 -O linux -T ramdisk -C noneinto/boot/initrd.uImage. - Compared with gbook. gbook pins one Debian snapshot and keeps a lock list and manifest (
build-rootfs.sh); the Frame pins a package snapshot by name and ships raw slot images with delta updates. gbook has one root partition on the stick; the Frame has two root slots, twovarslots, onehome. gbook signs a FIT for depthcharge with a dev key; the Frame signs RAUC bundles with Valve’s CA, and whether anything checks a signature on the rawImageand themkimage -T ramdiskinitrd before the kernel runs is unknown (verify=ninuboot.envonly disables U-Boot’s image data-checksum check; see section 5).
What gbook could adopt (proposals)
- Proposal (M4): name partitions the partsets way,
rootfs-A/rootfs-B,var-A/var-B,home, and resolveself/otherfrom one kernel-command-line token (the Frame usesrauc.slot=A|B; thepartsetshook parses it). gbook’s/initalready finds ROOT through depthcharge’skern_guid=%U; the same token could pick the matching root slot. The hook is GPL-2.0-only inside a proprietary package, so re-implement, do not copy. - Proposal (M4): read-only root with an
/etcoverlay in a writable slot partition. This is what makes “Previous slot” a real rollback: user state lives invar-Xandhome, never in the root image. - Proposal (M3, cheap): an initramfs “shell with auto-reboot”: the Frame’s
initrd_shellprints “Headset will auto-reboot in 20…” and stops the countdown on a keypress, a serial character or an adb connection, withgotoinitrd=yesorbreakon the command line forcing the shell. gbook’s capture-and-reboot/initcould add the countdown and thebreakescape without changing its sweep. - Proposal (M3): lock the stock partitions read-only at the block layer from
/init(blockdev --setroon every partition of the stock disk), which enforces CLAUDE.md’s “never write AP or EC firmware” rule mechanically. The idea comes from the proprietaryblockdev_rohook; a one-line re-implementation needs no code from it.
3. Kernel
Tree and version. linux-72-deckard is 7.2.0-g344f27110f05: compare v7.2...344f27110f05 gives ahead 1178, behind 0, merge base 8d3ae59288f1 “Linux 7.2” (2026-08-16). linux-618-deckard is v6.18 plus 1,215. Committer
dates of the 1,178: 987 in 2026-08 (a rebase onto 7.2 on 2026-08-24, inference), 58 in September, 132 in October,
1 in November. Author domains: valvesoftware.com 501, linaro.org 359, arcturus.industries 85, gmail.com 78,
marek.ca 57, tgoyen.com 37, quicinc.com 16, oss.qualcomm.com 16, igalia.com 10, intel.com 5, collabora.com 3.
Subject prefixes: gabe_defconfig 68, Revert 62, fixup! 60, HACK 44, WIP 9, FROMLIST 9, FEX 2, TEMP 1.
One CI commit says “Don’t run pipelines in linux-valve-publicly-released-branches” (d643bc7139a5), so a public
branch is planned or exists somewhere this search did not find (inference). The full subject list is in the
scratchpad (kdts/valve/all-1178.tsv), not in git.
Configuration (from linux-72-deckard-headers’ build/.config, # Linux/arm64 7.2.0 Kernel Configuration,
2,584 =y and 61 =m; a single gabe_defconfig maintained by 68 commits):
| Area | Frame (gabe_defconfig) | gbook (~/.cache/gbook/build-a804c57/.config) |
|---|---|---|
| Pages, VA, HZ, preempt | 4K, VA_BITS=48, HZ=1000, PREEMPT=y, SCHED_CLASS_EXT=y, KVM=y | VA_BITS=52, HZ=250, PREEMPT=y |
| cpufreq / cpuidle | ARM_QCOM_CPUFREQ_HW=y, default governor performance, ARM_PSCI_CPUIDLE=y with the teo governor; # CONFIG_ARM_SCMI_PROTOCOL is not set (SM8650 has no SCMI cpufreq) | ARM_SCMI_PROTOCOL=y, ARM_SCMI_CPUFREQ=y, ARM_QCOM_CPUFREQ_HW=y, schedutil, ARM_PSCI_CPUIDLE=y but no PSCI cpuidle on mica (ADR 0009) |
| GPU, display | DRM_MSM=y with DPU, DP, DSI; DRM_PANEL_DECKARD=y (Valve’s dual-DSI panel driver), # CONFIG_DRM_PANEL_EDP is not set | DRM_MSM=y, panel-edp with patch 0001 |
| Audio | SOUNDWIRE_QCOM=y, SND_SOC_WSA883X=y, WSA884X=y, MAX98390=y, the four LPASS macros, the whole QDSP6 stack built in, QCOM_Q6V5_PAS=y, QCOM_APR=y; # CONFIG_SND_SOC_SM8250 is not set | SOUNDWIRE_QCOM=y, WSA884X=y |
| IOMMU | IOMMU_DEFAULT_DMA_STRICT=y (worked around in userspace, section 6) | IOMMU_DEFAULT_DMA_STRICT=y |
| Wi-Fi, BT | ATH12K=m (one of the few modules), BT_QCA=y, BT_HCIUART_QCA=y | ATH12K=y, BT_QCA=y |
| pstore | PSTORE_RAM=y, PSTORE_CONSOLE=y, PSTORE_PMSG=y, ramoops node in the DT | PSTORE_RAM=y, PSTORE_CONSOLE=y, no ramoops node in dts/x1e80100-google-mica.dts |
| Modules, signing | MODULE_SIG=y, # MODULE_SIG_FORCE is not set, # KEXEC is not set | |
| Android | ANDROID_BINDER_IPC=y, ANDROID_BINDER_DEVICES="anbox-binder,anbox-hwbinder,anbox-vndbinder" (for Lepton) | |
| Stray x1e80100 bits | CLK_X1E80100_GCC=m, CLK_X1P42100_CAMCC=m, CLK_X1P42100_VIDEOCC=m (modules, so not used on the Frame; inference) |
Device tree. The production DTB in the kernel package is sm8650-mp.dtb (model = "SM8650 MP rev1 4slam 2et",
compatible = "qcom,sm8650-dv1", "qcom,sm8650", 9,488 lines decompiled), beside sm8650-dv1/dv2/ev1/ev2/ev3*,
sm8650-mp-power, sm8650-mp-dbg, sm8650-el2-hdk, sm8650-ayaneo-* and the HDK boards. U-Boot picks
/boot/dtbqcom/sm8650-<variant>.dtb (section 5). Compared with the mainline sm8650-valve-deckard.dts:
- Both run at EL2 without the DSPs: the mainline file
/delete-node/sipa,gpu_zap_shader, the five LPASS macros,lpass_tlmm,remoteproc_adsp/cdsp/mpss,reserved_memory, the threesmp2pnodes andswr0-3, then redefinesreserved-memory(aop,ramoopsat0x84a00000,cmd-db,smem,cpucp-fw,video,tz-stat, four LPASS LPM pools, a 48 MiBframebufferat0xaf400000). That is the full-DTS version of what gbook’sdts/mica-el2.fixupsdoes on top of an overlay (overlays cannot delete nodes), and it validates the approach. - Mainline enables
gpu(no zap node),pcie0with the WCN7850 aspci17cb,1107and its PMU regulators,uart14(qcom,wcn7850-bt),uart15console,ufs_mem_hc,sdhc_2,usb_1as a USB 2.0 peripheral (dr_mode = "peripheral",maximum-speed = "high-speed", USB 3 PHY removed), four I2C buses with twomaxim,max98390amplifiers (Speaker Left/Right),gpio-keys, PMIC temperature alarms;dispccis disabled and thesimple-framebufferunderchosenis 4320x2160 ARGB8888 “Bootloader will enable the node”. - The production DTB adds what is still out of tree: the
deckard-panel(dual-panel,low-fps-support, eight supplies), theqcom,lpass-sndcardcard (“SM8250 LPASS” model) drivinglpaif@7d00000with the two MAX98390s on the “SPKR Playback” link and the VA macro DMIC on “VA Capture” (no ADSP: the DTB has noremoteprocnode at all),valve,fpga, twoarcimx616cameras plus eyes,allegro,als31300, PSCIidle-statesfor all three clusters (arm,psci-suspend-param = <0x40000004>, “silver/gold/gold-plus-rail-power-collapse”),qcom,sm8650-cpufreq-epss,qcom,adreno-43051401with its OPP table, andqcom,pm8550b-charger.
Patch areas, against gbook’s kernel/patches/0001-0011 (line 1 of each gbook patch read 2026-11-05; Valve-tree
commit messages and touched files read through the GitHub commit endpoint):
| gbook patch | Valve’s tree carries | Overlap |
|---|---|---|
0001 drm/panel-edp LGD 0x0804 (LOCAL) | no panel-edp, eDP or x1e80100 subject among the 1,178; the Frame’s panel is DSI with its own driver | none |
| 0002-0007 “drm/msm: Attach a driver to GMU” (Akhil P Oommen, FROMLIST) plus the Omarchy fix-up | not the series. On GMU: c76da00c0873 “a6xx_gmu: fixup bw voting to use AB voting” (+4 lines), d36695feb7f3 “a6xx_gmu_wait_for_idle uses sleeps instead of spinning hard”, f43185c19923 “vote minimal opp on shutdown” (reverted in 272f8245e6f5), 768a6e680987 “HACK: use correct register address for GMU AO cnter on a750” (reverted and reapplied twice). Two other Akhil commits are present: 00b93c80cab0 “drm/msm: Skip devfreq IDLE when possible”, e01f24c3b2f5 “drm/msm/adreno: Relax devfreq tunings”. For the no-TZ GPU: 6eb3e65348d9 “HACK: drm/msm: adreno: add a7xx zap init”, afd29180cc00 “fix for a7xx without zap/tz” (one line in sm8650.dtsi), c4eca5004b53 “enable back zap shader loading via SCM” | same area (GMU, EL2 GPU bring-up), different changes; nothing to copy for the stateless device-link problem |
0008 gcc-x1e80100 PCIe GDSC retention (FROMLIST, Krishna Chaitanya Chundru), 0009 PCIe PHY GDSC retention (LOCAL) | no x1e80100 or PCIe-GDSC change. GDSC work is display and camera: 0ae07b602274 “clk: qcom: tie mdss gdsc to branch clocks” (gdsc.c, clk-branch.c, dispcc-sm8550.c), a6ddcda546d1 “dispcc-sm8550: … mdss_gdsc as ALWAYS_ON”, fb9ea49232da “HACK: media: qcom: camss: add ALWAYS_ON flag to titan top and ife gdscs”. PCIe: f0aaa175a5f7 “pcie: qcom: add opt-in link_reset sysfs attr for PERST# pulse” (Arcturus), and the defconfig commits ca6987de7ff9 “enable CONFIG_PCIEPORTBUS to allow the PCIe bus to go in D3Cold” and 9a8c8ea46950 “enable CONFIG_PCIEASPM to allow the PCIe endpoints to enter Lower energy levels” (both 2026-10-20/21) | none on the retention bit; Valve’s endpoint low-power path is ASPM + port-bus D3cold rather than GDSC retention (inference from the two defconfig subjects) |
0010 soundwire: qcom re-arm enumerator after clock stop (LOCAL) | 603ad2317660 “WIP: soundwire: qcom: add some post bank switch delay” (cherry-picked from Srinivas Kandagatla: “on wsa883x we see Port Collision during bank switch”), 8f6efbaf3646 “HACK: soundwire: qcom: disable runtime pm” (“there’s a bug somewhere that needs to be debugged”, +17/-11 in qcom.c), c5e49e3d99ac “allow more rates and formats” (PDM-only bus) | same driver, same family of bug; Valve sidesteps clock-stop by disabling runtime PM (inference: no runtime PM means no clock-stop entry). No fix to copy, and these commits come from the EL2/HDK/Aya experiments (65834eec3fcb “aya el2 audio”), not from the production Frame, which has no SoundWire codec |
0011 wsa884x clock-stop mode 1 (LOCAL) | 9066a20246f7 “ASoC: codecs: wsa884x: remove mute_unmute_on_trigger” (with a Fixes: tag: “trigger is atomic … soundwire register writes are not safe to run in an atomic context”), ad9e0544ab85 the same for wsa883x, 5f9768d70cd3 “HACK: ASoC: codec: wsa884x: workaround for perma muted state” (“If the codec is unmuted and muted again immediately, no more audio will play ever again”), 24098704fd61/7f4b1c3f9436 “use older driver here” and its revert | same codec driver, mute and trigger path; no clock-stop-mode change |
Other Valve-tree work worth knowing (counts by subject): ath12k 65 (board-id from DT for a new diplexer, FTM/test mode,
txpower forcing, “work around dp_tx: failed to find the peer”, forced RDDM and coredump, “fix survey indexing across
bands” from Collabora); display 109 (DSI CTRL v2.8.0, bonded-DSI DSC, a DPU “CAC” pipeline from Linaro, the
acronym unexpanded in the subjects, drm/msm/dpu1: fix early delivery of page flip events); camera 71; charger and fuel gauge 36
(qcom_pm8550b_charger, max1720x); IOMMU 8 (iommu: arm-smmu-qcom: add display bypass if missing, defer attach of nodes with iommu-defer-attach property, an LPAC SID split); cpufreq 7ab3f57ab244 “set lowest freq and disable
OPP on cpu offline” and a1190a326d84 “set suspend frequency”; PSCI 1b265e654037 “HACK: workaround for TZ PSCI
CPU_OFF/CPU_ON bug” (7 lines in psci.c, 4 in irq-gic-v3.c); EL2 plumbing e9f42a84bfc1 “HACK: power: reset:
qcom-pon: add support for EL2 PS_HOLD control setup” and 55476188d208 “enable pmk8350-el2-pon to properly handle
reboot and shutdown”; a9505502e5e8 “FEX: arch: arm64: Implement unaligned atomic emulation/backpatching” (2,056
lines in arch/arm64/kernel/unaligned_atomic.c, a new prctl); ce749094e4ee “hexagon-baremetal: adsp, cdsp, cvp
support” (inference from the subject and the DTB’s lack of remoteproc nodes: Valve runs the DSP cores without the
Qualcomm remoteproc stack; out of gbook’s scope).
What gbook could adopt (proposals)
- Proposal (DTS, needs dt-validate and ADR 0004 tracing): a
ramoopsreserved-memory node for mica. gbook already buildsPSTORE_RAM=yandPSTORE_CONSOLE=y, so a region makes the last console survive a hang. The Frame uses0x84a00000(1 MiB mainline, 2.25 MiB in the production DTB); mica needs its own address chosen against the stock reserved-memory map, and the choice is “unverified” indts/README.mdterms until a stock pin is found. - Proposal (kernel/mica-pcie.config, measure first): try
PCIEASPMandPCIEPORTBUSbefore extending the GDSC retention patches: the Frame reaches endpoint low power that way on a sibling SoC. A boot-rate claim would needattempt.sh tallyfor both configurations (CLAUDE.md). - Contrast, not adoption: the Frame keeps PSCI cpuidle on, with a seven-line TZ CPU_OFF/CPU_ON hack and the
teogovernor. gbook’s ADR 0009 removes PSCI cpuidle because the per-CPU domains losecluster_c4; a different SoC and a different symptom, so ADR 0009 stands; the hack’s diff is the thing to read if mica ever shows CPU_OFF failures. - Contrast: Valve marks patch state in the subject (
HACK:,WIP:,FROMLIST:,fixup!) and carries 62 reverts; gbook’sStatus:line and renumbering rule (ADR 0007, check 34) are stricter and should stay.
4. Firmware handling
Arch’s split linux-firmware-* packages in core and one Valve package supply /usr/lib/firmware on the Frame
(file lists from deckard-arch-hotfixes.files and core.files); the two that matter here:
linux-firmware-qcom 20260519-1(Arch’s split package,LicenseRef-WHENCE, 163.2 MiB) in core, the same source gbook’sstage-firmware.shdraws the GPU and audio-topology files from (linux-firmware-qcom, “20260916-1” on the build host).linux-firmware-deckard 20261023.1+git594f649d-1(“custom”, 24.4 MB, 282 entries) in the hotfixes repo: the Adreno 750 set at the root of the firmware directory (a750_sqe.fw,a750_aqe.fw,a750_zap.mbn,gen70900_sqe.fw,gen70900_aqe.fw,gen70900_zap.mbn,gmu_gen70900.bin; gbook’s X1-85 loadsgen70500_sqe.fwandgen70500_gmu.binunderqcom/),ath12k/WCN7850/hw2.0/{amss,board,board_77,m3,regdb}.bin, the QCA Bluetooth set (qca/hmtbtfw20.tlv,hmtnv20.bin,hpbtfw21.tlv,hpnv21.bin,ht*),qcom/sm8650/{adsp,cdsp}.mbn,{adsp,cdsp}_dtb.mbn,adspr/adsps/adspua/cdspr/battmgr.jsn,ipa_fws.mbn,qupv3fw.elf,SM8650-MTP-tplg.bin,SM8650-QRD-tplg.bin,qcom/vpu/vpu33_4v.mbn,CAMERA_ICP.mbn,cvpss.mbn, the MAX98390dsm_param*.binfiles,i2c_fw.bin,spi_fw.bin,default.vrfont/default.vrterminal, and the kernel.orgrtl_bt,rtl_nicandrenesas_usb_fw.memsets. So it is a linux-firmware checkout with Valve’s additions on top (inference from the mix); which files are Qualcomm-licensed is not stated beyondcustom.- The initramfs carries only
qcom/sm8650/qupv3fw.elf,dsm_param.binand the two.vr*files (deckard_fwhook) plusregulatory.db; everything else loads from the root. - Boot firmware is a separate, proprietary package (section 5):
bootfw.tar.xzholdsxbl.fw,xblconfig.fw,tz.fw,hyp.fw,aop.fw,aopconfig.fw,cpucp.fw,cpucpconfig.fw,devcfg.fw,shrm.fw,qupfw.fw,uefi.fw, and zero-lengthkeymaster.fwanduefisecapp.fw(sizes frombsdtar -tvf).
Compared with gbook. Same layout (kernel.org paths), same idea of a hash-checked set (pacman’s desc holds the
package sha256; gbook keeps kernel/firmware.sha256), and the same two origins (a distro package plus device-specific
blobs). The difference is provenance: gbook’s device blobs come from the stock modem_a partition the owner dumped
(ADR 0005), Valve’s from Qualcomm under contract.
What gbook could adopt (proposals)
- Proposal: nothing structural;
stage-firmware.shalready matches. One check worth adding tokernel/firmware.sha256review: the Frame confirms that a7xx GPU firmware is versioned by chip (gen70900for the Adreno 750,gen70500for the X1-85), so a firmware bump that renames the file is a kernel-catalog change, not a packaging one (a6xx_catalog.c:1573-1577at the pin, perstage-firmware.sh’s header).
5. Boot flow, rootfs discovery, rollback and return to stock
Partition map of the two boot LUNs (GPT with 4 KiB sectors, parsed from the 256 MiB images in deckard-boot-luns;
names and sizes only, the content is proprietary):
- LUN 1 (28 entries):
xbl_a/b(3,584 KiB each;xbl_ahas attribute bit 2 set and typedea0ba2c-cbdd-4805-b4f9-f428251c3e98),xblconfig_a/b(240 KiB),shrm_a/b(256),aop_a/b(512),aopconfig_a/b(512),cpucp_a/b(1,024),cpucpconfig_a/b(64),tz_a/b(4,096),devcfg_a/b(128),hyp_a/b(8,192),uefi_a/b(32,768),uefisecapp_a/b(2,048),keymaster_a/b(512),qupfw_a/b(80). Every stage of Qualcomm’s boot firmware is A/B. - LUN 2 (14 entries):
bootenv,bootenvb(128 KiB),uboot_a(32 MiB),ubootenv_a(128 KiB),ubootfw_a(4 MiB), the same three for_b,uefivarstore,cdt,ddr,xblramdump,toolsfv,adpd. - The OS LUN (root slots,
var,home,esp) is not in these images; theblockdev_rohook locks/dev/sdb,sdcandsdd, so the OS lives on/dev/sda(inference). Sizes are unknown without the repair image.
Boot chain, as far as the public artefacts show it. Qualcomm PBL finds the XBL partition by a “magic parttype uuid”
(the kernelsetup.sh comment in the proprietary package says so; not quoted further) and runs the A/B boot firmware;
Valve’s U-Boot (U-Boot 2025.07-rc3-00656-gfbfd7e7ef9a0 for deckard board, from uboot.img strings) then:
- shows the “Deckard Boot Menu” (ANSI or graphical) with entries
Current <slot>,Previous <slot>,Boot from USB/USB: <disk> [<n> GB],Erase User Data,Repair Steam Installation,ADB mode,Battery Ship Mode, each asetenv slottarget ...;bootcount,bootlimit,altbootcmdandupgrade_availableare present (U-Boot’s standard boot-count fallback; that Valve uses it is inference); - loads
/boot/Image,/boot/initrd.uImageand/boot/dtbqcom/sm8650-<variant>.dtb(/boot/maindtb.dtbas an alternative) from the chosen root partition; the strings show adeckard_eeprom_initsymbol beside thesm8650-%s.dtbtemplate, so the variant comes from the EEPROM (inference); - builds the command line from a template ending
init=/sbin/init rauc.slot=<A|B> noresume rootwait earlycon, withroot=PARTLABEL=<rootfs-A|rootfs-B>(“Trying old rootfs partition names…” covers an older scheme), and the storeddefault_bootargs=loglevel=3 rd.log=console rd.log=file id_aa64mmfr0.ecv=1; the environment also holdsbootdelay=2andverify=n(the wholeuboot.envhas three variables); - can instead chain-load an EFI binary from
/efi/steamos/(deckard_bootefi); the aarch64steamcl.efi(“Stage 1 bootloader for SteamOS 3+”, the Deck’s chainloader) is packaged. Which path a shipping Frame takes is not established;fw_env.configindeckard-hw-supportsays the Deck-style partsets-from-bootloader scheme is the goal “until that is working”, so the direct U-Boot path is the current one (inference). - Linux starts at EL2 (mainline DTS commit message).
hyp_a/bpartitions exist andhyp.fwis 1.6 MB, so what runs there, if anything, is unknown.
In the initramfs: partsets parses rauc.slot= (and steamos.efi=) and builds /dev/disk/by-partsets/{A,B,self,other,shared};
etc_overlay mounts by-partsets/self/var and the /etc overlay; factory_reset mounts esp and, if
/esp/efi/steamos/factory-reset/*.cfg exists, re-creates var-A, var-B and home (mke2fs -t ext4, home with
casefold), zeroes ubootenv_a/b and reboots; initrd_shell starts adbd over USB FFS inside the initramfs
(vendor 0x28de, product 0x2460, “Steam Frame (initrd)”) with the auto-reboot countdown.
Updates to the boot firmware (deckard-boot-firmware, proprietary; behaviour only): for the inactive OS slot,
write uboot.img, ubootfw.img and uboot.env to uboot_<x>, ubootfw_<x>, ubootenv_<x> when their hash differs
from the partition’s; for the boot firmware, verify every *.fw against the active firmware slot, and if any differs
write all mismatching files to the other slot, read each back and compare, and only then splctl set-bootfw-slot.
Old layouts (EV2/EV3) get partition fix-ups. splctl (GPL-2.0+, the only public piece) also offers force-slot A|B,
force-usb and set-state A|B good.
Rollback and return to stock, the first-party recipe (Steam Support pages, read 2026-11-05):
| Need | Frame | gbook today |
|---|---|---|
| Boot the previous OS, keep data | hold AUX, press power, keep holding until the “SteamOS” menu, choose “Previous B” | KERN-A/KERN-B priorities exist in the ChromeOS GPT, gbook writes only KERN-A (ADR 0003) |
| Reset OS, keep games | “Repair Steam Installation” menu entry, or Settings > factory reset through steamos-reset-tool (platform.toml) | none |
| Wipe user data without booting | “Erase User Data” menu entry (/efi/steamos/factory-reset) | none |
| Reinstall from USB | boot menu (AUX + power), navigate with Volume Up/Down, select with AUX, boot the USB repair stick (4 GB image written with Rufus, Etcher or bzcat piped into dd), then “Wipe Device & Install SteamOS” from a desktop session | the stock recovery image on a USB stick (ADR 0001, Gate 1) |
| Unbrick the boot firmware | steamframe-oobe-repair-qdl-...tar.gz (4.1 GB): a QDL package, that is Qualcomm EDL flashing with the open qdl tool (inference from the name; not downloaded) | ChromeOS recovery mode; no EDL path documented |
| Know the state | “Purple, solid: Steam Frame is in bootloader” (LED reference) | the AP console over SuzyQ |
What gbook could adopt (proposals)
- Proposal (M4, the headline item): write mica’s recovery matrix in the same five rows, with the ChromeOS mechanisms
that already exist: KERN-A/KERN-B
cgptpriority, tries and successful bits are the analogue of Valve’s slot state (splctl set-state <slot> goodafter a verified boot,Previous= boot the other kernel partition), the stock recovery stick is the analogue of the repair image, and the “Erase User Data” step is amke2fsof the data partition from/init. The runbook already has the stick; what is missing is the documented “mark good” and “boot the other slot” commands, and a statement that there is no EDL path on mica. - Proposal (M3/M4): carry the U-Boot update discipline into
attempt.sh: write to the inactive slot, read back and hash-compare before switching.attempt.sh verifyalready hashes the stick; the “switch only after read-back” ordering is the part to keep when KERN-B comes. - Proposal (M3): an initramfs
adbd/sshdover the USB gadget is Valve’s answer to “the device is off Wi-Fi”; gbook’srootfs-access.mdalready studiedusb-drdgadget mode (sources.md row “Reaching the booted rootfs”); the Frame shows it working from the initramfs, before the root is found.
6. Userspace: GPU, x86 emulation, audio
GPU. The Arch rebuilds provide mesa 1:26.1.1-2 and vulkan-freedreno 1:26.1.1-2 (extra, 2026-10-30); the
Frame runs Valve’s own builds instead: deckard-mesa-linux-aarch64 27.0.0_devel+gitb826d29e-1 (“native aarch64
build”, 2026-11-04) ships libvulkan_freedreno.so (Turnip), zink_dri.so, vkms_dri.so, libgallium-27.0.0-devel.so,
libgbm, kmsro-named *_dri.so entries and the freedreno register XML; the 48-entry file list has no msm_dri.so
or freedreno_dri.so, so the build carries no freedreno GL driver and OpenGL goes through Zink on Turnip
(inference from the list). deckard-mesa-linux-x86_64 is “FEX x86_64 build”: a complete
x86_64 and i386 Mesa under /usr/share/guestos/fex-mesa/usr/{lib,lib32} (libvulkan_freedreno.so, zink_dri.so,
graphics_provider.json); that x86 games under FEX therefore use an x86 Turnip talking to the aarch64 kernel
directly rather than a thunk to the host driver is inference from the file list (the FEX blog describes thunks as
an option, and FEX-2610’s notes mention “Vulkan thunks”). deckard-mesa-android-aarch64
is Mesa for Lepton’s Android (vendor/lib64/hw/vulkan.freedreno.so, libEGL_mesa.so). deckard-gamescope-layer-x86_64
is a Vulkan layer for the x86 side (gamescope 3.16.24-1 in extra). Upstream, the Adreno 750 work that names Valve
content is the Igalia MR for Half-Life: Alyx (SP_CHICKEN_BITS_2 bit 30, “a performance uplift of 2.4%”, applied only
to that title) and the FEX-driven Turnip override that makes uncached buffers cached-coherent “when this option is
set” (!41323). None of the three MRs states Valve funding; the blog does for the FEX team.
x86 games. FEX is not a system package: the LGPL FEXBash wrapper in steamos-customizations-deckard runs
$STEAMPATH/steam/steamapps/common/FEX-Emu/fex-compat-tool inside SteamLinuxRuntime_4, so FEX arrives as a Steam
depot (“compat tool”) like Proton. fex-wtf (“what-the-FEX”) is the stats viewer. Windows games go through Proton
(partner docs), native arm64 Linux builds through “Steam Linux Runtime 3.0 ARM64 (Sniper)”, Android builds through
Lepton. The kernel side is the unaligned-atomic emulation commit (section 3), which the FEX blog credits for a 5x
latency improvement on unaligned atomics on the Frame’s Cortex-X4 versus the X925.
Audio. PipeWire 1:1.6.6-1, WirePlumber 0.5.14-1, alsa-ucm-conf 1.2.15.3-1 (extra). deckard-audio-config
(MIT) adds: UCM2 card files conf.d/SM8250_LPASS/{SM8250_LPASS,HiFi}.conf and conf.d/LPASS-SNDCARD/... with
codecs/sm8250-lpass/{SpkrEnableSeq,VAEnableSeq}.conf (the kernel card is qcom,lpass-sndcard, model “SM8250
LPASS”); WirePlumber rules that, for alsa_card.platform-*, set api.acp.disable-pro-audio = true and
device.routes.default-sink-volume = 0.6, and for alsa_output.platform-* force S16LE, 48,000 Hz, two channels
FL,FR, api.alsa.period-size = 256, api.alsa.headroom = 1024, node.force-quantum = 480, priority 800
(50-alsa-config.conf); 51-alsa-disable.conf disables three nodes by name
(alsa_output.platform-6b80000.lpass.playback.2.0, .3.0, alsa_input.platform-6b80000.lpass.capture.4.0);
spatial audio with a virtual 7.1 option (60-spatial-audio.conf, 70-spatial-node-config.conf, spatial-node-config.lua);
microphone processing with CPU and DSP echo-cancellation filter chains and a microphone-tracker.lua; Valve’s
plugins valve_boulder_speakers.lv2, valve_denver_speakers.lv2, valve_deckard_mic.lv2 (built with faust2lv2,
the Makefile says) and LADSPA vraudiocompositor.so; a deckard-audio-setup.service and PipeWire/WirePlumber user
drop-ins for SteamVR. The 0.5.6 and 0.5.7 notes add echo cancellation for external microphones and a fix for “audio
would stop working with USB or Bluetooth speakers if an application tried to access the microphone”.
Compared with gbook. mica’s speakers are WSA884x over SoundWire behind the ADSP (q6apm) with a topology file, which is the X1E80100 path; the Frame drives the LPASS from the application processor with I2C/I2S MAX98390 amplifiers and no DSP, so its UCM and kernel audio bits do not transfer. Its WirePlumber layer does.
What gbook could adopt (proposals)
- Proposal (M3 audio, cheap): a
wireplumber.conf.ddrop-in for mica’s card that fixes format, rate, period and quantum and disables the PCMs that are not wired (the Frame disables three by node name), instead of hiding unusable DAI links in the DTS. The currentscripts/audio-state.shread gives the node names. - Proposal (M3 Wi-Fi): the
60-ath12k-iommu-fq.rulesudev rule (LGPL, two lines) switches the WCN7850’s IOMMU group toDMA-FQ; its own comment gives the trade-off: withIOMMU_DEFAULT_DMA_STRICT=y“every RX buffer unmap does a synchronous SMMU TLB sync with IRQs off (~32% of NET_RX samples)”, and “a write-after-unmap bug corrupts memory silently instead of raising an SMMU fault”. gbook buildsIOMMU_DEFAULT_DMA_STRICT=y, so the same CPU cost applies; try it only with a measured before/after, and never on a debugging boot. - Proposal (later, suspend):
ath12k-irq-respread.sh(LGPL) records each ITS MSI’s effective CPU before suspend and pins it back after resume, because non-managed MSIs stay on CPU 0 after CPUs are hot-unplugged. Relevant once mica suspends. - Proposal (tooling):
steamos-manager’s device TOML shows GPU limits set through devfreq sysfs and CPU caps through cpufreq policies, both from userspace. gbook’sattempt.sh tracecould read the same sysfs nodes for the X1-85 when GPU clocks become a question; no kernel change.
Not found / not public
- No SteamOS-for-ARM source repository, image recipe or build pipeline:
potato/mash/*,potato/smash/holo,deckard/deckardos/{holo,linux-valve,linux-firmware,u-boot,steamos-efi,deckard-hw-support,deckard-audio-config,...},holo/{holo,steamos-efi,steamos-reset,atomupd-daemon,steamos-atomupd-client}all answer 404 to the public API (2026-11-05); GitLab blob search needs a token (401).holo-core-aarch64-previewhas no licence file of its own. - Valve’s kernel tree has no public branch or tag that this search found (
search/commits?q=hash:returns 0 for both tips; nolinux-valverepository on GitHub); the commits are readable only by SHA through GitHub’storvalds/linuxendpoint. Cite them by SHA with that caveat. - Mesa
b826d29eis not inmesa/mesa. - The repair image (4.07 GB) and the QDL package (4.10 GB) were not downloaded, so the OS LUN’s partition sizes, the
root filesystem type (U-Boot carries btrfs and ext4 code; the factory reset formats
varandhomeas ext4), the installer’s steps and the EDL flashing procedure are unread. The preview’ssystem.rootfs.zst(385 MB) likewise. - The proprietary packages (
deckard-boot-firmware,deckard-boot-luns,deckard-boot-images,deckard-hw-support) are described from their file lists, strings and GPT headers only. deckard-steam-rel(671 MiB) anddeckard-steamvr-relwere not read; where FEX’s binaries land on disk is known only from theFEXBashwrapper’s paths.- The Steam Frame release notes name no kernel, Mesa or firmware versions; the store page says “SteamOS 3”, the notes say “SteamOS 0.x (for Steam Frame)”.
- No first-party statement ties the Igalia or Connor Abbott Mesa work to Valve funding.
Sources
| URL | What it is | Read | Pin / commit | Licence |
|---|---|---|---|---|
https://api.github.com/orgs/ValveSoftware/repos | Valve’s GitHub organisation (55 repos) | 2026-11-05 | steamos_kernel last push 2023-05-22 | NOASSERTION |
https://gitlab.steamos.cloud/api/v4/projects (?search=, /<id>/repository/{tree,files,commits,branches,tags}) | Valve’s GitLab, public projects and files | 2026-11-05 | per project above | per project |
https://gitlab.steamos.cloud/holo/holo-core-aarch64-preview | Arch aarch64 preview package sources | 2026-11-05 | 67f0d559 | none stated |
https://holo-packages.steamos.cloud/holo-core-aarch64-preview/mash-20251118.3/ | preview binary repo (pacman.conf, pacman.mirrorlist, system.rootfs.zst) | 2026-11-05 | mash-20251118.3 (2026-07-10) | per package |
https://gitlab.steamos.cloud/frame-public/lepton | Lepton | 2026-11-05 | 5964972a, v3.0.10 | MIT, GPL-3.0 |
https://gitlab.steamos.cloud/frame-public/frame-developer-tools | developer tools, pacman/deckard-pacman-src | 2026-11-05 | 16d1942e | none stated |
https://gitlab.steamos.cloud/SteamHardware/SteamFrame | hardware references | 2026-11-05 | bdbb41ba | CC-BY-NC-SA-4.0 |
https://gitlab.steamos.cloud/holo/steamos-manager (data/devices/steam-frame.toml, data/platform.toml) | SteamOS Manager | 2026-11-05 | a3b20137 | MIT |
https://gitlab.steamos.cloud/holo/fakemachine | fakemachine fork | 2026-11-05 | 74dd229b | Apache-2.0 |
https://steamdeck-packages.steamos.cloud/archlinux-mirror/ | Deck/Machine package mirror (x86_64) | 2026-11-05 | listing | |
https://holo-packages.steamos.cloud/archlinux-deckard/archlinux/main/{core,extra}/os/aarch64/ and core.db.tar.xz | Frame base repos | 2026-11-05 | newest 2026-10-30 | Arch’s |
https://holo-packages.steamos.cloud/archlinux-deckard-hotfixes/ with deckard-arch-hotfixes.{db,files}.tar.xz | Frame Valve packages; eight packages downloaded and read: linux-72-deckard, -headers, deckard-boot-firmware, deckard-boot-luns, deckard-atomupd-config, deckard-audio-config, deckard-boot-images (listed only), steamos-customizations-deckard, deckard-hw-support, steamos-efi | 2026-11-05 | newest 2026-11-04 | per package (table in section 1) |
https://steamdeck-images.steamos.cloud/recovery/ | repair images (HEAD only) | 2026-11-05 | 20260922.5153644-0.3.0 | not stated |
https://store.steampowered.com/steamos/download/?ver=steamframe | download page (redirect target read from its HTML) | 2026-11-05 | ||
https://github.com/torvalds/linux/commit/f905cb6e600a and abbbaa99ac66 | Valve Deckard DTS and binding | 2026-11-05 | f905cb6e600a | BSD-3-Clause / GPL-2.0 |
https://api.github.com/repos/torvalds/linux/commits/344f27110f05 (and compare/v7.2..., commits?sha=...&path=...) | Valve’s kernel commits through GitHub’s fork network | 2026-11-05 | 344f27110f05, ee93c0d9394b, base 8d3ae59288f1 | GPL-2.0-only |
https://patchwork.kernel.org/api/patches/?q=deckard&project=linux-arm-msm | Deckard follow-up patches | 2026-11-05 | ||
https://partner.steamgames.com/doc/steamhardware/steamframe and /setup, /debugging, /loadgames, /compat, /engines/custom | Steam Frame developer documentation (fetched with firecrawl) | 2026-11-05 | Valve | |
https://help.steampowered.com/en/faqs/view/1B71-EDF2-EB6D-2BB3, 65B4-2AA3-5F37-4227, 2668-0B6E-79E0-A85B, 4F01-E85C-4977-EA0E | Steam Support: recovery, install, Frame guide, LEDs | 2026-11-05 | Valve | |
https://api.steampowered.com/ISteamNews/GetNewsForApp/v2/?appid=4165890 | SteamOS-for-Frame release notes | 2026-11-05 | 0.3.0 … 0.5.7 Beta | Valve |
https://store.steampowered.com/sale/steamframe and the 2026-09-14 announcement post | product page and launch post | 2026-11-05 | Valve | |
https://github.com/FEX-Emu/FEX, https://fex-emu.com/Scourge-of-emulation/, https://fex-emu.com/FEX-2610/ | FEX and its blog | 2026-11-05 | FEX-2610 | MIT |
https://github.com/bylaws/linux/commit/7ae989a43ae7e3cb8007ac21c28dacc24c9d8320 | the unaligned-atomics patch as published by its author | 2026-11-05 | branch fex-unaligned-atomic | GPL-2.0 |
https://gitlab.freedesktop.org/api/v4/projects/176/merge_requests/{41323,44840,44348,45615} | Mesa Turnip MRs | 2026-11-05 | merged 2026-05-08, 10-06, 09-14, 10-27 | MIT |
~/.cache/gbook/linux-a804c57, ~/.cache/gbook/build-a804c57/.config, kernel/patches/*.patch line 1, the three script headers | gbook’s own pin, config and patch list | 2026-11-05 | a804c57 |
Decisions (owner, 2026-11-05, 10:52 AM EST)
Grilling round on the five candidates; the owner took every recommendation:
- M4 recovery matrix: write it now as the M4 planning doc, before any SSD work (Valve’s five rows mapped onto ChromeOS mechanisms).
ramoops: add on a later diagnostic boot, with a stock-safe address and ADR 0004’s “unverified” marking.- ath12k IOMMU DMA-FQ rule: measure first, one boot with the rule, iperf against a LAN host before and after; never on a debugging boot.
- WirePlumber per-card drop-in: adopt when PipeWire enters the rootfs.
- Initramfs: the block-layer read-only lock of the stock partitions from
/initnow (re-implemented, not copied); the auto-reboot countdown shell later, with M4.
Each is on the task list (TaskList); this note is the source they cite.
Evidence
Dated read-only reads of the device and of each boot, redacted of per-unit identifiers at collection time. Newest first.
- Phase 3: Wi-Fi firmware, WCN7850 up (2026-11-03)
- USB under mainline on mica (2026-11-02)
- Every enabled DT node binds: bandwidth monitors, videocc, camcc (2026-11-04)
- Phase 3: touchpad, touchscreen and the Ti50 TPM (2026-11-03)
- Suspend (s2idle) on mica, 2026-11-04
- Phase 3: SCMI binds, the CPUs get cpufreq (2026-11-03)
- Rootfs from USB: the first two ROOT boots (2026-11-04)
- Recon 2026-11-02
- Recon 2026-11-02
- Recon 2026-11-02
- Recon 2026-11-02
- Recon 2026-11-02
- Recon 2026-10-21
- PCIe under mainline on mica (2026-11-02)
- Phase 3: panel-edp knows the mica panel (2026-11-03)
- Phase 3: GPU, the Adreno X1-85 boots its GMU (2026-11-03)
- Phase 3: the GMU gets a driver, the crypto node goes (2026-11-03)
- First boot of mainline Linux on mica (2026-11-02)
- Phase 3: the ChromeOS EC, built-in keyboard, keyboard backlight and fans (2026-11-03)
- Phase 3: external DP over Type-C works (2026-11-03)
- Phase 3: display, msm DRM drives the eDP panel (2026-11-03)
- Phase 3: the CDSP boots from stock firmware (2026-11-03)
- M3: the sound card binds (2026-11-04, 11:32 PM EST)
Phase 3: Wi-Fi firmware, WCN7850 up (2026-11-03)
Attempt 2026-11-03-1. Image 550a4055eee29513: kernel, config and DTB unchanged from attempt 2026-11-02-6.
The new /init and the Wi-Fi firmware went into the initramfs (commit 07c76e5). Source: the AP console log
dumps/firstboot/2026-11-03-ap.log (gitignored); MACs are not printed.
Result
The Wi-Fi works: the ath12k probe completes. wlan0 stays down; nothing scans or transmits.
- Firmware boot: MHI powers the chip on and it reaches mission mode, then QMI reports
chip_id 0x2 chip_family 0x4 board_id 0xff soc_id 0x40170200. - Firmware version:
fw_version 0x1103006c, buildWLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3(2026-03-06). That’s the linux-firmwareamss.bin. - Interfaces:
GBOOK: wifi-phy=phy0,GBOOK: wifi-net=wlan0 operstate=down driver=ath12k_wifi7_pci wireless=yes,wifi-wait=0s. - No errors: no
failed to fetch board dataand no regdb warning. - PCI subsystem IDs, model-level and new in the
pci=line:- WCN7850
0004:01:00.0: subsystem105b:e0ee; - NVMe
0006:01:00.0: subsystem1344:1100, stilldriver=none(# forbid-y: BLK_DEV_NVME).
- WCN7850
Board data
WCN7850 matches board data by bus and board (ath12k/wifi7/pci.c:135,159 at a804c57). The name it builds is
bus=pci,vendor=17cb,device=1107,subsystem-vendor=105b,subsystem-device=e0ee,qmi-chip-id=2,qmi-board-id=255
(core.c:232-247). board-2.bin in linux-firmware-atheros 20260916-1 has exactly that entry. It also has
variant=DE01 and DE02 entries, but those are only picked through an SMBIOS/ACPI variant, which a DT boot doesn’t
have (qmi.c:2973-2982). A board-data miss would have aborted the probe before wlan0 registered (core.c:520-580),
so the mica module uses the generic Foxconn 105b:e0ee entry. The stock bdwlan.* files from modem_a are not
needed.
Other captures (unchanged from attempt 6)
- ADSP running; CDSP offline.
- Type-C ports 0 and 1.
- Both PCIe links up: Gen4 x4 and Gen3 x2.
- Deferred:
arm-scmi(mailbox17430000) and3da0000.iommu(waits for GPU clock controller3d90000; also seen in the 2026-11-02 log). - The 5 s settle in
/initworks: the GBOOK lines no longer interleave with probe messages. - Ramoops readback: the newest dropbox
SYSTEM_LAST_KMSGentry (12:53 PM) holds this boot and ends at[ 81.894902] reboot: Restarting system. - The udev no-automount rule works: on 2026-11-03 the kernel stick plugged into the build host with nothing mounted.
Firmware in the initramfs (not in git, ADR 0005)
Decompressed from the build host’s linux-firmware-atheros 20260916-1 (/usr/lib/firmware) into ~/.cache/gbook/firmware:
| File | sha256 |
|---|---|
ath12k/WCN7850/hw2.0/amss.bin | 43aadfd3df887f27de74020273aee484bac6a31dd53068f91baf2a9b094d6a68 |
ath12k/WCN7850/hw2.0/m3.bin | 0e72f44df7defc269fe92dcea25d4d409046c04b77d41c510c52879b3dfc1055 |
ath12k/WCN7850/hw2.0/board-2.bin | 1abee7132dbccb523cca44a8de4e8968aa7bf5a5fcc032c338f687f94ea5bf4e |
regulatory.db (wireless-regdb) | 7e236caecd939c8ec98be4870bf30422f28ffef2565a38aaaa2d9ddabd0c2641 |
regulatory.db.p7s | 50332f0db09b8bcc719235ec4985c27377f2cc2f42abb7b5dcf951866c5a888a |
Notice.txt (the licence) sits next to them. The firmware tree hash in manifest.txt is
8a75fc5ebfeea9766b1d59c6a718c42e080721f68ccb078df3edf0ce59f03075 (68 files, ADSP included).
Stock reference (read, not used)
modem_a/image/kiwi/(the cnss2 layout):amss.bin,amss20.bin,bdwlan.e01…e54,bdwlan01.*,bdwlan.elf,regdb.bin,phy_ucode*.elf.- Stock Android drives the chip with the downstream
cnss2/cnss_pcidriver (/sys/module/cnss2), through the DT nodeqcom,cnss-kiwi@b0000000. - Shell can’t read the PCI IDs or
/sys/kernel/cnss/*there (Permission denied).
First try refused: “No valid image detected”
The first stick boot of this image failed with “No valid image detected” on screen. A re-insert gave the same error.
- What the firmware log showed: every rescan printed
GptNextKernelEntry … s0 t0 p0,no more kernelsandvb2api_load_kernel() = 0x100b2000. - What was on the stick: read back on the build host, KERN-A had priority 0, tries 0. The image had written 15/3.
- Why a re-insert can’t help: vboot marks a partition it rejects as bad (
GPT_UPDATE_ENTRY_BAD,2load_kernel.c:743atfirmware-bluey-16702.B34fbbe6). - Why the reason is lost: the log is a 128 KiB ring (
Read cbmem console: size=131064). It was full of rescans and menu lines, and the first attempt’s error line was gone. - Outcome: the stick was rewritten with the same image (read-back identical), and the next boot worked.
- Cause: unknown. Inference: a transient USB read on the first try, since the bytes and the size (the FIT is
58.9 MiB, against the 256 MiB
KERNEL_SIZE) were identical both times. The runbook’s failure playbook now says to read the log right after the first refusal and to rewrite the stick before retrying.
Attempt 2026-11-03-2: join, DHCP, ping
Image ec9f9f4b96ad4f86 (commit 4428f27). The kernel, DTB and firmware are as in attempt 1. The initramfs adds:
iw:arm64 6.17-1+b1andwpasupplicant:arm64 2:2.11-7from sid, with their 13 shared libraries in/lib;- the owner’s network block, from
~/.local/state/gbook/wifi.conf(mode 600, never printed or hashed).
AP console, MACs and the SSID redacted:
| Step | Result |
|---|---|
| Regulatory | iw reg set US → country US: DFS-FCC; phy0 is self-managed (the firmware reports US) |
| Scan | exit=0 bss=17 2g=7 5g=8 6g=2 strongest=-24.00dBm; own SSID seen at −33 dBm |
| Join | authenticated, associated (status=0) after 15 s; WPA: Key negotiation completed [PTK=CCMP GTK=CCMP] |
| Link | 6135 MHz (6 GHz), −41 dBm, tx 2882.6 MBit/s 320MHz EHT-MCS 7 EHT-NSS 2 (Wi-Fi 7) |
| DHCP | busybox udhcpc: lease of <the device's lease>/24 from <the gateway>, 86400 s |
| Ping | gateway <the gateway>: 3/3 received, rtt 3.4/9.7/16.2 ms |
| End | deauthenticating … (Reason: 3=DEAUTH_LEAVING), wlan0 down, reboot: Restarting system at 118.7 s |
The panel goes dark at about 33 s: VREG_EDP_3P3: disabling. The kernel switches off unclaimed regulators, and no
display driver holds the panel supply yet. That’s expected while only simpledrm drives the screen.
The wpa_supplicant log printed the own SSID once; /init now redacts SSID='…' too.
Update 2026-11-03: the cause of the “No valid image” refusals
The inference above (“a transient USB read”) was wrong. Attempt 2 was refused the same way on its first two power-ons.
The EC console logged boot_keys: recovery (boot_keys_value=0x00000009) before both refused boots in
dumps/firstboot/2026-11-03-ec.log, and not before either boot that worked.
The owner had powered on with the recovery keys, because the agent’s instructions said so; runbook section 6 says
plain power-on. In recovery mode vboot scans the stick for a recovery kernel. The dev-signed kernel fails that check,
and the partition is marked bad (GPT_UPDATE_ENTRY_BAD, cgptlib.c:155 at 34fbbe6). On that power-on, the first
USB scan already showed s0 t0 p0 (owner photo of the firmware log).
After a rewrite and a power-on with no keys held, the stick booted. Runbook section 6 step 3 now forbids the recovery keys with the image stick in.
Attempt 2026-11-03-3: netlog over Wi-Fi
Image 6582ef037a626828, commit d3b37f8. After GBOOK: end, /init sends /tmp/gbook.log (every GBOOK: line) and
the whole dmesg with busybox nc to <the build host's LAN address>:5140. That target is in /etc/gbook-netlog, written by
package-image.sh. On the build host, scripts/attempt.sh netlog start runs socat bound to the LAN address. It accepts only
<the device's lease> (range=), and the owner-approved ufw rule allows only that peer.
- Sent:
GBOOK: netlog=sent 1176 lines to <the build host's LAN address>:5140 exit=0. - Received:
dumps/firstboot/2026-11-03-netlog.txt(mode 600), 1176 lines: 361GBOOK:lines and 813 kernel lines. - The netlog covers the whole boot: it starts at
[ 0.000000] Booting Linux on physical CPU 0x0000000000and includes theLinux versionbanner. The AP console starts at about 0.6 s; ramoops starts mid-boot and has no banner. - Redaction: no MAC-like strings and no SSIDs; 12
<mac>substitutions. - One fix: the serial pattern also caught
Serial: AMBA PL011andSerial: 8250/16550. It now matches onlySerialNumber:andserial number:. - The rest matched attempt 2: join after 15 s, DHCP
<the device's lease>/24, gateway ping 3/3 (11.1 ms average), reboot at 117.6 s, stock Android back.
The Googlebook now has three independent log channels: the SuzyQ AP console, ramoops through dropbox, and netlog.
USB under mainline on mica (2026-11-02)
Attempt 2026-11-02-3: the first-boot image plus
kernel/mica-usb.config (the USB chain built in) and USB captures in /init. Source:
the AP console over SuzyQ (raw log dumps/firstboot/2026-11-02-ap.log). Same DTB as the first boot.
Result: USB host works on all four controllers
| Bus | Controller (DT) | Device | Speed |
|---|---|---|---|
| 1 | a200000 usb_2 (USB 2 only) | Integrated Webcam FHD 1bcf:28dc | 480M |
| 2, 3 | a400000 usb_mp (multiport) | FPMCU 18d1:5406 | 12M |
| 4, 5 | a600000 usb_1_ss0 | none (inference: the left Type-C port, holding the SuzyQ) | |
| 6, 7 | a800000 usb_1_ss1 | the kernel image stick, Lexar 21c4:0809 → sda, sda1 = KERN-A | 480M |
- Root hubs: four USB 2 (480M) and three SuperSpeed (10000M), one per SS-capable controller.
- The right Type-C port is
usb_1_ss1(the stick booted from it and enumerated on bus 6). - SuperSpeed does not come up on Type-C yet: the same stick runs at 5 Gbps on the build host. Inference: the PS8830
retimer and the QMP combo PHY get no orientation or mode, because
pmic-glinkstays deferred (qcom_pmic_glink: failed to initialize pdr): it needs the ADSP running with its firmware.
Still deferred (/sys/kernel/debug/devices_deferred)
| Device | Reason |
|---|---|
pmic-glink | failed to initialize pdr (ADSP not running) |
3da0000.iommu (GPU SMMU) | supplier 3d90000.clock-controller (GPU clock controller) not ready |
firmware:scmi | supplier 17430000.mailbox not ready |
aux_bridge.0-.3 | failed to acquire drm_bridge (DisplayPort chain) |
1bf8000.pcie, 1c08000.pcie | Failed to parse Root Port: -517 |
What changed from the first boot
The first boot left every dwc3 deferred (“failed to register DWC3 Core”) because the eUSB2 and QMP PHYs, the eUSB2
repeaters, the PTN3222 redrivers, GENI I2C, TYPEC and PS883X were all =m in defconfig and no module loads. The
QMP children also need their menu parent PHY_QCOM_QMP=y (drivers/phy/qualcomm/Kconfig:53-125 at a804c57);
scripts/build-kernel.sh caught that demotion.
Attempt 2026-11-02-4: ADSP (failed, cause found)
kernel/mica-adsp.config + the stock adsp.mdt firmware in the initramfs. Results (AP console):
remoteproc0: Booting fw image qcom/x1e80100/google/mica/adsp.mdt, thenqcom_q6v5_pas 6800000.remoteproc: Error in getting resource table: -5;adspstaysoffline.pmic-glinknow probes (no longer deferred): the PD mapper and QRTR are built in.qcom-battmgr-{ac,bat,usb,wls}register but read empty, since the ADSP is down. No Type-C ports yet. The stick is still at 480M.- The GPU SMMU and the DP aux bridges are no longer on the deferred list; SCMI and PCIe still are.
Cause: the resource-table request runs only when the PAS node has iommus (qcom_pas_parse_firmware, a804c57),
which mainline’s x1-el2.dtso adds for the 9345. Mica’s TrustZone answers -EIO. The stock mica tree has no iommus
on the ADSP or CDSP node (entry00-base.dts:5513, :12925) and uses the same adsp.mdt/adsp_dtb.mdt names.
Fix: dts/mica-el2.fixups deletes both properties after the overlay (attempt 2026-11-02-5).
Attempt 2026-11-02-5: ADSP running, SuperSpeed on Type-C
Same kernel and firmware as attempt 4; the DTB drops the PAS iommus (dts/mica-el2.fixups).
remoteproc remoteproc0: remote processor adsp is now upat 1.6 s;adsp running.cdspstays offline (no firmware shipped:-2).pmic_glinkUCSI registers two Type-C ports:port0(host, sink) andport1(host, source: powering the stick), bothorientation=normal.qcom-battmgranswers: USB supply online, batteryCharging(inference: power from the SuzyQ link in the left port); capacity still empty at capture time. UCSI adds twoucsi-source-psysupplies.- The kernel image stick enumerates on bus 7, the SuperSpeed hub of
usb_1_ss1, at 5000M (480M before). - Still deferred:
arm-scmi(mailbox) and both PCIe controllers.
USB on mica now works end to end: all four controllers, both Type-C ports at SuperSpeed (the right port tested), Type-C role/orientation and battery reporting through the ADSP.
Every enabled DT node binds: bandwidth monitors, videocc, camcc (2026-11-04)
Attempt 2026-11-03-35 (written before midnight, booted 2026-11-04 about 12:00 AM ET): kernel #18 (commit
d41ab28, kernel/mica-sync.config), the board DTS with ADR 0008
(no PSCI domain idle states), image 4c8699959d256a77. One cold start (the EC reset itself at power-on:
Reset cause: sysjump hard efs). Sources: the netlog dumps/firstboot/2026-11-04-netlog.txt and the AP console log
(both gitignored), attempt.sh compare against -34 boot 3.
Change
kernel/mica-sync.config builds in three drivers that arm64 defconfig has as modules (the initramfs loads none):
QCOM_ICC_BWMON: the four bandwidth-monitor PMUspmu@24091000(qcom,sc7280-llcc-bwmon) andpmu@240b3400,@240b5400,@240b6400(qcom,sdm845-bwmon).SM_VIDEOCC_8550:clock-controller@aaf0000(qcom,x1e80100-videocc).CLK_X1E80100_CAMCC:clock-controller@ade0000.
The symbols come from scripts/dt-kconfig-map.py at a804c57; their four lines left kernel/dt-gaps.txt in the
same commit.
Result
unbound=went from 6 to 0. No line in dmesg sayssync_state() pending due to, so every interconnect and clock provider reachedsync_state().deferred=is empty.- No probe error from the new drivers (dmesg has no failure line naming bwmon, videocc or camcc).
- SCMI: 78 replies, max 3.9 ms, 0 timeouts. That is the fourth stall-free cold boot with no domain idle states. cpufreq is on all three clusters (710 MHz to 3.42 GHz).
- The touchscreen still fails (
hid-generic ... -22). Its rail and enable pin read on this time (ts-vreg=enabled users=1,gpio88 out high, against disabled/low on -34 boot 3), which is the same failure with a different probe order (inference). comparelabels this boot “warm”: it took the AP reset at EC 179 s, which was/init’s reboot after the run, while the EC’s own reset at power-on restarted its clock. The boot was cold.
Open
- What the providers’
sync_state()changes in power (bandwidth and clock votes dropped to what consumers ask for) has not been measured. The suspend test covers it.
Phase 3: touchpad, touchscreen and the Ti50 TPM (2026-11-03)
Attempts 2026-11-03-15 (image b5f533fb178c67a2, kernel #13, commit 7bffb22) and 2026-11-03-16 (image
bf2dd269aa121412, same kernel, /init from commit a9d7aeb). Owner pick after the retro (2026-11-03): touchpad,
touchscreen and TPM. Sources: the netlog dumps/firstboot/2026-11-03-netlog.txt and the AP and EC console logs
dumps/firstboot/2026-11-03-{ap,ec}.log (all gitignored), and the pinned tree a804c57.
Why
The retro’s gap check (scripts/dt-kconfig-map.py --gaps, commit 4e1f825) listed
hid-over-i2c I2C_HID_OF=m touchscreen@10,touchpad@2c and google,cr50 TCG_TIS_I2C_CR50=m tpm@50. Both drivers
are =m in arm64 defconfig and the initramfs loads no modules, so none of the three nodes had ever bound.
Changes (commit 7bffb22)
kernel/mica-touch.config:I2C_HID_OF,I2C_HID_CORE,HID_MULTITOUCHbuilt in.kernel/mica-tpm.config:TCG_TIS_I2C_CR50built in;TCG_TPM2_HMACforbidden at=y(it creates a primary key in the null hierarchy at every boot,tpm2-sessions.c:1408-1412). TPM traffic is probe-time startup, self-test and capability reads, andTPM2_Shutdown(CLEAR)at reboot (tpm-chip.c:272).- Both compatibles left
kernel/dt-gaps.txt. /init:touch-*captures with a touch window,tpmandhwrngcaptures; fromb6b6662anda9d7aebthehid-over-i2cIRQ counts around the window and a 30 s window behind a banner.
TPM: pass (attempt -15)
cr50_i2c 1-0050: ti50 OT TPM 2.0 (i2c 0x50 irq 177 id 0x5066)at 2.80 s (the driver’s Ti50 name,tpm_tis_i2c_cr50.c:693-702).tpm=tpm0 tpmrm0 major=2 driver=cr50_i2c;hwrngliststpm-rng-0besidesmccc_trng(current).
Touchpad: pass (attempt -16)
hid-multitouch 0018:27C6:0D4F:Mouse(event0) andTouchpad(event1) on2-002c, both boots.- -15: 0 events, but the owner missed the 20 s prompt. -16:
touch-events=event1 bytes=373296 events=15554in the 30 s window; IRQmsmgpio 3 Level hid-over-i2cwent 646 to 2888.
Touchscreen: probes on one boot of two, reports never arrive whole
- -15:
hid-multitouch 0018:1FD2:5010bound (event2, andevent3namedUNKNOWN, likely a vendor collection; inference); no touch in the missed window. - -16: the descriptor read at probe came back garbled:
hid-generic 0018:FFA5:FFFF.0001: unexpected long global item,probe with driver hid-generic failed with error -22. In the window the IRQ (msmgpio 51) still went 305 to 921, and each report failedi2c_hid_of 0-0010: i2c_hid_get_input: incomplete report (64/106)(i2c-hid-core.c:570-573: 64 is the buffer size from the bad descriptor, 106 the size the panel sent). - So the panel answers and reports touches; the descriptor read at probe is unreliable (inference: power-on timing).
The node matches stock (descriptor address 0, TLMM 51 level-low, L15B and the TLMM 88 3.3 V switch with
startup-delay-us = <150000>andregulator-boot-on, overlayentry01fragments 69 and 75). The 9345’s panel is a different one (descriptor address 1), so it gives no precedent. - Candidate fix (not tried):
post-power-on-delay-msontouchscreen@10(i2c-hid-of.c:58-59,:105). Not in the stock tree, so it would be marked unverified (ADR 0004). Judge it over several boots: one good probe in two is the baseline.
Other results on these boots
compareprintedstart: coldfor both (the retro’sboot_start.pyfix works) and noWRONG IMAGE.unbound=lists1dfa000.crypto, the four CPU bandwidth-monitor PMUs,3d6a000.gmu,aaf0000andade0000clock controllers: the gap list’s leads, as expected.- SCMI failed on both cold starts (
Firmware version 0x0,cpufreq=none): four of the last five cold starts (scmi-2026-11-03.md).
Update: touchscreen attempts -17 to -19 (2026-11-03, 7:32 PM ET)
| Attempt | Change | Touchscreen at probe | In the window |
|---|---|---|---|
-17 | post-power-on-delay-ms = <150> (commit 42047ed) | ID right (1FD2:5010), report descriptor usage index exceeded, -22 | IRQ 1 to 730, every report incomplete (64/106) |
-18 | same, plus dyndbg for i2c-hid-core.c:944 and :804 (GBOOK_CMDLINE_EXTRA, commit bc705a1) | see the dump below | no input device |
-19 | i2c8 at 100 kHz (commit 2d4c257) | no message at all: no dump, not deferred, not unbound | no IRQ line registered |
The -18 dump (i2c_hid_of 0-0010: HID Descriptor:):
1e 00 00 01 a7 03 21 00 24 00 6a 00 25 00 6c 00 22 00 23 00 a5 ff ff ff ff ff ff ff ff ff.
- The first 20 bytes are a valid HID-over-I2C descriptor: length 30, version 1.00, report descriptor
0x3a7(935) bytes at register0x21, input register0x24,wMaxInputLength0x6a= 106, output0x25/108, command0x22, data0x23. - From byte 20 the read returns
0xff: vendora5 ffinstead ofd2 1f, thenffto the end. The report descriptor read gives05 0d 09 04 43and then0xff(a valid one starts05 0d 09 04 a1 01). - The touchpad’s dump on
2-002c(another bus) is clean:... 2f 00 ... c6 27 4f 0d ...(27C6:0D4F,wMaxInputLength47). - So the panel’s descriptor is right and the read loses data partway: the line reads high (
0xff) once the device stops driving it (inference). The “64” on -16/-17 was a corruptedwMaxInputLength. The 30-byte read uses the GENI FIFO path (DMA starts at 32 bytes,i2c-qcom-geni.c:528), so SE DMA is not the cause.
-19 at 100 kHz is inconclusive: the probe ran earlier than on any other boot (touchpad at 1.5 s against 3.2 to
5.4 s), and the one silent failure path in i2c-hid is nothing at this address (i2c-hid-core.c:1055, dev_dbg,
not enabled). Speed and timing are mixed.
Reverted (commits 9126648, 6d66b05): the 100 kHz bus and the power-on delay, neither shown to help; the DTS
traces to stock again. Three boots did not converge, so the touchscreen is parked.
Leads for a later session (inference, untested):
- Stock reaches the panel through Qualcomm’s vendor GENI I2C driver at 400 kHz; mainline’s GENI clock table and
transfer handling differ. Compare the SCL timing values (
i2c-qcom-geni.cclock table) with a stock-side read. - Enable
i2c-hid-core.c:1055and the GENI I2C error lines in the next touch boot, so a silent probe failure shows. - The panel’s own reset or report-enable line (TLMM 89,
dts/README.md) and its timing against the first read.
Update: the touchscreen on the wire, attempt -21 (2026-11-03, about 8 PM ET)
Attempt 2026-11-03-21 (kernel #16, commit 778abbf) traced the i2c and qcom_geni_i2c events
(kernel/mica-debug.config, debug-tools.md); the probe-time trace is in the netlog.
- Descriptor read clean this time (30 bytes, FIFO path):
… 22-00-23-00-d2-1f-10-50-30-50-00-00-00-00(1FD2:5010, version0x5030). - Report descriptor (935 bytes, DMA path):
05-0d-09-04thenffto the end; hid-generic failed-22. - Input reports (64-byte reads, the fallback buffer): every one starts
6a-00-10-03-01-48-36-1d-33(length 106, report ID0x10), and about half turn toffafter 9 to 12 bytes. - No controller error:
geni-erris empty; the GENI status shows ordinary done interrupts (m_stat=0x08000081). - Bus setup identical on both touch buses:
clk_freq=400000 clk_div=2 t_high=5 t_low=11 t_cycle=22(i2c-qcom-geni.c:188, the 19.2 MHz table). By the driver’s formula (:180-182) that is about 1.15 µs low and 0.52 µs high, under the I2C Fast-mode minimums (1.3 µs, 0.6 µs); the comment says the firmware adds cycles the formula leaves out (:176-178), so the real timing is unknown (inference). The touchpad tolerates it. - Reading: the panel answers and starts each reply correctly, then lets the data line float (
0xff) at a varying byte, with no error the controller sees. Bus timing that this touch controller does not tolerate fits (inference); -19’s 100 kHz boot was inconclusive and is worth repeating with the trace on.
Update: 100 kHz with the trace, attempt -22 (2026-11-03, 8:14 PM ET)
i2c8 at 100 kHz again (commit 20a1f8a), with tracing and dyndbg on i2c-hid-core.c:1055 and
i2c-qcom-geni.c:235-306. The bus came up at 100 kHz (clk_div=7 t_high=10 t_low=12 t_cycle=26), and the
touchscreen did not acknowledge its address: geni_i2c a80000.i2c: NACK: target device unresponsive, check its power/reset-ln twice, then i2c_hid_of 0-0010: nothing at this address: -6. That is -19’s silent failure.
So the panel answers at 400 kHz (and drops out mid-read) and not at all at 100 kHz: the slower bus made it worse, so
“too fast for the panel” does not fit (inference). Reverted (commit 6671f61); the DTS traces to stock again.
Parked. Untested leads: the panel’s own enable and reset sequencing before the first read (TLMM 89, the 3.3 V
switch on TLMM 88, vreg_l15b), compared with what stock does at boot; and the stock driver’s I2C settings.
Update: panel follower and a full stock comparison, attempt 2026-11-04-1 (2026-11-04, 12:15 AM ET)
Offline first: the touchscreen’s DT matches stock line for line. ts0_default (int-n gpio51; reset-n gpio48
output low; report-enable gpio89 output low), the 3.3 V switch on gpio88 with a 150 ms startup delay, vdd L15B,
IRQ_TYPE_LEVEL_LOW, hid-descr-addr 0 (stock entry01-overlay.dts fragment@68 and fragment@75). The bus pins
match as well: stock sets bias-pull-up plus qcom,i2c_pull, and mainline’s bias-pull-up = <2200> selects the same
TLMM I2C strong pull (pinctrl-msm.c:467, i2c_pull_bit = 13 in pinctrl-x1e80100.c at a804c57).
Attempt 2026-11-04-1 (kernel #18, dts/diag/touch-panel-follower.dtsi, commit 5b9ee4e): panel on the
touchscreen node points at the eDP panel, so i2c-hid powers and probes it only after the panel is prepared, as the
ChromeOS in-cell boards do (sc7180-trogdor-homestar.dtsi:119). The owner touched the screen during the prompt.
- The link works and changes nothing. The panel came up at 3.95 s and the touchscreen probed after it, at 4.30 s.
The 30-byte HID descriptor read cleanly. The 935-byte report descriptor read
05-0d-09-04-43and then0xffto the end (unknown main item tag 0x4,-22). - The interrupt fires and the controller reports. There were 1936
msmgpio 51interrupts on CPU 0 and 1940 input reads (64 bytes each; the device announces 106). 1549 of them turn to0xfffrom byte 5 to 8, while the rest read cleanly (6a-00-10-03-01-b6-47-cc-3a-c6-07-cc-04-00 …). There was no controller error. - Reading (inference): the controller starts every reply right and then stops driving SDA at a random byte. With the
DT, pins and pull-ups as stock and the panel timing ruled out, what is left is the controller side: the GENI SCL
timing (19.2 MHz table: t_low ≈ 1.15 µs, t_high ≈ 0.52 µs by the formula, under the Fast-mode minimums,
i2c-qcom-geni.c:174-188) or clock stretching that the controller does not honour. Stock runs a vendor GENI driver whose timing cannot be read from the image. -22’s 100 kHz NACK does not fit timing alone. - The fragment stays until the owner decides (status: failed for the probe; the link itself is harmless).
Update: in-spec GENI timing, attempt 2026-11-04-2 (2026-11-04, 12:26 AM ET)
kernel/patches/0008 (kernel #19) set the GENI 400 kHz entry to 2/6/13/24, and the trace confirms it on both
touch buses (geni_i2c_bus_setup: a80000.i2c: … t_high=6 t_low=13 t_cycle=24, the same on b80000.i2c). No panel
follower. No change: the touchscreen’s HID descriptor read FFFF:FFFF this time (-22), 608 of 773 reads ended
in 0xff, and there was one NACK: target device unresponsive. The touchpad (27C6:0D4F) and the TPM worked at the
new timing. SCL timing is ruled out (within the formula’s view of it). The touchscreen is parked again. Every
setting readable from the stock tree matches; what is left is the vendor GENI driver’s behaviour, or the panel
controller’s own state. The patch goes (a failed diagnostic, ADR 0007), as does dts/diag/touch-panel-follower.dtsi.
Suspend (s2idle) on mica, 2026-11-04
ADR 0009’s revisit trigger: one s2idle round trip per boot, from /init with gbook.suspend=1 on the command line
(commits cf49174, c3fa7e7). The wake is the cros-ec-rtc alarm, 20 s after entry. Six cold boots, attempts
2026-11-04-5 to -10 (dumps/firstboot/attempts.tsv). Sources: the AP and EC SuzyQ console logs, and stock’s
SYSTEM_LAST_KMSG (ramoops) via scripts/attempt.sh readback. Times are kernel uptime unless marked EC.
Result
Suspend works; resume resets the SoC every time. Entry, the device suspend and the wake all work. The resume dies in the PCIe noirq resume, and the EC then power-cycles the AP. No netlog arrives from these boots.
| Attempt | Kernel, DT extra | Where the resume died |
|---|---|---|
| -5 | #21 | Unseen: the console sleeps during suspend (Suspending console(s)) |
| -6 | #21, no_console_suspend | gcc_pcie_6a_gdsc status stuck at 'off' (WARNING gdsc.c:192, genpd_resume_noirq), then silence |
| -7 | #21, dts/diag/no-pcie6a.dtsi | gcc_pcie_4_gdsc status stuck at 'off', then silence |
| -8 | #22 (patch 0008) | No GDSC warning; nothing printed before the reset |
| -9 | #23 (0008 + pm_print_times) | pci 0006:01:00.0 (NVMe, no driver) pci_pm_resume_noirq never returned; last line qcom-pcie 1c08000 (PCIe 4) genpd_resume_noirq called |
| -10 | #23, no PCIe 6a | qcom-pcie 1c08000 (PCIe 4) genpd_resume_noirq called at 144.093 s, never returned; every other noirq resume returned. Before this boot the Googlebook went to G3 18 s after a first power-on (EC 451.46 s), cause not known, and was powered on again |
What the logs show
- The wake path works. In -5 the EC logged
Handle sleep: 1at EC 2359.68 s and the RTC host event (event set 0x02000000) at EC 2379.80 s, 20.1 s later. ThenAP_RST_L transitions not expectedcame 0.76 s later (-8: 0.70 s), andMKBP: The AP is failing to respond. The EC ran a host reboot (Reset cause: hard), and the firmware fell through to stock. - The PCIe GDSCs. At a804c57,
gcc-x1e80100.cgives the PCIe GDSCsPWRSTS_OFF_ON, sogdsc_disable()really switches them off at suspend (gdsc.c:369-380). On resume they stayed off (-6, -7). sc8280xp’s PCIe GDSCs usePWRSTS_RET_ON, which leaves them on and lets the hardware retain them.- Upstream: “clk: qcom: gcc-x1e80100: Do not turn off PCIe GDSCs during gdsc_disable()”, Krishna Chaitanya Chundru (Qualcomm), patch 6/7 of a series, LKML 2026-01-02, https://lkml.rescloud.iu.edu/2601.0/00459.html, Reviewed-by Bryan O’Donoghue. It is not in a804c57.
- It is now local patch
kernel/patches/0008(ADR 0007). With it, the GDSC warnings are gone (-8, -9, -10).
- The resume still dies in the PCIe controllers. With
PM_SLEEP_DEBUG(kernel/mica-pmdebug.config) andpm_print_times:- -9: PCIe 6a’s controller resumed (
genpd_resume_noirq526 µs) and so did its root port. The NVMe endpoint’spci_pm_resume_noirqthen never returned, while other devices went on resuming for about 100 ms. - -10: with 6a disabled, PCIe 4’s own controller resume (
qcom-pcie 1c08000) never returned. - In both boots, PCIe 4’s
genpd_suspend_noirqtook 3.07-3.29 s on the way in. - Ramoops ends on the same line as the console (-6, -9), so the reset leaves nothing more.
- -9: PCIe 6a’s controller resumed (
- Two different failures (
pm_print_timestiming and the source).qcom_pcie_suspend_noirq()(pcie-qcom.c:2340at a804c57) callsdw_pcie_suspend_noirq()(pcie-designware-host.c:1225). That either sends PME_Turn_Off, waits for L2 (polled in 1 ms steps) and shuts the host down, or returns at once and keeps the link, depending onpci_host_common_d3cold_possible()(pci-host-common.c:269-325).- PCIe 6a kept its link. Its
genpd_suspend_noirqtook 167 µs (-9) and its resume 526 µs: too short for either the L2 wait or a host re-init with link training. So the NVMe endpoint’s config access went over a link that was never shut down, and still hung. - PCIe 4 shut its link down. Its
genpd_suspend_noirqtook 3.07-3.29 s (unexplained: the L2 poll times out at 10 ms), and on resume its host re-init (qcom_pcie_resume_noirq,pcie-qcom.c:2398) never returned. - A lead, unverified. Patch 0008 and the upstream series change only the
gcc_pcie_N_gdscentries. The PCIe PHY GDSCs (gcc_pcie_3/4/5/6_phy_gdsc) are stillPWRSTS_OFF_ON(gcc-x1e80100.c:6595-6663), andpcie6a_phyhaspower-domains = <&gcc GCC_PCIE_6_PHY_GDSC>(hamoa.dtsi:3546). So genpd collapses the PHY’s domain at suspend. The USB PHY GDSCs in the same file are alreadyPWRSTS_RET_ON(gcc-x1e80100.c:6775-6867). Upstream changed those because “the QMP combo PHYs lose their configuration on machine suspend” (“clk: qcom: gcc-x1e80100: Fix USB 0 and 1 PHY GDSC pwrsts flags”, patchwork comment 25978987). A PCIe PHY that lost its configuration would explain a dead link on 6a. That is an inference; the next boot can test it. - Inference, not verified: an access to a PCIe block that is not powered, or behind a dead link, hangs the bus. On Qualcomm that ends in a reset with no kernel message.
- PCIe 6a kept its link. Its
- Other observations.
- The touchscreen bus times out on the way into suspend in every boot (
geni_i2c a80000.i2c: Timeout cancel_m_cmd); this is the parked touchscreen fault and not fatal. mem_sleepofferss2idle [deep], so PSCISYSTEM_SUSPENDis available too (inference from the kernel listing “deep”). It is untested.- SCMI was clean on every boot (0.1 ms).
- Idle power stays unmeasured: on the bench the SuzyQ powers the Googlebook, so the battery’s current is charge current.
- The touchscreen bus times out on the way into suspend in every boot (
Open, for the owner
- Test the PHY GDSC lead: a local patch moving the four
gcc_pcie_N_phy_gdsctoPWRSTS_RET_ON, then one boot. No upstream patch for it was found tonight. - Find the hanging step in PCIe 4’s re-init: dyndbg for
pcie-qcom.candpcie-designware-host.con one suspend boot. - Try
deep(mem_sleep=deep). It takes a different firmware path, andqcom_pciethen keeps the CPU-PCIe interconnect up (pcie-qcom.c:2385). - Keep ADR 0009 as it is. Suspend fails before any idle state matters, so cpuidle is not the blocker yet (inference).
Update: the first complete round trip, with patch 0009 (2026-11-04, 5:01 AM ET)
Attempt 2026-11-04-11: kernel #24 with patch 0008 and local patch 0009, which moves the four
gcc_pcie_N_phy_gdsc to PWRSTS_RET_ON. Board DTS, both PCIe links on. The resume completed. Source: netlog
2026-11-04-netlog.txt#6.
- Timing:
PM: suspend entry (s2idle)at 120.03 s;- suspend-to-idle from 126.24 s to 140.70 s (the RTC alarm);
- noirq resume 883 ms, early resume 594 ms, resume 4855 ms (much of it console output from
pm_print_times); PM: suspend exitat 147.72 s.
- Counters:
rc=0./sys/power/suspend_stats:success=1, every failure counter 0. - After the resume:
- cpufreq: 3 policies, with 0 SCMI timeouts since the suspend.
- Wi-Fi relinked in 8 s, same address, now on 5720 MHz (earlier boots joined on 6 GHz).
/initran toGBOOK: endand sent the netlog.
- Still to look at:
- PCIe 4’s
genpd_suspend_noirqstill takes 3.28 s. - ath12k’s
pci_pm_resume_earlytakes 0.54 s. - At 142.17 s qrtr logs
failed to announce new service/failed while handling packet from 7:-2. suspend-wakeupis empty: no wakeup source counted the wake, although the EC logs the RTC event.
- PCIe 4’s
- What fixed it (inference): retaining the PCIe PHY GDSCs is what changed between -9/-10 and -11.
- It fits the lead above: a collapsed QMP PCIe PHY loses its configuration, which explains both 6a’s dead kept link and PCIe 4’s hung re-init.
- One boot proves only that it can work. Repeat boots show whether it is reliable.
- Console boot #11 booted -10’s image again after -10’s reset, before -11 was written. It reset the same way after the resume (143.47 s), and is recorded with -10.
- Idle and suspend power stay unmeasured. On the bench the battery reports charging:
power_now-2.96 W before and -2.49 W after, sign convention not checked.
Update: 3 of 3 with patch 0009 (2026-11-04, 5:13 AM ET)
Two repeat cold boots of -11’s image (kernel #24, board DTS) were added to -11’s row with attempt.sh add-boots
(netlogs 2026-11-04-netlog.txt#7 and #8). Both completed the round trip, the same as -11:
| Boot | Entry | PM: suspend exit | rc | SCMI timeouts since | Wi-Fi relink |
|---|---|---|---|---|---|
| netlog #6 (-11) | 120.03 s | 147.72 s | 0 | 0 | 8 s |
| netlog #7 | 122.75 s | 150.30 s | 0 | 0 | 8 s |
| netlog #8 | 119.67 s | 147.01 s | 0 | 0 | 8 s |
So with 0008 and 0009 the round trip is 3 of 3 cold boots, against 0 of 7 before (-5 to -10 and console boot #11). Power is still unmeasured.
Update: the -11 loose ends, read from the netlogs (2026-11-04, 5:20 AM ET)
Read offline from 2026-11-04-netlog.txt#6 to #8 and the source at a804c57. No new boots.
- PCIe 4’s 3.1-3.3 s
genpd_suspend_noirqis silent. No kernel line falls between itscallingandreturnedlines, and there is noTimeout waiting for L2 entry!warning, so the 10 ms L2 poll (pcie-designware-host.c:1255-1268) did not time out.- What runs after the poll:
dw_pcie_stop_link(), thenqcom_pcie_host_deinit()(pcie-qcom.c:1453-1470). That asserts PERST, powers the WCN7850 off through pwrctrl (pci_pwrctrl_power_off_devices), powers the PHY off and runs the SoC-specific deinit. - The WCN power sequencer has only millisecond delays (
pwrseq-qcom-wcn.c:146-149, 234-249). Which step takes 3 s is not known. - Next: one suspend boot with dyndbg for
pcie-qcom.c,pci-pwrctrl*andpwrseq-qcom-wcn.c. It costs about 3 s of entry time only, so it is not urgent.
- What runs after the poll:
- ath12k’s 0.54 s
pci_pm_resume_earlyis the WCN7850 booting again. pwrctrl powered the chip off at suspend (above), so on resume MHI powers it on (mhi mhi0: Requested to power ONat 141.67 s,Wait for device to enter SBL or Mission mode). ath12k then logs the chip and firmware again (142.20 s). That is expected with pwrctrl power-off (inference). - The qrtr line is resume-only.
failed to announce new service/failed while handling packet from 7:-2appears once in each resume boot (netlog #6, #7, #8) and in none of the five boots without suspend (#1 to #5). It comes at 142.17 s, while the WCN firmware is coming back over MHI. It isnet/qrtr/ns.c:490-493: the name service could not broadcast a new server announcement (ctrl_cmd_new_server). Wi-Fi relinked in 8 s each time, so it looks harmless (inference).
Phase 3: SCMI binds, the CPUs get cpufreq (2026-11-03)
Attempt 2026-11-03-8. Image ca51a59382dff6c8, kernel #7 (2026-11-03 4:07 PM ET), commit ca81e32. Sources: the
netlog dumps/firstboot/2026-11-03-netlog.txt and the AP console log dumps/firstboot/2026-11-03-ap.log (both
gitignored).
Why
Every boot up to attempt -7 ended with one deferred device: firmware:scmi … supplier 17430000.mailbox not ready,
then arm-scmi: failed to setup channels and failed to setup channel for protocol:0x10. The CPUs stayed at whatever
clocks the firmware left them on.
mailbox@17430000isqcom,x1e80100-cpucp-mbox. Its driver isQCOM_CPUCP_MBOX(drivers/mailbox/Kconfig:343,qcom-cpucp-mbox.c:205),=min defconfig (arch/arm64/configs/defconfig:1614), and the initramfs loads no modules.- The stock tree drives the same block:
cpucp: qcom,cpucp@18830000(qcom,cpucp-v2) with the same two register ranges (0x17430000,0x18830000), anarm,scminode withmboxes = <cpucp 0 cpucp 2>and shmem inmmio-sram@18b4e000(entry00-base.dts:384-402,:908-926,:10534-10540).hamoa.dtsidescribes the same, and each CPU takes itsperfdomain fromscmi_dvfs(hamoa.dtsi:78-79). So no DTS change. The stock tree also lists a vendorprotocol@80thathamoa.dtsidoes not have.
Changes (commit ca81e32)
kernel/mica-scmi.config:QCOM_CPUCP_MBOX=y. It also checks that the mailbox, SCMI core, SCMI mailbox transport, SCMI perf domain and SCMI cpufreq options are=yafter the merge (they were already)./init: onecpufreqline per policy (CPUs, driver, governor, current, min and max kHz) and anscmi-dmesgcapture.
Result: pass
-
SCMI binds over the mailbox:
arm-scmi arm-scmi.0.auto: Using scmi_mailbox_transportat 0.358 s,SCMI Protocol v2.0 'Qualcomm:' Firmware version 0x20000,scmi-perf-domain scmi_dev.4: Initialized 3 performance domainsat 0.376 s. -
cpufreq on all three clusters (
schedutilgovernor):Policy CPUs Driver Current at ~110 s Range policy00-3 scmi806.4 MHz 710.4 MHz to 3417.6 MHz policy44-7 scmi2188.8 MHz 710.4 MHz to 3417.6 MHz policy88-11 scmi2188.8 MHz 710.4 MHz to 3417.6 MHz -
Nothing deferred:
deferredthroughdeferred5are all empty, for the first time on mica. -
Everything else unchanged: both DSPs
runningat the end, Type-C, PCIe, Wi-Fi (6 GHz, DHCP), eDP 2560x1600, GPU firmware and GMU loaded, netlog sent (1379 lines).reboot: Restarting systemat 174.8 s (about 4:11 PM ET). -
Earlier milestones than attempt -7: CDSP up at 0.79 s (was 1.99 s), ADSP at 6.13 s (was 7.41 s, still after the known 5 s SSCTL wait, cdsp-2026-11-03.md), PCIe Gen4 link at 1.11 s (was 2.28 s). Inference: the CPUs now run faster than the firmware’s default clocks. Not measured directly.
Log lines checked
[Firmware Bug]: Failed to add opps_by_lvl at 3417600 for NCC1 - ret:-16(twice each for NCC1 and NCC2). This is adev_infofromdrivers/firmware/arm_scmi/perf.c:381-383(and:403-405):xa_insert()returned-EBUSY, so the firmware lists the 3.4176 GHz level more than once for those domains. The level still registers asmax=3417600. Inference: harmless.region@80a00000 … overlaps with cpucp-log@80e00000at 0.000 s is not new: it is in every boot’s log on 2026-11-03 (7 of 7) and in2026-11-02-2.region@80a00000is not in the mica DTB. Inference: the boot firmware adds it. Not traced further.
Update: attempt -9 (2026-11-03, 4:20 PM ET): SCMI timed out, no cpufreq
Same SCMI config as attempt -8 (the only kernel change was kernel/patches/0001, panel-edp). Sources as above.
arm-scmibound overscmi_mailbox_transportat 0.367 s as before. From 0.437 s on, 10timed out in resp(caller: do_xfer+0xdc/0x180), each about 36 ms after the previous one (the 30 msmax-rx-timeout).- Late answers arrive for older requests:
scmi_protocol scmi_dev.1: Message for 7 type 0 is not expected!, then for 9 and for 32. Inference: the CPUCP firmware answered, but after the kernel had given up on each request. - Results:
SCMI Protocol v2.0 'Qualcomm:' Firmware version 0x0(was0x20000, so the version query failed and thequirk_perf_level_get_fc_forcequirk was not applied),Failed to query supported version for protocol 0x13,scmi-cpufreq scmi_dev.3: probe with driver scmi-cpufreq failed with error -110, and/initsawcpufreq=none.scmi-perf-domainstill initialized 3 performance domains at 0.806 s. - Nothing else failed:
deferredstayed empty, both DSPs ran, display, GPU and Wi-Fi as in -8. - Upstream has seen the same timeout on x1e80100. Review of the series that enabled X1E cpufreq reports
arm-scmi firmware:scmi: timed out in respon the CRD (PATCH V7 0/2 “qcom: x1e80100: Enable CPUFreq”, 2024-11). The mailbox driver ata804c57already clears each channel’s interrupt under the channel lock (drivers/mailbox/qcom-cpucp-mbox.c,qcom_cpucp_mbox_irq_fn).
Open: with one good boot (-8) and one bad (-9), the cause is not known. Next step: more boots of the same image to get a failure rate, then compare the first SCMI exchange’s timing across good and bad boots.
Update: cold boot vs warm reset (2026-11-03, 5:25 PM ET)
Attempts -10, -11 and -12, plus the EC console log dumps/firstboot/2026-11-03-ec.log (gitignored).
| Attempt | Kernel | Start (EC console) | SCMI firmware version | cpufreq |
|---|---|---|---|---|
| -8 | #7, no patches | cold: power button pressed, G3->S5 at EC 10691 s; its reboot 184 s later | 'Qualcomm:' 0x20000 | 3 policies |
| -9 | #9, patch 0001 | warm: AP reset in S0 at EC 11401 s, no G3; reboot 198 s later | 'Qualcomm:' 0x0 | none (-110) |
| -10 | #9, patch 0001 | not logged (the consoles were started mid-boot) | '' 0x0, 13 timeouts | none (-110) |
| -11 | #10, no patches | warm: AP reset in S0 at EC 14388 s; reboot 138 s later | none: the first message timed out, arm-scmi probe -110 | none |
| -12 | #10, no patches (the -11 image) | cold: stock powered off (power state 0 = G3 at EC 14712 s), then power button | 'Qualcomm:' 0x20000, both quirks on, no timeouts | 3 policies |
Matching the EC times to boots: each mainline boot’s reboot comes its uptime plus ~9 s of firmware later (-8: 174.8 s uptime, 184 s; -9: 189.5 s, 198 s; -11: 129.5 s, 138 s).
- Result: SCMI worked in both cold boots and failed in every warm reset from running stock Android, with and
without the panel patch (-11 used the plain pin,
GBOOK_SKIP_PATCHES=1). The same image failed warm (-11) and worked cold (-12). - Inference: stock Android’s kernel leaves the CPUCP in a state that survives a warm reset (the stock tree drives it
as
qcom,cpucp-v2with a vendorprotocol@80), and in that state it answers mainline’s SCMI late or not at all. How the stock kernel uses it is not traced. With two cold and two logged warm boots, this is a strong pattern, not proof. - The failures grew worse from -9 to -11. Inference: each failed boot may leave the firmware in a worse state for the next warm reset (the research note’s quirk hazard, scmi-cpucp-timeouts.md).
Procedure from now on: power stock off fully before each attempt. attempt.sh verify prints that step,
attempt.sh watch shows the EC’s G3->S5 line for a cold start, and /init reports the firmware version as the
scmi-fw key so compare flags a warm-reset boot. arm,max-rx-timeout-ms is not needed for cold boots; it stays
an option if a warm boot must work (for example after a crash reboot).
Update: cold starts fail too (2026-11-03, 5:59 PM ET)
Three later boots each followed an EC G3->S5 (a cold start; EC uptime 15984, 17025 and 17236 s in
dumps/firstboot/2026-11-03-ec.log), with the EC work’s images (ec-2026-11-03.md):
| Boot | SCMI | Firmware version | cpufreq |
|---|---|---|---|
2026-11-03-13 | probe with driver arm-scmi failed with error -110 | none | none |
| repeat of -13 | timed out in resp, many times from 0.43 s | 0x0 | none |
2026-11-03-14 | one timed out in resp at 0.418 s, then bound | 0x20000 | all three policies |
So a cold start does not always bring SCMI up: the “cold boot only” rule above holds for the warm side (no warm
reset has worked) but not as a guarantee on the cold side. Open lead 2 in the handoff (SCMI after a warm reset) widens
to SCMI in general; docs/research/scmi-cpucp-timeouts.md ranks arm,max-rx-timeout-ms first.
Update: the 250 ms window and the trace (2026-11-03, about 8 PM ET)
arm,max-rx-timeout-ms = <250> (commit d555a07, unverified diagnostic) took effect on both boots
(SCMI max-rx-timeout: 250ms).
2026-11-03-20(cold; the EC itself reset at power-on):xfers_response_timeout=7ofsent_ok=78(response_ok=74,err_protocol=1), firmware0x0, noquirk_perf_level_get_fc_force(the hazard path, scmi-cpucp-timeouts.md section 4), cpufreq on 2 of 3 clusters.2026-11-03-21(cold, tracing on): firmware0x20000, the FC quirk, 3 of 3 clusters, 0 timeouts; the trace’sscmi_xfer_begintoscmi_rx_donelatency over 78 replies peaked at 4.0 ms.- So when CPUCP answers, it answers within milliseconds; on a bad boot some messages get no reply even in 250 ms.
A longer window does not help (inference from 2 boots); the next lever is why CPUCP stops answering
(research options 3-4: the guard,
arm,no-completion-irq,cpuidle.off=1).
Update: reply latency per message, attempt -22 (2026-11-03, 8:14 PM ET)
Cold start, 250 ms window, tracing on: firmware 0x20000, the FC quirk, 3 of 3 clusters, and 4 timeouts. The trace
(scmi_xfer_begin to scmi_rx_done, netlog “trace at probe”):
- 67 of 75 replies in under 1 ms.
- The base protocol’s discovery messages (
pt=10,msg_id01 to 06) took 23 to 36 ms each, one after another.msg_id05 (implementation version, the “Firmware version” line) took 36.0 ms. With the driver’s default 30 ms window that reply is missed, the version stays0x0and the FC quirk does not match (inference: it fits every bad boot’stimed out in resplines about 36 ms apart andFirmware version 0x0). - Slower still: base
msg_id07 at 222.4 ms, perf (pt=13)msg_id02 at 155.2 ms and0Bat 67.9 ms. - Timed out at 250 ms: base
msg_id06, perfmsg_id04 and 02 (twice). - So the window matters: 250 ms turned this boot good where 30 ms would have failed it. -20 still failed at 250 ms, so some replies take longer or never come. Two good boots (-21, -22) also both had tracing on; that it shifts the timing is unproven (inference).
Update: 1000 ms window, attempts -23 to -25 (2026-11-03, 8:31 PM ET)
arm,max-rx-timeout-ms = <1000> (commit after 927480c), tracing on, three cold starts of one image:
| Attempt | Result | Timeouts | Slow replies (trace) |
|---|---|---|---|
-23 | pass: 0x20000, FC quirk, 3 of 3 | 1 | base 03 628 ms, perf 09 872 ms, perf 04 1014 ms (the timeout) |
-24 | pass | 1 | base 04 884 ms, perf 02 940 ms, 0B 950 ms, 04 830/950 ms; perf 09 timed out |
-25 | pass | 0 | none: 78 replies, all within 3.6 ms |
- Since the window went to 250 ms and tracing on: 5 good cold starts in a row (-21 to -25), against 2 of 7 before with the default 30 ms (-20 at 250 ms failed).
- Reply time varies by boot: some boots answer everything within milliseconds (-21, -25), others have a tail of single messages at 0.6 to 1 s. Why CPUCP stalls is still open; the window only absorbs it.
- Not a fix: the property is local and unverified (no stock counterpart), and a boot can still lose a message past
1 s (-23’s perf
04at 1014 ms). It costs nothing on a fast boot.
Update: cluster_cl5 A/B, attempt -27 (2026-11-03, 9:50 PM ET)
Attempt 2026-11-03-27: kernel #17 (as -26), 1000 ms window, tracing on, plus the diagnostic fragment
dts/diag/hamoa-no-cluster-cl5.dtsi (commit 5aa73de): domain-idle-states = <&cluster_cl4> on cluster_pd0-2,
so the three CPU clusters never enter cluster_cl5. Source: Jens Glathe’s “arm64: dts: qcom: hamoa: Drop cluster_cl5
idle state from CPU clusters” (https://lore.kernel.org/all/20260604-dc_zva_mitigation-v1-1-d1148c1c0259@oldschoolsolutions.biz/),
carried by Omarchy as linux-omarchy-aarch64 patch 2151. Image a563a107aeedf282, DTB 4fa8ee79b09d1ea9; three cold
starts of one image (stock powered off, EC G3 before each):
| Boot | Result | Timeouts | Max reply | Slow replies (trace, >= 100 ms) |
|---|---|---|---|---|
| 1 | pass: 0x20000, FC quirk, 3 of 3 | 0 | 943.7 ms | base DISCOVER_SUB_VENDOR 111.8 ms, DISCOVER_LIST_PROTOCOLS 496.0 ms, perf DESCRIBE_LEVELS 943.7 ms |
| 2 | pass | 0 | 3.9 ms | none (the AP console’s scmi-latency= line; this boot’s netlog did not arrive) |
| 3 | pass | 0 | 812.0 ms | base DISCOVER_VENDOR 812.0 ms; perf DESCRIBE_LEVELS 507.2/433.7/222.4 ms, PROTOCOL_MESSAGE_ATTRIBUTES 442.0/362.0 ms, NOTIFY_LIMITS 795.2 ms (SCMI probe 0.37 s to 4.11 s) |
- Against the same 1000 ms window with
cluster_cl5(-23 to -26): 2 of 4 boots stalled (-23, -24), 2 answered within 4 ms (-25, -26). Withoutcluster_cl5: 2 of 3 stalled. No improvement: the A/B failed. The stalls do not come from thecluster_cl5idle state (3 boots; a small sample, but the worst probe of the day, boot 3, ran without it). - All three boots came up fully, so the 1000 ms window keeps absorbing the stalls; 0 timeouts here.
- The stalls fall between 0.37 s and 4.1 s of kernel time, during the SCMI probe and before
/initruns, so they do not depend on anything done at the keyboard. - The fragment is deleted (its text is in commit
5aa73de), perdts/diag/README.md. Open: the other idle-state lever in scmi-cpucp-timeouts.md (cpuidle.off=1, which also covers the CPU idle states the fragment left alone).
Update: cpuidle.off=1 A/B, attempt -28 (2026-11-03, 10:02 PM ET)
Attempt 2026-11-03-28: kernel #17, the board DTB as -26 (f30182513670c4e3, no dts/diag), 1000 ms window, tracing
on, plus GBOOK_CMDLINE_EXTRA=cpuidle.off=1 (cpuidle_register_driver() returns -ENODEV while off is set, drivers/cpuidle/driver.c:225 at
a804c57, so the idle loop falls back to plain WFI and no PSCI idle state is requested: not the CPU ret state, the
cluster states or domain_ss3 on system_pd; the last part is inference from the PSCI cpuidle driver being the only
requester). Image
21db583f38e653a3; three cold starts of one image (stock powered off, EC G3 before each):
| Boot | Result | Timeouts | Max reply | Replies under 1 ms |
|---|---|---|---|---|
| 1 | pass: 0x20000, FC quirk, 3 of 3 | 0 | 0.1 ms | 78 of 78 |
| 2 | pass | 0 | 0.1 ms | 78 of 78 |
| 3 | pass | 0 | 0.1 ms | 78 of 78 |
The trace’s only flagged rows are base DISCOVER_SUB_VENDOR and DISCOVER_AGENT with s=-13 (not supported, 0.0 ms),
as on every boot; the SCMI probe ends by 0.38 s.
- With idle states on (1000 ms window, -23 to -27): 4 of 7 cold boots stalled (-23, -24, -27 boots 1 and 3; replies of
0.5 to 1 s), and even the clean ones peaked at 3.6 to 4.0 ms (-21, -25, -26, -27 boot 2). With
cpuidle.off=1: 0 of 3 stalled and every reply within 0.1 ms, a floor 40 times lower. - So the CPUCP’s slow replies follow the AP’s idle states. That matches Sibi Sankar’s statement that the CPUCP firmware bug “happens when idle states are enabled” (scmi-cpucp-timeouts.md section 4). Three boots is a small sample: with 4 of 7 stalling, three clean boots in a row by chance is about 8 % (inference).
cluster_cl5alone is ruled out (-27). Left are the CPUretstate (cluster_c4),cluster_cl4anddomain_ss3; the stock tree has the same four states and topology (entry00-base.dts:cluster_pd0-2with cl4 and cl5,system_pdwithsystem_ss3), so stock Android boots with them (inference: they are not wrong for this board).- Not a config to keep:
cpuidle.off=1costs idle power. It is a diagnostic; the 1000 ms window stays.
Update: what the trace cannot tell (2026-11-03, 10:20 PM ET)
The latency in the tables above runs from scmi_xfer_begin to scmi_rx_done, and rx_done is traced in the mailbox
interrupt’s handler (driver.c:1164 at a804c57), so it cannot separate a late CPUCP reply from an on-time reply whose
interrupt woke an idle AP CPU late. “The slow replies follow the AP’s idle states” (-28) holds either way; which side is
slow is open. Sibi Sankar’s “idle states” statement is about PERF_LEVEL_GET hard resets, not reply latency, so it does
not decide this (inference). The discriminator is arm,no-completion-irq with idle states on
(dts/diag/scmi-no-completion-irq.dtsi): polling skips the interrupt; trace_report.py times a polled reply from begin
to end. Stock wires the mailbox the same way (qcom,cpucp-v2 at 0x17430000/0x18830000, GIC SPI 28 level-high, no
PDC; SCMI channels 0 and 2, no arm,max-rx-timeout-ms), with a downstream mailbox driver.
Update: arm,no-completion-irq A/B, attempt -29 (2026-11-03, 10:24 PM ET)
Attempt 2026-11-03-29: kernel #17, 1000 ms window, tracing on, idle states on, plus
dts/diag/scmi-no-completion-irq.dtsi (polling for every SCMI reply; dmesg: Enabled polling mode TX channel). Image
aa0661198276109c; three cold starts of one image (EC G3 before each):
| Boot | Result | Timeouts | Polled replies | Max reply | apss_cpucp_mbox IRQs |
|---|---|---|---|---|---|
| 1 | pass: 0x20000, FC quirk, 3 of 3 | 0 | 78 of 78 | 0.1 ms | 0 |
| 2 | pass | 0 | 78 of 78 | 0.1 ms | 0 |
| 3 | pass | 0 | 78 of 78 | 0.1 ms | 0 |
(trace_report.py times a polled reply from scmi_xfer_begin to scmi_xfer_end; the two s=-13 replies count as
replies after a fix the same night, so /init’s on-device line on this image still says 76.)
- The CPUCP is not slow. With idle states on, it frees the shared-memory channel within 0.1 ms of every request on all three boots, where 4 of 7 interrupt-mode boots stalled for 0.5 to 1 s. The slow part is the completion interrupt.
- The IRQ count is 0 because a polled message asks for no interrupt (
shmem.c:123: the channel’s flagSCMI_SHMEM_FLAG_INTR_ENABLEDis written as 0 whenpoll_completionis set). In interrupt mode every boot of the day counted oneapss_cpucp_mboxIRQ per reply (75 to 78), all on CPU 0 (GBOOK: irq-counts=, netlog), stalled boots included: the interrupts arrive, late. - Which half is late, the CPUCP raising it or CPU 0 waking for it, is still open. A lead (inference): the stall ceiling
tracks the rx window (timeouts about 36 ms apart at 30 ms, a 222 ms reply at 250 ms, 940 to 1014 ms at 1000 ms), which
fits a CPU 0 that sleeps through the interrupt until its own timeout timer wakes it, then takes the pending interrupt
just before or just after the timeout. GIC SPI 28 has no PDC pin (
qcom,pdc-rangesinhamoa.dtsi:6028maps SPIs 251-255, 480-573, 609-640, 717-728, 816-834), so it cannot wake the SoC from a state that needs the PDC; whetherdomain_ss3onsystem_pdis such a state is unverified. - The fragment stays (status: proven as a diagnostic) until the owner decides between polling, the window, or a narrower idle-state change.
Update: what -29 shows and does not (2026-11-03, 10:28 PM ET)
Polling keeps one AP core spinning through the whole SCMI probe, and cpuidle.off=1 (-28) keeps every core out of the
PSCI states, so both fast cases share one property: the SoC never reaches its deepest state during the probe. -29 shows
that the CPUCP answers within 0.1 ms while one AP core is awake; whether it also does when all cores sleep is untested.
Two readings fit: CPU 0 sleeps through an on-time IRQ (SPI 28 has no PDC pin) until a timer wakes it, or the CPUCP
defers its reply while the AP is in a deep state. The window-tracking lead favours the first (inference). Next A/B:
domain_ss3 dropped from system_pd alone (dts/diag/hamoa-no-system-ss3.dtsi).
Update: domain_ss3 A/B, attempt -30 (2026-11-03, 10:34 PM ET)
Attempt 2026-11-03-30: kernel #17, 1000 ms window, interrupt mode, CPU and cluster idle states as stock, plus
dts/diag/hamoa-no-system-ss3.dtsi (system_pd without domain-idle-states; the built DTB’s
/psci/power-domain-system has none). Image 8296ea098da5e5b3. One cold start (EC G3 before it):
- Pass (
0x20000, FC quirk, cpufreq 3 of 3), but the worst SCMI boot of the day: 1 timeout (perfDESCRIBE_LEVELS, 1012.1 ms,s=-110), baseDISCOVER_AGENT587.0 ms; 76 replies, 76apss_cpucp_mboxIRQs, all on CPU 0. - Failed: dropping the system-level state does not stop the stalls. One stalled boot settles it, so boots 2 and 3
were skipped. With -27, the cluster
cl5state and the systemss3state are each ruled out alone; left are the CPUretstate andcluster_cl4, or a combination. - Also on this boot, unexplained: the touchscreen probe failed with
-110at 12.8 s (usually-22), with GENI I2C errors ona80000.i2c: 4 transaction timeouts, plus NACK, arbitration-lost and bus-protocol errors (attempt.sh trace i2c touchscreen). The NACK and arbitration errors point at the bus or the device, not at interrupt delivery (inference); see touch-tpm-2026-11-03.md. - The fragment is deleted (its text is in commit
9fa5a2d), perdts/diag/README.md.
Update: the wake trace, attempts -31 and -32 (2026-11-03, 11:00 PM ET)
Board DTB, interrupt mode, idle states on, plus trace_event=irq:irq_handler_entry,power:cpu_idle,timer:hrtimer_expire_entry
(debug-tools.md, “the wake trace”). /init keeps these events for CPU 0 only.
-31(image053a4bced558aa41): the default 1.4 MB per-CPU ring wrapped before the probe snapshot and lost the SCMI probe (replies=0). The boot itself stalled (SCMI 1.26 s to 2.30 s), 0 timeouts.-32(image5e565f64ce0e9d30,trace_buf_size=8M): the ring still wrapped (2061301 of 2260937 events kept) but kept all 78 SCMI replies. One slow reply: perfPROTOCOL_MESSAGE_ATTRIBUTES, 534.3 ms (request at 0.434542 s, reply interrupt at 0.968882 s); 0 timeouts. Cold start after an EC reset (Reset cause: sysjump hard efs).
What CPU 0 did during those 534 ms (attempt.sh trace wake, and the raw netlog lines):
- It entered idle
state=1(cpuidle index 1, the PSCIretstatecluster_c4) and left it 26694 times, once every ~20 µs, and took only 3 interrupts: 2 IPIs and 1arch_timer. Per 100 ms bin: about 4960-5000 idle exits and 0-6 interrupts during the stall; in normal periods (0.0-0.4 s, 2.4-4.0 s) the exits roughly match the interrupts. - The stall ended with the
arch_timerinterrupt at 0.968876 s; it expired fourpm_suspend_timer_fnhrtimers, and 6 µs later theapss_cpucp_mboxinterrupt ran and the reply was read. - The same 20 µs loop with no interrupts covers 1.2 to 2.0 s, when no SCMI message was pending: the loop is not caused by the SCMI request (inference from one boot).
Reading (inference, not proven): CPU 0’s ret entries return early every 20 µs without the kernel taking any
interrupt, so something wakes the core that the GIC does not deliver as an interrupt, or delivers only after another
interrupt is acknowledged. With cpuidle.off=1 (-28, plain WFI) the replies take 0.1 ms. The other CPUs were not
traced (the capture keeps CPU 0 only), so whether they were idle is unknown. Neither tree marks an idle state
local-timer-stop, and both have the memory-mapped timer (arm,armv7-timer-mem), so a missing tick broadcast is
not the explanation.
Update: precision on the -32 loop (2026-11-03, 11:08 PM ET)
state=1is not only theretstate: its enter callback ispsci_enter_domain_idle_state(cpuidle-psci.c:271ata804c57), which can escalate to a cluster or system domain state when CPU 0 is the last CPU of its domain. Thecpu_idleevent cannot tell which was requested;power:psci_domain_idle_enter/_exit(power.h:89,:96) can.- Each cycle spends ~20 µs inside the state and ~0 µs outside: a PSCI call that returns without holding the state. Two readings fit: the firmware refuses (or immediately ends) the state while an SCMI command is in flight, or something wakes the core that the kernel does not see as an interrupt. Not decided.
- Two IPIs ran on CPU 0 mid-stall (0.502444 s, 0.538444 s) and the mailbox interrupt still did not follow them: SGIs reached CPU 0 while the SCMI reply’s SPI did not (or was not yet raised).
- No I2C or GENI events fall in 0.43-2.1 s on -32, so this boot cannot say whether other SPIs stall the same way.
- Next capture (one boot, no fragment):
cpuidle/state*/{usage,rejected,time}per CPU (rejectedexists,drivers/cpuidle/sysfs.c:266),pm_genpd_summary(CONFIG_DEBUG_FS=y,CONFIG_PM_GENERIC_DOMAINS=yin the build), the PSCI domain tracepoints, and per-CPU idle-exit counts over the SCMI window.
Update: idle counters, attempt -33 (2026-11-03, 11:21 PM ET)
Attempt 2026-11-03-33 (image fb75f74a4a79d29e, commit 68667c5): as -32 plus power:psci_domain_idle_enter/exit
and the /init idle counters, read at the probe snapshot (cumulative since boot, about 95 s in by the genpd times).
Cold start after an EC reset. SCMI stalled: firmware version at 1.03 s, a timeout at 4.11 s, perf domains at 4.11 s.
The trace ring wrapped (2185344 of 3404814 events kept) and lost the SCMI lines, so the per-window counts are missing
(idle-window=no SCMI window); the cumulative counters are complete.
cpuidle/state1 (cpu-sleep-0, the deepest cpuidle state, whose enter can request a domain state):
| CPUs | usage | rejected |
|---|---|---|
| cpu0 | 218498 | 37 |
| cpu1, cpu2, cpu3 | 3022, 2178, 1730 | 45, 28, 27 |
| cpu4-cpu7 (cluster 1) | 983, 1465, 2215, 1360 | 22563, 44568, 89639, 94845 |
| cpu8-cpu11 (cluster 2) | 1636, 1075, 1273, 1291 | 266040, 20354, 23072, 37093 |
genpd idle_states (debugfs; Usage, Rejected, Above):
power-domain-systemS0 (domain_ss3): usage 2402, rejected 598159, above 599516.power-domain-cpu-cluster1S1 (cl5): usage 252401, rejected 7, above 251683;cluster2S1: 347327, 4, 346622.power-domain-cpu-cluster0S0/S1: usage 1593/3565, rejected 74/18.
What the counters mean at a804c57: cpuidle’s rejected counts an enter that returned an error, and a PSCI domain
state’s genpd rejected counts a CPU_SUSPEND for that composite state that returned an error
(pm_genpd_inc_rejected(), cpuidle-psci.c:102; core.c:816). genpd’s above counts a stay shorter than the
state’s latency plus residency (genpd_reflect_residency(), core.c:331).
- The firmware refused the system state about 598000 times. The refusals sit on the last CPU of clusters 1 and 2
(cpu4-11’s cpuidle rejections add up to 598174, matching
system_pd’s 598159): each time one of them is the last CPU awake, it asks forss3, PSCI returns an error at once, and it tries again. - CPU 0 is not refused (37): it enters
state1and is woken, 218498 times, the 20 µs loop of -32. - Clusters 1 and 2 entered
cl5about 250000 and 350000 times, almost all shorter than its residency. - Reading (inference): a storm of refused system-state requests and early wakes runs while the SoC should be idle. A
busy CPU (polling, -29) or no PSCI idle (-28) never gets all CPUs idle, so no storm, and the replies take 0.1 ms. That
the storm delays the CPUCP’s completion interrupt is not shown. The counters are cumulative to about 95 s, so they
do not prove the storm runs during the SCMI stall; -30 (no
ss3) still stalled, with no counters to show what ran.
Update: precision on -33 (2026-11-03, 11:30 PM ET)
- The refused CPUs are not the stalled one. The mailbox interrupt lands on CPU 0, and CPU 0 is entered-and-woken
(
state1usage 218498, rejected 37); the refusals are on clusters 1 and 2 asking forss3. Two coupled loops, causality unknown either way: CPU 0 bouncing could be why the firmware refusesss3(cluster 0 is not down), or the refusals could be what wakes CPU 0. - The counters are cumulative over about 95 s; -32 showed CPU 0’s loop at about 50000 exits/s during stalls against 1000-6000/s otherwise, so the cumulative numbers understate the rate inside a stall.
- Mode:
psci: OSI mode supportedandCPUidle PSCI: Initialized CPU PM domain topology using OSI mode(-33 dmesg). - Ruled out as a link: the stock tree’s four
qcom,cpucp-enabledflags sit inqcom,memlatmonitors (the memory latency governor), not in an idle-state node. The stockpscinode matches mainline’s (arm,psci-1.0,smc). - Not yet tested: no composite requests at all (
domain-idle-statesremoved fromcluster_pd0-2andsystem_pd, CPUretkept). -27 removed onlycl5, -30 onlyss3.
Decisions (2026-11-03, 11:27 PM ET, owner, grilling round)
- One more boot before choosing the fix: every PSCI domain idle state removed (
dts/diag/hamoa-no-domain-idle.dtsi), CPUretkept, interrupt mode, idle counters read early and at the snapshot. - The board DTS keeps
arm,max-rx-timeout-ms = <1000>until the cause is known; polling stays adts/diagfragment (it would hide the stall from every later trace). - Nothing goes to a mailing list until the cause is known and a fix is tested on mica; then one report to
linux-arm-msm(SCMI and PSCI maintainers on Cc) with the numbers, the cause and a patch, after the owner reviews it. - Time box: this boot plus at most one follow-up, then the next handoff item (the never-bound devices, then the touchscreen).
Update: no PSCI domain idle states, attempt -34 (2026-11-03, 11:41 PM ET)
Attempt 2026-11-03-34 (image 30354ef5b4fcf76c, commit d458b08): kernel #17, interrupt mode, 1000 ms window,
dts/diag/hamoa-no-domain-idle.dtsi (no domain-idle-states on cluster_pd0-2 or system_pd; the 12 per-CPU
domains keep cluster_c4, the CPU ret state). Three cold starts of one image (EC G3 before each):
| Boot | Result | Timeouts | Max reply | cpuidle rejected (all CPUs) | CPU 0 state1 usage, early / snapshot |
|---|---|---|---|---|---|
| 1 | pass: 0x20000, FC quirk, 3 of 3 | 0 | 4.0 ms | 0 | 19190 at 7.04 s / 72367 at 78.71 s |
| 2 | pass | 0 | 1.4 ms | 0 | 14284 at 6.96 s / 97553 at 92.82 s |
| 3 | pass | 0 | 3.6 ms | 0 | 4725 at 6.84 s / 53456 at 78.63 s |
- No stall on any boot, and no PSCI refusal anywhere. With domain idle states present, 8 of 11 interrupt-mode cold
boots stalled (-23, -24, -27 boots 1 and 3, -30, -31, -32, -33); three clean boots in a row by chance at that rate
is about 6 % (inference). Removing
cl5alone (-27) orss3alone (-30) was not enough; removing every composite state is. - CPU 0 entered
state1about 700 to 2700 times a second up to the early read, against about 50000 a second inside -32’s stall: no loop. power-domain-system’s default state still countsrejected(23796, 38188, 40386 at the snapshot). These are vetoes from the RPMh controller’s genpd notifier, which refusesGENPD_NOTIFY_PRE_OFFwhile it is busy or cannot flush (rpmh_rsc_pd_callback(),rpmh-rsc.c:949-951; counted atcore.c:1014): kernel-side, no PSCI call.- Reading (inference): the stall needs the composite cluster or system states that PSCI refuses in OSI mode (-33); why the firmware refuses them, and how that delays the CPUCP’s interrupt to CPU 0, is still open. What the change costs: the clusters and the SoC never power down when idle (more idle power; suspend is untested either way).
Update: ADR 0008 is not enough, attempts 2026-11-04-1 and -2 (2026-11-04, 12:26 AM ET)
Two more cold boots on the ADR 0008 board DTS (no PSCI domain idle states), kernel #18 then #19 (the second
adds only kernel/patches/0008, a GENI I2C timing test for the touchscreen): both stalled.
| Boot (all without domain idle states) | Max reply | Timeouts | CPU 0 state1 usage at the early read | cpuidle rejected |
|---|---|---|---|---|
| -34 boot 1 | 4.0 ms | 0 | 19190 at 7.04 s | 0 |
| -34 boot 2 | 1.4 ms | 0 | 14284 at 6.96 s | 0 |
| -34 boot 3 | 3.6 ms | 0 | 4725 at 6.84 s | 0 |
| -35 | 3.9 ms | 0 | 7775 at 6.92 s | 0 |
| 2026-11-04-1 | 1019.2 ms | 2 | 155711 at 9.94 s | 0 |
| 2026-11-04-2 | 652.0 ms (base DISCOVER_IMPLEMENT_VERSION) | 0 | 132524 at 9.52 s | 0 |
- 2 of 6 stalled, so removing the domain idle states lowers the rate (8 of 11 before) but does not stop it. -34’s 3 of 3 was the lucky case. The 2026-11-04-1 stall was missed live: the touch monitor was not watching SCMI.
- The stall goes with CPU 0’s
state1loop, not with PSCI refusals. Stalled boots reach 130000-156000 CPU 0state1entries by the early read, against 4700-19000 on clean ones (the early read also comes later, because the stall delays the boot). Every CPU’srejectedis 0, and no composite state exists. -33’s refusal storm ran alongside the stall, not as its cause (inference). - What is left fits all the data (inference): CPU 0 enters the PSCI
retstate (cluster_c4,0x4), wakes within ~20 µs again and again, and the SCMI completion interrupt is not taken until another interrupt runs (-32). Plain WFI (cpuidle.off=1, -28) and a busy CPU (polling, -29) never stalled (0 of 3 each). - ADR 0008 gets a dated update; the board DTS change stays until the owner decides.
Update: precision and two dead ends for the next test (2026-11-04, 12:31 AM ET)
- The rate claim was too strong. 2 of 6 stalled boots without domain idle states, against 8 of 11 with them, is not a significant difference at these sample sizes (one-sided p about 0.14, inference). Read it as “may reduce the rate; the sample cannot tell”, not as “cut it to 2 of 6”.
- The window held on the 2-timeout boot. 2026-11-04-1 (2 timeouts, max 1019.2 ms) still came up fully: firmware
0x20000,quirk_perf_level_get_fc_force, cpufreq on all three clusters (netlog, boot 2 of 3 in2026-11-04-netlog.txt). - When the loop starts (-32): the request left at 0.434542 s and CPU 0’s first loop exit came at 0.434606 s,
64 µs later, about when the reply’s interrupt would be asserted (polling replies take ~0.1 ms, -29). Mid-loop
CPU 0 took two SGIs (IPIs) but not the SPI, and a PPI (
arch_timer) released it. Reading (inference): a pending SPI for CPU 0 wakes the core fromretbut is not delivered to its CPU interface; the core re-enters, is woken again every ~20 µs, until a PPI runs. It also fits the 1.2-2.0 s loop with no SCMI pending (another SPI) and -30’s GENI I2C-110timeouts (that controller’s interrupt is an SPI on CPU 0 too). irqaffinity=cannot move SPI 28 off CPU 0:irq_affinity_setup()always adds the boot CPU to the mask (kernel/irq/irqdesc.c:36ata804c57).- CPU 0 alone cannot lose
retby DT: with no state for one CPU,psci_idle_init_cpu()returns-ENODEV(cpuidle-psci.c:399-401) andpsci_cpuidle_probe()fails the whole driver (:437-447), which iscpuidle.off=1for every CPU. Isolating CPU 0 or the interrupt needs a kernel patch.
Update: the stall follows the interrupt’s CPU, attempt 2026-11-04-3 (2026-11-04, 12:44 AM ET)
Attempt 2026-11-04-3: kernel #20 (kernel/patches/0009, commit cee735d), the ADR 0008 board DTS, interrupt
mode, 1000 ms window. The patch calls irq_set_affinity(irq, cpumask_of(1)) in the CPUCP mailbox probe; dmesg shows
qcom_cpucp_mbox 17430000.mailbox: gbook: irq 15 affinity to CPU 1: 0 at 0.226519 s. One cold start (EC G3 first).
- All 78 mailbox interrupts ran on CPU 1:
irq-counts= 15: 0 78 0 0 0 0 0 0 0 0 0 0 GICv3 60 Level apss_cpucp_mbox. - SCMI stalled: max reply 872.0 ms, 0 timeouts, cpufreq on all three clusters.
- The loop moved with it. At the early read (9.63 s), CPU 1’s
state1count was 134862. CPU 0 had 6131 and every other CPU had 79 to 471. - So CPU 0 is not special. The core that takes the reply’s SPI loops in and out of PSCI
retwithout taking it, wherever the SPI is routed (one stalled boot; it moved exactly as the reading predicted). With -28 (plain WFI, 0 of 3 stalled) and -29 (polling, 0 of 3), the mechanism reads (inference): on mica, a core in the PSCIretstate (cluster_c4,arm,psci-suspend-param0x4) is woken by a pending SPI but does not take it until a PPI runs. That fits the other SPIs seen stalling too (-30’s GENI I2C timeouts, -32’s 1.2-2.0 s loop with no SCMI pending). kernel/patches/0009stays until the owner decides the fix, then goes (a diagnostic, ADR 0007).
Update: ADR 0009 confirmed, attempt 2026-11-04-4 (2026-11-04, 12:57 AM ET)
Attempt 2026-11-04-4: kernel #21 (patches 0001-0007, no diagnostics), the board DTS with
ADR 0009 (cluster_c4 deleted from cpu_pd0-11; cluster and system states as
stock), interrupt mode, 1000 ms window. Image 161b32cde0296c3c, commit 1f67420. One cold start.
- No cpuidle driver: every CPU’s
cpuidle-early=andcpuidle=line is empty (/sys/devices/system/cpu/cpuN/ cpuidle/has no states), so cores idle in WFI. The PSCI domain driver still reportsCPUidle PSCI: Initialized CPU PM domain topology using OSI mode. That iscpuidle-psci-domain, not the cpuidle driver. - SCMI clean: 78 replies, max 0.1 ms, 0 timeouts; the SCMI probe ran 0.376-0.391 s. Firmware
0x20000, FC quirk, cpufreq on all three clusters. All 78 mailbox interrupts were on CPU 0. - With -28 (
cpuidle.off=1, 0 of 3), plain WFI is now 4 of 4 cold boots without a stall, every reply within 0.1 ms.
Update: the counts from attempt.sh tally (2026-11-04, 2:31 AM ET)
dumps/firstboot/attempts.tsv now carries machine-read boot health (retro 6), backfilled for -20 to 2026-11-04-4.
The counts above were hand-made; the tally reads:
- Domain idle states present, interrupt mode (-23 to -27, -30 to -33): 7 stalled of 10 known boots, plus -31 unknown (its trace ring lost the SCMI lines; its stall is from dmesg timing only). Against no PSCI cpuidle (-28, 2026-11-04-4): 0 of 4, one-sided Fisher p = 0.035. “8 of 11” above counted -31 as a stall.
- ADR 0008’s DT (-34, -35, 2026-11-04-1 to -3): 3 of 7. Against no PSCI cpuidle: 0 of 4, p = 0.21, not significant on its own. ADR 0009 rests on the mechanism (the stall followed the SPI to CPU 1, 2026-11-04-3) together with the first comparison, not on the boot count against ADR 0008.
- Polling (-29): 0 of 3. Commands:
scripts/attempt.sh tally '^2026-11-03-(2[3-7]|3[0-3])$' '^2026-11-03-28$|^2026-11-04-4$'andscripts/attempt.sh tally '^2026-11-03-3[45]$|^2026-11-04-[123]$' '^2026-11-03-28$|^2026-11-04-4$'.
Update: the tally at ten boots without PSCI cpuidle (2026-11-04, 4:40 AM ET)
The suspend-test boots (2026-11-04-5 to -10, suspend-2026-11-04.md) all ran the
no-PSCI-cpuidle board DT and probed SCMI before suspending. Every one was clean at 0.1 ms. With
'^2026-11-03-28$|^2026-11-04-([4-9]|10)$' as group 2 in the two commands above, no PSCI cpuidle is 0 of 10 known
boots. Against domain states present (7 of 10), the one-sided Fisher p = 0.0015. Against ADR 0008’s DT (3 of 7),
p = 0.051, just above 0.05.
Rootfs from USB: the first two ROOT boots (2026-11-04)
Read 2026-11-04 (6:00 PM ET) from the AP console log, /init’s netlogs and the rootfs’s persistent journal (read from the
stick’s partition 2 on the build host after boot 1). Attempts 2026-11-04-18 (warm start) and -19 (cold). Kernel #29
7.3.0-gbook-g82c43ed, KERN-A image 645d9f3dc3ab525c (15 tries), rootfs builds of 2026-11-04 (306, then 307 packages;
snapshot=none: deb.debian.org and offline builds). MACs, SSIDs and serials redacted; no identifier is quoted.
What worked
/initfound ROOT bykern_guid(root-lookup: the booted disk’s partition 2 isgbook-root), mounted it (init=/sbin/init), sent its netlog, andexec switch_rooted (GBOOK: switch_root=/dev/sda2), both boots.- systemd 262.1 booted Debian sid arm64 from the stick; the journal is persistent on ROOT.
- Mesa on the GPU (M3 criterion):
eglinfo -B: OpenGL 4.6 Core and Compatibility, vendor freedreno, renderer “Adreno X1-85”, Mesa 26.2.6-1;kmscube -D /dev/dri/card2 -c 120: exit 0, OpenGL ES 3.2, renderer “Adreno X1-85”; cage (-d): “EGL driver name: msm”, “GL renderer: Adreno X1-85”, GLES2 renderer oneDP-1at 2560x1600, 120 Hz./dev/dri/by-path:platform-3d00000.gpu-card -> card1,-render -> renderD128,platform-ae01000.display-controller-card -> card2(msm.separate_gpu_kms=1). - Ghostty 1.4.0+gbook (the cross build) ran under cage as user
gbookwith a bash shell; the owner typed in it (which ghosttygave/usr/local/bin/ghostty). Boot 1’sm3-cage=inactivewas an ordering bug (After=gbook-m3held the unit until the capture script ended); boot 2 captured it active with the renderer line. - Wi-Fi in userspace (M3 criterion), boot 2: with
10-wlan0.linkthe interface kept its name;wpa_supplicant@wlan0(reading/run/gbook/wpa_supplicant.conf) joined:Connected, 6135 MHz, -38 dBm, rx 1297 / tx 3843 Mbit/s 320 MHz EHT; networkd: “State: routable (configured)”, “Online state: online”, a DHCPv4 address via. - Audio state known (M3 criterion):
aplay -l: “no soundcards found” (no sound node in the DTS; ticket 10). - Both boots powered down cleanly on the owner’s command (
reboot: Power down, ECG3).
What failed, and the fix in the tree
| Boot | Failure | Cause (evidence) | Fix |
|---|---|---|---|
| 1 | wpa_supplicant@wlan0 never started; wait-online timed out (120 s); gbook-m3 at 260 s | udev: wlP4p1s0: renamed from wlan0 (console) | /etc/systemd/network/10-wlan0.link keeps wlan0 |
| 1 | tmp, mqueue, hugepages, configfs mounts failed | “Unable to locate executable ‘/usr/bin/mount’”: mount is not Essential in sid | mount in rootfs/packages.txt |
| 1 | m3-cage=inactive | After=gbook-m3.service on the cage unit (journal: started at 277.9 s, as gbook-m3 ended) | dropped |
| 1, 2 | m3-vulkaninfo empty, then “XDG_RUNTIME_DIR is invalid”, anv ICD noise, a display-plane error | the service environment; every Mesa ICD installed | XDG_RUNTIME_DIR=/run/gbook, VK_DRIVER_FILES = the freedreno ICD |
| 2 | rootfs netlog “sent 1307 lines … exit=124”, nothing arrived | networkd’s DUID got a second lease, /init’s udhcpc had the first; the build host’s ufw logged one block from the second to port 5140 | [DHCPv4] ClientIdentifier=mac |
| 2 | m3-ping: “ping: not found” | no iputils-ping | added |
| 1, 2 | cage: “Cannot find Xwayland binary” | not installed; harmless for Ghostty (Wayland) | none |
Timings
| Phase | Boot 1 | Boot 2 |
|---|---|---|
/init’s sweep to switch_root | about 138 s | about 138 s |
switch_root to gbook-m3 start | 121 s (wait-online timeout) | 19 s |
gbook-m3 start to end | 18 s | 18 s |
| Power-on to Ghostty | about 280 s | about 180 s |
The sweep is the next target (a fast path when ROOT is present), an owner decision.
Boot 3 (attempt 2026-11-04-20, cold; read 6:54 PM ET)
Rootfs round 3 (321 packages: mount, iputils-ping, openssh-server, sudo; DHCP client id = MAC; the cage unit
no longer waits for gbook-m3) and a KERN-A image carrying the ssh keys in the hand-off (ADR 0012 update, decisions 5
and 8):
gbook-m3at 156 s; the rootfs netlog arrived from the device’s first lease (sent 2175 lines ... exit=0), with a 1180-line redacted journal section;pingto the gateway 3 of 3;networkctlroutable, online, lease .204.- ssh from the build host works (
ssh mica, key-only, usergbook, one attempt):hostname= mica,sudo -nruns (warning “unable to resolve host mica”: no/etc/hostsin the apt variant, fixed in the tree); zero failed units. - Vulkan on Turnip works: over ssh,
vkcube --wsi wayland --c 120through cage’s socket: “Selected GPU 0: Adreno X1-85, type: IntegratedGpu”, exit 0; the device-select layer lists one device,5143:43050c01 "Adreno X1-85".vulkaninfo --summaryfails onvkGetPhysicalDeviceDisplayPlanePropertiesKHR(ERROR_OUT_OF_HOST_MEMORY) with the GPU-only card, a tool limitation;gbook-m3capturesvkcubeinstead from the next build. The firstvulkaninfoattempt had an emptyVK_DRIVER_FILES(the ICD isfreedreno_icd.json, no architecture infix): fixed. - cage on the Adreno again (
GL renderer: Adreno X1-85);m3-ssh=active listening 0.0.0.0:22. - The parked touchscreen failure printed as one
KNOWN:line per segment (before and afterGBOOK: begin).
Release boots (attempts 2026-11-04-21, -22, -23, all cold; read 8:37 PM EST)
The release rootfs, built from snapshot 20261104T000000Z (321 packages, rootfs.ext4 sha256 4b0e2e027c0b2d74…; the
afternoon’s deb.debian.org build differed in six package versions), written to ROOT with attempt.sh root-cmd and read
back block by block by root-verify (ROOT == rootfs.ext4, 1048576 blocks of 4 KiB); the KERN-A base image
9673d6d782ef4cdb unchanged. Each boot from G3 (the EC’s own reset, then G3->S5), with one ssh mica read:
attempt.sh tally '^2026-11-04-2[123]$':boots=3 known=3 stalled=0 clean=3, SCMI replies 0.1 ms, no polls./initswitched to ROOT on each boot;gbook-m3at 156, 155 and 156 s;m3-endat 177.5, 176.1 and 177.2 s.- kmscube, cage and
vkcubeon “Adreno X1-85” (Mesa 26.2.6-1) on all three;m3-sshlistening; the rootfs netlog arrived each time (2168, 2171 and 3498 lines). - Over ssh:
hostnamemica,getent hosts mica= 127.0.1.1 (the/etc/hostsfix is live),systemctl is-system-running= running, zero failed units,sudo -nruns, wlan0 .204,aplay -lno soundcards (unchanged). The journal’s only error-level lines are sudo’spam_env“Unable to open env file: /etc/default/locale” from the build host’s ssh sessions and onepam_systemdVarlink ReleaseSession line at logout; harmless, noted. - Boot 3’s journal carried 658
i2c_hid_of 0-0010: i2c_hid_get_input: incomplete reportlines within 0.1 s at 139.5 s (the switch_root moment), none on the AP console and none on boots 1 and 2: the parked touchscreen (touch-tpm-2026-11-03.md) bursting; no effect on the boot. TheKNOWN:row’s pattern does not cover that message yet. - Powered off over ssh after each read (
sudo systemctl poweroff: “reboot: Power down”, EC to G3), with the owner’s OK.
Fast sweep (attempt 2026-11-04-24, cold; read 9:21 PM EST)
Task #24: /init takes a fast sweep when it finds ROOT (GBOOK: sweep=fast: ROOT found, about 10 s): every instant
sysfs read, the Wi-Fi join and the netlog stay; the settles, the owner prompts, the backlight cycle, the standalone scan,
the display wait and the *-dmesg greps go. gbook.sweep=full, gbook.suspend=<mode>, a dp-wait image or a stick
without ROOT keep the M2 sweep (owner’s four decisions, before the stick write at 9:07 PM EST; .scratch/m3-rootfs/issues/14-fast-sweep.md).
Kernel #29, image 6f921ebb, ROOT rewritten first (root-verify found the release boots’ r/w mounts: 4675 blocks
differed, journal 1745; after root-cmd: equal).
attempt.sh tally '^2026-11-04-24$':boots=1 clean=1 stalled=0, start cold;trace_report.py: replies=78, max 0.1 ms, timeouts 0 (attempt.sh trace scmiitself gave up: its line-count check also counts the rootfs’s later transfer; retro-10 ticket 07).- Timeline (AP console):
begin6.9 s; Wi-Fi joined at 28 s (link wait 17 s, no scan);gpu-open=opened wait=1s(GMU firmware loaded at 31.0 s);switch_root32.9 s; systemd 34.3 s;wlan0: associatedagain at 51.4 s;m3-begin53.0 s; kmscube on Adreno X1-85 at 56.1 s; cage andvkcuberead at 73 to 74 s;m3-end74.2 s (177 s on -23). The rootfs side is now the larger half: 20 s tom3-begin, 21 s of captures. - The rootfs side, read 9:31 PM EST from the journal and the AP console (41 s,
switch_root32.9 s tom3-end74.2 s): systemd’s journald at 35.4 s,basic.target37.0 s,wpa_supplicant@wlan0started 37.0 s and connected at 51.4 s (its own scan: 14 s;/init’s association was dropped atswitch_root), DHCP 51.5 s, IPv6LL andwait-onlinedone 53.0 s,gbook-m3from 53.0 s (eglinfo, vulkaninfo, kmscube, ping: to 58.1 s),gbook-cage.servicestarted bygbook-m3at 58.1 s (the unit is not enabled at boot), then its fixedsleep 15to them3-cageread at 73.1 s, vkcube and ssh 74.2 s. So Ghostty came up at about 59 s (cage start plus its own start; unmeasured on the panel), and 29 s of the 41 are the second Wi-Fi scan and the capture sleep. Task #26. - Over ssh:
running, 0 failed units, no error-level journal lines; the rootfs netlog arrived (2181 lines). - Reads that differ from the full sweep by timing, not by state:
ts-vregdisabled users=0andgpio88low at 32 s (the parked touchscreen’s supply; -23 readenabled users=1at 105 s), nohid-over-i2cIRQ line for the touchscreen at 32 s,tracekept 586 lines (no touch window flooding the ring). Theedp-regline reads before the 33 s regulator cleanup; cage on the panel is the proof the panel survives it. - Powered off over ssh after the read (
sudo systemctl poweroff, 9:27 PM EST), with the owner’s OK. Power-on to Ghostty on the panel: unmeasured (cage’s lines at 73 s are gbook-m3’s journal read, not when it drew).
The rootfs side (attempt 2026-11-04-25, cold; read 9:59 PM EST)
Ticket 15 (task #26, the owner’s five decisions, every recommendation taken): gbook-cage.service enabled at boot
(WantedBy=multi-user.target, After=seatd.service, conditioned on the Ghostty binary), gbook-m3 skips kmscube when
Ghostty is there (cage holds the panel’s KMS device; its GL renderer line is the GPU proof) and polls up to 15 s for
cage’s socket and renderer line instead of sleep 15, and /init prepends freq_list=<MHz> from its own joined link to
the network block it hands over (GBOOK: wifi-handoff=freq_list=6135; the wpa_supplicant reference config: outside a
network block, freq_list limits the scan). Kernel #29, image 6a0cc711, rootfs cbd68763… (321 packages, snapshot
20261104T000000Z, lock_changed=0: the release’s packages, the files of ticket 15). attempt.sh tally '^2026-11-04-25$':
boots=1 clean=1 stalled=0, start cold; SCMI replies=78 max=0.1ms timeouts=0.
| Uptime | 2026-11-04-24 | 2026-11-04-25 | What (journal, sudo -n journalctl -b -o short-monotonic over ssh) |
|---|---|---|---|
switch_root | 32.9 s | 33.1 s | /init: wifi-link-wait=17s both (the first scan of a cold boot) |
basic.target | 37.0 s | 36.9 s | seatd, gbook-cage, wpa_supplicant@wlan0 and ssh start together |
cage GL renderer: Adreno X1-85 | about 59 s | 37.8 s | Ghostty on the panel about 38 s (inferred from cage’s renderer line; its draw is untimed) |
wpa_supplicant connected | 51.4 s (14.4 s after start) | 43.8 s (6.8 s after start) | one channel scanned, not the band |
| DHCPv4 | 51.5 s | 43.9 s | lease .204 again |
network-online | 53.0 s | 45.2 s | right after Gained IPv6LL both boots (the 1.3 s tail; decision 4: left) |
m3-begin | 53.0 s | 45.2 s | |
m3-cage read | 73.1 s (sleep 15) | 47.7 s (wait=0s) | cage was up 10 s before gbook-m3 asked |
m3-end | 74.2 s | 48.7 s | systemd-analyze: 33.075 s kernel + 16.029 s userspace |
The rootfs side is 15.6 s now (41 s on -24): 25.5 s saved, 29 s expected (the second scan kept 6.8 s on one channel; a
6 GHz AP’s beacons and the supplicant’s own delays, inference). Over ssh: running, 0 failed units, 0 error-level journal
lines; the rootfs netlog arrived (2167 lines). compare shows the new keys (wifi-handoff, m3-kmscube=skipped: cage holds the panel, m3-cage=... wait=0s uptime=) once as changed; ts-vreg read enabled users=1 and gpio88 high this
time (timing, as on -24). The Xwayland “Cannot find” line is the known harmless one.
Powered off over ssh after the read (sudo -n systemctl poweroff, 10:03 PM EST), with the owner’s OK: reboot: Power down,
EC S5->G3, G3 at EC 385 s, then G3->S5 and S5 30 s later with no S5->S3: the EC’s soft-off on AC, the same
sequence as after boot -24’s power-off (EC 3351 to 3381 s), not a power-on (a power-on continues S5->S3->S0 within a
second, as at EC 0.5 s of this boot).
Open
: met by the release boots above (8:37 PM EST); the owner tags (ADR 0011).release-draft.sh’s bar for M3- The boot time:
/init’s sweep is 138 s of the 176 s to Ghostty; a fast path when ROOT is present is an owner decision. 2026-11-04, 9:21 PM EST: done, the fast sweep (“Fast sweep” above):m3-endat 74 s. The rootfs side (20 s tom3-begin, 21 s of captures) is the next target. 2026-11-04, 9:59 PM EST: done, ticket 15 (“The rootfs side” above):m3-endat 48.7 s, Ghostty at about 38 s. Left: thewait-onlinetail after IPv6LL (1.3 s) and/init’s 17 s first scan (only a fixedscan_freq=in the owner’swifi.confshortens it). - Journal timestamps read 2026-10-16: no time source in the rootfs (the RTC is unset); harmless, noted.
Recon 2026-11-02
Collected 2026-11-02 20:22 EST by scripts/recon.sh: read-only, adb shell as uid 2000 over wireless debugging.
Serials, boot ids and MACs are redacted. denied and absent are findings about what the stock OS exposes.
| Item | Status | Value |
|---|---|---|
ro.product.device | ok | mica |
ro.product.board | empty | |
ro.boot.hardware | ok | android-desktop |
ro.bootloader | ok | depthcharge-Google_Mica.16702.59.0/Google_Mica.16702.143.0 |
ro.boot.hwc | empty | |
ro.build.fingerprint | ok | Dell/mica/mica:17/CL3B.260622.276.R2/16560795:user/release-keys |
ro.boot.verifiedbootstate | ok | orange |
ro.boot.flash.locked | ok | 0 |
ro.boot.vbmeta.device_state | ok | unlocked |
ro.boot.slot_suffix | ok | _a |
ro.boot.dtbo_idx | ok | 0,1,2 |
ro.boot.hypervisor.protected_vm.supported | ok | 1 |
kernel | ok | Linux localhost 6.12.81-android16-6-g08dd7534bf44-ab16287118-4k #1 SMP PREEMPT Tue Sep 8 10:57:08 UTC 2026 aarch64 Toybox |
/proc/cmdline | ok | console=ttynull stack_depot_disable=on cgroup_disable=pressure kasan.stacktrace=off kvm-arm.mode=protected bootconfig clk_ignore_unused pd_ignore_unused efi=noruntime efi=novamap android_arch_task_struct_size=512 id_aa64mmfr1.vh=0 arm64_sw.hvhe=0 irqaffinity=0-3 pcie_ports=compat cpufreq.default_governor=performance kvm-arm.ffa-unmap-on-lend=full,nr_handles=8192 kvm-arm.ffa_max_nr_constituents=16384 firmware_class.path=/vendor/firmware,/vendor/firmware_mnt/image console=null 8250.nr_uarts=4 console_msg_format=syslog log_buf_len=1M cgroup_disable=pressure root=/dev/ram0 reserve_mem=20M:2M:trace trace_instance=boot_mapped^traceoff@trace drm.trace=0x106 module_blacklist=cros_ec_debugfs proc_mem.force_override=ptrace binder.impl=rust no_console_suspend softlockup_panic=1 nmi_watchdog=panic pm.dpm_watchdog_enabled=1 cpufreq.default_governor=performance trace_instance=backup=boot_mapped cpuidle.governor=teo kvm-arm.protected_modules=qnoc-x1e80100.ko,pkvm-smc-filter.ko,rpmh-regulator.ko,clk-rpmh.ko,gcc-x1e80100.ko,gpucc-x1e80100.ko,pkvm-secure-memory-manager.ko,qcom_smmu_v2_v3_dispatcher.ko,pkvm_drng.ko bootconfig |
/proc/cpuinfo (head) | ok | processor : 0 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt CPU implementer : 0x51 CPU architecture: 8 CPU variant : 0x2 CPU part : 0x001 CPU revision : 1 processor : 1 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt |
/sys/firmware | ok | devicetree fdt log vpd |
/proc/device-tree/model | absent | cat: /proc/device-tree/model: No such file or directory |
/proc/device-tree/compatible | denied | /system/bin/sh: can’t open /proc/device-tree/compatible: Permission denied |
/sys/firmware/devicetree/base/compatible | denied | /system/bin/sh: can’t open /sys/firmware/devicetree/base/compatible: Permission denied |
/sys/firmware/devicetree/base (names) | ok | #address-cells #size-cells symbols aliases chosen cluster-device0 cluster-device1 cluster-device2 cluster-device3 compatible cpus firmware fpmcu-vcc-regulator interrupt-parent lpddr-channel0 lpddr-channel1 lpddr-channel2 lpddr-channel3 lpddr-channel4 lpddr-channel5 lpddr-channel6 lpddr-channel7 memory mmio-sram@18b4e000 name pmu psci qcom,hdcp regulator-dmic-en regulator-pp3300-ucam reserved-memory rsc@af20000 soc sram@c3f0000 sys-pm-vx@c320000 timer touchpad-vdd-regulator touchscreen-vdd-regulator wlan-3p3-regulator |
/sys/firmware/fdt (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/fdt: Permission denied |
/sys/firmware/log (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/log: Permission denied |
partition names | ok | KERN-A KERN-B ROOT-B bluetooth_a bluetooth_b boot_a boot_b desktop_security_persist desktop_security_storage dsp_a dsp_b dtb_a dtb_b dtbo_a dtbo_b frp_persist init_boot_a init_boot_b keymaster_a keymaster_b metadata misc modem_a modem_b nvme0n1 ota_recovery_a ota_recovery_b persist pvmfw_a pvmfw_b rawdump recovery_a recovery_b super userdata vbmeta_a vbmeta_b vendor_boot_a vendor_boot_b |
firmware tools on PATH | ok | crossystem: none futility: none gsctool: /system/bin/gsctool ectool: /system/bin/ectool vpd: none flashrom: none |
GSC firmware (gsctool -a -f) | ok | start device: NT keyids: RO 0xd4fd1f25, RW 0x405a3825 offsets: backup RO at 0, backup RW at 0x10000 Current versions: RO 2.0.119 RW 0.33.291 |
CCD state (gsctool -a -I) | ok | State: Locked Password: None Flags: 000000 Capabilities, current and default: UartGscRxAPTx Y Always UartGscTxAPRx Y Always UartGscRxECTx Y Always UartGscTxECRx - IfOpened UartGscRxFpmcuTx Y Always UartGscTxFpmcuRx - IfOpened FlashAP - IfOpened FlashEC - IfOpened OverrideWP - IfOpened RebootECAP - IfOpened GscFullConsole - IfOpened UnlockNoReboot Y Always UnlockNoShortPP Y Always OpenNoTPMWipe - IfOpened OpenNoLongPP - IfOpened RemoveBatteryBypassPP Y Always I2C - IfOpened FlashRead Y Always OpenNoDevMode - IfOpened OpenFromUSB - IfOpened OverrideBatt - IfOpened AllowUnverifiedRo - Never CCD caps bitmap: 0x29817 Capabilities are default. |
EC version (ectool version) | denied | Error: Permission denied (os error 13) |
Recon 2026-11-02
Collected 2026-11-02 20:11 EST by scripts/recon.sh: read-only, adb shell as uid 2000 over wireless debugging.
Serials, boot ids and MACs are redacted. denied and absent are findings about what the stock OS exposes.
| Item | Status | Value |
|---|---|---|
ro.product.device | ok | mica |
ro.product.board | empty | |
ro.boot.hardware | ok | android-desktop |
ro.bootloader | ok | depthcharge-Google_Mica.16702.59.0/Google_Mica.16702.143.0 |
ro.boot.hwc | empty | |
ro.build.fingerprint | ok | Dell/mica/mica:17/CL3B.260622.276.R2/16560795:user/release-keys |
ro.boot.verifiedbootstate | ok | orange |
ro.boot.flash.locked | ok | 0 |
ro.boot.vbmeta.device_state | ok | unlocked |
ro.boot.slot_suffix | ok | _a |
ro.boot.dtbo_idx | ok | 0,1,2 |
ro.boot.hypervisor.protected_vm.supported | ok | 1 |
kernel | ok | Linux localhost 6.12.81-android16-6-g08dd7534bf44-ab16287118-4k #1 SMP PREEMPT Tue Sep 8 10:57:08 UTC 2026 aarch64 Toybox |
/proc/cmdline | ok | console=ttynull stack_depot_disable=on cgroup_disable=pressure kasan.stacktrace=off kvm-arm.mode=protected bootconfig clk_ignore_unused pd_ignore_unused efi=noruntime efi=novamap android_arch_task_struct_size=512 id_aa64mmfr1.vh=0 arm64_sw.hvhe=0 irqaffinity=0-3 pcie_ports=compat cpufreq.default_governor=performance kvm-arm.ffa-unmap-on-lend=full,nr_handles=8192 kvm-arm.ffa_max_nr_constituents=16384 firmware_class.path=/vendor/firmware,/vendor/firmware_mnt/image console=null 8250.nr_uarts=4 console_msg_format=syslog log_buf_len=1M cgroup_disable=pressure root=/dev/ram0 reserve_mem=20M:2M:trace trace_instance=boot_mapped^traceoff@trace drm.trace=0x106 module_blacklist=cros_ec_debugfs proc_mem.force_override=ptrace binder.impl=rust no_console_suspend softlockup_panic=1 nmi_watchdog=panic pm.dpm_watchdog_enabled=1 cpufreq.default_governor=performance trace_instance=backup=boot_mapped cpuidle.governor=teo kvm-arm.protected_modules=qnoc-x1e80100.ko,pkvm-smc-filter.ko,rpmh-regulator.ko,clk-rpmh.ko,gcc-x1e80100.ko,gpucc-x1e80100.ko,pkvm-secure-memory-manager.ko,qcom_smmu_v2_v3_dispatcher.ko,pkvm_drng.ko bootconfig |
/proc/cpuinfo (head) | ok | processor : 0 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt CPU implementer : 0x51 CPU architecture: 8 CPU variant : 0x2 CPU part : 0x001 CPU revision : 1 processor : 1 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt |
/sys/firmware | ok | devicetree fdt log vpd |
/proc/device-tree/model | absent | cat: /proc/device-tree/model: No such file or directory |
/proc/device-tree/compatible | denied | /system/bin/sh: can’t open /proc/device-tree/compatible: Permission denied |
/sys/firmware/devicetree/base/compatible | denied | /system/bin/sh: can’t open /sys/firmware/devicetree/base/compatible: Permission denied |
/sys/firmware/devicetree/base (names) | ok | #address-cells #size-cells symbols aliases chosen cluster-device0 cluster-device1 cluster-device2 cluster-device3 compatible cpus firmware fpmcu-vcc-regulator interrupt-parent lpddr-channel0 lpddr-channel1 lpddr-channel2 lpddr-channel3 lpddr-channel4 lpddr-channel5 lpddr-channel6 lpddr-channel7 memory mmio-sram@18b4e000 name pmu psci qcom,hdcp regulator-dmic-en regulator-pp3300-ucam reserved-memory rsc@af20000 soc sram@c3f0000 sys-pm-vx@c320000 timer touchpad-vdd-regulator touchscreen-vdd-regulator wlan-3p3-regulator |
/sys/firmware/fdt (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/fdt: Permission denied |
/sys/firmware/log (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/log: Permission denied |
partition names | ok | KERN-A KERN-B ROOT-B bluetooth_a bluetooth_b boot_a boot_b desktop_security_persist desktop_security_storage dsp_a dsp_b dtb_a dtb_b dtbo_a dtbo_b frp_persist init_boot_a init_boot_b keymaster_a keymaster_b metadata misc modem_a modem_b nvme0n1 ota_recovery_a ota_recovery_b persist pvmfw_a pvmfw_b rawdump recovery_a recovery_b super userdata vbmeta_a vbmeta_b vendor_boot_a vendor_boot_b |
firmware tools on PATH | ok | crossystem: none futility: none gsctool: /system/bin/gsctool ectool: /system/bin/ectool vpd: none flashrom: none |
GSC firmware (gsctool -a -f) | ok | start device: NT keyids: RO 0xd4fd1f25, RW 0x405a3825 offsets: backup RO at 0, backup RW at 0x10000 Current versions: RO 2.0.119 RW 0.33.291 |
CCD state (gsctool -a -I) | ok | State: Locked Password: None Flags: 000000 Capabilities, current and default: UartGscRxAPTx Y Always UartGscTxAPRx Y Always UartGscRxECTx Y Always UartGscTxECRx - IfOpened UartGscRxFpmcuTx Y Always UartGscTxFpmcuRx - IfOpened FlashAP - IfOpened FlashEC - IfOpened OverrideWP - IfOpened RebootECAP - IfOpened GscFullConsole - IfOpened UnlockNoReboot Y Always UnlockNoShortPP Y Always OpenNoTPMWipe - IfOpened OpenNoLongPP - IfOpened RemoveBatteryBypassPP Y Always I2C - IfOpened FlashRead Y Always OpenNoDevMode - IfOpened OpenFromUSB - IfOpened OverrideBatt - IfOpened AllowUnverifiedRo - Never CCD caps bitmap: 0x29817 Capabilities are default. |
EC version (ectool version) | denied | Error: Permission denied (os error 13) |
Recon 2026-11-02
Collected 2026-11-02 19:58 EST by scripts/recon.sh: read-only, adb shell as uid 2000 over wireless debugging.
Serials, boot ids and MACs are redacted. denied and absent are findings about what the stock OS exposes.
| Item | Status | Value |
|---|---|---|
ro.product.device | ok | mica |
ro.product.board | empty | |
ro.boot.hardware | ok | android-desktop |
ro.bootloader | ok | depthcharge-Google_Mica.16702.59.0/Google_Mica.16702.143.0 |
ro.boot.hwc | empty | |
ro.build.fingerprint | ok | Dell/mica/mica:17/CL3B.260622.276.R2/16560795:user/release-keys |
ro.boot.verifiedbootstate | ok | orange |
ro.boot.flash.locked | ok | 0 |
ro.boot.vbmeta.device_state | ok | unlocked |
ro.boot.slot_suffix | ok | _b |
ro.boot.dtbo_idx | ok | 0,1,2 |
ro.boot.hypervisor.protected_vm.supported | ok | 1 |
kernel | ok | Linux localhost 6.12.81-android16-6-g08dd7534bf44-ab16287118-4k #1 SMP PREEMPT Tue Sep 8 10:57:08 UTC 2026 aarch64 Toybox |
/proc/cmdline | ok | console=ttynull stack_depot_disable=on cgroup_disable=pressure kasan.stacktrace=off kvm-arm.mode=protected bootconfig clk_ignore_unused pd_ignore_unused efi=noruntime efi=novamap android_arch_task_struct_size=512 id_aa64mmfr1.vh=0 arm64_sw.hvhe=0 irqaffinity=0-3 pcie_ports=compat cpufreq.default_governor=performance kvm-arm.ffa-unmap-on-lend=full,nr_handles=8192 kvm-arm.ffa_max_nr_constituents=16384 firmware_class.path=/vendor/firmware,/vendor/firmware_mnt/image console=null 8250.nr_uarts=4 console_msg_format=syslog log_buf_len=1M cgroup_disable=pressure root=/dev/ram0 reserve_mem=20M:2M:trace trace_instance=boot_mapped^traceoff@trace drm.trace=0x106 module_blacklist=cros_ec_debugfs proc_mem.force_override=ptrace binder.impl=rust no_console_suspend softlockup_panic=1 nmi_watchdog=panic pm.dpm_watchdog_enabled=1 cpufreq.default_governor=performance trace_instance=backup=boot_mapped cpuidle.governor=teo kvm-arm.protected_modules=qnoc-x1e80100.ko,pkvm-smc-filter.ko,rpmh-regulator.ko,clk-rpmh.ko,gcc-x1e80100.ko,gpucc-x1e80100.ko,pkvm-secure-memory-manager.ko,qcom_smmu_v2_v3_dispatcher.ko,pkvm_drng.ko bootconfig |
/proc/cpuinfo (head) | ok | processor : 0 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt CPU implementer : 0x51 CPU architecture: 8 CPU variant : 0x2 CPU part : 0x001 CPU revision : 1 processor : 1 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt |
/sys/firmware | ok | devicetree fdt log vpd |
/proc/device-tree/model | absent | cat: /proc/device-tree/model: No such file or directory |
/proc/device-tree/compatible | denied | /system/bin/sh: can’t open /proc/device-tree/compatible: Permission denied |
/sys/firmware/devicetree/base/compatible | denied | /system/bin/sh: can’t open /sys/firmware/devicetree/base/compatible: Permission denied |
/sys/firmware/devicetree/base (names) | ok | #address-cells #size-cells symbols aliases chosen cluster-device0 cluster-device1 cluster-device2 cluster-device3 compatible cpus firmware fpmcu-vcc-regulator interrupt-parent lpddr-channel0 lpddr-channel1 lpddr-channel2 lpddr-channel3 lpddr-channel4 lpddr-channel5 lpddr-channel6 lpddr-channel7 memory mmio-sram@18b4e000 name pmu psci qcom,hdcp regulator-dmic-en regulator-pp3300-ucam reserved-memory rsc@af20000 soc sram@c3f0000 sys-pm-vx@c320000 timer touchpad-vdd-regulator touchscreen-vdd-regulator wlan-3p3-regulator |
/sys/firmware/fdt (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/fdt: Permission denied |
/sys/firmware/log (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/log: Permission denied |
partition names | ok | KERN-A KERN-B ROOT-B bluetooth_a bluetooth_b boot_a boot_b desktop_security_persist desktop_security_storage dsp_a dsp_b dtb_a dtb_b dtbo_a dtbo_b frp_persist init_boot_a init_boot_b keymaster_a keymaster_b metadata misc modem_a modem_b nvme0n1 ota_recovery_a ota_recovery_b persist pvmfw_a pvmfw_b rawdump recovery_a recovery_b super userdata vbmeta_a vbmeta_b vendor_boot_a vendor_boot_b |
firmware tools on PATH | ok | crossystem: none futility: none gsctool: /system/bin/gsctool ectool: /system/bin/ectool vpd: none flashrom: none |
GSC firmware (gsctool -a -f) | ok | start device: NT keyids: RO 0xd4fd1f25, RW 0x405a3825 offsets: backup RO at 0, backup RW at 0x10000 Current versions: RO 2.0.119 RW 0.33.291 |
CCD state (gsctool -a -I) | ok | State: Locked Password: None Flags: 000000 Capabilities, current and default: UartGscRxAPTx Y Always UartGscTxAPRx Y Always UartGscRxECTx Y Always UartGscTxECRx - IfOpened UartGscRxFpmcuTx Y Always UartGscTxFpmcuRx - IfOpened FlashAP - IfOpened FlashEC - IfOpened OverrideWP - IfOpened RebootECAP - IfOpened GscFullConsole - IfOpened UnlockNoReboot Y Always UnlockNoShortPP Y Always OpenNoTPMWipe - IfOpened OpenNoLongPP - IfOpened RemoveBatteryBypassPP Y Always I2C - IfOpened FlashRead Y Always OpenNoDevMode - IfOpened OpenFromUSB - IfOpened OverrideBatt - IfOpened AllowUnverifiedRo - Never CCD caps bitmap: 0x29817 Capabilities are default. |
EC version (ectool version) | denied | Error: Permission denied (os error 13) |
Recon 2026-11-02
Collected 2026-11-02 19:39 EST by scripts/recon.sh: read-only, adb shell as uid 2000 over wireless debugging.
Serials, boot ids and MACs are redacted. denied and absent are findings about what the stock OS exposes.
| Item | Status | Value |
|---|---|---|
ro.product.device | ok | mica |
ro.product.board | empty | |
ro.boot.hardware | ok | android-desktop |
ro.bootloader | ok | depthcharge-Google_Mica.16702.59.0/Google_Mica.16702.143.0 |
ro.boot.hwc | empty | |
ro.build.fingerprint | ok | Dell/mica/mica:17/CL3B.260622.276.R2/16560795:user/release-keys |
ro.boot.verifiedbootstate | ok | green |
ro.boot.flash.locked | ok | 1 |
ro.boot.vbmeta.device_state | ok | locked |
ro.boot.slot_suffix | ok | _b |
ro.boot.dtbo_idx | ok | 0,1,2 |
ro.boot.hypervisor.protected_vm.supported | ok | 1 |
kernel | ok | Linux localhost 6.12.81-android16-6-g08dd7534bf44-ab16287118-4k #1 SMP PREEMPT Tue Sep 8 10:57:08 UTC 2026 aarch64 Toybox |
/proc/cmdline | ok | console=ttynull stack_depot_disable=on cgroup_disable=pressure kasan.stacktrace=off kvm-arm.mode=protected bootconfig clk_ignore_unused pd_ignore_unused efi=noruntime efi=novamap android_arch_task_struct_size=512 id_aa64mmfr1.vh=0 arm64_sw.hvhe=0 irqaffinity=0-3 pcie_ports=compat cpufreq.default_governor=performance kvm-arm.ffa-unmap-on-lend=full,nr_handles=8192 kvm-arm.ffa_max_nr_constituents=16384 firmware_class.path=/vendor/firmware,/vendor/firmware_mnt/image console=null 8250.nr_uarts=4 console_msg_format=syslog log_buf_len=1M cgroup_disable=pressure root=/dev/ram0 reserve_mem=20M:2M:trace trace_instance=boot_mapped^traceoff@trace drm.trace=0x106 module_blacklist=cros_ec_debugfs proc_mem.force_override=ptrace binder.impl=rust no_console_suspend softlockup_panic=1 nmi_watchdog=panic pm.dpm_watchdog_enabled=1 cpufreq.default_governor=performance trace_instance=backup=boot_mapped cpuidle.governor=teo kvm-arm.protected_modules=qnoc-x1e80100.ko,pkvm-smc-filter.ko,rpmh-regulator.ko,clk-rpmh.ko,gcc-x1e80100.ko,gpucc-x1e80100.ko,pkvm-secure-memory-manager.ko,qcom_smmu_v2_v3_dispatcher.ko,pkvm_drng.ko bootconfig |
/proc/cpuinfo (head) | ok | processor : 0 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt CPU implementer : 0x51 CPU architecture: 8 CPU variant : 0x2 CPU part : 0x001 CPU revision : 1 processor : 1 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt |
/sys/firmware | ok | devicetree fdt log vpd |
/proc/device-tree/model | absent | cat: /proc/device-tree/model: No such file or directory |
/proc/device-tree/compatible | denied | /system/bin/sh: can’t open /proc/device-tree/compatible: Permission denied |
/sys/firmware/devicetree/base/compatible | denied | /system/bin/sh: can’t open /sys/firmware/devicetree/base/compatible: Permission denied |
/sys/firmware/devicetree/base (names) | ok | #address-cells #size-cells symbols aliases chosen cluster-device0 cluster-device1 cluster-device2 cluster-device3 compatible cpus firmware fpmcu-vcc-regulator interrupt-parent lpddr-channel0 lpddr-channel1 lpddr-channel2 lpddr-channel3 lpddr-channel4 lpddr-channel5 lpddr-channel6 lpddr-channel7 memory mmio-sram@18b4e000 name pmu psci qcom,hdcp regulator-dmic-en regulator-pp3300-ucam reserved-memory rsc@af20000 soc sram@c3f0000 sys-pm-vx@c320000 timer touchpad-vdd-regulator touchscreen-vdd-regulator wlan-3p3-regulator |
/sys/firmware/fdt (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/fdt: Permission denied |
/sys/firmware/log (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/log: Permission denied |
partition names | ok | KERN-A KERN-B ROOT-B bluetooth_a bluetooth_b boot_a boot_b desktop_security_persist desktop_security_storage dsp_a dsp_b dtb_a dtb_b dtbo_a dtbo_b frp_persist init_boot_a init_boot_b keymaster_a keymaster_b metadata misc modem_a modem_b nvme0n1 ota_recovery_a ota_recovery_b persist pvmfw_a pvmfw_b rawdump recovery_a recovery_b super userdata vbmeta_a vbmeta_b vendor_boot_a vendor_boot_b |
firmware tools on PATH | ok | crossystem: none futility: none gsctool: /system/bin/gsctool ectool: /system/bin/ectool vpd: none flashrom: none |
GSC firmware (gsctool -a -f) | ok | start device: NT keyids: RO 0xd4fd1f25, RW 0x405a3825 offsets: backup RO at 0, backup RW at 0x10000 Current versions: RO 2.0.119 RW 0.33.291 |
CCD state (gsctool -a -I) | ok | State: Locked Password: None Flags: 000000 Capabilities, current and default: UartGscRxAPTx Y Always UartGscTxAPRx Y Always UartGscRxECTx Y Always UartGscTxECRx - IfOpened UartGscRxFpmcuTx Y Always UartGscTxFpmcuRx - IfOpened FlashAP - IfOpened FlashEC - IfOpened OverrideWP - IfOpened RebootECAP - IfOpened GscFullConsole - IfOpened UnlockNoReboot Y Always UnlockNoShortPP Y Always OpenNoTPMWipe - IfOpened OpenNoLongPP - IfOpened RemoveBatteryBypassPP Y Always I2C - IfOpened FlashRead Y Always OpenNoDevMode - IfOpened OpenFromUSB - IfOpened OverrideBatt - IfOpened AllowUnverifiedRo - Never CCD caps bitmap: 0x29817 Capabilities are default. |
EC version (ectool version) | denied | Error: Permission denied (os error 13) |
Recon 2026-11-02
Collected 2026-11-02 18:59 EST by scripts/recon.sh: read-only, adb shell as uid 2000 over wireless debugging.
Serials, boot ids and MACs are redacted. denied and absent are findings about what the stock OS exposes.
| Item | Status | Value |
|---|---|---|
ro.product.device | ok | mica |
ro.product.board | empty | |
ro.boot.hardware | ok | android-desktop |
ro.bootloader | ok | depthcharge-Google_Mica.16702.59.0/Google_Mica.16702.143.0 |
ro.boot.hwc | empty | |
ro.build.fingerprint | ok | Dell/mica/mica:17/CL3B.260622.276.R2/16560795:user/release-keys |
ro.boot.verifiedbootstate | ok | green |
ro.boot.flash.locked | ok | 1 |
ro.boot.vbmeta.device_state | ok | locked |
ro.boot.slot_suffix | ok | _b |
ro.boot.dtbo_idx | ok | 0,1,2 |
ro.boot.hypervisor.protected_vm.supported | ok | 1 |
kernel | ok | Linux localhost 6.12.81-android16-6-g08dd7534bf44-ab16287118-4k #1 SMP PREEMPT Tue Sep 8 10:57:08 UTC 2026 aarch64 Toybox |
/proc/cmdline | ok | console=ttynull stack_depot_disable=on cgroup_disable=pressure kasan.stacktrace=off kvm-arm.mode=protected bootconfig clk_ignore_unused pd_ignore_unused efi=noruntime efi=novamap android_arch_task_struct_size=512 id_aa64mmfr1.vh=0 arm64_sw.hvhe=0 irqaffinity=0-3 pcie_ports=compat cpufreq.default_governor=performance kvm-arm.ffa-unmap-on-lend=full,nr_handles=8192 kvm-arm.ffa_max_nr_constituents=16384 firmware_class.path=/vendor/firmware,/vendor/firmware_mnt/image console=null 8250.nr_uarts=4 console_msg_format=syslog log_buf_len=1M cgroup_disable=pressure root=/dev/ram0 reserve_mem=20M:2M:trace trace_instance=boot_mapped^traceoff@trace drm.trace=0x106 module_blacklist=cros_ec_debugfs proc_mem.force_override=ptrace binder.impl=rust no_console_suspend softlockup_panic=1 nmi_watchdog=panic pm.dpm_watchdog_enabled=1 cpufreq.default_governor=performance trace_instance=backup=boot_mapped cpuidle.governor=teo kvm-arm.protected_modules=qnoc-x1e80100.ko,pkvm-smc-filter.ko,rpmh-regulator.ko,clk-rpmh.ko,gcc-x1e80100.ko,gpucc-x1e80100.ko,pkvm-secure-memory-manager.ko,qcom_smmu_v2_v3_dispatcher.ko,pkvm_drng.ko bootconfig |
/proc/cpuinfo (head) | ok | processor : 0 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt CPU implementer : 0x51 CPU architecture: 8 CPU variant : 0x2 CPU part : 0x001 CPU revision : 1 processor : 1 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt |
/sys/firmware | ok | devicetree fdt log vpd |
/proc/device-tree/model | absent | cat: /proc/device-tree/model: No such file or directory |
/proc/device-tree/compatible | denied | /system/bin/sh: can’t open /proc/device-tree/compatible: Permission denied |
/sys/firmware/devicetree/base/compatible | denied | /system/bin/sh: can’t open /sys/firmware/devicetree/base/compatible: Permission denied |
/sys/firmware/devicetree/base (names) | ok | #address-cells #size-cells symbols aliases chosen cluster-device0 cluster-device1 cluster-device2 cluster-device3 compatible cpus firmware fpmcu-vcc-regulator interrupt-parent lpddr-channel0 lpddr-channel1 lpddr-channel2 lpddr-channel3 lpddr-channel4 lpddr-channel5 lpddr-channel6 lpddr-channel7 memory mmio-sram@18b4e000 name pmu psci qcom,hdcp regulator-dmic-en regulator-pp3300-ucam reserved-memory rsc@af20000 soc sram@c3f0000 sys-pm-vx@c320000 timer touchpad-vdd-regulator touchscreen-vdd-regulator wlan-3p3-regulator |
/sys/firmware/fdt (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/fdt: Permission denied |
/sys/firmware/log (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/log: Permission denied |
partition names | ok | KERN-A KERN-B ROOT-B bluetooth_a bluetooth_b boot_a boot_b desktop_security_persist desktop_security_storage dsp_a dsp_b dtb_a dtb_b dtbo_a dtbo_b frp_persist init_boot_a init_boot_b keymaster_a keymaster_b metadata misc modem_a modem_b nvme0n1 ota_recovery_a ota_recovery_b persist pvmfw_a pvmfw_b rawdump recovery_a recovery_b super userdata vbmeta_a vbmeta_b vendor_boot_a vendor_boot_b |
firmware tools on PATH | ok | crossystem: none futility: none gsctool: /system/bin/gsctool ectool: /system/bin/ectool vpd: none flashrom: none |
GSC firmware (gsctool -a -f) | ok | start device: NT keyids: RO 0xd4fd1f25, RW 0x405a3825 offsets: backup RO at 0, backup RW at 0x10000 Current versions: RO 2.0.119 RW 0.33.291 |
CCD state (gsctool -a -I) | ok | State: Locked Password: None Flags: 000000 Capabilities, current and default: UartGscRxAPTx Y Always UartGscTxAPRx Y Always UartGscRxECTx Y Always UartGscTxECRx - IfOpened UartGscRxFpmcuTx Y Always UartGscTxFpmcuRx - IfOpened FlashAP - IfOpened FlashEC - IfOpened OverrideWP - IfOpened RebootECAP - IfOpened GscFullConsole - IfOpened UnlockNoReboot Y Always UnlockNoShortPP Y Always OpenNoTPMWipe - IfOpened OpenNoLongPP - IfOpened RemoveBatteryBypassPP Y Always I2C - IfOpened FlashRead Y Always OpenNoDevMode - IfOpened OpenFromUSB - IfOpened OverrideBatt - IfOpened AllowUnverifiedRo - Never CCD caps bitmap: 0x29817 Capabilities are default. |
EC version (ectool version) | denied | Error: Permission denied (os error 13) |
Recon 2026-10-21
Collected 2026-10-21 12:35 EDT by scripts/recon.sh: read-only, adb shell as uid 2000 over wireless debugging.
Serials, boot ids and MACs are redacted. denied and absent are findings about what the stock OS exposes.
| Item | Status | Value |
|---|---|---|
ro.product.device | ok | mica |
ro.product.board | empty | |
ro.boot.hardware | ok | android-desktop |
ro.bootloader | ok | depthcharge-Google_Mica.16702.59.0/Google_Mica.16702.143.0 |
ro.boot.hwc | empty | |
ro.build.fingerprint | ok | Dell/mica/mica:17/CL3B.260622.276.R2/16560795:user/release-keys |
ro.boot.verifiedbootstate | ok | green |
ro.boot.flash.locked | ok | 1 |
ro.boot.vbmeta.device_state | ok | locked |
ro.boot.slot_suffix | ok | _b |
ro.boot.dtbo_idx | ok | 0,1,2 |
ro.boot.hypervisor.protected_vm.supported | ok | 1 |
kernel | ok | Linux localhost 6.12.81-android16-6-g08dd7534bf44-ab16287118-4k #1 SMP PREEMPT Tue Sep 8 10:57:08 UTC 2026 aarch64 Toybox |
/proc/cmdline | ok | console=ttynull stack_depot_disable=on cgroup_disable=pressure kasan.stacktrace=off kvm-arm.mode=protected bootconfig clk_ignore_unused pd_ignore_unused efi=noruntime efi=novamap android_arch_task_struct_size=512 id_aa64mmfr1.vh=0 arm64_sw.hvhe=0 irqaffinity=0-3 pcie_ports=compat cpufreq.default_governor=performance kvm-arm.ffa-unmap-on-lend=full,nr_handles=8192 kvm-arm.ffa_max_nr_constituents=16384 firmware_class.path=/vendor/firmware,/vendor/firmware_mnt/image console=null 8250.nr_uarts=4 console_msg_format=syslog log_buf_len=1M cgroup_disable=pressure root=/dev/ram0 reserve_mem=20M:2M:trace trace_instance=boot_mapped^traceoff@trace drm.trace=0x106 module_blacklist=cros_ec_debugfs proc_mem.force_override=ptrace binder.impl=rust no_console_suspend softlockup_panic=1 nmi_watchdog=panic pm.dpm_watchdog_enabled=1 cpufreq.default_governor=performance trace_instance=backup=boot_mapped cpuidle.governor=teo kvm-arm.protected_modules=qnoc-x1e80100.ko,pkvm-smc-filter.ko,rpmh-regulator.ko,clk-rpmh.ko,gcc-x1e80100.ko,gpucc-x1e80100.ko,pkvm-secure-memory-manager.ko,qcom_smmu_v2_v3_dispatcher.ko,pkvm_drng.ko bootconfig |
/proc/cpuinfo (head) | ok | processor : 0 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt CPU implementer : 0x51 CPU architecture: 8 CPU variant : 0x2 CPU part : 0x001 CPU revision : 1 processor : 1 BogoMIPS : 38.40 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 rng bti ecv afp rpres wfxt |
/sys/firmware | ok | devicetree fdt log vpd |
/proc/device-tree/model | absent | cat: /proc/device-tree/model: No such file or directory |
/proc/device-tree/compatible | denied | /system/bin/sh: can’t open /proc/device-tree/compatible: Permission denied |
/sys/firmware/devicetree/base/compatible | denied | /system/bin/sh: can’t open /sys/firmware/devicetree/base/compatible: Permission denied |
/sys/firmware/devicetree/base (names) | ok | #address-cells #size-cells symbols aliases chosen cluster-device0 cluster-device1 cluster-device2 cluster-device3 compatible cpus firmware fpmcu-vcc-regulator interrupt-parent lpddr-channel0 lpddr-channel1 lpddr-channel2 lpddr-channel3 lpddr-channel4 lpddr-channel5 lpddr-channel6 lpddr-channel7 memory mmio-sram@18b4e000 name pmu psci qcom,hdcp regulator-dmic-en regulator-pp3300-ucam reserved-memory rsc@af20000 soc sram@c3f0000 sys-pm-vx@c320000 timer touchpad-vdd-regulator touchscreen-vdd-regulator wlan-3p3-regulator |
/sys/firmware/fdt (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/fdt: Permission denied |
/sys/firmware/log (bytes) | denied | /system/bin/sh: can’t open /sys/firmware/log: Permission denied |
partition names | ok | KERN-A KERN-B ROOT-B bluetooth_a bluetooth_b boot_a boot_b desktop_security_persist desktop_security_storage dsp_a dsp_b dtb_a dtb_b dtbo_a dtbo_b frp_persist init_boot_a init_boot_b keymaster_a keymaster_b metadata misc modem_a modem_b nvme0n1 ota_recovery_a ota_recovery_b persist pvmfw_a pvmfw_b rawdump recovery_a recovery_b super userdata vbmeta_a vbmeta_b vendor_boot_a vendor_boot_b |
firmware tools on PATH | ok | crossystem: none futility: none gsctool: /system/bin/gsctool ectool: /system/bin/ectool vpd: none flashrom: none |
GSC firmware (gsctool -a -f) | ok | start device: NT keyids: RO 0xd4fd1f25, RW 0x405a3825 offsets: backup RO at 0, backup RW at 0x10000 Current versions: RO 2.0.119 RW 0.33.291 |
CCD state (gsctool -a -I) | ok | State: Locked Password: None Flags: 000000 Capabilities, current and default: UartGscRxAPTx Y Always UartGscTxAPRx Y Always UartGscRxECTx Y Always UartGscTxECRx - IfOpened UartGscRxFpmcuTx Y Always UartGscTxFpmcuRx - IfOpened FlashAP - IfOpened FlashEC - IfOpened OverrideWP - IfOpened RebootECAP - IfOpened GscFullConsole - IfOpened UnlockNoReboot Y Always UnlockNoShortPP Y Always OpenNoTPMWipe - IfOpened OpenNoLongPP - IfOpened RemoveBatteryBypassPP Y Always I2C - IfOpened FlashRead Y Always OpenNoDevMode - IfOpened OpenFromUSB - IfOpened OverrideBatt - IfOpened AllowUnverifiedRo - Never CCD caps bitmap: 0x29817 Capabilities are default. |
EC version (ectool version) | denied | Error: Permission denied (os error 13) |
PCIe under mainline on mica (2026-11-02)
Attempt 2026-11-02-6: kernel/mica-pcie.config on top of the USB and ADSP fragments.
Source: the AP console over SuzyQ (raw log dumps/firstboot/2026-11-02-ap.log).
Result: both PCIe controllers up, both devices enumerate
| Address | Device | Class | Link | Driver |
|---|---|---|---|---|
0006:00:00.0 | root port 17cb:0111 (pcie@1bf8000, gen4x4 PHY 1bfc000) | 0x060400 | 16 GT/s x4 | pcieport |
0006:01:00.0 | NVMe SSD 1344:5427 (vendor 0x1344: Micron) | 0x010802 | 16 GT/s x4 | none (on purpose) |
0004:00:00.0 | root port 17cb:0111 (pcie@1c08000, gen3x2 PHY 1c0e000) | 0x060400 | 8 GT/s x2 | pcieport |
0004:01:00.0 | WCN7850 Wi-Fi 17cb:1107 | 0x028000 | 8 GT/s x2 | none (ath12k probe failed) |
qcom-pcie 1bf8000.pcie: PCIe Gen.4 x4 link upat 1.6 s;1c08000.pcie: PCIe Gen.3 x2 link upat 1.75 s.- Wi-Fi: the WCN7850 PMU and PCI power control powered the chip;
ath12k_wifi7_pcibound, thenDirect firmware load for ath12k/WCN7850/hw2.0/amss.bin failed with error -2→failed to start mhi: -110. No Wi-Fi firmware was shipped. - SSD safety:
BLK_DEV_NVMEis=mand guarded by# forbid-y:in the fragment, so enumeration read config space only; no NVMe command was sent. The SSD is the stock disk and is never written (ADR 0003, ticket 07). - Deferred now: only
arm-scmi(“failed to setup channels”). PCIe, USB, the GPU SMMU, the DP aux bridges andpmic-glinkall probed.
What made it work
The QMP PCIe PHY was =m. The Wi-Fi port also needs PCI_PWRCTRL_PWRSEQ, which has no prompt and is selected only
by ATH11K/ATH12K; ATH12K in turn needs MAC80211, CFG80211 and RFKILL built in
(scripts/dt-kconfig-map.py, scripts/build-kernel.sh demotion check).
Phase 3: panel-edp knows the mica panel (2026-11-03)
Attempt 2026-11-03-9. Image c360e4af47255421, kernel #9 7.3.0-ga804c5742bff-dirty (2026-11-03 4:17 PM ET),
commit c6ef957. Sources: the netlog dumps/firstboot/2026-11-03-netlog.txt and the AP console log
dumps/firstboot/2026-11-03-ap.log (both gitignored).
Why
Every boot since the display came up (attempt -4, display-2026-11-03.md) logged a
WARNING: drivers/gpu/drm/panel/panel-edp.c:829 at generic_edp_panel_probe backtrace and
Unknown panel LGD 0x0804, using conservative timings. The fallback is unprepare 2000 ms and enable 200 ms
(panel-edp.c:755-772 at a804c57).
- No mica source for the timings: the stock tree has no panel node. Its vendor eDP node only names GPIOs and
backlight limits (
entry01-overlay.dts:119-140). A web search (2026-11-03) found no upstream entry forLGD 0x0804or “LP134WQ”. - Owner’s decisions (2026-11-03, “1a 2a 3b”): carry kernel patches in the repo (ADR 0007), use the LG Display delays, and keep the patch local.
Change (commit c6ef957)
kernel/patches/0001-drm-panel-edp-add-lgd-0x0804.patch:EDP_PANEL_ENTRY('L', 'G', 'D', 0x0804, &delay_200_500_e200_d200, "LP134WQ"), after0x07fe.delay_200_500_e200_d200(hpd_absent 200, unprepare 500, enable 200, disable 200 ms) is what all 12 LG Display entries use ata804c57. Not from a datasheet.- The name does not match the EDID’s cut-off text (
"RNYV4?LP134WQ"), sofind_edp_panel()matches by panel ID, its second loop (panel-edp.c:2211-2232).
scripts/build-kernel.shapplieskernel/patches/before each build.check.shcheck 20 fails when a patch stops applying (red-tested with a broken copy).
Result: pass (panel)
panel-simple-dp-aux aux-aea0000.displayport-controller: Detected LGD LP134WQ (0x0804)at 1.675 s. NoWARNINGand nogeneric_edp_panel_probebacktrace anywhere in the log.card2-eDP-1connected at 2560x1600,VREG_EDP_3P3andVBL9enabled, backlight 2048/4095.- At shutdown (189.5 s) the panel driver logs
Skipping disable of already disabled panelandSkipping unprepare of already unprepared panel. Inference: msm had already turned the panel off; not new information about the entry.
Regression seen in the same boot: SCMI timed out
Not caused by this change (the patch touches only panel-edp.c), and attempt -8 with the same config had working
SCMI. Details and the open question are in scmi-2026-11-03.md, “Update: attempt -9”.
Phase 3: GPU, the Adreno X1-85 boots its GMU (2026-11-03)
Attempt 2026-11-03-5. Image d862b3e055610ddf, kernel #6 (2026-11-03 2:23 PM ET), commit d67114c. Sources: the
netlog dumps/firstboot/2026-11-03-netlog.txt (1308 lines) and the AP console log dumps/firstboot/2026-11-03-ap.log
(both gitignored), plus the owner looking at the panel.
Why
In attempt 2026-11-03-4 (display-2026-11-03.md) the display bound only because the command
line carried msm.skip_gpu=1. The GPU SMMU 3da0000.iommu stayed deferred (“supplier 3d90000.clock-controller not
ready”). scripts/dt-kconfig-map.py on the mica-el2 DTB showed gpucc (CLK_X1E80100_GPUCC) as the only GPU-path
driver still at =m.
Changes (commit d67114c)
- Kernel:
kernel/mica-gpu.configsetsCLK_X1E80100_GPUCC=y. - Command line:
msm.separate_gpu_kms=1replacesmsm.skip_gpu=1. The GPU gets its own DRM device throughmsm_gpu_probe(adreno_device.c:275,msm_drv.c:1102-1121), so a GPU failure can’t unbind the display. - Firmware:
qcom/gen70500_sqe.fwandqcom/gen70500_gmu.bin(names froma6xx_catalog.c:1573-1577).- They come from
linux-firmware-qcom 20260916-1, downloaded into~/.cache/gbook/pkgs, signature-checked against pacman’s keyring and never installed on the build host. - sha256
05ae89e6…(sqe) and5dfba247…(gmu), the same as kernel.org linux-firmware tag20260916. scripts/stage-firmware.shgained this “pkg” source.
- They come from
/init: after the display block, it opens the GPU’s render node in a background subshell that is never waited for. That open triggersmsm_open→load_gpu.
Result: pass
- SMMU:
arm-smmu 3da0000.iommuprobed at 0.506 s (SMMUv2, 25 context banks).3d00000.gpuand3d6a000.gmujoined iommu groups 0 and 1.3da0000.iommuis gone from every deferred list. - GPU DRM device:
[drm] Initialized msm 1.13.0 for 3d00000.gpu on minor 1at 0.542 s./initfound it asrenderD128, driveradreno.- The display is a separate device:
msm-kmsforae01000.display-controlleron minor 2 (card2).
- Firmware and GMU, when
/initopened the node at 57.8 s:loaded qcom/gen70500_sqe.fw from new locationandloaded qcom/gen70500_gmu.bin from new locationLoaded GMU firmware v4.3.17Zap shader not enabled - using SECVID_TRUST_CNTL instead. This is the expected EL2 path:x1-el2.dtsodisablesgpu_zap_shader, anda6xx_gpu.c:1507-1516falls back.
- No GPU errors after the load: no GMU timeout, hang or fault in the rest of the log.
gpu-open=opened. - devfreq:
3d00000.gpuat 300 MHz, max 1250 MHz. - Display unchanged from attempt -4:
card2-eDP-1connected at 2560x1600.VREG_EDP_3P3andVBL9enabled after 40 s; backlight 2048/4095.- The owner saw the panel stay lit with the banner.
- Panel identity (the
edid_idfix2d2f2b5):mfr=LGD product=0804 text="RNYV4?LP134WQ". That’s an LG DisplayLP134WQ…13.4“ panel. The 0xfe text descriptor is cut at 13 bytes, and?replaces a non-printing byte. - Shutdown: clean.
/initsent netlog at about 68 s and ranreboot -fat 128 s. The kernel disabled the panel and both SMMUs, then printedreboot: Restarting systemat 128.6 s (2:29 PM ET). With the image stick still in, the firmware stayed on its boot screen. Once the owner pulled the stick, stock booted. - Deferred at the end: only
arm-scmi(mailbox17430000).
Leftovers (not failures)
gcc-x1e80100andgpucc-x1e80100reportsync_state() pending due to 3d6a000.gmu. Inference: the GMU node has no platform driver of its own (the a6xx code drives it during the GPU bind), so these providers never get theirsync_state(). Not checked against the source yet.clk_ignore_unused pd_ignore_unusedare on the command line anyway.adreno 3d00000.gpu: supply vdd not foundandvddcx not found: the GPU node inhamoa.dtsinames neither supply, so the driver uses dummy regulators.- No rendering test: the initramfs has no Mesa or freedreno userspace. The GPU is up as far as the kernel is concerned.
Next for the GPU
- A real workload needs a userspace with Mesa freedreno (a rootfs, not this initramfs).
- The zap shader under EL1 (stock firmware
qcdxkmsuc8380.mbn) only matters for a non-EL2 boot.
Phase 3: the GMU gets a driver, the crypto node goes (2026-11-03)
Attempt 2026-11-03-26 (kernel #17, commits a6e3987 and 63bddc5, image 963fb3004304e941), cold start. Owner
pick from the Omarchy review (2026-11-03, “1 2 3”). Sources: the netlog dumps/firstboot/2026-11-03-netlog.txt and
the AP console log (gitignored), and Omarchy’s linux-omarchy-aarch64 package
(https://github.com/omacom/omarchy-pkgs/tree/master/pkgbuilds/linux-omarchy-aarch64, read 2026-11-03).
Why
Every boot left unbound=3d6a000.gmu and unbound=1dfa000.crypto (the sync_state() pending due to … lines that
/init reports since the retro), handoff lead 3 and the gap list’s crypto lead.
- GMU: nothing binds the GPU’s management unit, so gcc and gpucc never reach
sync_state(). Akhil P Oommen’s (Qualcomm) “drm/msm: Attach a driver to the GMU” series binds it as a component of the GPU master; Omarchy carries it as2170-2174with its own fix-up2178. - Crypto: the pin makes QCE depend on
BROKEN(drivers/crypto/Kconfig:531), so nothing binds the node, andaggre2_nocandmc_virtnever reachsync_state(); Omarchy (2210) disables the node.
Changes
kernel/patches/0002-0007(ADR 0007): the six patches unchanged, each with a provenance header. They apply in order to the pin; check 20 now applies the series cumulatively to the pin’s files (0005-0007 build on 0002-0004).dts/x1e80100-google-mica.dts:&crypto { status = "disabled"; }, marked not from the stock tree;qcom,qceleftkernel/dt-gaps.txt.
Result: pass
adreno 3d00000.gpu: bound 3d6a000.gmu (ops a6xx_gmu_bind_ops)at 0.555 s.unbound=went from 8 to 6:3d6a000.gmuand1dfa000.cryptoare gone. Left: the four CPU bandwidth-monitor PMUs (24091000,240b3400,240b5400,240b6400) and the video and camera clock controllers (aaf0000,ade0000).- The GPU is unchanged on the
msm.separate_gpu_kms=1path that patch0005reworks:gpu-node=renderD128 driver=adreno,gpu-open=opened,gpu-freq=300000000 max=1250000000, SQE and GMU firmware loaded, zap off. errors=0(from 1). SCMI full (0x20000, 3 of 3, 78 replies, max 3.9 ms, 0 timeouts).- Not measured yet: whether suspend now reaches CX collapse and DDR self-refresh (the reason Omarchy carries
2210); there is no suspend test on mica yet.
First boot of mainline Linux on mica (2026-11-02)
Redacted excerpt (ADR 0005). Raw logs: dumps/firstboot/2026-11-02-ap.log
(AP console over SuzyQ) and dumps/firstboot/2026-11-02-2-lastkmsg.txt (dropbox SYSTEM_LAST_KMSG on the next stock
boot). Procedure: first-boot runbook. Attempt 2026-11-02-2.
Result: Gate 2 passes
From the ramoops readback (dropbox entry stamped 2026-11-02 20:19:42, Googlebook clock):
[ 0.000000] Linux version 7.3.0-ga804c5742bff (the owner@the build host) (clang version 23.1.3, LLD 23.1.3) #1 SMP PREEMPT_DYNAMIC Mon Nov 2 17:04:02 EST 2026
[ 0.000000] Machine model: Dell XPS Googlebook
[ 0.039540] CPU: All CPU(s) started at EL2
[ 61.158726] reboot: Restarting system
- Image:
mica-usb.imgsha25615a107e5…, KERN-A on a USB 3 stick in the right port, booted with Boot from external drive. torvaldsa804c57,defconfig+kernel/mica-firstboot.config,x1e80100-google-mica-el2.dtb. - Coreboot table:
board-id00000003; nosku-idand nofw-confignode. /initran to the end: everyGBOOK:line printed, thenreboot -fafter 60 s. No panic, no oops.
Channels
| Channel | Seen | Note |
|---|---|---|
AP console (-if01, ttyMSM0) | yes, from 0.67 s | earlycon: qcom_geni0 MMIO:0x894000 and the bootconsole registered, but nothing reached the SuzyQ before ttyMSM0 probed. Inference: the firmware does not leave QUP2 SE5 set up for 115200 (ticket 04’s open point), so the banner is not on the AP console |
| ramoops → dropbox | yes, full log | ramoops: using 0x100000@0x86584000; read on the next stock boot |
| Panel | fb0 up | simpledrm bound simple-framebuffer.0 from the coreboot table (no DRM_COREBOOTDRM, as expected) |
| EC console | power states only | warm reset at the reboot |
For Phase 3 (from the same log)
arm-smmu 3da0000.iommuprobe fails with-110.- USB (
a200000,a400000,a600000,a800000):dwc3-qcomdeferred, DWC3 core not registered. - PCIe
1bf8000and1c08000: root port parse deferred (-517);vdda/vddpe-3v3fall back to dummy regulators. arm-scmi: channel setup fails for protocol0x10.reserved-memory:ta@d8600000fails to reserve; depthcharge’sramoopsand several firmware-addedregion@…nodes overlap the DT’s own reservations (warnings only).- Firmware log: depthcharge disables the NVMe controller and all USB controllers before the jump (“Exiting depthcharge with code 4”).
Phase 3: the ChromeOS EC, built-in keyboard, keyboard backlight and fans (2026-11-03)
Attempts 2026-11-03-13 (image 17bf703d696856ed, kernel #11, commit f1d817e), a repeat boot of the same image,
and 2026-11-03-14 (image e2f2bee81254d578, kernel #12, commit 702de45). Owner round 2026-11-03: target the EC
(1a), with its children and captures in one boot (2a), kernel patches back on (3a), other unbound nodes as leads only (4a).
Sources: the netlog dumps/firstboot/2026-11-03-netlog.txt, the AP and EC console logs
dumps/firstboot/2026-11-03-{ap,ec}.log (all gitignored), and the pinned tree a804c57.
Why
All 10 boots on 2026-11-03 up to attempt -12 logged sync_state() pending due to a94000.spi (77 lines in the netlog)
and not one cros_ec line. a94000 is spi13 (hamoa.dtsi:1905), the bus of cros_ec: ec@0 in
dts/x1e80100-google-mica.dts. Its driver (qcom,spi-geni, drivers/spi/Kconfig:1013, SPI_QCOM_GENI) is =m in
arm64 defconfig, and the initramfs loads no modules. So the EC, the built-in keyboard (cros-ec-keyb) and everything
the EC adds had never probed. The keyboards in earlier AP logs were USB ones.
Attempt -13: the SPI bus binds, the EC answers garbage
- Change:
kernel/mica-ec.configbuilds inSPI_QCOM_GENI,CROS_KBD_LED_BACKLIGHT,PWM_CROS_EC,LEDS_CROS_ECandCROS_EC_CHARDEV, requires the already built-in EC core, and forbidsCROS_EC_TYPECandCROS_EC_UCSIat=y(pmic_glinkowns the Type-C ports)./initgained theec-*captures, a 20 s key-press window and one keyboard-backlight write (max for 5 s, then 0). - Result:
ec-spi=a94000.spi driver=geni_spi, thencros-ec-spi spi0.0: packet too long (252 bytes, expected 4)andCannot identify the EC: error -28.-28(-ENOSPC) comes from the v2 command path (cros_ec_spi.c:611): the v2 hello reply’s length byte read 252 (0xfc). The v3 protocol query before it had failed too, logged only atdev_dbglevel (cros_ec_proto.c:389, fallback at :492). Both failing points at the transfer, not the EC (inference).- The repeat boot of the same image (the stick reached the Googlebook before the -14 write) failed the same way at 3.94 s.
- The stock node matches ours (overlay
entry01fragment 48: 3 MHz, interrupt TLMM 66 level-low, nogoogle,cros-ec-spi-pre-delayor-msg-delay). Stock drives CS ongpio55with functionqup1_se5, drive 6, bias-disable (entry00-base.dts:3017-3028), but through Qualcomm’s vendor GENI driver. - Mainline precedent: both mainline Qualcomm Chromebooks with the EC on GENI SPI drive CS as a GPIO:
trogdor (
sc7180-trogdor.dtsi:910-911,cs-gpios = <&tlmm 62 GPIO_ACTIVE_LOW>) and herobrine (sc7280-herobrine.dtsi:542-544, with anoutput-highinit state at :895-899).
Attempt -14: GPIO chip-select, pass
- Change (commit
702de45):&spi13takespinctrl-0 = <&qup_spi13_data_clk>, <&qup_spi13_cs_gpio>andcs-gpios = <&tlmm 55 GPIO_ACTIVE_LOW>; the newqup_spi13_cs_gpiostate isgpio55, functiongpio, the stock drive 6 and bias-disable,output-high.CONFIG_DYNAMIC_DEBUG=yand an/initre-probe with the cros_ec debug lines on, for a failed EC (not needed: the EC registered at boot).dt-validateoutput unchanged (322 lines, all the known rootcompatiblewarning). - EC:
cros-ec-spi spi0.0: Chrome EC device registeredat 0.747 s;cros-ec-rtcregistered asrtc0at 0.677 s.a94000.spiis gone from thesync_state() pendinglines. - Built-in keyboard:
input: cros_ec(input0,event0) andcros_ec_buttons(input1) at 0.93 s. In the 20 s window the owner typed on it:ec-keys=bytes=9336 events=389(24-byteinput_events). - Keyboard backlight:
chromeos::kbd_backlight(thecros-keyboard-ledscell, so the EC reportsEC_FEATURE_PWM_KEYB;drivers/mfd/cros_ec_dev.c:145-147),max=100. Set to 100, read back 100; set to 0, read back 0. The owner saw the keyboard light up for the 5 s (reported 2026-11-03, about 6:05 PM ET). - Fans:
hwmon54 name=cros_ec fan1_input=0 fan2_input=0: two fans through the EC, both stopped (cool, idle). The EC runs them on its own; nothing on the AP side drives them. - Everything else unchanged;
errors=1(from 17 on the repeat -13 boot).
SCMI on these cold starts (aside)
The EC logged G3->S5 before each of the three boots (EC uptime 15984, 17025 and 17236 s), so all three were cold
starts. SCMI still failed on two of them: -13 (-110, no firmware version) and the repeat (timed out in resp,
firmware version 0x0, cpufreq=none). It worked on -14 (0x20000, all three policies, one timed out in resp line
at 0.418 s). So a cold start does not always bring SCMI up; “cold boot only” in scmi-2026-11-03.md needs a dated
update. compare printed start: unknown for these boots (the EC reset spacing did not match boot_start.py’s
expectation), although the EC log shows G3->S5.
Open
- Keyboard layout, Fn keys and the top-row keymap: only key events were counted, not which keys.
- Fan behaviour under load and fan control from the AP (
cros_echwmon PWM, if the EC offers it). - The EC’s Type-C and UCSI cells stay modules;
pmic_glinkkeeps the ports. - Other nodes that never bind (owner round 4a, leads only):
crypto@1dfa000,videocc@aaf0000,camcc@ade0000, the CPU PMUs (24091000,240b3400,240b5400,240b6400) and3d6a000.gmu. - SCMI on cold starts: intermittent (above).
Phase 3: external DP over Type-C works (2026-11-03)
Attempt 2026-11-03-6. Image 713bf8d219796e5b, kernel #6 (2026-11-03 2:23 PM ET, unchanged from attempt -5),
commits 91c5aa6, 2cda0fd. Sources: the netlog dumps/firstboot/2026-11-03-netlog.txt and the AP console log
dumps/firstboot/2026-11-03-ap.log (both gitignored), plus the owner looking at the monitor.
Why
In attempt 2026-11-03-5 the DP chain had already bound: msm_dpu bound all three DP controllers (ae90000, ae98000,
aea0000), card2-DP-1 and card2-DP-2 existed, and the aux_bridge … failed to acquire drm_bridge error from
usb-2026-11-02.md was gone. Both DP connectors read disconnected because nothing was plugged in.
The kernel already builds in every link of the path (a804c57 .config):
QCOM_PMIC_GLINK=y, which buildspmic_glink_altmode.o(drivers/soc/qcom/Makefile:14)TYPEC_MUX_PS883X=y(retimers),PHY_QCOM_QMP_COMBO=y,DRM_AUX_HPD_BRIDGE=y
pmic_glink_altmode.c sets the mux and retimer (typec_mux_set, typec_retimer_set) and reports HPD through
drm_aux_hpd_bridge_notify (lines 196-377), so TYPEC_DP_ALTMODE=m plays no part. The DTS already matched the 9345:
link-frequencies on mdss_dp0_out/dp1_out as in x1e80100-dell-xps13-9345.dts:891, :899, and
data-lanes = <0 1 2 3> from hamoa.dtsi:5714, :5803. The test needed only a sink and a capture.
Changes
/init(91c5aa6,2cda0fd): after the eDP block, it pollscard*-DP-*/statusfor up to 60 s. At 10 s with nothing connected, it printsplug in (or replug) the monitor nowon the panel. Then it reports each DP connector with its modes and EDID identity (the existingedid_id), the Type-C altmodes and adp-dmesgcapture.attempt.sh watch(d63d869) shows those lines.- No kernel, DTS or firmware change.
Setup
The SuzyQ stayed in the left port. The boot stick went in the right port as usual. The initramfs runs from RAM, so at
the prompt the owner pulled the stick (usb 4-1: USB disconnect at 66.8 s) and plugged a USB-C monitor into the right
port.
Result: pass
- DP connected:
dp-wait=25s, thendp-conn=card2-DP-2 status=connected enabled=enabled modes=1920x720 1920x540 1280x720 1280x720 1280x720. - Monitor identity:
dp-id=card2-DP-2 mfr=BSL product=9826 name="K3". Its USB side names itselfROYUAN K3andBJYP-K3, with keyboard and mouse HID interfaces behind a USB hub. - The owner saw the console text on the monitor.
- USB through the same port: the monitor’s hub enumerated at 70.3 s on
a800000.usb(usb_1_ss1), at high speed on bus 3 and SuperSpeed on bus 4, so DP and USB 3 run together on the port (inference: 2-lane DP). - Altmodes:
typec-altmode=port0.0 svid=ff01 mode=1 active=yesandport1.0 svid=ff01 mode=1 active=yes(the ports’ own DP altmodes; the partner directories stay empty, asucsi_glinkleaves partner fields blank). - Everything else unchanged from attempt -5: eDP-1 at 2560x1600, backlight 2048/4095, GPU firmware and GMU loaded
(
Loaded GMU firmware v4.3.17), Wi-Fi on 6 GHz with a DHCP lease and netlog sent. - Shutdown:
reboot: Restarting systemat 146.5 s (about 3:21 PM ET). The stick was already out, so stock booted. - Deferred at the end: only
arm-scmi(mailbox17430000).
Which port is which
Three readings put the 9345’s side labels the other way round on mica:
- SuzyQ, left port: typec
port0sinks power (source [sink]), as in every earlier boot. - Stick, right port: typec
port1sources power, and in usb-2026-11-02.md the stick enumerated onusb_1_ss1. - Monitor, right port: it came up as
DP-2, its USB ona800000.usb(usb_1_ss1).
Inference: typec portN is pmic-glink connector@N, and DP-2 is mdss_dp1 (the second DP connector registered).
So connector@0 (retimer on i2c3, usb_1_ss0, mdss_dp0) is the left port and connector@1 (i2c7,
usb_1_ss1, mdss_dp1) the right one. DP working on the right port supports this directly: pmic_glink_altmode
indexes its ports by each connector’s reg (pmic_glink_altmode.c:597, :615), so it programmed the connector@1
chain (i2c7 retimer, usb_1_ss1 PHY) for the right port. Had the index pointed at connector@0, the i2c3 retimer
would have been set and mdss_dp1 would not have linked (inference from the code path, not traced per message). The DTS comments now say so (dts/x1e80100-google-mica.dts); the DTB
itself is unchanged.
The device-link warning
qcom_pmic_glink pmic-glink: Failed to create device link (0x180) with supplier 5-0008 for /pmic-glink/connector@1
appears on every boot. At a804c57:
0x180isDL_FLAG_SYNC_STATE_ONLY | DL_FLAG_INFERRED(include/linux/device.h:444-445). fw_devlink makes the link fromfw_devlink_create_devlink()(drivers/base/core.c:2219-2245).- A sync-state-only link only orders
sync_state()callbacks.device_link_add()returnsNULLfor one if the supplier is not yet registered with PM, or if the consumer has already probed.
Inference, not traced further: the i2c7 retimer registered after pmic-glink had probed. Either way, DP and
SuperSpeed both work on connector@1, so the warning blocks nothing.
Next
- Test the left port (
connector@0,mdss_dp0), which needs the SuzyQ out, so netlog only. - A
panel-edpentry forLGD 0x0804. CDSP. The SCMI mailbox.
Phase 3: display, msm DRM drives the eDP panel (2026-11-03)
Attempt 2026-11-03-4. Image ab87e567a0eb05bd, kernel #5 (2026-11-03 2:09 PM ET), commit 072f347. Sources: the
netlog dumps/firstboot/2026-11-03-netlog.txt and the AP console log dumps/firstboot/2026-11-03-ap.log (both
gitignored), plus the owner looking at the panel.
Why
Every earlier boot went dark at about 33 s: VREG_EDP_3P3: disabling (attempt 2026-11-03-3,
wifi-2026-11-03.md). The mica DTS already described the mainline display path (&mdss,
&mdss_dp3 with an edp-panel on its aux-bus, &mdss_dp3_phy, vreg_edp_3p3, pwm-backlight), matching the stock
sde_edp (entry01-overlay.dts fragment 11). But every driver on it was =m in defconfig at a804c57, and the
initramfs loads no modules. Only simpledrm drove the screen, nothing claimed the panel supply, and the regulator core
turned it off as unused.
Changes (commit 072f347)
kernel/mica-display.config:DRM_MSM,PHY_QCOM_EDP,CLK_X1E80100_DISPCC,DRM_PANEL_EDP,BACKLIGHT_CLASS_DEVICE,BACKLIGHT_PWMandLEDS_QCOM_LPGset=y, plus the dependency chain:QCOM_LLCCandQCOM_OCMEM(DRM_MSM=yneeds them=yor=n), andLEDS_CLASS_MULTICOLORandQCOM_PBSfor the LPG.- Command line:
msm.skip_gpu=1. Otherwise the GPU would join the MDSS component match (msm_drv.c:1076-1086) and wait on gpucc, whose SMMU3da0000.iommuis still deferred. - DTS: the backlight’s
edp_bl_enpin state moves from TLMM 74 (left over from the 9345; stock muxes it to QUP SE18) to PMC8380-3 GPIO 4, the stockqcom,edp-backlight-en-gpio. The PWM pin takes the stockedp_pwm_activeconfig. /init: DRM connectors, the backlight, the panel supplies after uptime 40 s, the panel identity from the EDID (no serials;check.shtest 16), display dmesg, and a banner ontty1.
Result: pass
The panel stayed lit past 40 s and showed the GBOOK: display check banner (the owner saw it).
- msm binds early:
[drm] Initialized msm 1.13.0 for ae01000.display-controller on minor 1at 1.154 s.dpu hardware revision:0x90020000.- All three DP controllers bound (
ae90000,ae98000,aea0000). fb0: msmdrmfb frame buffer deviceat 1.595 s, taking over from simpledrm’sfb0(0.453 s).no GPU device was found: expected withmsm.skip_gpu=1.
- Connectors:
card1-eDP-1 status=connected enabled=enabled modes=2560x1600.card1-DP-1andcard1-DP-2are disconnected (nothing on Type-C), andcard1-Writeback-1is there too. - Panel supply:
VREG_EDP_3P3 state=enabled users=1andVBL9 state=enabled users=1after 40 s. NoVREG_EDP_3P3: disablingline this boot. - Backlight:
backlight max=4095 actual=2048 power=0, a 12-bit PWM at 50 %. Stock caps the level at0x190(fragment 11) and0x200(fragment 77); mainline uses the LPG’s full range. - Panel:
Unknown panel LGD 0x0804, using conservative timings. That’s LG Display, product0x0804, not inedp_panels[]ata804c57. TheWARN_ONsplat atpanel-edp.c:829is the driver’s deliberate “add a table entry” signal (comment at:824-828), not a fault. Conservative timings only lengthen the power sequencing delays. - Deferred at the end: only
arm-scmi(mailbox17430000).3da0000.iommuwas listed at the first check and not at the last.
Not yet captured
GBOOK: panel-id=card1-eDP-1 none: sysfs reports theedidattribute’s size as 0, so[ -s ]skipped a readable file. Fixed in commit2d2f2b5(test-r;noneon a short read). The next image will print the EDID name and text descriptors.- The dropbox readback (
attempt.sh readback) didn’t run because adb was down after the stock boot (Wireless debugging). Netlog has the whole log, so nothing is missing.
Next for display
- Add an
edp_panels[]entry forLGD 0x0804. It needs the panel’s datasheet delays; the EDID name from the next image identifies the part. This is for upstream; the panel works without it. - External DP over Type-C (
mdss_dp0/dp1are bound;aux_bridgestill reports “failed to acquire drm_bridge” in usb-2026-11-02.md). - The GPU:
CLK_X1E80100_GPUCC=y(unblocks3da0000.iommu), dropmsm.skip_gpu=1, and stage the GPU firmware. Zap is off under EL2.
Phase 3: the CDSP boots from stock firmware (2026-11-03)
Attempt 2026-11-03-7. Image 72eebf1b6e678b3f, kernel #6 (2026-11-03 2:23 PM ET, unchanged), commit 19128c0.
Sources: the netlog dumps/firstboot/2026-11-03-netlog.txt and the AP console log dumps/firstboot/2026-11-03-ap.log
(both gitignored).
Why
Every boot up to attempt -6 logged remoteproc remoteproc1: Direct firmware load for qcom/x1e80100/google/mica/cdsp.mdt failed with error -2 at about 0.57 s, and /init reported cdsp offline. The
DTS already named the stock files (remoteproc_cdsp firmware-name, dts/x1e80100-google-mica.dts), mica-el2.fixups
already dropped its iommus (as for the ADSP), and QCOM_Q6V5_PAS=y. Only the firmware was missing from the initramfs.
Changes (commit 19128c0)
scripts/stage-firmware.sh:stock_mdtaddscdsp cdsp_dtb, read from the same stockmodem_a/image/(recovery-stick copy) as the ADSP. 16cdspfiles and 4cdsp_dtbfiles.- No
cdsp.b14exists in/image/. The boot shows the loader does not need it. kernel/firmware.sha256: 20 new hashes, no changed ones (90 files in total).
- No
/init: reports every remoteproc again at the end (rproc-late: state, recovery), so a DSP that crashes after the early capture shows up.- No kernel, DTS or command-line change.
Result: pass
- CDSP up:
Booting fw image qcom/x1e80100/google/mica/cdsp.mdtat 1.876 s,remote processor cdsp is now upat 1.995 s. - Still running at the end:
rproc-late=remoteproc1 cdsp running recovery=enabled(andremoteproc0 adsp running), after about 130 s, with no crash or recovery lines in the log. - Everything else unchanged: Type-C, PCIe, Wi-Fi (6 GHz, DHCP), eDP at 2560x1600, GPU firmware and GMU loaded,
netlog sent (1350 lines).
reboot: Restarting systemat 189.5 s (about 4:00 PM ET). - Deferred at the end: only
arm-scmi(mailbox17430000).
Side effect: the ADSP starts 5 s later
- In attempt -6 the ADSP was up at 0.931 s. Here:
powering up adspat 1.873 s, thenqcom_q6v5_pas 32300000.remoteproc: timeout waiting for subsystem event responseat 7.408 s, andremote processor adsp is now upat 7.410 s. - Cause, from the source at
a804c57: when a remoteproc starts,sysmon_start()sends each already-running peer a “powered up” event (drivers/remoteproc/qcom_sysmon.c:496-530). For SSCTL v2 peers,ssctl_send_event()waits up to 5 s for the reply (qmi_txn_wait(&txn, 5 * HZ),:405) and logs this exact message on timeout (:407). The message names32300000.remoteproc, the CDSP, so the ADSP’s start waited on the CDSP, which never answered. - Not known: why the stock CDSP firmware does not answer the SSCTL event. It costs one 5 s delay per boot (Type-C, battery and USB SuperSpeed come up later), with no lasting effect seen.
- New in this boot, inference: the later ADSP start shifts probe order, so
pmic-glinknow also logsFailed to create device link (0x180)fora600000.usb(connector@0) anda800000.usb(connector@1). These are the same sync-state-only links as in dp-2026-11-03.md, “The device-link warning”.
Update 2026-11-03: the SSCTL timeout is not mica-specific
Read the same afternoon, no boot:
- Which side waits: the message carries
sysmon->dev, the remoteproc’s parent device (qcom_sysmon.c:635), so32300000.remoteprocis the CDSP’s sysmon. The ADSP’ssysmon_start()runs thesysmon_notifierschain (:509); the CDSP’ssysmon_notify()(:585-605) seesssctl_version == 2and sends the event to the CDSP.ssctl_versionis set only when a matching SSCTL server appears (:425-440, instance0x17fromx1e80100_cdsp_resource,qcom_q6v5_pas.c:1282). So the CDSP’s SSCTL service is registered and does not answer. - Another X1 laptop shows the same line. A Dell Latitude 5455 (
x1p64100) on the Ubuntu Concept kernel logsqcom_q6v5_pas 32300000.remoteproc: timeout waiting for subsystem event responseat boot, next to the samepmic-glink … Failed to create device link (0x180)lines (Ubuntu Discourse, “Ubuntu Concept ♥️ Snapdragon X Elite”, page 99, post #2008, 2026-07-09). Its CDSP firmware is Dell’s Windows set, not mica’s stock files. No reply there explains or fixes it. - No upstream fix found: a web search for the exact message (2026-11-03) found the driver source, an SDM845 modem report and the Dell post, but no patch for x1e80100.
- Inference: the X1 CDSP firmware does not answer SSCTL subsystem events, whoever built it. The cost is the one 5 s wait in whichever remoteproc starts after the CDSP; nothing else depends on it.
Decision: no change on mica. A kernel fix (skip the event for a peer that never answers, or a shorter timeout)
belongs upstream in qcom_sysmon.c and needs evidence from more than two machines.
Next
- FastRPC (
QCOM_FASTRPC=m, so not loaded): needed before anything can use the CDSP.
M3: the sound card binds (2026-11-04, 11:32 PM EST)
Attempts 2026-11-04-26, -27 and -28, M3 ticket 10 (.scratch/m3-rootfs/issues/10-audio-attempt.md), task #19. TEST
IMAGES with GBOOK_DTS_EXTRA=dts/diag/sound.dtsi: kernels #30 (g7072998+, image 5e59c669…), #31 (ge8c45c0,
c701044f…) and #32 (gc613c1d, 2947943c…); rootfs c1646b8a… (snapshot 20261104T000000Z, 321 packages) on ROOT
for all three. Sources: dumps/firstboot/2026-11-04-netlog.txt boots #24 to #26, 2026-11-04-ap.log, the m3-*
captures (both gitignored); attempt.sh tally '^2026-11-04-2[678]$': boots=3 clean=3 stalled=0, all cold starts.
Result
aplay -l on boot -28: card 0: X1E80100DellXPS [X1E80100-Dell-XPS-13-9345], device 1: MultiMedia2 Playback;
arecord -l: device 3: MultiMedia4 Capture; alsaucm listcards: 0: hw:0 X1E80100-Dell-XPS-13-9345 (Debian’s Dell
XPS 13 9345 profile resolves through the link rootfs/customize.sh adds). No audio device in any deferred read, no
tplg firmware loading … failed line, so the staged topology qcom/x1e80100/X1E80100-Dell-XPS-13-9345-tplg.bin loaded.
Sound itself is untested: no speaker or microphone test ran (the owner’s call, section “Next”).
| Boot | Kernel | Audio state | Evidence |
|---|---|---|---|
| -26 | #30 | 4 codecs enumerated, all deferred “Failed to get reset”; card deferred “WSA Playback: codec dai not found” | AP: GBOOK: deferred=sdw:4:0:0217:0204:00:0 wsa884x-codec: Failed to get reset (and :1, sdw:1 …); m3-aplay=… no soundcards found |
| -27 | #31 (RESET_GPIO=y) | reset.gpio.0 and .1 created, deferred “Could not get reset gpios”; codecs and card as on -26 | AP: GBOOK: deferred=reset.gpio.0 reset_gpio: Could not get reset gpios; m3-aplay=… no soundcards found |
| -28 | #32 (GPIO_SHARED_PROXY=y) | card 0 bound, playback device 1, capture device 3, UCM resolves | AP: m3-aplay=card 0: X1E80100DellXPS …, m3-arecord=… device 3, m3-alsaucm= 0: hw:0; no deferred= line |
Common to the three boots (netlog, kernel timestamps): PDR: Indication received from msm/adsp/audio_pd, state: 0x1fffffff at 1.05 to 1.09 s (the stock ADSP offers the audio protection domain); qcom-apm gprsvc:service:2:1: CMD timeout for [1001021] opcode at 6.12 s (APM_CMD_GET_SPF_STATE, audioreach.h:42: the first readiness poll from a DAI
probe times out, later ones answer, the card binds; a lead, not a blocker); wsa_macro …: Driver needs fixing: Unsorted reg_defaults and using zero-initialized flat cache (driver warnings, both macros); snd-x1e80100 sound: ASoC: Parent card not yet available, widget card binding deferred at 6.16 s on -28 (the card’s own deferral while the codecs probe).
What the boots proved about the mapping
- The SoundWire layout: the four WSA8845 enumerate as
sdw:1:0:0217:0204:00:0and:1(link 1 =swr0, unique ids 0 and 1) andsdw:4:0:0217:0204:00:0and:1(link 4 =swr3), as the stock codecswsa884x@02170220/1underwsa_swr_masterandwsa2_swr_mastersaid. The LPI pinswsa_swr_active(gpio10/11) andwsa2_swr_active(gpio15/16) carry the buses. - The shutdown pins TLMM 204 (bus 0’s pair) and 205 (bus 3’s pair) are the codecs’ resets: the codecs bound once the reset chain had its drivers, and the stock overlay’s pin groups are the same pins.
- Supplies:
vdd-1p8= L15B as in stock;vdd-io= L12B (every other x1e80100 board, not in stock): accepted by the probes, still unverified as the right rail. - DMICs and the mic rail: the VA macro bound with
dmic01_default/dmic23_defaultandvdd-micb-supply = <&vreg_dmic_3p3>(the stock overlay’sEN_PP3300_MICon TLMM 69); capture device 3 exists; no capture test ran.
The two blockers, both kernel options
At a804c57 the WSA884x driver asks the reset framework first (sound/soc/codecs/wsa884x.c:2022,
devm_reset_control_get_optional_shared); the reset core turns a reset-gpios property into an auxiliary reset-gpio
device (drivers/reset/core.c:1012-1105), one per distinct pin, and defers the consumer until that device’s driver binds.
That driver is CONFIG_RESET_GPIO, =m in the arm64 defconfig. With it built in (kernel #31) the reset-gpio devices
deferred themselves: TLMM 204 and 205 are each referenced by two codecs, and this kernel’s shared-GPIO support
(CONFIG_GPIO_SHARED=y, drivers/gpio/gpiolib-shared.c) flags such a pin GPIOD_FLAG_SHARED and routes every request
for it through a gpio-shared-proxy platform lookup (drivers/gpio/gpiolib.c:4805-4834), whose chip only the
CONFIG_GPIO_SHARED_PROXY driver registers (=m by default). Both are =y in kernel/mica-audio.config since kernel #32.
A distro kernel loads both as modules, which is why the Yoga Slim 7x never showed this.
Timing (boot -28, from the AP console and the journal)
| Point | Uptime | Evidence |
|---|---|---|
audio_pd up | 1.09 s | AP: PDR: Indication received from msm/adsp/audio_pd |
| first APM poll timeout | 6.12 s | AP: qcom-apm gprsvc:service:2:1: CMD timeout for [1001021] opcode |
| card’s widget binding deferred | 6.16 s | AP: snd-x1e80100 sound: ASoC: Parent card not yet available |
| card bound | before the first deferred read (empty) | AP: no GBOOK: deferred= line; the kernel logs no registration line |
m3-aplay | 52.2 s | AP: gbook-m3[2233]: GBOOK: m3-aplay=card 0: … |
m3-end | 55.7 s | AP: GBOOK: m3-end=uptime=55.69s |
The speaker test (2026-11-04, 11:51 PM EST): two of four units play, and the bus degrades
Owner’s OK given. Over ssh mica as root (the gbook user is not in audio: alsaucm and speaker-test see no card
without sudo -n; retro item): alsaucm -c X1E80100-Dell-XPS-13-9345 set _verb HiFi set _enadev Speaker (exit 0), then
speaker-test -D hw:0,1 -c 4 -t sine -l 1 (exit 0, 4 channels, 48 kHz; all four PA volumes at the cap of 6, digital volumes 81).
| Time (uptime of boot -28) | Played | Heard (owner) | Evidence |
|---|---|---|---|
| 506 s | 4-channel sine, one loop | two sounds, one louder, one lower; no left/right difference | boot28-speaker-test2.txt (scratch); AP: 6ab0000.soundwire: no slave alert found.spurious interrupt |
| 619 s | -t wav (spoken channel names), then two sine loops | “Front Right” and “Rear Right” (channels 1 and 3); four beeps | AP: 6ab0000.soundwire: qcom_swrm_irq_handler: SWR bus clsh detected |
| 685 to 708 s | each channel alone (-s 1..4), 8 s apart | one long loud beep, one long softer beep | boot28-speaker-test4.txt |
| 736 to 763 s | each channel alone at 250/500/1000/2000 Hz | no answer recorded | boot28-speaker-test5.txt |
| 957 and 974 s | 4-channel sine with bus 0 muted, then bus 3 muted (digital volumes by numid) | nothing, nothing | mute-test2.sh |
| 1018 s | 4-channel sine, every control restored | nothing | plain-test.sh |
What the device said meanwhile (root reads, files in the session scratchpad boot28-*.txt):
/sys/kernel/debug/gpio: TLMMgpio204andgpio205“out high” (both resets released; “out low” on boot -27 when the reset chain had no driver),gpio69“out high” (the mic rail).- DAPM during a tone (
/sys/kernel/debug/asoc/X1E80100-Dell-XPS-13-9345/*/dapm/*): both macros’WSA RX0/RX1paths On, and on all four codecs<name> IN: Onand<name> SPKR: On. The drivers believe all four play. /sys/bus/soundwire/devices/sdw:*/status:UNATTACHEDfor all four, before, during and after a tone (runtime_statusactiveduring it); the slaves’ debugfsregistersread a constant0x21(bus 0) or0for every register.- With
dyndbgondrivers/soundwire/qcom.c(1130 s):SWR new slave attached,Slave status not changed 1; then (1148 s, 1260 s)Slave status not changed 101,SWR unknown interrupt value: 2048(bit 11,SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED,qcom.c:62, unhandled by the driver),SWR bus clsh detected,SWR read FIFO underflow fifo status 0x4000000f, on both masters. - The masters’ enumerator registers (
/sys/kernel/debug/soundwire/master-*/qualcomm-sdw/qualcomm-registers, decoded perqcom_swrm_enumerate,qcom.c:636-686): on each busSWRM_MCP_SLV_STATUS = 0x1(a slave attached at device number 0, never enumerated) and one enumerated slot, slot 1, id0x02170221: version 2, unique id 1, mfg 0x0217, part 0x0204. No slot carries unique id 0 on either bus.
Reading: on each bus the hardware auto-enumerator numbers only the unique-id-1 amplifier; the other amplifier stays at
device number 0 (AUTO_ENUM_FAILED, bus clashes), so the DT’s speaker@0,0 slaves never attach and get no port setup.
The audible pair are the two id-1 units: stock names bus 0’s wsa884x@02170221 “SpkrRight” and bus 3’s “Spkr2Right”
(the downstream node names are these very register values), so the owner heard the right-side woofer and tweeter: one
loud, one soft, no left/right difference. The left pair (stock @02170220, unique id 0) never enumerates. Why is open:
both amplifiers on a bus share one reset pin (as in stock), the same rails, the same bus; candidates are an id-0 amplifier
that answers the enumeration read late or not at all, two amplifiers strapped to the same unique id (a clash on the id
read), or a reset/timing order the stock driver handles and mainline’s does not. Sound then stops altogether after some
minutes: the re-enumeration after each bus clock-stop fails the same way, and reads of the id-1 slaves fail too
(FIFO underflow), inference from the debug lines at 1148 and 1260 s. sdw status UNATTACHED for the id-1 slaves while
they played is unexplained (the sysfs attribute may lag the hardware enumeration; unverified).
The debug boot (2026-11-05, 12:25 AM EST): all four enumerate at boot; the clock-stop exit loses them
Attempt 2026-11-05-1: the same TEST IMAGE with dyndbg="file drivers/soundwire/bus.c +p; file drivers/soundwire/qcom.c +p; file sound/soc/codecs/wsa884x.c +p" (image 22c46be8…, kernel #32), rootfs 6fea12f2… (the audio group, the m3-swrm
capture). Cold start, tally clean=1. Evidence: dumps/firstboot/2026-11-05-netlog.txt boot #1, 2026-11-05-ap.log,
dumps/firstboot/2026-11-05-ssh-*.txt (the mica-ssh.sh reads).
| Uptime | What | Evidence |
|---|---|---|
| 6.15 to 6.16 s | both buses enumerate both amplifiers: unique_id 0x1 as Slave 1, unique_id 0x0 as Slave 2, “signaling initialization completion” for all four | AP: soundwire sdw-master-4-0: SDW Slave Addr: 210217020400 … 200217020400; wsa884x-codec sdw:4:0:0217:0204:00:0: signaling initialization completion for Slave 2 (and bus 1) |
| 45.96 s | the first aplay -l wakes bus 0 from clock stop: Slave 1/2 state check1: UNATTACHED, two attach interrupts with Slave status not changed 0, then both re-enumerate; a burst of SWR read FIFO underflow follows | AP lines at 45.956 to 46.069 s |
| 47.15 s | bus 3 wakes the same way, with one SWR bus clsh detected, both re-enumerate | AP lines at 47.145 to 47.157 s |
| 48.25 s | m3-swrm=1-0: status=0x14 dev1=1 dev2=1 slot1=v2:uid1:0217:0204:00 slot2=v2:uid0:0217:0204:00; 4-0: (the same) | AP: GBOOK: m3-swrm= |
| 69.9 s | the first tone wakes both buses: UNATTACHED, SWR new slave attached three times, Slave status not changed 0, and no SDW Slave Addr line: neither bus re-enumerates; the enumerator reads status=0x1 dev0=attached no entry afterwards; owner heard nothing | 2026-11-05-ssh-audio-state.txt (first run); AP lines at 69.910 to 74.012 s |
| 145.9 s | the second tone: bus 0 re-enumerates both, bus 3 gets SWR unknown interrupt value: 2048 (AUTO_ENUM_FAILED), swrm_wait_for_wr_fifo_avail err write overflow, and its id-0 codec’s resume fails (ASoC error (-61) … pm_runtime_get); owner heard nothing | second audio-state run; AP lines at 145.86 to 153.46 s |
| 202.7 s | power/control=on on both masters (runtime PM forced on): both buses re-enumerate both amplifiers (status=0x14 on both, held through a second tone 27 s later); speaker-test -t wav: the owner heard “front left, front right, rear right, rear left”; the sine: a loud, a mid and a soft long beep | 2026-11-05-ssh-noclkstop.txt (both enumerator dumps decoded); AP lines at 202.767 to 202.790 s; Parity error detected twice on bus 3’s codecs |
Reading: the amplifiers and the mapping are right (all four enumerate at boot, with the unique ids the DT says), and
the two silent units of boot -28 were the losers of a race the Qualcomm master runs on every exit from clock stop: the
NEW_SLAVE_ATTACHED interrupts arrive before the hardware enumerator has filled its table, qcom_swrm_enumerate
finds nothing and completes, and the driver never retries (qcom.c:636-686; AUTO_ENUM_FAILED is unhandled, qcom.c:62).
Which bus wins is chance: both at 45.9 s, neither at 69.9 s, one at 145.9 s. With the masters kept clocked
(/sys/bus/platform/devices/6b10000.soundwire/power/control and 6ab0000.soundwire set to on) the slaves never
detach and all four play. The read FIFO underflow bursts on every wake (the codecs’ regcache_sync reads) and the two
Parity error detected lines remain open; they did not stop playback. The stock kernel’s log shows its driver meeting
the same class of errors with retries (swr_mstr_interrupt: SWR CMD error … flushing fifo, swrm_check_link_status;
dumps/firstboot/2026-11-05-stock-lastkmsg-swr.txt), and assigning devnum 1 to the id-1 units in the saved window; its VPHX EN Status line for 0220 is a register read that
needs the device number, so both amplifiers per bus enumerate on stock too, and on AUTO_ENUM_FAILED its driver re-arms the
hardware enumerator, which mainline’s does not (asoc-debug-tools.md, “The downstream driver”).
The udev-rule boot (2026-11-05, 1:01 AM EST): the rule’s own write is the first wake, and bus 1 lost it
Second boot of the same TEST IMAGE (22c46be8…, kernel #32; attempt 2026-11-05-1, netlog boot #2, clean), rootfs
59781d60… with /etc/udev/rules.d/90-gbook-soundwire.rules. Cold start, m3-end at 51.9 s. Evidence:
dumps/firstboot/2026-11-05-ap.log (the second boot), 2026-11-05-netlog.txt boot #2, 2026-11-05-ssh-audio-state.txt (the run at
12:59 AM EST).
| Uptime | What | Evidence |
|---|---|---|
| 6.15 to 6.16 s | all four amplifiers enumerate, as on the first boot | AP: SDW Slave Addr: 210217020400 … 200217020400 on sdw-master-1-0 and -4-0 |
| 35.93 s | the udev trigger’s power/control=on resumes both masters: a clock-stop exit on each. Bus 4 (6ab0000) re-enumerates both (Slave status not changed 14, then the two SDW Slave Addr lines by 36.00 s). Bus 1 (6b10000) gets SWR unknown interrupt value: 2048 (AUTO_ENUM_FAILED), Slave status not changed 101, three SWR new slave attached and no SDW Slave Addr line | AP lines at 35.926 to 36.049 s |
| about 7 min | audio-state.sh --tone: masters control=on status=active on both; decode master-1-0: status=0x1 dev0=attached no entry, master-4-0: status=0x14 dev1 dev2 | 2026-11-05-ssh-audio-state.txt |
Reading: the rule cannot protect the first wake, because its write is that wake (the masters autosuspend after probe, and
control=on resumes them through the same clock-stop exit). Bus 1 lost that one race and stays lost: the enumerator stalls as
on the first boot, with no later wake to re-roll it. The workaround is a coin toss per bus per boot, so it is not the
workaround; patch 0010 (which re-arms the stalled enumerator) is the fix under test next, on the same rootfs.
The patch boot, revision 1 (2026-11-05, 1:14 AM EST): the resume wait reads a stale status; a loss outside the resume path too
Attempt 2026-11-05-2: kernel #33 (7.3.0-gbook-2026.11.04-29-g50ac603-…, patch 0010 revision 1), image adf0c522…, the same
extras, rootfs 59781d60… (the udev rule). Cold start, m3-end at 50.4 s. Evidence: dumps/firstboot/2026-11-05-ap.log (third
boot), 2026-11-05-ssh-audio-state.txt (runs at 1:06, 1:07 and 1:11 AM EST), 2026-11-05-ssh-pm-auto.txt, 2026-11-05-ssh-swr-journal.txt.
| Uptime | What | Evidence |
|---|---|---|
| 6.16 s | all four enumerate | AP: the four SDW Slave Addr lines |
| 35.99 s | the rule’s control=on wake: both buses re-enumerate both (no stall this time) | AP lines at 35.986 to 36.000 s |
| about 1:06 AM | tone 1, masters on: both buses 0x14; the owner heard the three loudness levels | first audio-state run |
| 1:07:42 AM | owner OK: power/control=auto on both masters; both suspended within 4 s | 2026-11-05-ssh-pm-auto.txt |
| 146.11 s | tone 2’s wake. Bus 1: status 0, then 0x1 on the third attach interrupt, no SDW Slave Addr, no AUTO_ENUM_FAILED, no patch warn. Bus 4: both re-enumerated and initialised by 146.15 s, then at 146.37 s both UNATTACHED, status was 1 again; the dump ends status=0x1, slot1=v0:uid1:0000:0000:00 (a zeroed table entry) | journal lines 146.112 to 146.369 s; second audio-state run |
| 1:11 AM | tone 3: both buses still 0x1 dev0=attached no entry | third audio-state run |
Reading: revision 1’s wait in swrm_runtime_resume never fired. Right after the link comes back the status register still
reads the value from before the reset (both peripherals present), so the wait’s condition held at once; the real detach and
re-attach come later, through the interrupts. And a loss happened 240 ms after a good re-enumeration, outside the resume path
(bus 4 at 146.37 s), which no resume-side wait can see. Revision 2 (the next boot) checks 100 ms after every status change and
every resume whether a peripheral still sits at device 0, and re-arms the enumerator then; while the bus is short of its declared
peripherals it re-checks up to ten times. Note for the record: with the masters set to auto the buses did not recover by chance
across two further wakes.
The patch boot, revision 2 (2026-11-05, 1:25 AM EST): the check sees the stall, the downstream re-arm does not clear it
Attempt 2026-11-05-3: kernel #34 (patch 0010 revision 2), image f8d11625…, the same extras and rootfs. Cold start, m3-end
at 51.2 s. Evidence: dumps/firstboot/2026-11-05-ap.log (fourth boot), 2026-11-05-ssh-swr-journal-2.txt, 2026-11-05-ssh-pm-auto-2.txt,
2026-11-05-ssh-audio-state.txt (runs at 1:20 and 1:21 AM EST).
| Uptime | What | Evidence |
|---|---|---|
| 6.16 s | all four enumerate | AP: the four SDW Slave Addr lines |
| 36.95 to 37.10 s | the rule’s wake: both buses re-enumerate both; the new check reports enumeration check 0: slave status 0x14, enumerator status 0x0, 2 of 2 attached on each | AP lines at 36.953 to 37.100 s |
| about 1:20 AM | tone 1, masters on: both buses 0x14; the owner heard all the beeps | first audio-state run |
| 1:20:59 AM | owner OK: auto on both masters; both suspended within 4 s | 2026-11-05-ssh-pm-auto-2.txt |
| 116.52 s | tone 2’s wake. Bus 1: UNATTACHED, SWR bus clsh detected, status 0 then 0x1. Bus 4: Slave status not changed 1, then AUTO_ENUM_FAILED (the new handler: “re-arming the enumerator”). 100 ms later the check on each bus: enumeration stalled (slave status 0x1, enumerator status 0x3, 0 of 2 attached): re-arming the enumerator, enumerator status 0x3 not idle before re-arm; ten checks per bus, 100 ms apart, every one the same; the dump afterwards: both status=0x1 dev0=attached | journal lines 116.516 to 117.604 s; second audio-state run |
Reading: the detection is right and the downstream recipe is not enough here. SWRM_ENUMERATOR_STATUS sits at 0x3 (yesterday
0x6), and writing SWRM_ENUMERATOR_CFG 0 then 1 leaves it there. What has cleared this state every time so far is the boot
sequence: SWRM_COMP_SW_RESET, qcom_swrm_init (the CGCR reset, the frame, ping and FIFO configuration, the enumerator enabled,
the clock started), then the slaves attach and enumerate (8 of 8 at boot across four boots, and both buses at 35.9 to 37.1 s on
every rule wake). The clock-stop exit does only the CGCR reset and the clock restart (qcom.c:1751-1778 at a804c57), and that is
the path every wake has taken, because WSA884x sets simple_clk_stop_capable and not clk_stop_mode1 (wsa884x.c:2094); WSA881x
sets clk_stop_mode1 (wsa881x.c:1134) and its controllers take the full-init path on every resume. Revision 3 of 0010 escalates a
re-arm that leaves the enumerator busy to the reset-and-init sequence; patch 0011 sets clk_stop_mode1 for WSA884x so every resume
takes the full-init path in the first place. Both for the next boot.
The patch boot, revision 3 and patch 0011 (2026-11-05, 1:34 AM EST): three wakes, all four amplifiers back each time
Attempt 2026-11-05-4: kernel #35 (7.3.0-gbook-2026.11.04-35-g7c6a1aa-…, patch 0010 revision 3 and patch 0011), image 62a2e345…,
the same extras and rootfs (59781d60…, the udev rule). Cold start, m3-end at 50.4 s. Evidence: dumps/firstboot/2026-11-05-ap.log
(fifth boot), 2026-11-05-ssh-pm-auto-3.txt, 2026-11-05-ssh-audio-state.txt (runs at 1:31, 1:32 and 1:33 AM EST).
| Uptime | What | Evidence |
|---|---|---|
| 6.15 s | all four enumerate; enumeration check 0: … 2 of 2 attached on both buses at 6.26 s | AP lines at 6.147 to 6.257 s |
| 36.21 s | the rule’s wake, now through the full-init resume path (0011): both buses 2 of 2 | AP lines at 36.211 s |
| about 1:31 AM | tone 1, masters on: both buses 0x14; the owner heard all the beeps | first audio-state run |
| 1:32:43 AM | owner OK: auto on both masters; both suspended within 4 s | 2026-11-05-ssh-pm-auto-3.txt |
| 173.58 s | tone 2’s wake: both buses UNATTACHED then SDW Slave Addr for both amplifiers within 300 ms (bus 1 by 173.60 s, bus 4 by 173.86 s); 2 of 2 attached on both; no stall, no AUTO_ENUM_FAILED, no escalation; the owner heard all the beeps | AP lines at 173.584 to 173.973 s; second audio-state run |
| 212.88 s | tone 3’s wake, a minute later: the same, 2 of 2 on both | AP lines at 212.877 to 213.137 s; third audio-state run |
Reading: with clk_stop_mode1 set on WSA884x (0011) every resume runs SWRM_COMP_SW_RESET, qcom_swrm_init and the enumeration
wait, the sequence that has enumerated at every boot, and three wakes in a row came back whole where boots -2 and -3 lost a bus
on the first. Revision 3’s escalation (reset and init from the check) did not have to fire; its detection lines (2 of 2 attached)
are the evidence that each wake completed. Open: whether 0011 alone is the fix (0010’s escalation is unexercised), the 10-minute
idle case from the ticket’s bar, the Parity error detected lines, and the udev rule, which can now go.
The rule-free boot (2026-11-05, 2:05 AM EST): the full bar on kernel #35, no udev rule
Second boot of attempt 2026-11-05-4’s image (kernel #35, patches 0011 and 0010 revision 3), rootfs 6fea12f2… (no udev rule: the masters
read control=auto status=suspended before every tone). Cold start, m3-end at 51.8 s. Evidence: dumps/firstboot/2026-11-05-ap.log
(sixth boot), 2026-11-05-netlog.txt boot #6, 2026-11-05-ssh-audio-state.txt (runs at 1:53, 1:54 and 2:04 AM EST),
2026-11-05-ssh-speaker-names-2.txt.
| Uptime | What | Evidence |
|---|---|---|
| 6.27 s | all four enumerate; 2 of 2 attached on both buses | AP lines at 6.265 and 6.273 s |
| 46.5 and 47.6 s | the M3 script’s own reads wake the masters: 2 of 2 each | AP lines at 46.457 and 47.641 s |
| 64.0 s | tone 1 (1:53 AM): both buses 0x14; the owner heard the beeps | AP lines at 63.993 to 64.257 s; first audio-state run |
| 129.4 s | tone 2 a minute later: both 0x14; the owner counted eight beeps (four channels, two loops) | AP lines at 129.427 to 129.685 s; second run |
| 761.8 s | tone 3 after 10 minutes idle (2:04:39 AM): both 0x14, 2 of 2 | AP lines at 761.847 to 762.121 s; third run |
| about 2:05 AM | speaker-test -D hw:0,1 -c 4 -t wav -l 1: Front Left, Front Right, Rear Right, Rear Left (first try -16 busy: the tone was still playing) | 2026-11-05-ssh-speaker-names-2.txt |
Reading: every wake of this boot (five, counting the script’s reads) brought both buses back whole; no stall, no AUTO_ENUM_FAILED,
the escalation never fired. The owner’s hearing reports for tone 3 and the names are in ticket 16.
The capture test (2026-11-05, 9:26 AM EST): the four DMICs record, and the owner heard the playback
Boot 7 of the day (netlog #7, attempt 2026-11-05-4’s image, kernel #35, rootfs 6fea12f2…; cold start, clean, SCMI 0.1 ms), the
TEST IMAGE still on the stick (dts/diag/sound.dtsi extra; the same node sits in the board DTS since commit 9d120a5). Over ssh:
alsaucm -c X1E80100-Dell-XPS-13-9345 set _verb HiFi list _devices lists 0: Speaker and 1: Mic (“Internal microphones”); one
invocation enabled both (set _enadev Speaker set _enadev Mic), then arecord -D hw:0,3 -c 2 -f S16_LE -r 48000 -d 10 /tmp/mic.wav
while the owner spoke, then aplay -D plughw:0,1 /tmp/mic.wav (exit 0). The owner heard their own voice from the speakers
(report at 9:26 AM EST). The file, measured on the build host (dumps/firstboot/2026-11-05-mic.wav, gitignored): 480000 frames, RMS about 900 on
the left channel and 1200 on the right, varying second by second (1506 and 2776 in the first second, 444 and 408 in the last), peaks
at full scale once per channel (inferred: the start click). The journal has no VA macro, DMIC or LPASS line for the capture.
So the mic rail (EN_PP3300_MIC, TLMM 69, as vdd-micb-supply) and the DMIC clock (qcom,dmic-sample-rate = <4800000>, divider 16)
work as described; dts/README.md, “Unverified”, records which values stay the Yoga’s. Evidence:
dumps/firstboot/2026-11-05-ssh-ucm-devices.txt, -ssh-capture.txt, -ssh-playback.txt, -ssh-va-journal.txt.
The 0011-alone boot (2026-11-05, 12:32 PM EST): patch 0011 carries the fix
Attempt 2026-11-05-6 (kernel #36, patches 0001 to 0009 and 0011, 0010 set aside for this build only and restored in the repo; the
release string carries + for the set-aside file; TEST IMAGE bf48ab37…, SoundWire dyndbg; rootfs 6fea12f2…; cold, clean).
Tone 1 at 12:29 PM EST (first wake), tone 2 at 12:31 PM EST after both masters read suspended, the names right after: every wake
re-enumerated both amplifiers on both buses (master-1-0 and master-4-0 status=0x14 dev1=attached dev2=attached), the journal
shows only the read FIFO underflow bursts at each wake, no stall, no AUTO_ENUM_FAILED. The owner heard tone 1, tone 2 and the
four names twice (reports at 12:30 and 12:32 PM EST). So the full-init resume path that 0011 selects (clk_stop_mode1 on WSA884x) is the fix;
0010’s stall check and escalation never had to act on kernel #35 and are absent here with the same result. Owner decision (12:38 PM EST): 0010 stays until the M5 patch
review, then it is dropped (one patch fewer to defend). The same boot showed /init’s new
GBOOK: lock-ro=none: only the stick (sda) (task #36). Evidence: dumps/firstboot/2026-11-05-ssh-audio-state.txt (the last three
runs), 2026-11-05-ssh-swr-journal.txt, netlog boot #11.
Next
- Workaround in the rootfs: not a workaround (“The udev-rule boot”); to be removed from
rootfs/customize.shnow that the patches pass. - The driver fix passes the ticket’s bar (“The rule-free boot”, 2:05 AM EST): tones a minute apart, a tone after 10 minutes idle, the four names, all on the rootfs without the udev rule. Both patches stay (ticket 16). The 0011-alone boot (“The 0011-alone boot”, 2026-11-05) shows 0011 carries the fix; 0010’s fate is the owner’s call.
- The reads planned on 2026-11-04 for the left amplifiers ran on 2026-11-05-1 (“The debug boot”); the stock log read is now
scripts/recon.sh stock-kmsg '<regex>'. - Tools for those reads: asoc-debug-tools.md (research pass, 2026-11-04).
- The ssh user joins
audio(rootfs/customize.shgroup loop), so the tests run withoutsudo -n. - The speaker test as run tonight is the regression check:
speaker-test -D hw:0,1 -c 4 -t wav -l 1right after boot, all four names audible. The PA volumes stay at the driver’s cap of 6 (x1e80100.c:42-51). - Capture test: passed (“The capture test”, 2026-11-05). The sound node is in the board DTS (commit
9d120a5). Next: a base image, three clean cold boots, the release. - The APM first-poll timeout at 6.12 s: read
q6apm.cfor the poll’s caller and timeout; a lead for boot time, not for function.