fix(raspi): the Pi 5's native path turns the MMU and caches on before any C; bounded mailbox; framebuffer address from dma-ranges

boards/BRINGUP.md R2/R3, FABRIC-3 SXXXIX.  The entry stub cleans the cache
over the image, builds an identity map (the image's GiB Normal, the rest
Device) and enables MMU and caches at EL2 (or EL1); rpi5_mmu.c makes the
devicetree's RAM Normal and the framebuffer uncached.  Mailbox waits give
up after one second; the buffer is cleaned around the exchange; the
framebuffer address goes through dma-ranges, not a Pi 4 mask.

New rpi5-rehearsal target runs the native image on QEMU virt at EL2.  It
found three more that would each have stopped the board: the PL011 shift
(mine, c9519173; the host test asserted it), the GIC address not carried
through /soc's ranges (new fdt_reg_address(), checked against the real
Pi 5 DTB), and no timer IRQ at EL2 (HCR_EL2.IMO/FMO/AMO).

Rehearsal: POST 1052 passed, same parity hash as UEFI.  Standard acceptance
on three ISAs: POST 1052, hash 0x82f4ae7cb2e2d424.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-14 12:58:35 -04:00
co-authored by Claude Opus 5.5
parent 4da370f492
commit 0e2c60dac3
23 changed files with 36835 additions and 85 deletions
+94
View File
@@ -654,3 +654,97 @@ const void* fdt_find_prop_in_node(const void* fdt, const void* node,
return (void*)0;
}
/* ---- a node's address, carried through its buses (2026-10-14) ---- */
#define FDT_PATH_MAX 12u
static uint32_t node_cells(const void* fdt, const void* node, const char* name, uint32_t otherwise)
{
uint32_t len;
const void* v = fdt_find_prop_in_node(fdt, node, name, &len);
return (v && len == 4) ? be32(v) : otherwise;
}
static uint64_t read_cells_at(const unsigned char* p, uint32_t n)
{
return n == 2 ? ((uint64_t)be32(p) << 32) | be32(p + 4) : (uint64_t)be32(p);
}
static int node_enabled(const void* fdt, const void* node)
{
uint32_t len;
const char* v = (const char*)fdt_find_prop_in_node(fdt, node, "status", &len);
return !v || compat_list_contains(v, len, "okay") || compat_list_contains(v, len, "ok");
}
/* Depth first: the path from the root to the first enabled node
* compatible with `want` -- path[0] the root, path[depth-1] the node. */
static uint32_t path_to(const void* fdt, const void* node, const char* want,
const void** path, uint32_t depth)
{
const void* child = (const void*)0;
uint32_t len;
const char* c;
if (depth >= FDT_PATH_MAX) return 0;
path[depth] = node;
c = (const char*)fdt_find_prop_in_node(fdt, node, "compatible", &len);
if (c && compat_list_contains(c, len, want) && node_enabled(fdt, node)) return depth + 1;
while ((child = fdt_next_child_node(fdt, node, child)) != (const void*)0)
{
uint32_t d = path_to(fdt, child, want, path, depth + 1);
if (d) return d;
}
return 0;
}
int fdt_reg_address(const void* fdt, const char* compatible, uint32_t index, uint64_t* out)
{
const void* path[FDT_PATH_MAX];
const unsigned char* reg;
const void* root;
uint32_t depth, len, ac, sc, i;
uint64_t addr;
if (!out || !compatible || !fdt_complete(fdt)) return 0;
root = fdt_root_node(fdt);
if (!root) return 0;
depth = path_to(fdt, root, compatible, path, 0);
if (depth < 2) return 0;
/* entry `index` of its reg, in its parent's cells */
ac = node_cells(fdt, path[depth - 2], "#address-cells", 2);
sc = node_cells(fdt, path[depth - 2], "#size-cells", 1);
if (ac < 1 || ac > 2 || sc > 2) return 0;
reg = (const unsigned char*)fdt_find_prop_in_node(fdt, path[depth - 1], "reg", &len);
if (!reg || len < 4u * (ac + sc) * (index + 1u)) return 0;
addr = read_cells_at(reg + 4u * (ac + sc) * index, ac);
/* up through each bus to the CPU's addresses: an empty ranges is the
* same addresses above, no ranges at all is no way up */
for (i = depth - 2; i >= 1; i--)
{
uint32_t cac = node_cells(fdt, path[i], "#address-cells", 2);
uint32_t csc = node_cells(fdt, path[i], "#size-cells", 1);
uint32_t pac = node_cells(fdt, path[i - 1], "#address-cells", 2);
const unsigned char* r = (const unsigned char*)fdt_find_prop_in_node(fdt, path[i], "ranges", &len);
uint32_t entry, at;
int carried = 0;
if (!r) return 0;
if (len == 0) continue;
if (cac < 1 || cac > 2 || pac < 1 || pac > 2 || csc < 1 || csc > 2) return 0;
entry = 4u * (cac + pac + csc);
for (at = 0; at + entry <= len; at += entry)
{
uint64_t from = read_cells_at(r + at, cac);
uint64_t to = read_cells_at(r + at + 4u * cac, pac);
uint64_t size = read_cells_at(r + at + 4u * (cac + pac), csc);
if (addr >= from && addr - from < size) { addr = to + (addr - from); carried = 1; break; }
}
if (!carried) return 0;
}
*out = addr;
return 1;
}
+5 -1
View File
@@ -146,7 +146,11 @@ int fdt_console_uart(const void* fdt, fdt_uart_t* out)
if (!v) return 0;
if (compatible_has(v, len, "arm,pl011") || compatible_has(v, len, "arm,pl011-axi"))
{
out->reg_shift = 0;
/* A PL011's registers are 32-bit words: register n is at n << 2.
* (0 until 2026-10-14, which put the flag register at +6 instead
* of +0x18 -- an alignment fault on Device memory, found
* rehearsing the Pi 5's native path; FABRIC-3.md SXXXIX.) */
out->reg_shift = 2;
out->io_width = 4;
out->base = addr;
out->kind = FDT_UART_PL011;