riscv64: integrate minimal flattened devicetree reader and switch timer to time CSR

Punch list §25 item 0.3 NOT complete.

- Added `starkernel/fdt.h` and `fdt.c` for minimal read-only devicetree parsing: sufficient for boot-time lookups such as `timebase-frequency`.
- Bootloader now captures the devicetree blob (DTB) from `EFI_DTB_TABLE_GUID` into `BootInfo::dtb`.
- RISC-V timer subsystem now uses the `time` CSR as the primary timestamp source, abandoning the hardcoded `cycle` frequency assumption.
- Timer rate is read from `timebase-frequency` in the DTB when accessible; otherwise, a fallback value is used with a RELATIVE trust level.
- Integrated the SBI TIME extension for one-shot timer deadlines, ensuring re-arming occurs after each tick to avoid missing heartbeats.

Verified: riscv64 builds clean, boots to the ok> prompt with no regression; `riscv64/timer.c` reports accurate frequencies on QEMU's default firmware.
Signed-off-by: Robert Allan James <robert.allan.james@gmail.com>
This commit is contained in:
Robert Allan James
2026-08-03 13:10:17 -04:00
parent f3821ed686
commit accd79fc70
9 changed files with 561 additions and 67 deletions
+14 -8
View File
@@ -15,19 +15,25 @@
* 16: memory_map_descriptor_size u64 (8)
* 24: runtime_services ptr (8)
* 32: acpi_table ptr (8)
* 40: framebuffer FramebufferInfo (32)
* 40: dtb ptr (8)
* 48: framebuffer FramebufferInfo (32)
* .base ptr (8)
* .size u64 (8)
* .width u32 (4)
* .height u32 (4)
* .pixels_per_scanline u32 (4)
* .pixel_format u32 (4)
* 72: uefi_boot_services_exited u8 (1)
* 73: [7 bytes padding]
* 80: kernel_stack_base ptr (8) ← BOOT_INFO_KERNEL_STACK_BASE_OFFSET
* 88: kernel_stack_size u64 (8) ← BOOT_INFO_KERNEL_STACK_SIZE_OFFSET
* 96: args KernelArgs
* 80: uefi_boot_services_exited u8 (1)
* 81: [7 bytes padding]
* 88: kernel_stack_base ptr (8) ← BOOT_INFO_KERNEL_STACK_BASE_OFFSET
* 96: kernel_stack_size u64 (8) ← BOOT_INFO_KERNEL_STACK_SIZE_OFFSET
* 104: args KernelArgs
*
* 2026-08-03, punch-list item 0.3: `dtb` inserted at 40, shifting everything
* below it by 8. The _Static_asserts in uefi_loader.c caught the stale
* constants immediately — that is what they are for; do not silence them by
* moving a field, fix the offsets.
*/
#define BOOT_INFO_KERNEL_STACK_BASE_OFFSET 80
#define BOOT_INFO_KERNEL_STACK_SIZE_OFFSET 88
#define BOOT_INFO_KERNEL_STACK_BASE_OFFSET 88
#define BOOT_INFO_KERNEL_STACK_SIZE_OFFSET 96
+65
View File
@@ -0,0 +1,65 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 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. 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); item 0.6 will need node-scoped `reg`
* lookups for the aarch64 GIC and may extend this.
*/
#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);
#endif /* STARKERNEL_FDT_H */
+13
View File
@@ -383,6 +383,14 @@ typedef struct {
static const EFI_GUID EFI_ACPI_20_TABLE_GUID = {0x8868e871,0xe4f1,0x11d3,{0xbc,0x22,0x00,0x80,0xc7,0x3c,0x88,0x81}};
static const EFI_GUID EFI_ACPI_TABLE_GUID = {0xeb9d2d30,0x2d88,0x11d3,{0x9a,0x16,0x00,0x90,0x27,0x3f,0xc1,0x4d}};
/* Devicetree Blob GUID (UEFI 2.10 §4.6, "Devicetree Tables").
* On QEMU virt for riscv64 and aarch64 the firmware publishes the FDT here;
* it is the only route to timebase-frequency (riscv64, item 0.3) and to the
* GIC base addresses and timer PPI (aarch64, item 0.6). */
static const EFI_GUID EFI_DTB_TABLE_GUID = {
0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}
};
/* Graphics Output Protocol */
typedef enum {
PixelRedGreenBlueReserved8BitPerColor,
@@ -627,6 +635,11 @@ typedef struct {
UINTN memory_map_descriptor_size;
EFI_RUNTIME_SERVICES *runtime_services;
void *acpi_table;
/* Devicetree blob, located by EFI_DTB_TABLE_GUID in the configuration
* table; NULL when the firmware publishes none (amd64 typically, and any
* platform that is ACPI-only). Consumers must handle NULL rather than
* assume presence. */
void* dtb;
FramebufferInfo framebuffer;
UINT8 uefi_boot_services_exited;