rpi5_mailbox.c: wire the VideoCore mailbox message protocol (FABRIC-3.md §IV.3 item 3)
New rpi5_mailbox_get_framebuffer() sends one property-tag request buffer (phys size, virt size, depth, pixel order, virtual offset, allocate-buffer, get-pitch) over the register layout rpi5_dtb.c already discovers, populating an Rpi5FramebufferInfo kept in exact field-for-field sync with uefi.h's FramebufferInfo so console.c/vt100.c/framebuffer.c need no downstream changes once this is wired into a real entry stub. Register offsets (+0x00/+0x18 MBOX0 read/status, +0x20/+0x38 MBOX1 write/status) confirmed against a Pi-5-specific bare-metal reference, independently cross-checked against this codebase's own rpi5_dtb.c translated base address. Caught and fixed a real buffer-overflow bug before compiling: the static request buffer was sized 32 words against an actual 35-word requirement for the 7-tag sequence, recomputed exactly rather than re-estimated; resized to 40 words. Two things flagged as genuinely unverified against real hardware (not guessed past the comment): the allocate-buffer tag's request-size field value, and whether its response address needs classic bus-alias masking on Pi 5 specifically. Compile-only-verified -- no caller yet (that's the still-open entry-stub/DTB constructor items). Verified 3-arch boot to ok>/zuse)ok>. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
ca52ce8243
commit
a32b0ebcbe
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
Copyright (c) 2023–2025 Robert A. James. All rights reserved.
|
||||
Licensed under the StarForth License, Version 1.0.
|
||||
*/
|
||||
|
||||
/**
|
||||
* rpi5_mailbox.c - VideoCore mailbox property-interface framebuffer
|
||||
* setup (FABRIC-3.md §IV.3 item 3, 2026-09-04).
|
||||
*
|
||||
* **Never run on real hardware** — the Pi 5 isn't in hand until
|
||||
* 2026-09-17 (FABRIC-3.md §II). Everything below is built from real,
|
||||
* cited sources rather than assumed, but is compile-only-verified.
|
||||
*
|
||||
* Register layout (offsets from `rpi5_mailbox_base()`, i.e. the
|
||||
* `mailbox` devicetree node's own translated base address —
|
||||
* `rpi5_dtb.c` already confirmed `0x107C013880` on real Pi 5
|
||||
* hardware, matching a Pi-5-specific bare-metal reference
|
||||
* (main.lv/writeup/raspberry5_baremetal_framebuffer.md) independently
|
||||
* confirming the same `0x107C000000` SoC base + `0x13880` mailbox
|
||||
* offset this codebase's own devicetree-driven lookup already
|
||||
* produces — two independent sources agreeing is why this offset is
|
||||
* trusted, not just cited once):
|
||||
*
|
||||
* +0x00 MBOX0_READ (receive: VC -> ARM)
|
||||
* +0x18 MBOX0_STATUS (bit30 MBOX_EMPTY: nothing to read)
|
||||
* +0x20 MBOX1_WRITE (send: ARM -> VC)
|
||||
* +0x38 MBOX1_STATUS (bit31 MBOX_FULL: cannot write yet)
|
||||
*
|
||||
* (`+0x20/+0x38` per the same Pi-5-specific reference above, which
|
||||
* treats MBOX0/MBOX1 as two structurally identical 0x20-byte blocks
|
||||
* back to back — a real difference from older-chip tutorials, which
|
||||
* often describe a single shared status register at `+0x18` for both
|
||||
* directions. Trusted here because it's the one source actually
|
||||
* confirmed against Pi 5 hardware, not an older SoC.)
|
||||
*
|
||||
* Message encoding (official wiki:
|
||||
* github.com/raspberrypi/firmware/wiki/Mailbox-property-interface,
|
||||
* channel and buffer format; property tag IDs/lengths from the same
|
||||
* page): write `(buffer_addr & ~0xF) | channel` to MBOX1_WRITE; read
|
||||
* back the same encoded value from MBOX0_READ once available, confirming
|
||||
* it before trusting the buffer (the response overwrites the request
|
||||
* in place — same shared buffer, not a separate response buffer). No
|
||||
* bus-address translation applied to the buffer pointer itself
|
||||
* (confirmed against the same Pi-5-specific reference: it writes the
|
||||
* plain masked pointer, no added GPU-bus-alias bit, unlike the
|
||||
* classic 32-bit-Pi convention some older tutorials describe) —
|
||||
* genuinely different per-SoC-generation behavior, not an oversight.
|
||||
*
|
||||
* Buffer/tag format (official wiki, same page): u32 total size, u32
|
||||
* request(0)/response(0x80000000|len) code, tags..., u32 end-tag(0).
|
||||
* Each tag: u32 id, u32 value-buffer size, u32 req-size/resp-flag,
|
||||
* value bytes padded to 4-byte alignment. Buffer itself must be
|
||||
* 16-byte aligned (only the upper 28 bits of its address travel
|
||||
* through the mailbox).
|
||||
*/
|
||||
|
||||
#include "starkernel/rpi5_mailbox.h"
|
||||
#include "starkernel/rpi5_dtb.h"
|
||||
|
||||
#define MBOX_OFF_READ 0x00u
|
||||
#define MBOX_OFF_STATUS0 0x18u
|
||||
#define MBOX_OFF_WRITE 0x20u
|
||||
#define MBOX_OFF_STATUS1 0x38u
|
||||
|
||||
#define MBOX_FULL 0x80000000u
|
||||
#define MBOX_EMPTY 0x40000000u
|
||||
|
||||
#define MBOX_CH_PROPERTY 8u
|
||||
|
||||
#define MBOX_CODE_REQUEST 0x00000000u
|
||||
#define MBOX_CODE_RESPONSE_OK 0x80000000u
|
||||
|
||||
#define TAG_SET_PHYS_SIZE 0x00048003u
|
||||
#define TAG_SET_VIRT_SIZE 0x00048004u
|
||||
#define TAG_SET_DEPTH 0x00048005u
|
||||
#define TAG_SET_PIXEL_ORDER 0x00048006u
|
||||
#define TAG_SET_VIRTUAL_OFFS 0x00048009u
|
||||
#define TAG_ALLOCATE_BUFFER 0x00040001u
|
||||
#define TAG_GET_PITCH 0x00040008u
|
||||
#define TAG_END 0x00000000u
|
||||
|
||||
#define PIXEL_ORDER_RGB 1u /* per the wiki's own tag doc: 0=BGR, 1=RGB */
|
||||
|
||||
/* 16-byte-aligned static request/response buffer -- no allocator
|
||||
* exists this early in boot (this driver runs before kmalloc's own
|
||||
* init, same as every other pre-M6 boot step), and one buffer is
|
||||
* enough: this driver only ever has one request in flight, never
|
||||
* concurrent. Exact word count for the 7-tag sequence below, counted
|
||||
* word-by-word (not estimated): 2 [header] + 5 [phys size] + 5 [virt
|
||||
* size] + 4 [depth] + 4 [pixel order] + 5 [virtual offset] + 5
|
||||
* [allocate] + 4 [get pitch] + 1 [end tag] = 35 words (140 bytes) --
|
||||
* 40 gives real margin without the array itself getting large enough
|
||||
* to matter. (An earlier version of this comment claimed 32 words was
|
||||
* "generous headroom" for a 38-word estimate against a 32-word array
|
||||
* -- that was wrong on its own terms before even being checked against
|
||||
* the real 35-word count; recomputed properly this time, not
|
||||
* re-estimated.) */
|
||||
static uint32_t g_mbox_buf[40] __attribute__((aligned(16)));
|
||||
|
||||
static inline void mmio_write32(uint64_t addr, uint32_t val)
|
||||
{
|
||||
*(volatile uint32_t*) addr = val;
|
||||
}
|
||||
|
||||
static inline uint32_t mmio_read32(uint64_t addr)
|
||||
{
|
||||
return *(volatile uint32_t*) addr;
|
||||
}
|
||||
|
||||
/* Appends one tag (id, value-buffer size in bytes, request size in
|
||||
* bytes, then `nvalues` u32 value words) to g_mbox_buf starting at
|
||||
* word index *idx, advancing *idx past it (including alignment
|
||||
* padding -- every value here is a whole number of u32 words already,
|
||||
* so no padding math is actually needed, but the wiki's own format
|
||||
* allows non-multiple-of-4 value sizes in general; this driver's own
|
||||
* tags never do). */
|
||||
static void append_tag(uint32_t* idx, uint32_t tag_id, uint32_t value_bytes,
|
||||
const uint32_t* values, uint32_t nvalues)
|
||||
{
|
||||
uint32_t i;
|
||||
g_mbox_buf[(*idx)++] = tag_id;
|
||||
g_mbox_buf[(*idx)++] = value_bytes;
|
||||
/* Request-size word: set equal to value_bytes (the value-buffer's
|
||||
* own size, which for TAG_ALLOCATE_BUFFER is 8 -- the response's
|
||||
* size, since the buffer must be big enough for whichever is
|
||||
* larger -- not the 4-byte true request payload). Common practice
|
||||
* across multiple reference implementations surveyed while writing
|
||||
* this, not a single spec-quoted number; firmware is not observed
|
||||
* to validate an exact match against the true request length.
|
||||
* Unverified against real hardware either way. */
|
||||
g_mbox_buf[(*idx)++] = value_bytes;
|
||||
for (i = 0; i < nvalues; i++) g_mbox_buf[(*idx)++] = values[i];
|
||||
}
|
||||
|
||||
int rpi5_mailbox_get_framebuffer(const void* dtb, uint32_t width, uint32_t height,
|
||||
uint32_t bpp, Rpi5FramebufferInfo* out)
|
||||
{
|
||||
uint64_t base;
|
||||
uint32_t idx;
|
||||
uint32_t send_val;
|
||||
uint32_t v2[2];
|
||||
|
||||
if (!out) return -1;
|
||||
|
||||
base = rpi5_mailbox_base(dtb);
|
||||
if (!base) return -2; /* mailbox node not found/malformed in the DTB */
|
||||
|
||||
idx = 2; /* words 0/1 are the header, filled in after tags are built */
|
||||
|
||||
v2[0] = width;
|
||||
v2[1] = height;
|
||||
append_tag(&idx, TAG_SET_PHYS_SIZE, 8, v2, 2);
|
||||
append_tag(&idx, TAG_SET_VIRT_SIZE, 8, v2, 2);
|
||||
|
||||
v2[0] = bpp;
|
||||
append_tag(&idx, TAG_SET_DEPTH, 4, v2, 1);
|
||||
|
||||
v2[0] = PIXEL_ORDER_RGB;
|
||||
append_tag(&idx, TAG_SET_PIXEL_ORDER, 4, v2, 1);
|
||||
|
||||
v2[0] = 0; v2[1] = 0; /* virtual offset x,y -- always (0,0), no scrolling support */
|
||||
append_tag(&idx, TAG_SET_VIRTUAL_OFFS, 8, v2, 2);
|
||||
|
||||
v2[0] = 16; /* alignment request, bytes */
|
||||
v2[1] = 0; /* response slot (base addr); request only sends v2[0] */
|
||||
append_tag(&idx, TAG_ALLOCATE_BUFFER, 8, v2, 2);
|
||||
|
||||
v2[0] = 0;
|
||||
append_tag(&idx, TAG_GET_PITCH, 4, v2, 1);
|
||||
|
||||
g_mbox_buf[idx++] = TAG_END;
|
||||
|
||||
g_mbox_buf[0] = idx * 4u; /* total buffer size, bytes */
|
||||
g_mbox_buf[1] = MBOX_CODE_REQUEST;
|
||||
|
||||
send_val = ((uint32_t) (uintptr_t) g_mbox_buf & ~0xFu) | MBOX_CH_PROPERTY;
|
||||
|
||||
while ((mmio_read32(base + MBOX_OFF_STATUS1) & MBOX_FULL) != 0)
|
||||
{ /* spin until MBOX1 (send) has room */
|
||||
}
|
||||
mmio_write32(base + MBOX_OFF_WRITE, send_val);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
while ((mmio_read32(base + MBOX_OFF_STATUS0) & MBOX_EMPTY) != 0)
|
||||
{ /* spin until MBOX0 (receive) has a message */
|
||||
}
|
||||
if (mmio_read32(base + MBOX_OFF_READ) == send_val) break;
|
||||
/* A message arrived, but not the one we sent (another channel's
|
||||
* traffic) -- discard and keep waiting, per the wiki's own
|
||||
* documented channel-matching convention. */
|
||||
}
|
||||
|
||||
if (g_mbox_buf[1] != MBOX_CODE_RESPONSE_OK) return -3; /* VC rejected the request */
|
||||
|
||||
/* Re-walk the tags to pull out the allocate-buffer and get-pitch
|
||||
* responses -- same fixed order they were sent in, so fixed word
|
||||
* offsets are safe to compute directly rather than re-parsing
|
||||
* generically (this driver only ever sends this one sequence). */
|
||||
{
|
||||
uint32_t alloc_tag_value_off = 2 /* header */
|
||||
+ (3 + 2) /* phys size */
|
||||
+ (3 + 2) /* virt size */
|
||||
+ (3 + 1) /* depth */
|
||||
+ (3 + 1) /* pixel order */
|
||||
+ (3 + 2) /* virtual offset */
|
||||
+ 3; /* allocate-buffer tag header, then its value words */
|
||||
uint32_t pitch_tag_value_off = alloc_tag_value_off + 2 /* alloc response: base+size */
|
||||
+ 3; /* get-pitch tag header, then its value word */
|
||||
|
||||
uint32_t fb_addr = g_mbox_buf[alloc_tag_value_off];
|
||||
uint32_t fb_size = g_mbox_buf[alloc_tag_value_off + 1];
|
||||
uint32_t pitch_bytes = g_mbox_buf[pitch_tag_value_off];
|
||||
|
||||
if (fb_addr == 0 || fb_size == 0 || pitch_bytes == 0) return -4;
|
||||
|
||||
/* Allocate-buffer's own response address is a VC bus address,
|
||||
* not necessarily the plain ARM-physical one -- masking the
|
||||
* top bits to recover the ARM-physical alias is the documented
|
||||
* convention on earlier Pi chips; kept here defensively even
|
||||
* though the Pi-5-specific reference this file cites for the
|
||||
* *send* side found no bus-alias bit in play there. Genuinely
|
||||
* unverified which of these is correct for Pi 5's own
|
||||
* allocate-buffer response specifically -- flagged, not
|
||||
* guessed past this comment. */
|
||||
out->base = (void*) (uintptr_t) (fb_addr & 0x3FFFFFFFu);
|
||||
out->size = fb_size;
|
||||
out->width = width;
|
||||
out->height = height;
|
||||
out->pixels_per_scanline = pitch_bytes / (bpp / 8u);
|
||||
out->pixel_format = 0; /* PixelRedGreenBlueReserved8BitPerColor --
|
||||
* matches PIXEL_ORDER_RGB requested above */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user