stub: accept LBIO physical_address == 0 — it is the real record here

Round 5: the Round-4 diagnostic blinks decoded as parse_fail. Root cause
confirmed in coreboot 4.14 source (kukui mainboard.c + edid_fill_fb.c):
the LBIO framebuffer record genuinely has physical_address = 0 — the
menu renders through DRAM address 0, which the never-stopped OVL scans
out (display_init_required() is false, so depthcharge registers no
display ops and its handoff cleanup's backlight/stop calls are no-ops).

Round-2's 'menu proves pa != 0' reasoning was wrong; the original
open-item note was right. The stub's !fb->pa rejection caused every
parse failure. find_framebuffer now accepts pa == 0 and the stub paints
at address 0, the same region depthcharge's own cleanup black-fills.

host_test gained a pa==0 regression test; qemu_test unchanged. Payload
b25d9132... flashed and verified (cmp + vbutil).
This commit is contained in:
vhaudiquet
2026-08-29 23:09:37 +02:00
parent 9e50b6ad61
commit da4aaca1c2
5 changed files with 76 additions and 1 deletions
+37
View File
@@ -284,6 +284,43 @@ Payload `55ccc1bd…` flashed (cmp + vbutil OK). host/qemu tests pass.
| colors appear but sequence stops early | fill/mask problem — stuck color identifies stage |
| red → yellow → green → blue, blue held | pipeline fully validated |
## Round 5 — blinks decoded: LBIO physical_address really IS 0
The Round-4 payload's diagnostic worked: 35 backlight blinks then steady
backlit black = `parse_fail()`. The stub runs, stage-0 works, the GPIO fix
works — the parse rejected the LBIO framebuffer record. Root cause found
in the actual coreboot **4.14** source (fetched from the 4.14 tag):
- `src/mainboard/google/kukui/mainboard.c` (4.14) is identical to
mainline: `fb_new_framebuffer_info_from_edid(edid, 0)` — and 4.14's
`edid_fill_fb.c` passes `fb_addr` through verbatim, no carveout, no
allocation. **The LBIO record on this device genuinely has
physical_address = 0.**
How the menu still renders: libpayload cbgfx draws into
`phys_to_virt(pa)` = DRAM address 0, and coreboot's display pipeline scans
out address 0. With `pa = 0`, depthcharge's `display_init_required()` is
false, so board.c never registers display ops — meaning at handoff
`display_cleanup` runs but `backlight_update` and `stop` are no-ops:
**the OVL was never stopped and the backlight never disabled by
depthcharge.** The "menu vanishing" was just the 9.2 MB black fill at
address 0 (safe for the payload: depthcharge itself does it after the
kernel slot is chosen, and the stub demonstrably survived every round).
**Round-2's "open item resolved" reasoning was wrong** — menu rendering
does NOT prove pa != 0, because address 0 works as a framebuffer region.
The original Phase-1 note ("mainline coreboot passes fb_addr=0 for kukui")
was correct all along. All five black screens so far trace to the stub's
`!fb->pa` rejection of a legitimate record (plus the two Round-1 register
offset misreads, which additionally kept the backlight dark).
Fix: `find_framebuffer` accepts `pa == 0` and the stub paints at address
0 — the buffer the still-running OVL scans. OVL revival and backlight
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).
Recovery: power-cycle, boot USB (unchanged), `dd if=mmcblk0p1-pmos-backup.img
of=/dev/mmcblk0p1 bs=4M conv=fsync`.
+30
View File
@@ -103,6 +103,36 @@ int main(void)
(unsigned long)fbmem[1200 * 7680 + 32], 0xAA);
}
/* --- test 3: device-realistic pa==0 record must be accepted ---------- */
/* coreboot 4.14 kukui publishes physical_address = 0; depthcharge's
* menu renders through DRAM address 0. Regression guard for the
* Round-5 bug (blinking parse_fail on the real device). */
{
memset(table, 0, sizeof table);
memcpy(table, "LBIO", 4);
put32(table + 4, 24);
put32(table + 20, 1);
u8 *r = table + 24;
put32(r, 0x12);
put32(r + 4, 48);
put32(r + 8, 0); /* physical_address = 0 */
put32(r + 12, 0);
put32(r + 16, 1200); /* x */
put32(r + 20, 1920); /* y */
put32(r + 24, 4800); /* bytes_per_line */
r[28] = 32; /* bpp */
r[29] = 16; r[30] = 8;
r[31] = 8; r[32] = 8;
r[33] = 0; r[34] = 8;
struct fbinfo fb0;
int rc0 = find_framebuffer((u64)(unsigned long)table, &fb0);
check("pa==0 record accepted", (unsigned long long)(rc0 == 0), 1);
check("pa==0 fb pa stays 0", fb0.pa, 0);
check("pa==0 xres", fb0.xres, 1200);
check("pa==0 mkcolor red", mkcolor(&fb0, 1, 0, 0), 0x00ff0000ull);
}
printf(fails ? "\nFAILED: %d\n" : "\nALL PASS\n", fails);
return fails != 0;
}
Binary file not shown.
BIN
View File
Binary file not shown.
+9 -1
View File
@@ -150,6 +150,13 @@ static int find_coreboot_reg(const void *dt, u64 *addr, u32 *size)
* bits_per_pixel @28, red_pos/size @29/30, green @31/32,
* blue @33/34, reserved @35/36, orientation @37, flags @38, pad @39
* sizeof(struct lb_framebuffer) == 40 (host-verified with real header).
*
* **physical_address == 0 is LEGITIMATE on this device**: coreboot 4.14
* kukui calls fb_new_framebuffer_info_from_edid(edid, 0) with no carveout,
* so the LBIO record really says pa=0 and depthcharge's UI draws its menu
* into DRAM address 0, which the (never-stopped) OVL scans out. Do NOT
* reject it; paint at address 0 — the same 9.2 MB region depthcharge's own
* display_cleanup black-fills at handoff.
*/
static int find_framebuffer(u64 table, struct fbinfo *fb)
{
@@ -173,6 +180,7 @@ static int find_framebuffer(u64 table, struct fbinfo *fb)
if (tag == LB_TAG_FRAMEBUFFER) {
if (rsize < 40)
return -1;
/* pa == 0 is legitimate here (see block comment). */
fb->pa = (u64)rd32le(rec + 8) |
(u64)rd32le(rec + 12) << 32;
fb->xres = rd32le(rec + 16);
@@ -182,7 +190,7 @@ static int find_framebuffer(u64 table, struct fbinfo *fb)
fb->rpos = rec[29]; fb->rsize = rec[30];
fb->gpos = rec[31]; fb->gsize = rec[32];
fb->bpos = rec[33]; fb->bsize = rec[34];
if (!fb->pa || !fb->xres || !fb->yres || !fb->bpl)
if (!fb->xres || !fb->yres || !fb->bpl)
return -1;
if (fb->bpp % 8 || fb->bpp < 8 || fb->bpp > 32)
return -1;