universalisos/kernel/arch/arm/exceptions.cpp
Fábio Coutada 9540b0528c feat(universalisos): PikeOS-style Phase B/C device drivers + Phase D microkernel
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>
2026-07-09 09:10:53 +01:00

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;
}