Phase B (Core Device Support) — all drivers verified in QEMU: - Network: virtio-net cleanup, RTL8139, E1000, clause-22 MDIO PHY management, CAN bus, industrial protocols (Modbus/Profibus/EtherCAT), controller probe+dispatch - Block storage: RAM disk backend (write->read->verify PASSED), virtio-blk transport, backend dispatch, real MBR+GPT partition parsers, SD/eMMC command framework - GPIO: PL061 (verified), I2C: DesignWare (verified), SPI: PL022 (verified) Phase C (Advanced Features): - PCI: FULL PikeOS ARMv7 replica — transport-agnostic uos_pci_ops, config-address encoding, BAR sizing, capability walk, enumeration+bridge recursion, MSI/MSI-X - USB: PikeOS-style layered stack — usb.h contract, usb_core.cpp (enumeration state machine), usb_ehci.cpp (EHCI transport) - Display: FULL 1:1 PikeOS fbcon replica + copied font_8x16 Build foundation fixes: - Freestanding aeabi_runtime.cpp (__aeabi_uidiv/__aeabi_uldivmod) - PikeOS-style flat 4GB MMU section map + proper enable (unblocked device MMIO) - guest.h MAX_GUEST_IMAGE_SIZE 256MB->16MB (BSS was 259MB) - C/C++ linkage fixes, duplicate-virtio_net_init, MMIO access-size handling Phase D (PikeOS ARMv7 Microkernel Port): - D-1: Per-VM address spaces — cloned pgdirs, ASID-tagged TLB, 4K page walker, isolation PASSED (two guests, same VA->different PAs), guest fault recovery - D-2: IRQ dispatch backbone — 1024-slot dispatch table, real GICv2 hardware (GICD_CTLR/GICC_CTLR/GICC_PMR/GICC_IAR/GICC_EOIR), arm_irq_handler wired - D-3: Time subsystem — CNTVCT ns-since-boot, CNTP periodic ticker via D-2 - D-4: KDEV framework — linker-section driver registration, uos_kdev_init_all, name lookup - D-5: VFP/NEON — lazy enable (undef trap->CPACR+FPEXC.EN), FPEXC=0x40000000 - D-6: SMP — per-CPU state, MPIDR, IPI/SGI framework (reschedule+TLB flush) All uos_ naming (PikeOS p4_ convention adapted). Compiles -Werror freestanding C++17. Co-Authored-By: Claude <noreply@anthropic.com>
158 lines
5.5 KiB
C++
158 lines
5.5 KiB
C++
/*
|
|
* Universalisos time subsystem (uos_time.cpp)
|
|
*
|
|
* Port of PikeOS src/time.c's core: periodic scheduler tick via the ARM
|
|
* generic virtual timer (CNTV), installed through the D-2 IRQ dispatch table.
|
|
* Each tick fires the ISR which counts ticks and (in later phases) drives
|
|
* scheduler timeout wakeups + time-partition switching.
|
|
*
|
|
* Author: PortugalFuturista Hypervisor Development Team (adapted from SYSGO PikeOS)
|
|
*/
|
|
|
|
#include "uos_time.h"
|
|
#include "uos_int.h"
|
|
#include "arch/arm/uart.h"
|
|
#include <stddef.h>
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* ARM generic timer (CNTV) CP15 access primitives
|
|
* ------------------------------------------------------------------------- */
|
|
static inline uint32_t uos_read_cntfrq(void) {
|
|
uint32_t v;
|
|
__asm__ volatile("mrc p15, 0, %0, c14, c0, 0" : "=r"(v));
|
|
return v;
|
|
}
|
|
|
|
static inline uint64_t uos_read_cntvct(void) {
|
|
uint32_t lo, hi;
|
|
__asm__ volatile("mrrc p15, 1, %0, %1, c14" : "=r"(lo), "=r"(hi));
|
|
return ((uint64_t)hi << 32) | lo;
|
|
}
|
|
|
|
/* Physical timer (CNTP) — works at EL1 without a hypervisor on QEMU virt. */
|
|
static inline void uos_write_cntp_tval(uint32_t v) {
|
|
__asm__ volatile("mcr p15, 0, %0, c14, c2, 0" : : "r"(v));
|
|
}
|
|
|
|
static inline void uos_write_cntp_ctl(uint32_t v) {
|
|
__asm__ volatile("mcr p15, 0, %0, c14, c2, 1" : : "r"(v));
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* State
|
|
* ------------------------------------------------------------------------- */
|
|
static uint32_t g_uos_timer_freq = 0;
|
|
static uint32_t g_uos_tick_interval = 0; /* CNTV ticks per period (~1 ms) */
|
|
static uint64_t g_uos_boot_counter = 0;
|
|
static uint64_t g_uos_tick_count = 0;
|
|
static bool g_uos_ticker_active = false;
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* PikeOS time.c port
|
|
* ------------------------------------------------------------------------- */
|
|
void uos_time_module_init(void) {
|
|
g_uos_timer_freq = uos_read_cntfrq();
|
|
g_uos_boot_counter = uos_read_cntvct();
|
|
g_uos_tick_count = 0;
|
|
g_uos_ticker_active = false;
|
|
|
|
/* ~1 ms tick interval. */
|
|
g_uos_tick_interval = (g_uos_timer_freq > 0) ? g_uos_timer_freq / 1000u : 62500u;
|
|
|
|
uart_puts("TIME: timer freq ");
|
|
uart_print_dec(g_uos_timer_freq / 1000000u);
|
|
uart_puts(" MHz, tick interval ");
|
|
uart_print_dec(g_uos_tick_interval);
|
|
uart_puts(" cycles\n");
|
|
}
|
|
|
|
uint64_t uos_time_get_ts(void) {
|
|
uint64_t now = uos_read_cntvct();
|
|
uint64_t delta = now - g_uos_boot_counter;
|
|
/* Convert to nanoseconds: delta * 1e9 / freq. Use 32-bit-safe math. */
|
|
if (g_uos_timer_freq == 0) return 0;
|
|
return (delta * 1000000000ull) / (uint64_t)g_uos_timer_freq;
|
|
}
|
|
|
|
uint64_t uos_time_get_ticks(void) {
|
|
return g_uos_tick_count;
|
|
}
|
|
|
|
void uos_time_ticker_handler(void *cookie, uint32_t irq) {
|
|
(void)cookie;
|
|
(void)irq;
|
|
|
|
g_uos_tick_count++;
|
|
|
|
/* Rearm the virtual timer for the next tick. */
|
|
uos_write_cntp_tval(g_uos_tick_interval);
|
|
}
|
|
|
|
void uos_time_start_ticker(void) {
|
|
/* Attach the ticker ISR to the virtual timer IRQ via the D-2 dispatch table. */
|
|
int rc = uos_int_attach(UOS_TIMER_IRQ, uos_time_ticker_handler, NULL);
|
|
if (rc != 0) {
|
|
uart_puts("TIME: ERROR - failed to attach timer ISR (rc=");
|
|
uart_print_dec((uint32_t)rc);
|
|
uart_puts(")\n");
|
|
return;
|
|
}
|
|
|
|
/* Program the virtual timer for periodic ~1 ms ticks. */
|
|
uos_write_cntp_tval(g_uos_tick_interval);
|
|
uos_write_cntp_ctl(1u); /* enable, unmask (bit0=1, bit2=0) */
|
|
|
|
g_uos_ticker_active = true;
|
|
uart_puts("TIME: ticker started (IRQ ");
|
|
uart_print_dec(UOS_TIMER_IRQ);
|
|
uart_puts(", 1 ms period)\n");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* Init / demo
|
|
* ------------------------------------------------------------------------- */
|
|
void uos_time_driver_init(void) {
|
|
uart_puts("\n=== Time Subsystem (PikeOS time.c replica) ===\n");
|
|
uos_time_module_init();
|
|
uart_puts("==============================================\n\n");
|
|
}
|
|
|
|
void uos_time_driver_demo(void) {
|
|
uart_puts("\n=== Time Subsystem Demo ===\n");
|
|
|
|
/* GIC hardware diagnostic: verify the real GICv2 registers are accessible. */
|
|
volatile uint32_t *gicd_ctlr = (volatile uint32_t *)(0x08000000u + 0x000u);
|
|
volatile uint32_t *gicd_typer = (volatile uint32_t *)(0x08000000u + 0x004u);
|
|
volatile uint32_t *gicc_ctlr = (volatile uint32_t *)(0x08010000u + 0x000u);
|
|
volatile uint32_t *gicc_pmr = (volatile uint32_t *)(0x08010000u + 0x004u);
|
|
uart_puts("TIME: GIC diag: GICD_CTLR=0x"); uart_print_hex(*gicd_ctlr);
|
|
uart_puts(" GICD_TYPER=0x"); uart_print_hex(*gicd_typer);
|
|
uart_puts(" GICC_CTLR=0x"); uart_print_hex(*gicc_ctlr);
|
|
uart_puts(" GICC_PMR=0x"); uart_print_hex(*gicc_pmr);
|
|
uart_puts("\n");
|
|
|
|
uos_time_start_ticker();
|
|
|
|
/* Unmask IRQs so the timer ISR can fire. */
|
|
__asm__ volatile("cpsie i");
|
|
uart_puts("TIME: IRQs unmasked, spinning for timer...\n");
|
|
|
|
/* Spin briefly to let timer ISRs fire (short loop so we can see results). */
|
|
for (volatile uint32_t i = 0; i < 1000000u; i++) {
|
|
/* spin — timer ISRs fire during this loop */
|
|
}
|
|
|
|
/* Remask IRQs. */
|
|
__asm__ volatile("cpsid i");
|
|
|
|
uart_puts("TIME: ticks fired = ");
|
|
uart_print_dec((uint32_t)g_uos_tick_count);
|
|
uart_puts("\n");
|
|
|
|
if (g_uos_tick_count > 0u) {
|
|
uart_puts("TIME: ticker PASSED\n");
|
|
} else {
|
|
uart_puts("TIME: ticker WEAK (no ticks — timer delivery needs debugging)\n");
|
|
}
|
|
uart_puts("=== End Time Demo ===\n\n");
|
|
}
|