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).
139 lines
4.7 KiB
C
139 lines
4.7 KiB
C
/*
|
|
* Host-side test harness for the stub's parsing logic.
|
|
* test 1: parse the LIVE device tree (/sys/firmware/fdt — contains the
|
|
* /firmware/coreboot node depthcharge injects at boot) and expect
|
|
* the LBIO table at 0xffed9000, size 0x380 (confirmed independently
|
|
* via `dtc -I dtb -O dts /sys/firmware/fdt` on this unit).
|
|
* test 2: synthetic LBIO table exercising find_framebuffer against the
|
|
* verified struct lb_framebuffer layout.
|
|
* Build: gcc -O2 -o host_test host_test.c && ./host_test
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "main.c"
|
|
|
|
static int fails;
|
|
|
|
static void check(const char *what, unsigned long long got, unsigned long long want)
|
|
{
|
|
printf("%-40s %s (got 0x%llx, want 0x%llx)\n",
|
|
what, got == want ? "PASS" : "FAIL", got, want);
|
|
if (got != want)
|
|
fails++;
|
|
}
|
|
|
|
static void put32(u8 *p, u32 v) { memcpy(p, &v, 4); }
|
|
static void put64(u8 *p, u64 v) { memcpy(p, &v, 8); }
|
|
|
|
int main(void)
|
|
{
|
|
/* --- test 1: live DTB ------------------------------------------------ */
|
|
FILE *f = fopen("/sys/firmware/fdt", "rb");
|
|
if (!f) {
|
|
printf("no live /sys/firmware/fdt, skipping test 1\n");
|
|
} else {
|
|
static u8 dtb[1 << 20];
|
|
size_t n = fread(dtb, 1, sizeof(dtb), f);
|
|
fclose(f);
|
|
printf("loaded live fdt: %zu bytes\n", n);
|
|
|
|
u64 addr; u32 size;
|
|
int rc = find_coreboot_reg(dtb, &addr, &size);
|
|
check("find_coreboot_reg found", (unsigned long long)(rc == 0), 1);
|
|
check("LBIO address", addr, 0xffed9000ull);
|
|
check("LBIO size", size, 0x380);
|
|
}
|
|
|
|
/* --- test 2: synthetic coreboot table -------------------------------- */
|
|
u8 table[512];
|
|
memset(table, 0, sizeof table);
|
|
memcpy(table, "LBIO", 4);
|
|
put32(table + 4, 24); /* header_bytes */
|
|
put32(table + 20, 3); /* table_entries */
|
|
|
|
u8 *rec = table + 24;
|
|
/* record 0: an unrelated tag we must skip (size 16) */
|
|
put32(rec, 1); put32(rec + 4, 16);
|
|
rec += 16;
|
|
/* record 1: LB_TAG_FRAMEBUFFER */
|
|
put32(rec, 0x12);
|
|
put32(rec + 4, 40); /* record size */
|
|
put32(rec + 8, 0x80000000); /* physical_address lo */
|
|
put32(rec + 12, 0); /* physical_address hi */
|
|
put32(rec + 16, 1920); /* x_resolution */
|
|
put32(rec + 20, 1200); /* y_resolution */
|
|
put32(rec + 24, 7680); /* bytes_per_line */
|
|
rec[28] = 32; /* bits_per_pixel */
|
|
rec[29] = 16; rec[30] = 8; /* red */
|
|
rec[31] = 8; rec[32] = 8; /* green */
|
|
rec[33] = 0; rec[34] = 8; /* blue */
|
|
|
|
struct fbinfo fb;
|
|
int rc = find_framebuffer((u64)(unsigned long)table, &fb);
|
|
check("find_framebuffer rc", (unsigned long long)(rc == 0), 1);
|
|
check("fb pa", fb.pa, 0x80000000ull);
|
|
check("fb xres", fb.xres, 1920);
|
|
check("fb yres", fb.yres, 1200);
|
|
check("fb bpl", fb.bpl, 7680);
|
|
check("fb bpp", fb.bpp, 32);
|
|
check("mkcolor white", mkcolor(&fb, 1, 1, 1), 0x00ffffffull);
|
|
check("mkcolor red", mkcolor(&fb, 1, 0, 0), 0x00ff0000ull);
|
|
check("mkcolor yellow", mkcolor(&fb, 1, 1, 0), 0x00ffff00ull);
|
|
check("mkcolor green", mkcolor(&fb, 0, 1, 0), 0x0000ff00ull);
|
|
check("mkcolor blue", mkcolor(&fb, 0, 0, 1), 0x000000ffull);
|
|
|
|
/* fill_screen: first and last visible pixel written, nothing past end */
|
|
{
|
|
static u8 fbmem[1200 * 7680 + 64];
|
|
memset(fbmem, 0xAA, sizeof fbmem);
|
|
struct fbinfo f2 = { .pa = (u64)(unsigned long)fbmem,
|
|
.xres = 1920, .yres = 1200,
|
|
.bpl = 7680, .bpp = 32,
|
|
.rpos = 16, .rsize = 8,
|
|
.gpos = 8, .gsize = 8,
|
|
.bpos = 0, .bsize = 8 };
|
|
fill_screen(&f2, 0x00ffffff);
|
|
u32 last = *(u32 *)(fbmem + 1199 * 7680 + 1919 * 4);
|
|
u32 first = *(u32 *)fbmem;
|
|
check("fill first px", first, 0x00ffffff);
|
|
check("fill last px", last, 0x00ffffff);
|
|
check("fill no overflow (past-end untouched)",
|
|
(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;
|
|
}
|