riscv64: PLIC base address DTB-discovered, QEMU-virt constant as fallback (§V.3 item 3)
plic_init() now takes boot_info->dtb (threaded through apic_init()) and tries fdt_find_node_by_compatible(dtb, "sifive,plic-1.0.0") -> fdt_find_prop_in_node(..., "reg", ...) before falling back to the QEMU-virt-specific constant it previously hardcoded unconditionally. Reuses the node-scoped DTB lookup primitive built for the aarch64 GIC base fix unchanged. s_plic_base is now a runtime uintptr_t, same shape as apic.c's s_gicd_base/s_gicc_base. This system's QEMU/UEFI riscv64 firmware does not forward a DTB to the guest (timer.c's own timebase-frequency read falls back too, confirmed in this boot's own log), so only the no-DTB fallback branch is exercised here -- the success branch (a real DTB with a matching PLIC node) stays unverified until real Milk-V Mars hardware. FABRIC-3.md's first-drafted claim that the success branch would run (based on a stale comment in plic.c's own pre-fix header) was checked against the actual log and corrected before this commit. 3-arch acceptance: amd64/aarch64 don't compile these files, so their runs are non-regression on untouched files only. riscv64's own boot log confirms the fallback path prints exactly as designed and boot reaches zuse)ok> unchanged. 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
90ee8deb6d
commit
9b6de5d6c7
@@ -160,15 +160,15 @@ void riscv64_timer_rearm(void)
|
||||
* context's threshold, and enables @c sie.SEIE. No source is enabled here;
|
||||
* that is each future consumer's own job (4.3.5c for virtio-keyboard).
|
||||
*
|
||||
* @param boot_info Kernel boot information; unused (PLIC base is a verified
|
||||
* constant, not discovered from @c boot_info->dtb -- see
|
||||
* plic.c's file header for why).
|
||||
* @param boot_info Kernel boot information; @c dtb is passed through to
|
||||
* @c plic_init() for real-hardware PLIC base discovery as
|
||||
* of the 2026-09-05 fix (FABRIC-3.md §V.3 item 3) -- see
|
||||
* plic.c's file header for the discovery mechanism.
|
||||
* @return 0 always.
|
||||
*/
|
||||
int apic_init(BootInfo *boot_info)
|
||||
{
|
||||
(void)boot_info;
|
||||
plic_init();
|
||||
plic_init(boot_info ? boot_info->dtb : (void *) 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
* architectural, not board-specific, same status as the GICD/GICC register
|
||||
* *offsets* item 0.6 cross-checked against the Linux arm-gic header.
|
||||
*
|
||||
* PLIC_BASE and PLIC_CONTEXT_S are QEMU-virt-specific and were NOT
|
||||
* hardcoded from memory: verified 2026-08-08 via
|
||||
* PLIC_BASE_QEMU_DEFAULT and PLIC_CONTEXT_S are QEMU-virt-specific and were
|
||||
* NOT hardcoded from memory: verified 2026-08-08 via
|
||||
* `qemu-system-riscv64 -machine virt,dumpdtb=...`, decoded with a script
|
||||
* using this tree's own fdt.c parsing logic (not dtc, which is not
|
||||
* installed). The real devicetree shows:
|
||||
@@ -28,34 +28,111 @@
|
||||
* Confirmed for this exact QEMU 10.2.1 build; not assumed stable across
|
||||
* versions, same caveat item 0.6 recorded for its own dumped values.
|
||||
*
|
||||
* No live in-kernel node-scoped devicetree lookup here even though
|
||||
* riscv64 DTB access does work (unlike aarch64): fdt.c's fdt_find_prop()
|
||||
* is a flat, whole-tree search by property *name*, which is exactly right
|
||||
* for a globally-unique name like "timebase-frequency" (already used by
|
||||
* timer.c) but wrong for "reg" -- that name recurs on nearly every node,
|
||||
* so a flat search would return the wrong node's base address. Finding
|
||||
* PLIC's own reg specifically needs node-scoped (by-compatible) lookup,
|
||||
* which does not exist in fdt.c and is a bigger addition than this item's
|
||||
* stated scope (PLIC programming, not a general devicetree query engine).
|
||||
* DTB-discovered base address, as of FABRIC-3.md §V.3 item 3's fix,
|
||||
* 2026-09-05: the node-scoped ("reg" is not globally unique the way
|
||||
* "timebase-frequency" is) devicetree lookup this file's own comment
|
||||
* previously flagged as "a bigger addition than this item's stated scope"
|
||||
* was subsequently built anyway -- `fdt_find_node_by_compatible()` +
|
||||
* `fdt_find_prop_in_node()` (`fdt.c`/`fdt.h`), added for the aarch64 GIC
|
||||
* base fix (FABRIC-3.md §IV.3 item 7) and reused here unchanged.
|
||||
* `plic_base_from_dtb()` below tries `"sifive,plic-1.0.0"` first; on
|
||||
* failure (no DTB, e.g. this system's QEMU/UEFI firmware -- same
|
||||
* `fdt_valid()` failure mode apic.c's GIC discovery already documents) it
|
||||
* falls back to the QEMU constant above, unchanged from this function's
|
||||
* previous unconditional behaviour. Not yet confirmed against the Milk-V
|
||||
* Mars's real DTB -- the compatible string is architectural (SiFive
|
||||
* PLIC-1.0.0 is the standard binding), not board-specific, but the actual
|
||||
* JH7110 `reg` value is unverified until real hardware arrives (2026-09-17).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "starkernel/plic.h"
|
||||
#include "starkernel/fdt.h"
|
||||
#include "console.h"
|
||||
|
||||
#define PLIC_BASE 0x0C000000UL
|
||||
#define PLIC_BASE_QEMU_DEFAULT 0x0C000000UL
|
||||
#define PLIC_CONTEXT_S 1u /* S-mode, hart 0 -- verified, see file header */
|
||||
|
||||
#define PLIC_PRIORITY(irq) (PLIC_BASE + 4u * (irq))
|
||||
#define PLIC_ENABLE(ctx) (PLIC_BASE + 0x2000u + 0x80u * (ctx))
|
||||
#define PLIC_THRESHOLD(ctx) (PLIC_BASE + 0x200000u + 0x1000u * (ctx))
|
||||
#define PLIC_CLAIM(ctx) (PLIC_BASE + 0x200004u + 0x1000u * (ctx))
|
||||
/* Runtime PLIC base address -- set once by plic_init(), defaulting to the
|
||||
* QEMU constant above until/unless plic_base_from_dtb() overrides it.
|
||||
* uintptr_t, not a #define, precisely because this is no longer a
|
||||
* compile-time constant on this path (same shape as aarch64 apic.c's
|
||||
* s_gicd_base/s_gicc_base). */
|
||||
static uintptr_t s_plic_base = PLIC_BASE_QEMU_DEFAULT;
|
||||
|
||||
#define PLIC_PRIORITY(irq) (s_plic_base + 4u * (irq))
|
||||
#define PLIC_ENABLE(ctx) (s_plic_base + 0x2000u + 0x80u * (ctx))
|
||||
#define PLIC_THRESHOLD(ctx) (s_plic_base + 0x200000u + 0x1000u * (ctx))
|
||||
#define PLIC_CLAIM(ctx) (s_plic_base + 0x200004u + 0x1000u * (ctx))
|
||||
|
||||
static inline volatile uint32_t *reg32(uintptr_t addr) {
|
||||
return (volatile uint32_t *)addr;
|
||||
}
|
||||
|
||||
int plic_init(void) {
|
||||
/**
|
||||
* @brief Read one big-endian 32-bit cell from a devicetree property blob.
|
||||
*
|
||||
* Duplicated from aarch64 apic.c's identical helper rather than shared --
|
||||
* same "a few lines is simpler than a new shared dependency" precedent
|
||||
* that file's own comment already set for this exact situation.
|
||||
*/
|
||||
static uint32_t be32_cell(const unsigned char *b) {
|
||||
return ((uint32_t) b[0] << 24) | ((uint32_t) b[1] << 16) |
|
||||
((uint32_t) b[2] << 8) | (uint32_t) b[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read one two-cell (64-bit) big-endian address from a `reg` entry.
|
||||
*
|
||||
* QEMU-virt's `plic` node's own dumped `reg = <0x0 0xc000000 0x0 0x600000>`
|
||||
* (see file header) is address(2 cells) + size(2 cells) = 4 cells total,
|
||||
* same shape as aarch64's `gicv2` node -- reads only the two address
|
||||
* cells, ignoring the size pair that follows.
|
||||
*/
|
||||
static uint64_t be64_addr_cell_pair(const unsigned char *b) {
|
||||
return ((uint64_t) be32_cell(b) << 32) | (uint64_t) be32_cell(b + 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to discover the real PLIC base address from the DTB.
|
||||
*
|
||||
* Looks up the `"sifive,plic-1.0.0"` node and reads its `reg` property's
|
||||
* first (address) cell pair. Returns 0 (leaving @p base_out untouched) if
|
||||
* @p dtb is not a valid FDT, no matching node exists, or `reg` is shorter
|
||||
* than one address-cell pair -- the caller keeps the QEMU constant default
|
||||
* in that case, exactly as before this function existed.
|
||||
*
|
||||
* @param dtb Candidate devicetree blob (@c BootInfo->dtb); NULL-safe.
|
||||
* @param base_out Receives the PLIC base address on success.
|
||||
* @return 1 on success, 0 if discovery failed for any reason.
|
||||
*/
|
||||
static int plic_base_from_dtb(const void *dtb, uintptr_t *base_out) {
|
||||
const void *node;
|
||||
const unsigned char *reg;
|
||||
uint32_t len;
|
||||
|
||||
if (!fdt_valid(dtb)) return 0;
|
||||
|
||||
node = fdt_find_node_by_compatible(dtb, "sifive,plic-1.0.0");
|
||||
if (!node) return 0;
|
||||
|
||||
reg = (const unsigned char *) fdt_find_prop_in_node(dtb, node, "reg", &len);
|
||||
if (!reg || len < 8u) return 0;
|
||||
|
||||
*base_out = (uintptr_t) be64_addr_cell_pair(reg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int plic_init(const void *dtb) {
|
||||
if (plic_base_from_dtb(dtb, &s_plic_base)) {
|
||||
console_puts("PLIC: base discovered from DTB (0x");
|
||||
for (int s = 60; s >= 0; s -= 4)
|
||||
console_putc("0123456789abcdef"[(s_plic_base >> s) & 0xF]);
|
||||
console_println(")");
|
||||
} else {
|
||||
console_println("PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000)");
|
||||
}
|
||||
|
||||
/* Threshold 0 = maximally permissive. Safe: nothing is enabled at any
|
||||
* source yet, so nothing can actually reach claim() until a caller
|
||||
* explicitly enables it. */
|
||||
@@ -63,7 +140,7 @@ int plic_init(void) {
|
||||
|
||||
__asm__ volatile ("csrs sie, %0" :: "r"(1UL << 9) : "memory"); /* SEIE */
|
||||
|
||||
console_println("PLIC: init (base=0x0c000000, S-mode context 1, threshold=0)");
|
||||
console_println("PLIC: init (S-mode context 1, threshold=0)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user