diff --git a/RESEARCH.md b/RESEARCH.md index 5d2ccee..984e8e7 100644 --- a/RESEARCH.md +++ b/RESEARCH.md @@ -321,6 +321,36 @@ writes are kept but are now believed to be redundant on this firmware. Payload `b25d9132…` flashed (cmp + vbutil OK). host_test gained a pa==0 regression test; qemu_test unchanged (its synthetic record uses pa!=0). +## Round 6 — pa==0 accepted but parse STILL fails: bit-band diagnostic dump + +Reboot with `b25d9132…` (pa==0 accepted): **5 blinks again** — parse_fail +persists, and with pa==0 accepted the failure is somewhere else in +find_coreboot_reg or find_framebuffer. Every local reproduction passes: +host_test parses the live fdt (21/21 checks incl. the new pa==0 test), +qemu end-to-end passes, and the runtime tree shape is provably identical +to the live fdt (same fixup code; krane DTB has no pre-existing /firmware +node; root cells 2/2). The remaining unknowns are the actual runtime +values — what is really at x0 and what the real LBIO bytes are. + +Key enabler: on failure the scanout address is KNOWN without any parse — +DRAM address 0 (Round 5; the visible menu erase proves the OVL scans it). +So the stub can render diagnostic data directly on the panel. + +### New failure path (payload `e619166a…`) + +`fail_dump()`: 5 blinks (execution proof, same signature), then paint a +bit-band dump into address 0: one row per u32, 32 cells of 32×32 px, MSB +first, white=1 / black=0, rows every 64 px. Word 0 = marker: + +- `0xC0DE0001` — find_coreboot_reg failed; words 1-2 = x0 pointer, + words 3-6 = raw DTB header (magic, totalsize, off_struct, off_strings, + off_mem_rsvmap). +- `0xC0DE0002 | rc` — find_framebuffer failed; words 1-3 = LBIO + addr/size, words 4-6 = raw table magic/header_bytes/entries, words 7-12 + = raw words at table+24..+68 (first record headers). + +User photographs the panel; values are decoded offline. + Recovery: power-cycle, boot USB (unchanged), `dd if=mmcblk0p1-pmos-backup.img of=/dev/mmcblk0p1 bs=4M conv=fsync`. diff --git a/krane-fb-stub-payload.bin b/krane-fb-stub-payload.bin index 59fd62f..5218e33 100644 Binary files a/krane-fb-stub-payload.bin and b/krane-fb-stub-payload.bin differ diff --git a/krane-fb-stub.bin b/krane-fb-stub.bin index 0097f66..b5e5a40 100755 Binary files a/krane-fb-stub.bin and b/krane-fb-stub.bin differ diff --git a/main.c b/main.c index 2afb423..ad1275e 100644 --- a/main.c +++ b/main.c @@ -302,11 +302,11 @@ static void blink_backlight(int times) static void stage0(void) { /* - * Stage 0 — first instructions after entry, BEFORE any parsing. - * Backlight on + OVL engine re-enabled (undoing depthcharge's - * cleanup). No reads, no parse: if even this never becomes visible, - * the payload was never handed off (boot-path problem), not a - * display-revival problem. + * First instructions after entry, before any parsing. On this + * firmware the OVL is never stopped and the backlight never disabled + * (display_init_required() is false — see RESEARCH.md Round 5), so + * these writes are redundant; they exist to keep the stub correct if + * a future firmware does tear the display down. */ wr32(DISP_OVL0_BASE + DISP_REG_OVL_EN, 1); wr32(DISP_OVL0_BASE + DISP_REG_OVL0_2L_EN, 1); @@ -314,6 +314,56 @@ static void stage0(void) wr32(GPIO_DOUT_SET(PAD_EN_LCD_BL), GPIO_DOUT_BIT(PAD_EN_LCD_BL)); } +/* ---- failure diagnostics ------------------------------------------------- + * + * The parse failed. Paint the failing values as bit-bands directly into + * the scanout buffer (DRAM address 0 — the OVL is still scanning it; this + * is exactly where depthcharge's own clear_screen() erased the menu). + * No parse result is needed to know that address. Format: one row per + * u32 word, 32 cells of 32x32 px, MSB first, white=1 / black=0, rows + * every 64 px. The user photographs the screen; the values are decoded + * offline. Marker word 0xC0DE00xx identifies the failure stage. + */ + +#define DIAG_FB_BASE 0x0ull /* scanout address on this fw */ +#define DIAG_FB_WIDTH 1200 /* menu geometry, 32bpp xRGB */ + +static void render_diag(const u32 *d, int n) +{ + volatile u32 *fb = (volatile u32 *)DIAG_FB_BASE; + + for (int i = 0; i < n; i++) { + u32 y0 = 40 + (u32)i * 64; + if (y0 + 32 > 1920) + break; + for (u32 b = 0; b < 32; b++) { + u32 c = ((d[i] >> (31 - b)) & 1) ? 0x00FFFFFFu + : 0x00000000u; + for (u32 yy = 0; yy < 32; yy++) { + volatile u32 *row = fb + + (u64)(y0 + yy) * DIAG_FB_WIDTH + b * 32; + for (u32 xx = 0; xx < 32; xx++) + row[xx] = c; + } + } + } +} + +static void fail_dump(u32 marker, const u32 *words, int n) +{ + u32 diag[16]; + for (int i = 0; i < 16; i++) + diag[i] = 0; + + diag[0] = marker; + for (int i = 0; i < n && i < 15; i++) + diag[1 + i] = words[i]; + + blink_backlight(5); /* audible-free "I ran" signal */ + render_diag(diag, 16); + halt(); +} + /* ---- main checkpoint sequence ------------------------------------------- */ static void checkpoint(const struct fbinfo *f, int r, int g, int b) @@ -322,13 +372,6 @@ static void checkpoint(const struct fbinfo *f, int r, int g, int b) delay_ms(2000); } -/* Parse failure: black screen + 5 slow backlight blinks, then spin. */ -static void parse_fail(void) -{ - blink_backlight(5); - halt(); -} - void cmain(u64 dtb) { struct fbinfo fb; @@ -337,11 +380,30 @@ void cmain(u64 dtb) stage0(); - if (find_coreboot_reg((const void *)dtb, &lbio_addr, &lbio_size)) - parse_fail(); /* 5 blinks: DTB parse failed */ + if (find_coreboot_reg((const void *)dtb, &lbio_addr, &lbio_size)) { + /* Dump the DTB header words + the x0 pointer itself. */ + const volatile u32 *dt = (const volatile u32 *)dtb; + u32 w[6] = { + (u32)dtb, (u32)(dtb >> 32), + dt[0], dt[1], dt[2], dt[3], + }; + fail_dump(0xC0DE0001u, w, 6); + } - if (find_framebuffer(lbio_addr, &fb)) - parse_fail(); /* 5 blinks: LBIO/fb record bad */ + int rc = find_framebuffer(lbio_addr, &fb); + if (rc) { + /* Dump the LBIO location + raw table header + raw words at + * several offsets, exactly as found in memory. */ + const volatile u32 *t = (const volatile u32 *)lbio_addr; + u32 w[12] = { + (u32)lbio_addr, (u32)(lbio_addr >> 32), lbio_size, + t[0], t[1], t[5], /* magic, header_bytes, entries */ + t[6], t[7], /* rec0 tag, size (if hb=24) */ + t[8], t[9], /* raw words at +32, +36 */ + t[16], t[17], /* raw words at +64, +68 */ + }; + fail_dump(0xC0DE0002u | (u32)rc, w, 12); + } /* Checkpoint 1: red — parsed; LBIO framebuffer record is sane. */ checkpoint(&fb, 1, 0, 0); /* red */