stub: render failure diagnostics as bit-bands on the panel
Round 6: pa==0 accepted but parse still fails on device (5 blinks).
All local reproductions pass, so the remaining unknowns are the actual
runtime values. Since the scanout address is known without any parse
(DRAM address 0 — Round 5), fail_dump() now renders diagnostic words as
32 white/black bit-cells per row directly into the scanout buffer after
the 5-blink execution signal.
Marker 0xC0DE0001: find_coreboot_reg failed — dumps x0 + raw DTB header.
Marker 0xC0DE0002|rc: find_framebuffer failed — dumps LBIO addr/size,
raw table magic/header_bytes/entries, and raw words at +24..+68.
Also fix a build break: u32 array = {0} emitted memset in freestanding.
host_test and qemu_test pass; payload e619166a... flashed and verified.
This commit is contained in:
+30
@@ -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`.
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user