From da4aaca1c2e9a5932a9c193297a842560f4c1e1a Mon Sep 17 00:00:00 2001 From: vhaudiquet Date: Sat, 29 Aug 2026 23:09:37 +0200 Subject: [PATCH] =?UTF-8?q?stub:=20accept=20LBIO=20physical=5Faddress=20?= =?UTF-8?q?=3D=3D=200=20=E2=80=94=20it=20is=20the=20real=20record=20here?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- RESEARCH.md | 37 +++++++++++++++++++++++++++++++++++++ host_test.c | 30 ++++++++++++++++++++++++++++++ krane-fb-stub-payload.bin | Bin 159744 -> 159744 bytes krane-fb-stub.bin | Bin 18464 -> 18448 bytes main.c | 10 +++++++++- 5 files changed, 76 insertions(+), 1 deletion(-) diff --git a/RESEARCH.md b/RESEARCH.md index 8462902..5d2ccee 100644 --- a/RESEARCH.md +++ b/RESEARCH.md @@ -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: 3–5 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`. diff --git a/host_test.c b/host_test.c index 736474a..fddb1a6 100644 --- a/host_test.c +++ b/host_test.c @@ -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; } diff --git a/krane-fb-stub-payload.bin b/krane-fb-stub-payload.bin index c117429d8c157b0f151c87c93d0a358c0fbee5ea..59fd62f0a9a2ff79f097670026b080438b257e23 100644 GIT binary patch delta 1229 zcmX|94OEj=7{2%0#x@)}e&$d~ewX0S0fY@E$gUs|gAnE{4n#xH6ej2tL`vM3<;E`@_a0kwX~@s#z)t_Dh+4-B+X6PM)D3;pyJuj>)rYHk#D!D*kD3?E zGvAWA{GQ_3R7!GN4!cvQyeHygQdNz&>#l3>$5ic-RHf#zeINUlRyt)AoD_{J3X0|& zEll4iXjU8)ESZi?aJ(}xeDN&@LHePbkViDuKsWwuPZT%KL{(#h-K@X_r}m(xY6 zO&OIj+s$O#q@Tc^> zn4*c-zaNl=iWkLUZ><4ElEFZ+HSaz-Fw)r#0}Mn zx`T45+Rn*Ad2?@0cSO7CQ$yY%*3ErvL6tXow&0vrx96S2pJq&cc7vg#>mFx|kfD_` z-2A-Vr($=6;fjB#W^e!DRRLT4JWSkpJ9>f>R>Zvp|KDX%VQwgt1^{dGG^O-vtxeoai^Oh_+eR5nS zymTt3o9TW0dGS|)?3>fIgw3kMY_1(bC=}ta%8&V2h?8mq+0Mpwa?Im0p^%qTE6@Ep z2?yc%d^mc+&js-Rk8eDRaWE#GH!!}>U~w``3a+t(g*~|(wVsWXD#SJ3W#Mj&jE#19 zHIJ+Iz({$5hctv~9I?a8blANnhpAVp(@N$>uFlaLa2I_;oNE_ z|LQ2vwYu5ri`9)UX=Mp`8faQ~03XAO3rBk#r69$*TtR@IcP0?am zIMQjl_D5&rz0S5ug0f&*)0A^H4qF{ANN0`~v}rP2O{V zJ5*}#$)BAAbsK$6tZzSV9;9~U>}uVhS*hqeo$535>lzQs>z`X59vSE`qz9eU{WM)V zNHqoF4;KpIB;lcFi{VmpL5mnf>&GfGxS_1KX}`S6LE6h6{Sur;eJYrk=T1 zY$~~!a!fcU&vdPKG;H#{@#_h9se1P_@yQYgYn3`E!IJX0Zu7E0*m*OQx8P2Ga)i=5 zw|dFfp51JH*NkM-ha2jZ``4+A?~k07Qu4S9?Xs}mm&e~3PVqMQzbM-9CbcREk zeL_}>g7I~Y!)TM!(1}wqDaCHEROX?kv@=+Q#4Ip1|0bnHXyh1 zk-neXwQh>0-6q|T)v4P@bCplY2#Ca)0Ei|qzY^;%gV*G~vouLyuYEKvTERwQ-3wY| z_C!_=0Oi}Uh64qf%>Q)i8(8RsFJA^u^7Q94X$xT!5(E<8vC<+&c19$Dg|YUOOos%T z3_Pq4YfgHh$W&Xn8Jf(%gLh+1JqwAYGqfl=3MjgYrAp&7nvCWFh2arRCIU*Oy4=ikZqQNxZh?WfU^DiC-tsF?XtEH8If{*S*m%VgTBN{R5c0a#eTR*r zzX6K*f72x7AUzZFgd@@$nK1ZH2+;de%m$M)urgebUJbyb@r6JyWq~y;cch+M&x7XMI` y%+@e68Ns$@g!T1hZC) z_Le0FJ&2}-T!NS4X-`QHo)j-01O-j3q8Lz7u%;A0XKMu)9`knI%zN|IDy&jr{ZXPp z`tOT+{gaF@pL`>$+S!^Jsvc01AkA&<5BkN&=9IjOJ>Uf#O zy^D%#Dp*+9%~KhTCbOeFm1C;y3Sp^4m99L{sPn%%3A)BeFdwxSH-vQ^o(Dqcb%h)I zHiYg(xIYnY&9;RY`aHyYvAMYSG~${W#*G4z{tEml>~GR`yR$B=as~_Lu^_Vzf_0&H zhj}lIqgP?hsd8&Xj;R1$Ucx463p81Nm04rWL8gZw89h72{a R#qC8Om;>(M1c1*)nD-*onE50`|3*LS##ZC*^N4Ly z!i+qCzJ~r8V%Bvb^vM7-YCnbE8DL!%z`2NwN;EgW%T-<>7{kkTh*{@< z2<|uGgOoC3y(@I7kV{eoGka~b#xysFk3B=?;UG(9{pNNA&*8&Yk-4uhBfTT^)Cnjy zZlz1DZNXDvC^k0*KMS_`1i1`0L;QFann<%=qvci_55QP|K+AP;JyU>OAU4FdiTbH<{7wfYB5; z%+)9>r5#q9zr#xKRd7v%sg%mT!63vW^<1g&1--I74P`p80ARI03*Nj2u8dQVa3ydZ o|1EA0C$aDkTCz0eA09>X-^F3v6I_ROy%DT^*!K=TUJhp2AB0oqs{jB1 diff --git a/main.c b/main.c index 9fa575f..2afb423 100644 --- a/main.c +++ b/main.c @@ -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;