Flaky boot: bootm runs in place from the fastboot buffer and may overlap the decompressed kernel #4
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Booting a kernel — both
fastboot bootandsdboot— fails intermittently. The image transfers fine (Finished. Total time: 0.5s, andsdbooteven CRC-verifies it), then nothing comes up. Retrying the identical image usually works. This has been happening since early in the project and cost a dozen retries in one session; it is now the single biggest drag on iteration.Likely cause: load/decompress overlap
Both paths boot in place from the fastboot download buffer:
But our boot images are built with
--base 0x80000000 --kernel_offset 0x8000, sobootmrelocates and gunzips the kernel to 0x80008000. OurImage.gzis ~12.9 MB and the decompressedImageis substantially larger. If that decompression reaches$fastboot_addr_r, it overwrites the source image mid-decompress — which would fail intermittently and in a size-dependent way, and would get progressively worse as the kernel grows. That matches the symptom exactly.Relevant config:
Diagnosis (needs the U-Boot console)
From the serial console (
gts6l-reboot console, menu entry 17):Then compare
$fastboot_addr_ragainst0x80008000 + <decompressed Image size>. If they overlap, that is the bug.Also worth capturing a failing attempt on the console —
bootmprints its relocation/decompress steps, so it should be obvious where it dies (or whether it dies silently after handing off).Likely fix
Load and boot from different addresses rather than in place: keep the download/read buffer well clear of the kernel's load+decompress range, or point
bootmat a copy. Bumping$fastboot_addr_rhigh (well above0x80008000 + SYS_BOOTM_LEN) is the simple version.Related: u-boot-gts6l#1 proposes loading the kernel directly from ext4, which changes these addresses anyway — worth fixing the layout as part of that rather than twice.
Note
Not a kernel bug: the same image (identical CRC, verified) has booted successfully and then failed on the next attempt with nothing changed.
Root cause found — and it is not the fastboot buffer
My original hypothesis above (fastboot download buffer overlap) was wrong. The actual bug is in the boot image header offsets, and it is deterministic and measurable.
Dumping the header of a known image:
But
kernel_sizeis the compressed size. What matters for placement is the arm64Imageheader'simage_sizefield — text+data+BSS, the runtime footprint:The device tree sits 864 KiB inside the kernel's own BSS. The arm64 kernel zeroes its BSS during early startup, so the DTB is destroyed before it is ever parsed.
--tags_offset 0x1e00000is the stock Android default, sized for a ~10 MiB kernel. A mainline defconfig build is 30.8 MiB and runs straight through it. Headroom before the DTB is 29.97 MiB; we are at 30.81 MiB — we crossed that line recently, which is exactly why the flakiness got worse over time rather than being there from day one.Why it is intermittent rather than a hard failure: whether a given boot survives depends on U-Boot's FDT relocation policy (
fdt_high), i.e. whether it leaves the DTB attags_addror relocates it clear before handing off. Same image, different outcome, nothing else changed — which is precisely the symptom reported.Note
ramdisk_addrhad only 1.1 MiB of margin. It is unused today (ramdisk_size = 0), but it would have broken the moment we added the mkinitcpio initramfs.Fix (applied)
New layout, 2 MiB aligned, all clear of the first firmware carveout at
0x85e00000:tools/mkbootimg.pydefaults updated (tags 0x01E00000 -> 0x03000000,dtb 0x01F00000 -> 0x03100000,ramdisk 0x02000000 -> 0x03400000) andbuild-617.shupdated to match.Guard against regression
mkbootimg.pynow parses the arm64 image header (transparently through gzip — only the first 64 bytes are needed) and refuses to build an image whose kernel footprint overlaps the DTB, ramdisk, or tags address. Verified against the old offsets:It returns silently for non-arm64 payloads (U-Boot itself, the 4.14 kernel), so the other build scripts are unaffected until they actually have a conflict.
--allow-overlapis available as an escape hatch.Consequence for past debugging
Every image we have booted in this project carried this defect. Any boot that "just didn't come up" may have been DTB corruption rather than the change under test. Differential results where only the DTB content changed are still meaningful — placement was identical on both sides — but individual boot attempts were unreliable, and conclusions drawn from a single failed boot should be re-checked now that the layout is fixed.
Correction — the previous comment's root cause is wrong
I checked the U-Boot source instead of assuming, and my "DTB is clobbered" conclusion does not survive it. Retracting it.
tags_addris never consumed by U-Boot. Grepping every use in the tree:That is all. Nothing reads it back.
tags_addris an ATAGS-era field; U-Boot places the FDT itself viaboot_relocate_fdt(), which (withfdt_highunset, as ours is) allocates fromlmbinside the bootmap rather than at the address in the image header. So the arithmetic in my previous comment was correct but described a placement that never happens. The DTB was not at0x81e00000.Two further things that argue against my earlier theories:
bootm_load_os()has an explicit source/destination overlap check that printsERROR: new format image overwritten - must RESET the board to recover.So the original fastboot-buffer theory would produce a visible error, not a silent failure.
lmb_reserve(images->os.load, load_end - images->os.load, LMB_NONE)runs after decompression, so the decompressed kernel is reserved before the FDT is placed.What is actually still a real hazard
One genuine issue survives, and it is narrower than what I claimed. U-Boot reserves
load_end - load— the decompressed file size (31,750,656). The kernel's true footprint is the arm64 header'simage_size(32,309,248), which includes BSS:Anything U-Boot places in that 545 KiB window — FDT or ramdisk — is considered free memory by the bootloader and is then zeroed by the kernel as BSS. In practice
boot_relocate_fdt()usually allocates high in the bootmap, well clear of it, so this may never bite. It is a latent hazard, not a demonstrated cause.Status of the flaky boot
Unknown. I do not have a root cause, and I should not have claimed one. The way to get it is the serial console during a failing attempt —
bootmprints each stage, so a failure between "Loading Kernel Image" and the kernel's own output will localise it immediately. That still needs doing.What was changed anyway
The offset changes (
tags 0x03000000,dtb 0x03100000,ramdisk 0x03400000) and themkbootimg.pyoverlap guard are kept, but as hygiene, not a fix: the header should not advertise addresses that sit inside the kernel, and the guard documents the BSS-vs-file-size distinction that tripped me up. The ramdisk move is the one with concrete future value —0x82000000had only 1.1 MiB of clearance, which matters once the mkinitcpio initramfs exists.No claim that any of this makes boots more reliable. That remains open.