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>
362 lines
No EOL
11 KiB
C++
362 lines
No EOL
11 KiB
C++
/*
|
|
* Universalisos ARMv7 Exception Handlers
|
|
* PikeOS 5.0 Feature Parity - Exception Implementation
|
|
*
|
|
* This file implements C handlers for ARMv7 exceptions:
|
|
* - Undefined instruction handling
|
|
* - Supervisor Call (SVC) system calls
|
|
* - Prefetch abort (instruction fetch error)
|
|
* - Data abort (data access error)
|
|
* - IRQ/FIQ interrupt handling
|
|
*
|
|
* Author: PortugalFuturista Hypervisor Development Team
|
|
* Version: 2.0.0 (Phase A - Complete System Call Framework)
|
|
*/
|
|
|
|
#include "exceptions.h"
|
|
#include "uart.h"
|
|
#include "uos/uos_syscalls.h"
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
|
|
// Static instance of exception statistics (defined in header)
|
|
static exception_stats_t exception_stats = {
|
|
.undefined_instruction_count = 0,
|
|
.svc_count = 0,
|
|
.prefetch_abort_count = 0,
|
|
.data_abort_count = 0,
|
|
.irq_count = 0,
|
|
.fiq_count = 0
|
|
};
|
|
|
|
// External system call dispatcher
|
|
extern uint32_t uos_syscall_dispatch(uint32_t svc_number, uint32_t* args);
|
|
extern void uos_syscall_get_stats(uint32_t* total, uint32_t* errors);
|
|
|
|
/**
|
|
* Undefined Instruction Handler
|
|
* Called when an undefined instruction is executed
|
|
*/
|
|
extern "C" void arm_undefined_instruction_handler(uint32_t instruction, uint32_t address) {
|
|
exception_stats.undefined_instruction_count++;
|
|
|
|
/* D-5: PikeOS _check_vundef pattern — try lazy VFP/NEON enable first. */
|
|
extern bool uos_fpu_lazy_enable(void);
|
|
if (uos_fpu_lazy_enable()) {
|
|
return; /* VFP just enabled — instruction will re-execute successfully */
|
|
}
|
|
|
|
/* Real undefined instruction — halt. */
|
|
uart_puts("\n!!! UNDEFINED INSTRUCTION !!!\n");
|
|
uart_puts("Instruction: 0x");
|
|
uart_print_hex(instruction);
|
|
uart_puts(" Address: 0x");
|
|
uart_print_hex(address);
|
|
uart_puts("\nSystem halted.\n");
|
|
while(1) { __asm__("wfi"); }
|
|
}
|
|
|
|
/**
|
|
* Supervisor Call (SVC) Handler
|
|
* Complete PikeOS 5.0 System Call Entry Point
|
|
*
|
|
* Parameters:
|
|
* svc_number: The SVC immediate value (system call number)
|
|
* args: Pointer to saved register state (r0-r15, cpsr, pc)
|
|
* Original r0-r3 contain the actual system call arguments
|
|
*/
|
|
extern "C" uint32_t arm_svc_handler(uint32_t svc_number, uint32_t* args) {
|
|
exception_stats.svc_count++;
|
|
|
|
uart_puts("\n>>> UOS SYSTEM CALL <<<\n");
|
|
uart_puts("SVC Number: 0x");
|
|
uart_print_hex(svc_number);
|
|
uart_puts("\n");
|
|
|
|
// Validate arguments pointer
|
|
if (!args) {
|
|
uart_puts("Invalid arguments pointer\n");
|
|
return UOS_SC_ERROR;
|
|
}
|
|
|
|
// Dispatch to comprehensive system call implementation
|
|
uint32_t result = uos_syscall_dispatch(svc_number, args);
|
|
|
|
// Check for errors
|
|
if (result == (uint32_t)UOS_SC_ERROR) {
|
|
uart_puts("System call returned error\n");
|
|
exception_stats.svc_count++; // Count as error
|
|
} else {
|
|
uart_puts("System call completed: 0x");
|
|
uart_print_hex(result);
|
|
uart_puts("\n");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Prefetch Abort Handler
|
|
* Instruction fetch error (e.g., trying to execute from non-executable memory)
|
|
*/
|
|
extern "C" void arm_prefetch_abort_handler(uint32_t fault_address, uint32_t fault_status) {
|
|
exception_stats.prefetch_abort_count++;
|
|
|
|
uart_puts("\n!!! PREFETCH ABORT !!!\n");
|
|
uart_puts("Fault Address: 0x");
|
|
uart_print_hex(fault_address);
|
|
uart_puts("\n");
|
|
uart_puts("Fault Status: 0x");
|
|
uart_print_hex(fault_status);
|
|
uart_puts("\n");
|
|
|
|
// Decode fault status bits
|
|
uart_puts("Fault Status Decode:\n");
|
|
if (fault_status & 0x08) uart_puts(" - Debug event\n");
|
|
if (fault_status & 0x04) uart_puts(" - Translation fault\n");
|
|
if (fault_status & 0x02) uart_puts(" - Access flag fault\n");
|
|
if (fault_status & 0x01) uart_puts(" - Domain fault\n");
|
|
|
|
uart_puts("\nPossible causes:\n");
|
|
uart_puts("1. Trying to execute data memory as code\n");
|
|
uart_puts("2. Branch to non-executable region\n");
|
|
uart_puts("3. Memory protection violation\n");
|
|
|
|
uart_puts("\nSystem halted for safety.\n");
|
|
while(1) {
|
|
__asm__("wfi");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Data Abort Handler
|
|
* Data access error (e.g., accessing invalid memory, permission violation)
|
|
*/
|
|
extern "C" void arm_data_abort_handler(uint32_t fault_address, uint32_t fault_status) {
|
|
exception_stats.data_abort_count++;
|
|
|
|
/* Read the current ASID (CONTEXTIDR) to distinguish guest vs kernel faults. */
|
|
uint32_t contextidr;
|
|
__asm__ volatile("mrc p15, 0, %0, c13, c0, 1" : "=r"(contextidr));
|
|
uint32_t asid = contextidr & 0xFFu;
|
|
|
|
uart_puts("\n!!! DATA ABORT !!!\n");
|
|
uart_puts("Fault Address: 0x");
|
|
uart_print_hex(fault_address);
|
|
uart_puts(" Status: 0x");
|
|
uart_print_hex(fault_status);
|
|
uart_puts(asid ? " [GUEST]" : " [KERNEL]");
|
|
uart_puts("\n");
|
|
|
|
/* Decode the fault status (ARMv7 short-format DFSR). */
|
|
uint32_t fs = fault_status & 0x1Fu; /* status[4:0] (or [10:3]+ext for long) */
|
|
const char *ftype = "unknown";
|
|
if (fs == 0x01u) ftype = "alignment fault";
|
|
else if (fs == 0x04u) ftype = "translation fault (L1)";
|
|
else if (fs == 0x05u) ftype = "translation fault (L2)";
|
|
else if (fs == 0x08u) ftype = "precise external abort";
|
|
else if (fs == 0x0Cu) ftype = "L1 translation (external)";
|
|
else if (fs == 0x0Du || fs == 0x0Fu) ftype = "permission fault";
|
|
else if (fs == 0x16u || fs == 0x17u) ftype = "permission fault (async)";
|
|
if (ftype[0] != 'u') { uart_puts(" Type: "); uart_puts(ftype); uart_puts("\n"); }
|
|
if (fault_status & (1u << 11)) uart_puts(" Write access\n");
|
|
else uart_puts(" Read access\n");
|
|
|
|
if (asid != 0u) {
|
|
/* Guest fault: restore the kernel address space and return (the
|
|
* boot.S wrapper returns to PC+8, skipping past the fault). The guest
|
|
* is effectively killed — the kernel resumes. */
|
|
uart_puts(" -> guest fault, restoring kernel address space\n");
|
|
|
|
/* Restore TTBR0 to the kernel flat map + ASID 0. */
|
|
extern uint32_t *uos_get_kernel_pgdir(void);
|
|
uint32_t *pgdir = uos_get_kernel_pgdir();
|
|
__asm__ volatile("mcr p15, 0, %0, c13, c0, 1" : : "r"(0)); /* ASID 0 */
|
|
/* TTB_FLAGS = (1<<3)|(1<<6) = 0x48 */
|
|
uint32_t ttbr0 = (uint32_t)(uintptr_t)pgdir | 0x48u;
|
|
__asm__ volatile("mcr p15, 0, %0, c2, c0, 0" : : "r"(ttbr0)); /* TTBR0 */
|
|
__asm__ volatile("mcr p15, 0, %0, c8, c7, 0" : : "r"(0)); /* TLBIALL */
|
|
__asm__ volatile("dsb" ::: "memory");
|
|
__asm__ volatile("isb");
|
|
return; /* boot.S wrapper restores regs and returns (skips faulting insn) */
|
|
}
|
|
|
|
/* Kernel fault: print diagnostics and halt (fatal). */
|
|
uart_puts("\nPossible causes:\n");
|
|
uart_puts("1. Accessing unmapped memory\n");
|
|
uart_puts("2. Permission violation\n");
|
|
uart_puts("3. Page table entry invalid\n");
|
|
uart_puts("\nKernel halted for safety.\n");
|
|
while(1) { __asm__("wfi"); }
|
|
}
|
|
|
|
/**
|
|
* IRQ Handler
|
|
* Normal interrupt handling - platform-specific interrupt controller
|
|
*/
|
|
extern "C" void arm_irq_handler(void) {
|
|
exception_stats.irq_count++;
|
|
|
|
/* D-2: dispatch through the PikeOS-style interrupt table (uos_int_dispatch).
|
|
* Reads the GIC IAR, looks up the handler, calls it, EOI's the IRQ. */
|
|
extern void uos_int_dispatch(void);
|
|
uos_int_dispatch();
|
|
}
|
|
|
|
/**
|
|
* FIQ Handler
|
|
* Fast interrupt handling - minimal latency for time-critical interrupts
|
|
*/
|
|
extern "C" void arm_fiq_handler(void) {
|
|
exception_stats.fiq_count++;
|
|
|
|
uart_puts("\n>>> FIQ INTERRUPT <<<\n");
|
|
uart_puts("Fast interrupt - minimal latency path\n");
|
|
|
|
// TODO: Platform-specific FIQ handling
|
|
// FIQ uses banked registers for faster response
|
|
|
|
uart_puts("FIQ handled\n");
|
|
}
|
|
|
|
/**
|
|
* Get exception statistics for debugging
|
|
*/
|
|
extern "C" void arm_get_exception_stats(exception_stats_t* stats) {
|
|
if (stats) {
|
|
*stats = exception_stats;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print all exception statistics via UART
|
|
*/
|
|
extern "C" void arm_print_exception_stats(void) {
|
|
uart_puts("\n=== Exception Statistics ===\n");
|
|
uart_puts("Undefined Instructions: ");
|
|
uart_print_dec(exception_stats.undefined_instruction_count);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("SVC Calls: ");
|
|
uart_print_dec(exception_stats.svc_count);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("Prefetch Aborts: ");
|
|
uart_print_dec(exception_stats.prefetch_abort_count);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("Data Aborts: ");
|
|
uart_print_dec(exception_stats.data_abort_count);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("IRQs: ");
|
|
uart_print_dec(exception_stats.irq_count);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("FIQs: ");
|
|
uart_print_dec(exception_stats.fiq_count);
|
|
uart_puts("\n");
|
|
uart_puts("============================\n\n");
|
|
}
|
|
|
|
/**
|
|
* Initialize exception handling system
|
|
*/
|
|
extern "C" void arm_exceptions_init(void) {
|
|
// Clear exception statistics
|
|
exception_stats.undefined_instruction_count = 0;
|
|
exception_stats.svc_count = 0;
|
|
exception_stats.prefetch_abort_count = 0;
|
|
exception_stats.data_abort_count = 0;
|
|
exception_stats.irq_count = 0;
|
|
exception_stats.fiq_count = 0;
|
|
|
|
uart_puts("Exception handling system initialized\n");
|
|
uart_puts("ARMv7 exception vector table: 0x00000000\n");
|
|
uart_puts("Exception handlers registered:\n");
|
|
uart_puts(" - Undefined instruction: arm_undefined_instruction_handler\n");
|
|
uart_puts(" - SVC (system call): arm_svc_handler\n");
|
|
uart_puts(" - Prefetch abort: arm_prefetch_abort_handler\n");
|
|
uart_puts(" - Data abort: arm_data_abort_handler\n");
|
|
uart_puts(" - IRQ: arm_irq_handler\n");
|
|
uart_puts(" - FIQ: arm_fiq_handler\n");
|
|
}
|
|
|
|
/*
|
|
* System call wrapper functions for user/kernel interface
|
|
*/
|
|
|
|
/**
|
|
* System call: Print string via kernel console
|
|
*/
|
|
extern "C" uint32_t sys_print(const char* message) {
|
|
// Trigger SVC 0x01 with message pointer in r0
|
|
register uint32_t result asm("r0");
|
|
__asm__ volatile (
|
|
"svc #0x01"
|
|
: "=r" (result)
|
|
: "0" (message) // Use same register as output (r0)
|
|
);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* System call: Get system time counter
|
|
*/
|
|
extern "C" uint32_t sys_get_time(void) {
|
|
uint32_t result;
|
|
__asm__ volatile (
|
|
"svc #0x02"
|
|
: "=r" (result)
|
|
);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* System call: Yield processor to scheduler
|
|
*/
|
|
extern "C" uint32_t sys_yield(void) {
|
|
uint32_t result;
|
|
__asm__ volatile (
|
|
"svc #0x03"
|
|
: "=r" (result)
|
|
);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* System call: Request VM context creation (future)
|
|
*/
|
|
extern "C" uint32_t sys_vm_create(void) {
|
|
uint32_t result;
|
|
__asm__ volatile (
|
|
"svc #0x10"
|
|
: "=r" (result)
|
|
);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* System call: Request VM context switch (future)
|
|
*/
|
|
extern "C" uint32_t sys_vm_switch(uint32_t vm_id) {
|
|
uint32_t result;
|
|
__asm__ volatile (
|
|
"svc #0x11"
|
|
: "=r" (result)
|
|
: "r" (vm_id)
|
|
);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* System call: Yield to scheduler (future)
|
|
*/
|
|
extern "C" uint32_t sys_sched_yield(void) {
|
|
uint32_t result;
|
|
__asm__ volatile (
|
|
"svc #0x20"
|
|
: "=r" (result)
|
|
);
|
|
return result;
|
|
} |