Round 18: vectors-before-_start discovered; wrapper-only 4K isolator flash

This commit is contained in:
vhaudiquet
2026-08-30 12:56:55 +02:00
parent 26984768cf
commit 391756e573
4 changed files with 61 additions and 21 deletions
+23
View File
@@ -711,3 +711,26 @@ contiguous — wfi hang expected) + 8 KiB zero tail padding → total
|---|---|
| green | total size innocent → the 0x1000 alignment padding/placement is the killer (re-test padding content) |
| black | total size (image_size value) alone gates the handoff |
## Round 18 — total size ruled out; wrapper-only isolator
R17 (R16 content + 8 KiB tail pad, total 457964 > dead 453432):
**green** — total size ruled out. The only remaining structural
difference between ran (R12/13/16/17: U-Boot contiguous at 0xB4) and
dead (R14/15: U-Boot at 0x1000 behind interior zero pad): the interior
pad + shifted placement. Also discovered: the U-Boot binary does NOT
start at _start — `arch/arm/cpu/u-boot.lds` places 0x40 bytes of
`.vectors` first, so link _start = 0x4C000140 (all prior alignment math
shifts by 0x40; the PIE requirement is runtime _start ≡ link _start
mod 4K AND start.S's wfi check demands runtime _start ≡ 0 mod 4K, so
link _start must be 4K-aligned too).
### Test B (payload 9485c59e…, flashed, cmp+vbutil OK, body 0x14000)
4 KiB image: header + wrapper v3 (magenta + 3 blinks + ~5 s hold) +
zeros to 0x1000 + NO U-Boot, tail branch patched to self (hang):
| observation | conclusion |
|---|---|
| magenta + blinks + hang | interior pad innocent; the trigger is U-Boot content/placement at 0x1000 |
| black, nothing | depthcharge rejects the padded image before any execution |
+27 -14
View File
@@ -4,15 +4,21 @@
# 0x0000 64-byte arm64 Image header (code0 = b +0x40, image_size,
# flags bit3, magic at 0x38 — booting.rst contract, verified
# against depthcharge src/arch/arm/boot64.c)
# 0x0040 uboot-wrapper.S (diagnostic: revive display + green fill,
# then branch to U-Boot; branch imm26 patched below)
# 0x0040+ uboot.bin (u-boot-nodtb.bin + embedded control DTB)
# 0x0040 uboot-wrapper.S (diagnostic: revive display + magenta fill +
# blink train, then branch to U-Boot; branch imm26 patched)
# 0x0040+ uboot.bin (u-boot-nodtb.bin + embedded control DTB),
# contiguous — NO interior padding.
#
# U-Boot's PIE fixup requires the run-vs-link delta to be 4K-aligned:
# CONFIG_TEXT_BASE must equal 0x4c000000 + the U-Boot file offset. The
# script verifies this against the _start symbol in u-boot.sym.
#
# then pack with mkdepthcharge (devkeys) and verify.
set -e
cd "$(dirname "$0")"
UBOOT_BIN="${UBOOT_BIN:-/home/vhaudiquet/u-boot/u-boot.bin}"
UBOOT_SYM="${UBOOT_SYM:-/home/vhaudiquet/u-boot/u-boot.sym}"
DTB="${DTB:-krane-sku176.dtb}"
WRAP_IMG=krane-uboot.bin
OUT_PAYLOAD=krane-uboot-payload.bin
@@ -22,30 +28,38 @@ aarch64-linux-gnu-gcc -c uboot-wrapper.S -o uboot-wrapper.o
aarch64-linux-gnu-objcopy -O binary uboot-wrapper.o uboot-wrapper.bin
WRAP_LEN=$(stat -c %s uboot-wrapper.bin)
# 2. header + wrapper + u-boot, patch both branches
python3 - "$UBOOT_BIN" "$WRAP_IMG" "$WRAP_LEN" <<'EOF'
# 2. header + wrapper + u-boot, patch the wrapper's tail branch
python3 - "$UBOOT_BIN" "$UBOOT_SYM" "$WRAP_IMG" "$WRAP_LEN" <<'EOF'
import struct, sys
uboot_path, out, wrap_len = sys.argv[1], sys.argv[2], int(sys.argv[3])
uboot_path, sym_path, out = sys.argv[1], sys.argv[2], sys.argv[3]
wrap_len = int(sys.argv[4])
uboot = open(uboot_path, 'rb').read()
wrapper = open('uboot-wrapper.bin', 'rb').read()
assert len(wrapper) == wrap_len
# wrapper entry at 0x40, U-Boot entry right after the wrapper
# wrapper entry at 0x40, U-Boot right after the wrapper (contiguous)
wrap_off = 0x40
uboot_off = wrap_off + wrap_len
total = uboot_off + len(uboot)
# verify the U-Boot link address matches its runtime placement
for line in open(sym_path):
parts = line.split()
if len(parts) == 3 and parts[2] == '_start':
start = int(parts[0], 16)
break
else:
raise SystemExit('_start not found in u-boot.sym')
assert start == 0x4c000000 + uboot_off, \
"TEXT_BASE/_start 0x%x != expected 0x%x" % (start, 0x4c000000 + uboot_off)
hdr = bytearray(64)
hdr[0:4] = struct.pack('<I', (0x40 >> 2) | 0x14000000) # code0: b +0x40
struct.pack_into('<Q', hdr, 0x10, total) # image_size
struct.pack_into('<Q', hdr, 0x18, 1 << 3) # flags: bit3
# U-Boot requires its runtime _start to be 4K-aligned (start.S checks
# `adr _start` & 0xfff and hangs in wfi otherwise; it uses ADRP+ADD with
# lo12 relocations during the PIE fixup). depthcharge loads the image at
# a 2 MiB-aligned slot, so place U-Boot at the next 4 KiB boundary.
uboot_off = (wrap_off + wrap_len + 0xfff) & ~0xfff
total = uboot_off + len(uboot)
hdr[0x38:0x3c] = b'ARM\x64' # magic
# patch the wrapper's trailing `b .` to jump to the U-Boot entry
br_off = wrap_off + wrap_len - 4
imm = (uboot_off - br_off) // 4
@@ -55,7 +69,6 @@ wrapper[-4:] = struct.pack('<I', (imm & 0x03ffffff) | 0x14000000)
with open(out, 'wb') as f:
f.write(hdr)
f.write(wrapper)
f.write(bytes(uboot_off - wrap_off - wrap_len))
f.write(uboot)
print("layout: header 64, wrapper %d (0x40..0x%x), uboot %d @0x%x, total %d" %
(wrap_len, uboot_off, len(uboot), uboot_off, total))
+11 -7
View File
@@ -1,16 +1,15 @@
/*
* krane diagnostic wrapper runs before U-Boot proper after the
* depthcharge handoff (MMU off, caches off, arbitrary 2 MiB-aligned
* load slot; position-independent: immediate-encoded addresses only).
* depthcharge handoff (MMU off, caches off; position-independent:
* immediate-encoded addresses only).
*
* Purpose: prove that the payload actually executes and revive the
* display before U-Boot gets a chance to crash, so a dark screen can be
* attributed unambiguously:
* - magenta screen + backlight on, then 3 slow blinks = wrapper ran,
* U-Boot crashed later (bands from the U-Boot checkpoints narrow
* the window further)
* - dark screen, no blink train = wrapper never executed
* - blink train repeating forever = watchdog reset loop
* - magenta screen + backlight on, 3 slow blinks, magenta held ~5 s,
* then U-Boot takes over: wrapper ran
* - blink train repeating periodically: watchdog reset loop
* - dark screen, no blink train: wrapper never executed
*
* Register sources (all verified on device, see RESEARCH.md rounds 4/10):
* OVL0 base 0x14008000: OVL_EN @ +0x000c, OVL0_2L_EN @ +0x100c,
@@ -83,6 +82,11 @@ _start:
bl delay
subs w11, w11, #1
b.ne 6b
/* hold magenta for ~5 s so the phase cannot be missed */
mov w12, #20
7: bl delay
subs w12, w12, #1
b.ne 7b
1: b . /* PATCHED: branch to U-Boot entry */
/* ~250 ms delay using the arch timer */
BIN
View File
Binary file not shown.