# HAL Platform Implementation Guide ## Overview This document explains how to implement the HAL for a new platform. It provides detailed examples for Linux, L4Re, and LithosAnanke (freestanding) implementations. > **HISTORICAL — L4Re/Fiasco.OC**: the L4Re examples below describe a target that was a supported platform through mid-2026. L4Re support has since been removed as an active target — `src/platform/l4re/time.c` and related `#ifdef __l4__` code are retained for reference but no longer wired into any build. **Target audience:** Platform developers adding HAL support for a new target. --- ## Platform Directory Structure ``` src/platform// ├── hal_time.c # Time & timer HAL implementation ├── hal_interrupt.c # Interrupt HAL implementation ├── hal_memory.c # Memory HAL implementation ├── hal_console.c # Console HAL implementation ├── hal_cpu.c # CPU HAL implementation ├── hal_panic.c # Panic/error HAL implementation └── platform_init.c # Platform-specific initialization (optional) ``` ### Build Integration Each platform is compiled independently via Makefile: ```makefile # Platform selection PLATFORM ?= linux # linux | l4re | kernel # Platform-specific source files PLATFORM_SOURCES = \ src/platform/$(PLATFORM)/hal_time.c \ src/platform/$(PLATFORM)/hal_interrupt.c \ src/platform/$(PLATFORM)/hal_memory.c \ src/platform/$(PLATFORM)/hal_console.c \ src/platform/$(PLATFORM)/hal_cpu.c \ src/platform/$(PLATFORM)/hal_panic.c # Platform-specific flags ifeq ($(PLATFORM),kernel) CFLAGS += -ffreestanding -nostdlib -mno-red-zone LDFLAGS += -nostdlib -static endif ``` --- ## Implementation Examples ### 1. Linux Platform (`src/platform/linux/`) Linux is the **reference platform** for development and testing. It provides the richest debugging environment. #### `hal_time.c` (Linux) ```c #include "hal/hal_time.h" #include #include #include #include /* Timer state */ typedef struct { timer_t timer_id; hal_timer_callback_t callback; void *ctx; } linux_timer_t; static linux_timer_t *periodic_timers = NULL; static int timer_count = 0; void hal_time_init(void) { /* Linux: clock_gettime is always available */ struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { hal_panic("hal_time_init: CLOCK_MONOTONIC unavailable"); } } uint64_t hal_time_now_ns(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec; } void hal_time_delay_ns(uint64_t ns) { struct timespec req; req.tv_sec = ns / 1000000000ULL; req.tv_nsec = ns % 1000000000ULL; nanosleep(&req, NULL); } /* Signal handler for periodic timer */ static void timer_signal_handler(int signo, siginfo_t *si, void *uc) { (void)signo; (void)uc; linux_timer_t *timer = (linux_timer_t *)si->si_value.sival_ptr; if (timer && timer->callback) { timer->callback(timer->ctx); } } int hal_timer_periodic(uint64_t period_ns, hal_timer_callback_t callback, void *ctx) { /* Allocate timer structure */ linux_timer_t *timer = malloc(sizeof(linux_timer_t)); if (!timer) return -1; timer->callback = callback; timer->ctx = ctx; /* Set up signal handler */ struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = timer_signal_handler; sigemptyset(&sa.sa_mask); sigaction(SIGRTMIN, &sa, NULL); /* Create POSIX timer */ struct sigevent sev; memset(&sev, 0, sizeof(sev)); sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGRTMIN; sev.sigev_value.sival_ptr = timer; if (timer_create(CLOCK_MONOTONIC, &sev, &timer->timer_id) != 0) { free(timer); return -1; } /* Arm the timer */ struct itimerspec its; its.it_value.tv_sec = period_ns / 1000000000ULL; its.it_value.tv_nsec = period_ns % 1000000000ULL; its.it_interval = its.it_value; /* Periodic */ if (timer_settime(timer->timer_id, 0, &its, NULL) != 0) { timer_delete(timer->timer_id); free(timer); return -1; } return timer_count++; /* Return timer ID */ } int hal_timer_cancel(int timer_id) { /* TODO: Implement timer cancellation */ (void)timer_id; return 0; } uint64_t hal_time_frequency_hz(void) { /* Linux: CLOCK_MONOTONIC is variable frequency, return 0 */ return 0; } ``` #### `hal_interrupt.c` (Linux) ```c #include "hal/hal_interrupt.h" #include /* Linux uses signals as "interrupts" */ static __thread int in_signal_handler = 0; void hal_interrupt_init(void) { /* No initialization needed on Linux */ } void hal_irq_enable(void) { /* Linux: interrupts (signals) always enabled */ } unsigned long hal_irq_disable(void) { /* Linux: can't disable signals, return dummy state */ return 0; } void hal_irq_restore(unsigned long state) { (void)state; } int hal_irq_register(unsigned int irq, hal_isr_t isr, void *ctx) { /* Linux: IRQs are handled via signal mechanism in hal_timer_periodic() */ (void)irq; (void)isr; (void)ctx; return 0; } int hal_irq_unregister(unsigned int irq) { (void)irq; return 0; } int hal_in_interrupt_context(void) { return in_signal_handler; } ``` #### `hal_memory.c` (Linux) ```c #include "hal/hal_memory.h" #include #include void hal_mem_init(void) { /* malloc is always available on Linux */ } void *hal_mem_alloc(size_t size) { if (size == 0) return NULL; void *ptr = malloc(size); if (ptr) { memset(ptr, 0, size); /* Zero-initialize */ } return ptr; } void hal_mem_free(void *ptr) { free(ptr); } uint64_t hal_mem_alloc_pages(size_t count) { /* Linux: just allocate from heap (no separate PMM) */ size_t size = count * hal_mem_page_size(); return (uint64_t)hal_mem_alloc(size); } void hal_mem_free_pages(uint64_t paddr, size_t count) { (void)count; hal_mem_free((void *)paddr); } int hal_mem_map(uint64_t vaddr, uint64_t paddr, size_t size, unsigned int flags) { /* Linux: identity mapping, no-op */ (void)vaddr; (void)paddr; (void)size; (void)flags; return 0; } size_t hal_mem_page_size(void) { return 4096; /* Standard page size */ } ``` #### `hal_console.c` (Linux) ```c #include "hal/hal_console.h" #include #include #include void hal_console_init(void) { /* stdin/stdout always available */ } void hal_console_putc(char c) { putchar(c); fflush(stdout); /* Ensure character is visible */ } void hal_console_puts(const char *s) { if (s) { fputs(s, stdout); fflush(stdout); } } int hal_console_getc(void) { return getchar(); } int hal_console_has_input(void) { struct pollfd pfd; pfd.fd = STDIN_FILENO; pfd.events = POLLIN; return poll(&pfd, 1, 0) > 0; } ``` #### `hal_cpu.c` (Linux) ```c #include "hal/hal_cpu.h" #include #include void hal_cpu_init(void) { /* No initialization needed */ } unsigned int hal_cpu_id(void) { /* Linux: return thread ID (not true CPU ID) */ return 0; /* Single-threaded for now */ } void hal_cpu_relax(void) { sched_yield(); } void hal_cpu_halt(void) { pause(); /* Wait for signal */ } unsigned int hal_cpu_count(void) { return sysconf(_SC_NPROCESSORS_ONLN); } ``` #### `hal_panic.c` (Linux) ```c #include "hal/hal_panic.h" #include #include void hal_panic(const char *msg) { fprintf(stderr, "PANIC: %s\n", msg ? msg : "unknown error"); fflush(stderr); abort(); } ``` --- ### 2. Kernel Platform (`src/platform/kernel/`) The kernel platform is **freestanding**—no libc, no OS, direct hardware access. #### `hal_time.c` (Kernel) ```c #include "hal/hal_time.h" #include "hal/hal_interrupt.h" #include "kernel/apic.h" #include "kernel/hpet.h" #include "kernel/tsc.h" /* TSC calibration state */ static uint64_t tsc_frequency_hz = 0; static uint64_t tsc_offset_ns = 0; void hal_time_init(void) { /* Calibrate TSC using HPET */ tsc_frequency_hz = tsc_calibrate_hpet(); if (tsc_frequency_hz == 0) { hal_panic("hal_time_init: TSC calibration failed"); } /* Validate monotonicity */ uint64_t t1 = hal_time_now_ns(); for (volatile int i = 0; i < 1000; i++); uint64_t t2 = hal_time_now_ns(); if (t2 < t1) { hal_panic("hal_time_init: TSC not monotonic"); } } uint64_t hal_time_now_ns(void) { /* Read TSC and convert to nanoseconds */ uint64_t tsc = rdtsc(); return tsc_to_ns(tsc) - tsc_offset_ns; } void hal_time_delay_ns(uint64_t ns) { uint64_t end = hal_time_now_ns() + ns; while (hal_time_now_ns() < end) { hal_cpu_relax(); } } /* APIC timer state */ static hal_timer_callback_t apic_timer_callback = NULL; static void *apic_timer_ctx = NULL; /* APIC timer ISR (called from IDT) */ void apic_timer_isr(void) { if (apic_timer_callback) { apic_timer_callback(apic_timer_ctx); } apic_eoi(); /* Send End-Of-Interrupt */ } int hal_timer_periodic(uint64_t period_ns, hal_timer_callback_t callback, void *ctx) { if (period_ns < 1000) return -2; /* Minimum 1µs */ apic_timer_callback = callback; apic_timer_ctx = ctx; /* Configure APIC timer */ uint64_t ticks = (period_ns * apic_timer_frequency_hz()) / 1000000000ULL; apic_timer_periodic(ticks); return 1; /* Timer ID */ } int hal_timer_cancel(int timer_id) { (void)timer_id; apic_timer_stop(); apic_timer_callback = NULL; return 0; } uint64_t hal_time_frequency_hz(void) { return tsc_frequency_hz; } ``` #### `hal_interrupt.c` (Kernel) ```c #include "hal/hal_interrupt.h" #include "kernel/idt.h" #include "kernel/apic.h" /* Per-CPU interrupt nesting count */ static __thread unsigned int interrupt_depth = 0; void hal_interrupt_init(void) { /* Initialize IDT */ idt_init(); /* Initialize Local APIC */ apic_init(); /* Disable legacy PIC (use APIC instead) */ pic_disable(); } void hal_irq_enable(void) { __asm__ volatile("sti"); } unsigned long hal_irq_disable(void) { unsigned long flags; __asm__ volatile( "pushfq\n" "popq %0\n" "cli\n" : "=r"(flags) ); return flags; } void hal_irq_restore(unsigned long state) { __asm__ volatile( "pushq %0\n" "popfq\n" : : "r"(state) ); } /* ISR table (256 entries for x86_64 IDT) */ static hal_isr_t isr_table[256] = {0}; static void *isr_ctx_table[256] = {0}; /* Common ISR entry point (called from IDT stubs) */ void common_isr_handler(unsigned int vector) { interrupt_depth++; if (isr_table[vector]) { isr_table[vector](vector, isr_ctx_table[vector]); } interrupt_depth--; apic_eoi(); } int hal_irq_register(unsigned int irq, hal_isr_t isr, void *ctx) { if (irq >= 256) return -1; isr_table[irq] = isr; isr_ctx_table[irq] = ctx; /* Configure IOAPIC to route IRQ to Local APIC */ ioapic_route_irq(irq, 0); /* Route to CPU 0 */ return 0; } int hal_irq_unregister(unsigned int irq) { if (irq >= 256) return -1; isr_table[irq] = NULL; isr_ctx_table[irq] = NULL; return 0; } int hal_in_interrupt_context(void) { return interrupt_depth > 0; } ``` #### `hal_memory.c` (Kernel) ```c #include "hal/hal_memory.h" #include "kernel/pmm.h" /* Physical Memory Manager */ #include "kernel/vmm.h" /* Virtual Memory Manager */ #include "kernel/kmalloc.h" /* Kernel heap allocator */ void hal_mem_init(void) { /* Initialize physical memory manager (parse UEFI memory map) */ pmm_init(); /* Initialize virtual memory manager (set up page tables) */ vmm_init(); /* Initialize kernel heap allocator */ kmalloc_init(); } void *hal_mem_alloc(size_t size) { if (size == 0) return NULL; return kmalloc(size); /* Kernel heap allocator */ } void hal_mem_free(void *ptr) { kfree(ptr); } uint64_t hal_mem_alloc_pages(size_t count) { return pmm_alloc_pages(count); } void hal_mem_free_pages(uint64_t paddr, size_t count) { pmm_free_pages(paddr, count); } int hal_mem_map(uint64_t vaddr, uint64_t paddr, size_t size, unsigned int flags) { /* Translate HAL flags to page table flags */ unsigned int pt_flags = 0; if (flags & HAL_MEM_WRITE) pt_flags |= PAGE_WRITE; if (flags & HAL_MEM_EXEC) pt_flags |= PAGE_EXEC; if (flags & HAL_MEM_NOCACHE) pt_flags |= PAGE_NOCACHE; return vmm_map(vaddr, paddr, size, pt_flags); } size_t hal_mem_page_size(void) { return 4096; } ``` #### `hal_console.c` (Kernel) ```c #include "hal/hal_console.h" #include "kernel/uart.h" #include "kernel/framebuffer.h" void hal_console_init(void) { /* Initialize UART 16550 (serial console) */ uart_init(0x3F8, 115200); /* COM1, 115200 baud */ /* Initialize framebuffer (if available from UEFI GOP) */ fb_init(); } void hal_console_putc(char c) { /* Output to both serial and framebuffer */ uart_putc(c); fb_putc(c); } void hal_console_puts(const char *s) { if (!s) return; while (*s) { hal_console_putc(*s++); } } int hal_console_getc(void) { /* Read from UART (blocking) */ return uart_getc(); } int hal_console_has_input(void) { return uart_has_data(); } ``` #### `hal_cpu.c` (Kernel) ```c #include "hal/hal_cpu.h" #include "kernel/apic.h" static unsigned int cpu_count = 1; void hal_cpu_init(void) { /* Detect CPU features */ cpu_detect_features(); /* Bring up secondary CPUs (SMP) */ cpu_count = smp_init(); } unsigned int hal_cpu_id(void) { /* Get Local APIC ID */ return apic_get_id(); } void hal_cpu_relax(void) { __asm__ volatile("pause"); } void hal_cpu_halt(void) { __asm__ volatile("hlt"); } unsigned int hal_cpu_count(void) { return cpu_count; } ``` --- ## Platform Testing Strategy ### 1. **Unit Tests (Per-Platform)** Each platform should have unit tests for HAL functions: ```c /* test/platform/linux/hal_time_test.c */ void test_hal_time_monotonic(void) { hal_time_init(); uint64_t t1 = hal_time_now_ns(); hal_time_delay_ns(1000000); /* 1ms */ uint64_t t2 = hal_time_now_ns(); assert(t2 > t1); assert((t2 - t1) >= 1000000); assert((t2 - t1) < 2000000); /* Allow 100% jitter for test */ } ``` ### 2. **Cross-Platform VM Tests** VM tests should run identically on all platforms: ```bash # Linux make PLATFORM=linux test # L4Re make PLATFORM=l4re test # Kernel (QEMU) make PLATFORM=kernel test-qemu ``` **Success criteria:** All 936+ tests pass on all platforms. ### 3. **Determinism Validation** Physics subsystems must show 0% algorithmic variance: ```bash # Run DoE on each platform for platform in linux l4re kernel; do make PLATFORM=$platform fastest ./build/$ARCH/fastest/starforth --doe > results_$platform.csv done # Compare results (should be identical) diff results_linux.csv results_kernel.csv ``` --- ## Common Implementation Pitfalls ### 1. **Non-Monotonic Time** **Symptom:** Physics metrics show negative deltas, rolling window corrupted. **Cause:** TSC goes backward (multi-core, frequency scaling). **Fix:** Use `rdtscp` instead of `rdtsc`, synchronize TSC across cores, or fall back to HPET. ### 2. **Timer Jitter** **Symptom:** Heartbeat intervals vary by 10-50%, metrics show high variance. **Cause:** Signal latency (Linux), interrupt latency (kernel). **Fix:** Minimize ISR work, use high-priority timer interrupt, disable frequency scaling. ### 3. **Memory Leaks** **Symptom:** `hal_mem_alloc()` calls grow without matching `hal_mem_free()`. **Cause:** Missing free in error paths, lost pointers. **Fix:** Valgrind on Linux, manual leak tracking on kernel. ### 4. **Interrupt Context Violations** **Symptom:** Crashes, hangs, or corruption when calling blocking HAL functions from ISR. **Cause:** Calling `hal_mem_alloc()` or `hal_console_getc()` from timer ISR. **Fix:** Assert `!hal_in_interrupt_context()` in blocking functions. --- ## Next Steps - See `migration-plan.md` for refactoring existing code - See `lithosananke-integration.md` for kernel-specific details