rpi5_native_boot() populates the existing BootInfo struct from the devicetree instead of UEFI protocols, then calls the existing, unmodified kernel_main() -- the crux of why most of M1-M9 stays shared between the UEFI and native boot paths. native_rpi5_entry.S now tail-calls into it instead of halting. memory_map is built from /memory's own reg, honoring the *root* node's #address-cells/#size-cells (confirmed against bcm2712.dtsi's actual root node -- <2>/<2> -- not assumed; a wrong cell width here would compile and boot clean in QEMU while silently corrupting the real memory map on real silicon). Required a new fdt_find_node_by_device_type() since /memory is identified by device_type = "memory" per DT spec, not compatible. args comes from /chosen's bootargs fed into the existing cmdline_parse_ascii() (confirmed pure C99 with no UEFI coupling before reusing it). framebuffer comes from the already-built rpi5_mailbox_get_framebuffer() at a fixed 1920x1080x32 default -- no EDID query exists in this codebase, flagged rather than guessed past. Deliberately scoped out, not silently skipped: /reserved-memory is not parsed. Carving reserved sub-ranges out of /memory's span needs interval-splitting logic that would be written blind against hardware not yet in hand -- exactly the kind of code that hides a bug until real silicon. pmm.c's Pass 3 only ever clears pages this file lists as EfiConventionalMemory, so the gap is "less usable RAM than optimal," never "reserved RAM wrongly marked free." Left as its own future item. Also fixes a real link failure this work surfaced: boot/cmdline.c was only in LOADER_SRCS_BASE (the .efi target), not KERNEL_SRCS_BASE (the separate .elf target arch/aarch64/*.c also wildcards into) -- added it there too. Compile-only-verified; nothing in the existing UEFI/QEMU path calls rpi5_native_boot(), so this cannot be exercised until real hardware. 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
130 lines
5.7 KiB
C
130 lines
5.7 KiB
C
/*
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
Copyright (c) 2023–2025 Robert A. James. All rights reserved.
|
||
Licensed under the StarForth License, Version 1.0.
|
||
*/
|
||
|
||
/**
|
||
* fdt.h - Minimal flattened-devicetree reader
|
||
*
|
||
* Just enough of the Devicetree Specification v0.4 §5 to pull values out of
|
||
* the blob the UEFI firmware publishes under EFI_DTB_TABLE_GUID, or that a
|
||
* native (non-UEFI) boot entry passes directly. Read-only, no allocation, no
|
||
* tree construction — it walks the structure block each call, which is fine
|
||
* for the handful of boot-time lookups the kernel needs.
|
||
*
|
||
* Deliberately not a general devicetree library. Added for punch-list item
|
||
* 0.3 (riscv64 timebase-frequency); extended (FABRIC-3.md §IV.3/§V.3,
|
||
* 2026-09-04) with node-scoped lookup, for exactly the case this header
|
||
* originally flagged as a future need (item 0.6's aarch64 GIC) plus its
|
||
* real, concrete consumers as of this pass: the Raspberry Pi 5's UART/
|
||
* mailbox register addresses (native boot, no ACPI) and the Milk-V Mars's
|
||
* real PLIC base address (currently hardcoded to QEMU-virt's own value,
|
||
* `arch/riscv64/plic.c`'s own doc comment already warned this isn't
|
||
* assumed stable across configurations).
|
||
*/
|
||
|
||
#ifndef STARKERNEL_FDT_H
|
||
#define STARKERNEL_FDT_H
|
||
|
||
#include <stdint.h>
|
||
|
||
/**
|
||
* @brief Test whether @p fdt points at a valid flattened devicetree.
|
||
*
|
||
* Checks the 0xd00dfeed magic and that the structure and strings blocks lie
|
||
* inside totalsize. Does not validate the token stream.
|
||
*
|
||
* @param fdt Candidate blob; NULL is safe and returns 0.
|
||
* @return 1 if the header is usable, 0 otherwise.
|
||
*/
|
||
int fdt_valid(const void* fdt);
|
||
|
||
/**
|
||
* @brief Find the first property with @p name anywhere in the tree.
|
||
*
|
||
* Scans the structure block in document order and returns the first match
|
||
* regardless of which node it belongs to. That is sufficient for properties
|
||
* which are uniform across a machine (timebase-frequency being the case this
|
||
* was written for) and is *not* sufficient for anything node-scoped.
|
||
*
|
||
* @param fdt Blob, already checked with @c fdt_valid().
|
||
* @param name Property name, NUL-terminated.
|
||
* @param len_out Receives the property length in bytes; may be NULL.
|
||
* @return Pointer to the property value inside @p fdt, or NULL if not found.
|
||
* The value is big-endian as stored in the blob.
|
||
*/
|
||
const void* fdt_find_prop(const void* fdt, const char* name, uint32_t* len_out);
|
||
|
||
/**
|
||
* @brief Read a single-cell (32-bit) property by name.
|
||
*
|
||
* Convenience over @c fdt_find_prop() that also handles the big-endian
|
||
* conversion. Fails if the property is absent or not exactly 4 bytes.
|
||
*
|
||
* @param fdt Blob, already checked with @c fdt_valid().
|
||
* @param name Property name, NUL-terminated.
|
||
* @param out Receives the host-order value on success; untouched on failure.
|
||
* @return 1 on success, 0 on failure.
|
||
*/
|
||
int fdt_prop_u32(const void* fdt, const char* name, uint32_t* out);
|
||
|
||
/**
|
||
* @brief Find the first node whose "compatible" property matches @p compatible.
|
||
*
|
||
* "compatible" is a NUL-separated list of strings (DT spec §2.3.1) — matches
|
||
* if @p compatible equals any one entry in the list, not just the whole
|
||
* property verbatim. Scans the whole tree in document order; the first
|
||
* matching node wins if more than one exists.
|
||
*
|
||
* @param fdt Blob, already checked with @c fdt_valid().
|
||
* @param compatible Compatible string to match, NUL-terminated.
|
||
* @return An opaque handle to the matched node, for use with
|
||
* @c fdt_find_prop_in_node() only (not a raw offset or a pointer
|
||
* to anything else meaningful) — or NULL if no node matches.
|
||
*/
|
||
const void* fdt_find_node_by_compatible(const void* fdt, const char* compatible);
|
||
|
||
/**
|
||
* @brief Find the first node whose "device_type" property equals @p type.
|
||
*
|
||
* Some standard nodes (`/memory` per DT spec §3.4) are identified by
|
||
* `device_type`, not `compatible` — unlike `compatible`, `device_type` is a
|
||
* single NUL-terminated string, not a list, so this matches the whole
|
||
* property value rather than scanning entries within it. Scans the whole
|
||
* tree in document order; the first matching node wins if more than one
|
||
* exists.
|
||
*
|
||
* @param fdt Blob, already checked with @c fdt_valid().
|
||
* @param type device_type value to match, NUL-terminated.
|
||
* @return An opaque handle to the matched node, for use with
|
||
* @c fdt_find_prop_in_node() only — or NULL if no node matches.
|
||
*/
|
||
const void* fdt_find_node_by_device_type(const void* fdt, const char* type);
|
||
|
||
/**
|
||
* @brief Find a property by name, scoped to one node.
|
||
*
|
||
* Like @c fdt_find_prop(), but scans only @p node's own direct properties
|
||
* (as returned by @c fdt_find_node_by_compatible()) — stops at the first
|
||
* child node or the end of @p node's property list, never descends into
|
||
* children, never continues into a sibling. This is the difference that
|
||
* matters for a property name like "reg", which is not unique across the
|
||
* tree the way "timebase-frequency" (the whole reason @c fdt_find_prop()
|
||
* was originally sufficient) happens to be.
|
||
*
|
||
* @param fdt Blob, already checked with @c fdt_valid().
|
||
* @param node Handle from @c fdt_find_node_by_compatible(); NULL is
|
||
* safe and returns NULL (propagates a failed node lookup
|
||
* without a separate caller-side check).
|
||
* @param name Property name, NUL-terminated.
|
||
* @param len_out Receives the property length in bytes; may be NULL.
|
||
* @return Pointer to the property value inside @p fdt, or NULL if not
|
||
* found (or if @p node is NULL). The value is big-endian as
|
||
* stored in the blob.
|
||
*/
|
||
const void* fdt_find_prop_in_node(const void* fdt, const void* node,
|
||
const char* name, uint32_t* len_out);
|
||
|
||
#endif /* STARKERNEL_FDT_H */
|