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>
502 lines
No EOL
14 KiB
C++
502 lines
No EOL
14 KiB
C++
/*
|
|
* Universalisos Interrupt Controller Implementation - Stage 4
|
|
* PikeOS 5.0 Feature Parity - Interrupt Virtualization
|
|
*
|
|
* Stage 4: Device Virtualization + Interrupt Handling
|
|
* Author: PortugalFuturista Hypervisor Development Team
|
|
* Version: 1.0.0
|
|
*/
|
|
|
|
#include "gic.h"
|
|
#include "arch/arm/uart.h"
|
|
#include <stdint.h>
|
|
|
|
// Global GIC state
|
|
static gic_state_t g_gic_state = {
|
|
.distributor = {
|
|
.enable = 0,
|
|
.enable_set = {},
|
|
.enable_clear = {},
|
|
.pending_set = {},
|
|
.pending_clear = {},
|
|
.active = {},
|
|
.priority = {},
|
|
.targets = {},
|
|
.config = {},
|
|
.sgi = {}
|
|
},
|
|
.cpu_interface = {
|
|
.enable = 0,
|
|
.priority_mask = 0xFF,
|
|
.irq_ack = 0,
|
|
.eoi = 0,
|
|
.running_priority = 0,
|
|
.highest_priority = 0,
|
|
.binary_point = 0
|
|
},
|
|
.virtual_gics = {},
|
|
.active_virtual_gic_count = 0,
|
|
.interrupt_configs = {},
|
|
.configured_interrupts = 0,
|
|
.total_interrupts = 0,
|
|
.spurious_interrupts = 0,
|
|
.virtual_interrupts_injected = 0,
|
|
.interrupt_masked = 0
|
|
};
|
|
|
|
// GIC physical base addresses for QEMU virt platform
|
|
#define GIC_DIST_BASE 0x08000000 // GIC Distributor base
|
|
#define GIC_CPU_BASE 0x08010000 // GIC CPU Interface base
|
|
|
|
/* Real GICv2 register offsets (for driving actual hardware). */
|
|
#define GICD_CTLR 0x000u
|
|
#define GICD_ISENABLER(n) (0x100u + 4u*(n))
|
|
#define GICD_ICENABLER(n) (0x180u + 4u*(n))
|
|
#define GICD_IPRIORITYR(n) (0x400u + (n))
|
|
#define GICD_ITARGETSR(n) (0x800u + (n))
|
|
#define GICD_ICFGR(n) (0xC00u + 4u*(n))
|
|
#define GICC_CTLR 0x000u
|
|
#define GICC_PMR 0x004u
|
|
#define GICC_IAR 0x00Cu
|
|
#define GICC_EOIR 0x010u
|
|
|
|
#define GIC_REG32(base, off) (*((volatile uint32_t*)((base) + (off))))
|
|
|
|
/**
|
|
* Initialize GIC
|
|
*/
|
|
extern "C" void gic_init(void) {
|
|
uart_puts("GIC: Initializing ARMv7 GIC\n");
|
|
|
|
// Initialize GIC state
|
|
g_gic_state.distributor.enable = 0;
|
|
g_gic_state.cpu_interface.enable = 0;
|
|
g_gic_state.cpu_interface.priority_mask = 0xFF;
|
|
g_gic_state.cpu_interface.binary_point = 0;
|
|
|
|
// Clear all interrupt configurations
|
|
for (int i = 0; i < GIC_MAX_INTERRUPTS; i++) {
|
|
g_gic_state.interrupt_configs[i].interrupt_id = i;
|
|
g_gic_state.interrupt_configs[i].type = INTERRUPT_TYPE_SPI;
|
|
g_gic_state.interrupt_configs[i].priority = GIC_PRIORITY_NORMAL;
|
|
g_gic_state.interrupt_configs[i].target_cpu = TARGET_CPU0;
|
|
g_gic_state.interrupt_configs[i].enabled = false;
|
|
g_gic_state.interrupt_configs[i].pending = false;
|
|
g_gic_state.interrupt_configs[i].active = false;
|
|
g_gic_state.interrupt_configs[i].edge_triggered = false;
|
|
g_gic_state.interrupt_configs[i].is_virtual = false;
|
|
g_gic_state.interrupt_configs[i].owner_vm_id = 0xFFFFFFFF;
|
|
g_gic_state.interrupt_configs[i].virq = 0;
|
|
}
|
|
|
|
// Configure SGI interrupts (0-15)
|
|
for (int i = 0; i < GIC_SGI_MAX; i++) {
|
|
g_gic_state.interrupt_configs[i].type = INTERRUPT_TYPE_SGI;
|
|
g_gic_state.interrupt_configs[i].priority = GIC_PRIORITY_HIGHEST;
|
|
}
|
|
|
|
// Configure PPI interrupts (16-31)
|
|
for (int i = GIC_SGI_MAX; i < GIC_PPI_MAX; i++) {
|
|
g_gic_state.interrupt_configs[i].type = INTERRUPT_TYPE_PPI;
|
|
g_gic_state.interrupt_configs[i].priority = GIC_PRIORITY_HIGH;
|
|
}
|
|
|
|
g_gic_state.configured_interrupts = 0;
|
|
g_gic_state.active_virtual_gic_count = 0;
|
|
g_gic_state.total_interrupts = 0;
|
|
g_gic_state.spurious_interrupts = 0;
|
|
g_gic_state.virtual_interrupts_injected = 0;
|
|
g_gic_state.interrupt_masked = 0;
|
|
|
|
uart_puts("GIC: ARMv7 GIC initialized\n");
|
|
uart_puts("GIC: Maximum interrupts: ");
|
|
uart_print_dec(GIC_MAX_INTERRUPTS);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("GIC: SGI range: 0-15\n");
|
|
uart_puts("GIC: PPI range: 16-31\n");
|
|
uart_puts("GIC: SPI range: 32-1020\n");
|
|
}
|
|
|
|
/**
|
|
* Enable GIC
|
|
*/
|
|
extern "C" void gic_enable(void) {
|
|
uart_puts("GIC: Enabling GIC\n");
|
|
|
|
// Enable distributor
|
|
g_gic_state.distributor.enable = 1;
|
|
|
|
// Enable CPU interface
|
|
g_gic_state.cpu_interface.enable = 1;
|
|
g_gic_state.cpu_interface.priority_mask = 0xFF; // Allow all priorities
|
|
|
|
/* Drive the real GICv2 hardware. */
|
|
GIC_REG32(GIC_DIST_BASE, GICD_CTLR) = 1u; /* enable distributor */
|
|
GIC_REG32(GIC_CPU_BASE, GICC_PMR) = 0xFFu; /* allow all priorities */
|
|
GIC_REG32(GIC_CPU_BASE, GICC_CTLR) = 1u; /* enable CPU interface */
|
|
|
|
uart_puts("GIC: GIC enabled (hardware)\n");
|
|
}
|
|
|
|
/**
|
|
* Disable GIC
|
|
*/
|
|
extern "C" void gic_disable(void) {
|
|
uart_puts("GIC: Disabling GIC\n");
|
|
|
|
// Disable CPU interface
|
|
g_gic_state.cpu_interface.enable = 0;
|
|
|
|
// Disable distributor
|
|
g_gic_state.distributor.enable = 0;
|
|
|
|
uart_puts("GIC: GIC disabled\n");
|
|
}
|
|
|
|
/**
|
|
* Configure interrupt
|
|
*/
|
|
extern "C" bool gic_configure_interrupt(uint32_t irq_id, interrupt_type_t type,
|
|
uint32_t priority, uint32_t target_cpu,
|
|
bool edge_triggered) {
|
|
if (irq_id >= GIC_MAX_INTERRUPTS) {
|
|
uart_puts("GIC: Invalid interrupt ID ");
|
|
uart_print_dec(irq_id);
|
|
uart_puts("\n");
|
|
return false;
|
|
}
|
|
|
|
uart_puts("GIC: Configuring interrupt ");
|
|
uart_print_dec(irq_id);
|
|
uart_puts("\n");
|
|
|
|
interrupt_config_t* config = &g_gic_state.interrupt_configs[irq_id];
|
|
|
|
config->type = type;
|
|
config->priority = priority & 0xFF;
|
|
config->target_cpu = target_cpu;
|
|
config->edge_triggered = edge_triggered;
|
|
config->enabled = true;
|
|
|
|
/* Real GIC hardware: write priority + target (SPI only; PPI/SGI are fixed). */
|
|
volatile uint8_t *prio = (volatile uint8_t *)(GIC_DIST_BASE + GICD_IPRIORITYR(irq_id));
|
|
*prio = (uint8_t)(priority & 0xFFu);
|
|
if (irq_id >= 32u) {
|
|
/* SPI: write target CPU. PPI/SGI targets are fixed. */
|
|
volatile uint8_t *tgt = (volatile uint8_t *)(GIC_DIST_BASE + GICD_ITARGETSR(irq_id));
|
|
*tgt = (uint8_t)(1u << target_cpu);
|
|
}
|
|
|
|
g_gic_state.configured_interrupts++;
|
|
|
|
uart_puts("GIC: Interrupt ");
|
|
uart_print_dec(irq_id);
|
|
uart_puts(" configured (priority ");
|
|
uart_print_dec(priority);
|
|
uart_puts(")\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Enable interrupt
|
|
*/
|
|
extern "C" void gic_enable_interrupt(uint32_t irq_id) {
|
|
if (irq_id >= GIC_MAX_INTERRUPTS) return;
|
|
|
|
g_gic_state.interrupt_configs[irq_id].enabled = true;
|
|
|
|
/* Real GIC: set-enable register (one bit per 32 IRQs). */
|
|
GIC_REG32(GIC_DIST_BASE, GICD_ISENABLER(irq_id / 32u)) = 1u << (irq_id % 32u);
|
|
}
|
|
|
|
/**
|
|
* Disable interrupt
|
|
*/
|
|
extern "C" void gic_disable_interrupt(uint32_t irq_id) {
|
|
if (irq_id >= GIC_MAX_INTERRUPTS) return;
|
|
|
|
g_gic_state.interrupt_configs[irq_id].enabled = false;
|
|
|
|
/* Real GIC: clear-enable register. */
|
|
GIC_REG32(GIC_DIST_BASE, GICD_ICENABLER(irq_id / 32u)) = 1u << (irq_id % 32u);
|
|
}
|
|
|
|
/**
|
|
* Set interrupt priority
|
|
*/
|
|
extern "C" void gic_set_priority(uint32_t irq_id, uint32_t priority) {
|
|
if (irq_id >= GIC_MAX_INTERRUPTS) return;
|
|
|
|
g_gic_state.interrupt_configs[irq_id].priority = priority & 0xFF;
|
|
}
|
|
|
|
/**
|
|
* Get interrupt priority
|
|
*/
|
|
extern "C" uint32_t gic_get_priority(uint32_t irq_id) {
|
|
if (irq_id >= GIC_MAX_INTERRUPTS) return GIC_PRIORITY_LOWEST;
|
|
|
|
return g_gic_state.interrupt_configs[irq_id].priority;
|
|
}
|
|
|
|
/**
|
|
* Set interrupt target
|
|
*/
|
|
extern "C" void gic_set_target(uint32_t irq_id, uint32_t target_cpu) {
|
|
if (irq_id >= GIC_MAX_INTERRUPTS) return;
|
|
|
|
g_gic_state.interrupt_configs[irq_id].target_cpu = target_cpu;
|
|
}
|
|
|
|
/**
|
|
* Generate Software Generated Interrupt (SGI)
|
|
*/
|
|
extern "C" void gic_generate_sgi(uint32_t sgi_id, uint32_t target_cpu) {
|
|
if (sgi_id >= GIC_SGI_MAX) {
|
|
uart_puts("GIC: Invalid SGI ID\n");
|
|
return;
|
|
}
|
|
|
|
uart_puts("GIC: Generating SGI ");
|
|
uart_print_dec(sgi_id);
|
|
uart_puts(" to CPU ");
|
|
uart_print_dec(target_cpu);
|
|
uart_puts("\n");
|
|
|
|
// Mark interrupt as pending
|
|
g_gic_state.interrupt_configs[sgi_id].pending = true;
|
|
}
|
|
|
|
/**
|
|
* Acknowledge interrupt
|
|
*/
|
|
extern "C" uint32_t gic_acknowledge_interrupt(void) {
|
|
/* Read the real GICC_IAR — atomically acknowledges the highest-priority
|
|
* pending interrupt and returns its ID (1023 = spurious). */
|
|
uint32_t irq_id = GIC_REG32(GIC_CPU_BASE, GICC_IAR) & 0x3FFu;
|
|
|
|
/* Track in software model for diagnostics. */
|
|
if (irq_id < GIC_MAX_IRQ) {
|
|
g_gic_state.interrupt_configs[irq_id].active = true;
|
|
g_gic_state.total_interrupts++;
|
|
} else {
|
|
g_gic_state.spurious_interrupts++;
|
|
}
|
|
|
|
return irq_id;
|
|
}
|
|
|
|
/**
|
|
* End of interrupt
|
|
*/
|
|
extern "C" void gic_end_of_interrupt(uint32_t irq_id) {
|
|
/* Write the real GICC_EOIR to signal end-of-interrupt. */
|
|
GIC_REG32(GIC_CPU_BASE, GICC_EOIR) = irq_id;
|
|
|
|
if (irq_id >= GIC_MAX_IRQ) return;
|
|
|
|
/* Update software model. */
|
|
g_gic_state.interrupt_configs[irq_id].active = false;
|
|
g_gic_state.cpu_interface.eoi = irq_id;
|
|
}
|
|
|
|
/**
|
|
* Get interrupt configuration
|
|
*/
|
|
extern "C" interrupt_config_t* gic_get_interrupt_config(uint32_t irq_id) {
|
|
if (irq_id >= GIC_MAX_INTERRUPTS) return nullptr;
|
|
|
|
return &g_gic_state.interrupt_configs[irq_id];
|
|
}
|
|
|
|
/**
|
|
* Assign interrupt to VM
|
|
*/
|
|
extern "C" bool gic_assign_interrupt_to_vm(uint32_t irq_id, uint32_t vm_id, uint32_t virq) {
|
|
if (irq_id >= GIC_MAX_INTERRUPTS) return false;
|
|
|
|
uart_puts("GIC: Assigning interrupt ");
|
|
uart_print_dec(irq_id);
|
|
uart_puts(" to VM ");
|
|
uart_print_dec(vm_id);
|
|
uart_puts(" as virq ");
|
|
uart_print_dec(virq);
|
|
uart_puts("\n");
|
|
|
|
interrupt_config_t* config = &g_gic_state.interrupt_configs[irq_id];
|
|
|
|
config->is_virtual = true;
|
|
config->owner_vm_id = vm_id;
|
|
config->virq = virq;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Create virtual interrupt controller for VM
|
|
*/
|
|
extern "C" virtual_interrupt_controller_t* gic_create_virtual_gic(uint32_t vm_id) {
|
|
if (vm_id >= 16) return nullptr;
|
|
|
|
uart_puts("GIC: Creating virtual GIC for VM ");
|
|
uart_print_dec(vm_id);
|
|
uart_puts("\n");
|
|
|
|
virtual_interrupt_controller_t* v_gic = &g_gic_state.virtual_gics[vm_id];
|
|
|
|
v_gic->vm_id = vm_id;
|
|
|
|
// Clear interrupt bitmaps
|
|
for (int i = 0; i < 32; i++) {
|
|
v_gic->pending_interrupts[i] = 0;
|
|
v_gic->enabled_interrupts[i] = 0;
|
|
v_gic->masked_interrupts[i] = 0;
|
|
}
|
|
|
|
v_gic->virtual_interrupt_count = 0;
|
|
|
|
g_gic_state.active_virtual_gic_count++;
|
|
|
|
uart_puts("GIC: Virtual GIC created\n");
|
|
return v_gic;
|
|
}
|
|
|
|
/**
|
|
* Inject virtual interrupt into VM
|
|
*/
|
|
extern "C" bool gic_inject_virtual_interrupt(uint32_t vm_id, uint32_t virq) {
|
|
if (vm_id >= 16 || virq >= 1024) return false;
|
|
|
|
virtual_interrupt_controller_t* v_gic = &g_gic_state.virtual_gics[vm_id];
|
|
|
|
// Set bit in pending interrupts
|
|
v_gic->pending_interrupts[virq / 32] |= (1U << (virq % 32));
|
|
|
|
g_gic_state.virtual_interrupts_injected++;
|
|
|
|
uart_puts("GIC: Injected virtual interrupt ");
|
|
uart_print_dec(virq);
|
|
uart_puts(" to VM ");
|
|
uart_print_dec(vm_id);
|
|
uart_puts("\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Mask virtual interrupt for VM
|
|
*/
|
|
extern "C" void gic_mask_virtual_interrupt(uint32_t vm_id, uint32_t virq) {
|
|
if (vm_id >= 16 || virq >= 1024) return;
|
|
|
|
virtual_interrupt_controller_t* v_gic = &g_gic_state.virtual_gics[vm_id];
|
|
|
|
// Set bit in masked interrupts
|
|
v_gic->masked_interrupts[virq / 32] |= (1U << (virq % 32));
|
|
|
|
g_gic_state.interrupt_masked++;
|
|
|
|
uart_puts("GIC: Masked virtual interrupt ");
|
|
uart_print_dec(virq);
|
|
uart_puts(" for VM ");
|
|
uart_print_dec(vm_id);
|
|
uart_puts("\n");
|
|
}
|
|
|
|
/**
|
|
* Get pending virtual interrupt for VM
|
|
*/
|
|
extern "C" uint32_t gic_get_pending_virtual_interrupt(uint32_t vm_id) {
|
|
if (vm_id >= 16) return 1023; // Spurious
|
|
|
|
virtual_interrupt_controller_t* v_gic = &g_gic_state.virtual_gics[vm_id];
|
|
|
|
// Find first pending, enabled, unmasked interrupt
|
|
for (int i = 0; i < 32; i++) {
|
|
uint32_t pending = v_gic->pending_interrupts[i];
|
|
uint32_t enabled = v_gic->enabled_interrupts[i];
|
|
uint32_t masked = v_gic->masked_interrupts[i];
|
|
|
|
uint32_t available = pending & enabled & ~masked;
|
|
|
|
if (available) {
|
|
// Find first set bit
|
|
for (int j = 0; j < 32; j++) {
|
|
if (available & (1U << j)) {
|
|
return i * 32 + j;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1023; // No pending interrupts
|
|
}
|
|
|
|
/**
|
|
* Handle IRQ interrupt
|
|
*/
|
|
extern "C" void gic_handle_irq(void) {
|
|
uart_puts("\n>>> GIC IRQ HANDLER <<<\n");
|
|
|
|
// Acknowledge interrupt
|
|
uint32_t irq_id = gic_acknowledge_interrupt();
|
|
|
|
if (irq_id >= GIC_MAX_IRQ) {
|
|
uart_puts("GIC: Spurious interrupt\n");
|
|
return;
|
|
}
|
|
|
|
uart_puts("GIC: Handling interrupt ");
|
|
uart_print_dec(irq_id);
|
|
uart_puts("\n");
|
|
|
|
// Get interrupt configuration
|
|
interrupt_config_t* config = &g_gic_state.interrupt_configs[irq_id];
|
|
|
|
// Check if this is a virtual interrupt
|
|
if (config->is_virtual && config->owner_vm_id != 0xFFFFFFFF) {
|
|
// Inject into target VM
|
|
gic_inject_virtual_interrupt(config->owner_vm_id, config->virq);
|
|
} else {
|
|
// Handle as physical interrupt
|
|
uart_puts("GIC: Physical interrupt (would dispatch to handler)\n");
|
|
}
|
|
|
|
// End of interrupt
|
|
gic_end_of_interrupt(irq_id);
|
|
}
|
|
|
|
/**
|
|
* Print GIC statistics
|
|
*/
|
|
extern "C" void gic_print_statistics(void) {
|
|
uart_puts("\n=== GIC Statistics ===\n");
|
|
|
|
uart_puts("Total Interrupts: ");
|
|
uart_print_dec(g_gic_state.total_interrupts);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("Spurious Interrupts: ");
|
|
uart_print_dec(g_gic_state.spurious_interrupts);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("Virtual Interrupts Injected: ");
|
|
uart_print_dec(g_gic_state.virtual_interrupts_injected);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("Interrupts Masked: ");
|
|
uart_print_dec(g_gic_state.interrupt_masked);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("Configured Interrupts: ");
|
|
uart_print_dec(g_gic_state.configured_interrupts);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("Active Virtual GICs: ");
|
|
uart_print_dec(g_gic_state.active_virtual_gic_count);
|
|
uart_puts("\n");
|
|
|
|
uart_puts("======================\n\n");
|
|
} |