Repository navigation
Write /system in pieces where adb shell cannot carry it, and add two diagnostics - #107
Merged
Merged
Conversation
bboe
force-pushed
the
dot-firmware-report
branch
9 times, most recently
from
October 9, 2026 01:48
7f713cc to
2b9e164
Compare
An issue report carried the failing step and nothing about the Dot. This collects the host, the state, the build, the eMMC, the partition table with each name's node, and the kernel's own mmc lines. Only a recovery answers all of that, so it starts one. A Dot already in a recovery is left alone: the one a failed run left is the safe place to be, and a first version cost a reporter theirs. system_a is the reason it exists. Fire OS 5's image is 805,257,216 bytes against a 805,306,368-byte partition, so the write has 48 KiB of headroom and nothing checks it. Build 8149's report showed only a mismatched md5. Wear comes from sysfs where a part reports it, and the erase count only from the kernel log, which reaches back about 3,000 seconds, so uptime is printed beside it. cid and serial are left unread, and GUIDs are masked. Each device-side answer is read behind a marker of its own, because adb cuts the tail: a table that does not arrive whole is printed as it came rather than parsed into claims about which names point nowhere. A failed system write discards the image and keeps the record of its size, which is the number a report is run for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A failed install blames an md5, and the card, the cable and the host are all candidates. This writes one pattern over two paths that share no hardware: into /tmp, which is RAM, for adb and the cable with no eMMC in it; and from the Dot to its own cache, for the card with no USB in it. It writes over cache, which an install formats anyway, so a part-written system_a stays as evidence. The pattern is not zeros, because a card that writes nothing passes a zero test. It is every byte value followed by a word, so nothing a transport mangles goes unnoticed, and its odd length makes a slice written at the wrong offset hash differently. The USB leg leaves its verified file in RAM and the eMMC leg writes that same file, so the legs carry identical bytes and the second is skipped when the first proved nothing. cache is reformatted first and the pattern goes behind the new filesystem's own blocks. So the writes are raw, like the /system write they stand in for, nothing is mounted while they land, and cache holds a whole-partition filesystem at every moment: a run stopped part way leaves a Dot that still boots with its cache the size it should be. A Dot whose cache carries no filesystem does not boot -- fs_mgr_mount_all fails in init's on fs, so class main never starts and the Dot answers adb as root with no framework. The layout is pinned, not inherited, because that boundary has to be an input. -J size=4 -N 8192 with -O ^sparse_super,^resize_inode -E packed_meta_blocks=1 puts every metadata block inside the first 6.11 MiB of a 784 MiB cache, read out of the superblock and the journal inode, against a 16 MiB margin. Left to its defaults mke2fs puts a 16 MiB journal at 12.33-28.33 MiB, and e2fsck is no guard there: after writing from 16 MiB it passed all five passes on a filesystem missing 12.3 MiB of that journal, because a clean filesystem's journal body is never checked. A test that cannot run must not read as a card that failed. So the digest comes back behind a marker and dd's stderr is kept; a refused umount, a target that is not a block device and a format that did not happen each say so, because the read-back names the node it wrote and a file in TWRP's RAM /dev would match itself; and each skip says which of the three reasons it was. conv=notrunc is toybox dd only, so it asks which dd is there, and the whole command is one line with no quotes or Windows loses it. Verified on bryce in TWRP 3.7.0_9-bboe2: 128 MiB over USB at 20 MiB/s, then 768 MiB written and read back at 18 MiB/s, both digests matching. Separately, overwriting 16-784 MiB of the pinned filesystem left e2fsck clean, mounted, carried a 4 MiB file through the journal, unmounted, checked clean again, and read that file back unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Between clearing boot0's header and writing the preloader, a stopped run leaves a Dot that shows no light and starts nothing. Only the stock restore said so, and a reporter stopped at the system image, rebooted, and read the dark ring as a brick. _die now says it whenever a run that writes meets the marker, Ctrl-C included, which is the likeliest way to stop inside that window. It says what to do: the Dot waits in its bootrom and a rerun on the same computer picks it up by itself. No button gesture reached fastboot from there when it was tried. It is a run that writes, not any error, because the marker is one file for every Dot: --report and --write-test write nothing and must not speak for a marker another Dot left behind. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bboe
force-pushed
the
dot-firmware-report
branch
2 times, most recently
from
October 9, 2026 02:00
82af88e to
68a5789
Compare
A Windows host cannot root a Dot, and has not since 1c71a24 put the /system write on adb shell's stdin: that client reads stdin in text mode, where 0x1a ends the file. The image holds 1,421,626 of them and the first is at offset 238, so 238 of 385,745,930 bytes arrive and gunzip writes 12. A pipeline carries its last command's status, so dd exits 0 and only the md5 complains, which reads as a failing card. 1c71a24 dropped exec-in because this TWRP truncates it, and said "Windows is untried". The image is now built as 128 MiB slices, each its own gzip member. Where stdin carries a stream they go out back to back, a valid multi-member gzip and what macOS has always done; where it does not, each slice is pushed to /tmp and unpacked at its own offset, so nothing is staged on the eMMC and a recovery's 240 MiB tmpfs is room enough. The route is chosen by sending 1 KiB holding 0x1a and hashing it on the Dot, not by naming an operating system. Slicing costs 23.4 s against 15.9 s for 256 MiB, and only where it is needed. Measured and rejected: gzip cannot be told to avoid a byte; escaping needs a decoder on the Dot, where base64 runs at 1.2 MB/s; a forwarded socket accepts with nothing listening, and a reverse one is not honoured; exec-in returns with 4.7 MB in flight and reports no status. Each route is accepted on its own word and nothing else. gunzip touches a file when it fails, dd's status ends the pipeline instead of being discarded, and adb's status is read after the child is waited for, not before, where it is always None. Silence is a failure: adb shell exits 0 whatever happened remotely, so every one of those would otherwise have named the card. gunzip reports a damaged gzip but not a missing one. toybox 0.7.6 exits 1 on a stream cut at 0x1a and on a corrupted body, so the marker appears for both. On input with no gzip magic it exits 0 and behaves like cat, passing the bytes to dd to write. So each slice is checked for the gzip magic on the host before the cache is used, where it also catches a build that did not finish. Verified on bryce. A whole v1-bboe install from a Windows host took the sliced route by its own probe and wrote /system in 108 s, md5 verified on the card -- the first since 1c71a24. The stream route was then driven against the same Dot: a good 16 MiB stream matched, a gzip cut at 0x1a was named, a dd that could not write returned its own message, and a vanished Dot returned adb's. Corrupt slices are refused before any of that runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bboe
force-pushed
the
dot-firmware-report
branch
from
October 9, 2026 02:12
68a5789 to
55d09ec
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A Windows host cannot root a Dot: its adb ends a stream at the first 0x1a, so the system image stops after 238 bytes and the failure reads as a bad eMMC. The write now goes over in pieces where that happens. Two commands collect what an issue needs.
This is what a reporter saw, three times, with a healthy card and cable:
and this is the same stream, same Dot, same recovery, minutes apart:
🤖 Generated with Claude Code