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
+12 -5
View File
@@ -17,6 +17,12 @@ volatile const char *g_sk_fault_word = (void *)0;
extern void riscv64_install_vectors(void);
/* Defined in arch/riscv64/apic.c. Declared here rather than in the shared
* starkernel/apic.h because re-arming is specific to the one-shot SBI timer
* and has no meaning for the amd64 periodic APIC timer. Same extern-in-place
* convention as riscv64_install_vectors above. */
extern void riscv64_timer_rearm(void);
/* scause cause codes for supervisor-mode interrupts (RISC-V Privileged Spec
* §4.1.9, Table "Supervisor cause register values"). Only the timer is used;
* software (1) and external (9) interrupts are not enabled. */
@@ -30,11 +36,11 @@ extern void riscv64_install_vectors(void);
* Unlike @c riscv64_exception_handler() this **returns** — the trap entry
* restores the caller-saved register set and issues @c SRET.
*
* Supervisor timer (cause 5) is routed to @c heartbeat_tick(). No timer is
* armed yet: arming via the SBI TIME extension, enabling @c sie.STIE, and the
* mandatory per-tick re-arm are punch-list item 0.3. Until then this path is
* unreachable, which is why item 0.2 accepts on "boots with no regression"
* rather than on having observed an interrupt.
* Supervisor timer (cause 5) re-arms the one-shot SBI timer and then accounts
* the tick. **Re-arm comes first**: the SBI timer fires once per programmed
* deadline, so any return path that skips the re-arm stops the heartbeat
* permanently and silently. Ordering it ahead of @c heartbeat_tick() means a
* fault in the bookkeeping cannot also cost the next tick.
*
* Any other cause is ignored rather than fatal: an unexpected-but-harmless
* asynchronous interrupt should not take the kernel down, and none are
@@ -47,6 +53,7 @@ void riscv64_interrupt_handler(uint64_t scause)
uint64_t cause = scause & ~SCAUSE_INTERRUPT_BIT;
if (cause == SCAUSE_S_TIMER) {
riscv64_timer_rearm();
heartbeat_tick();
}
}