170 lines
5.6 KiB
Zig
170 lines
5.6 KiB
Zig
// SPDX-License-Identifier: LCL-1.0
|
|
// Copyright (c) 2026 Markus Maiwald
|
|
// Stewardship: Self Sovereign Society Foundation
|
|
//
|
|
// This file is part of the Nexus Commonwealth.
|
|
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
|
|
|
//! Rumpk Layer 0: GICv2 Driver (ARM64)
|
|
//!
|
|
//! Minimal Generic Interrupt Controller v2 for QEMU virt machine.
|
|
//! Handles interrupt enable, claim, and complete for timer and device IRQs.
|
|
//!
|
|
//! SAFETY: All register accesses use volatile pointers to MMIO regions.
|
|
|
|
// =========================================================
|
|
// GICv2 MMIO Base Addresses (QEMU virt machine)
|
|
// =========================================================
|
|
|
|
const GICD_BASE: usize = 0x08000000; // Distributor
|
|
const GICC_BASE: usize = 0x08010000; // CPU Interface
|
|
|
|
// =========================================================
|
|
// Distributor Registers (GICD)
|
|
// =========================================================
|
|
|
|
const GICD_CTLR: usize = 0x000; // Control
|
|
const GICD_TYPER: usize = 0x004; // Type (read-only)
|
|
const GICD_ISENABLER: usize = 0x100; // Set-Enable (banked per 32 IRQs)
|
|
const GICD_ICENABLER: usize = 0x180; // Clear-Enable
|
|
const GICD_ISPENDR: usize = 0x200; // Set-Pending
|
|
const GICD_ICPENDR: usize = 0x280; // Clear-Pending
|
|
const GICD_IPRIORITYR: usize = 0x400; // Priority (byte-accessible)
|
|
const GICD_ITARGETSR: usize = 0x800; // Target (byte-accessible)
|
|
const GICD_ICFGR: usize = 0xC00; // Configuration
|
|
|
|
// =========================================================
|
|
// CPU Interface Registers (GICC)
|
|
// =========================================================
|
|
|
|
const GICC_CTLR: usize = 0x000; // Control
|
|
const GICC_PMR: usize = 0x004; // Priority Mask
|
|
const GICC_IAR: usize = 0x00C; // Interrupt Acknowledge
|
|
const GICC_EOIR: usize = 0x010; // End of Interrupt
|
|
|
|
// =========================================================
|
|
// IRQ Numbers (QEMU virt)
|
|
// =========================================================
|
|
|
|
/// Non-Secure Physical Timer PPI
|
|
pub const TIMER_IRQ: u32 = 30;
|
|
|
|
/// UART PL011 (SPI #1 = IRQ 33)
|
|
pub const UART_IRQ: u32 = 33;
|
|
|
|
/// VirtIO MMIO IRQ base (SPI #16 = IRQ 48)
|
|
/// QEMU virt assigns SPIs 48..79 to MMIO slots 0..31
|
|
pub const VIRTIO_MMIO_IRQ_BASE: u32 = 48;
|
|
|
|
// Spurious interrupt ID
|
|
const SPURIOUS_IRQ: u32 = 1023;
|
|
|
|
// =========================================================
|
|
// MMIO Helpers
|
|
// =========================================================
|
|
|
|
fn gicd_read(offset: usize) u32 {
|
|
const ptr: *volatile u32 = @ptrFromInt(GICD_BASE + offset);
|
|
return ptr.*;
|
|
}
|
|
|
|
fn gicd_write(offset: usize, val: u32) void {
|
|
const ptr: *volatile u32 = @ptrFromInt(GICD_BASE + offset);
|
|
ptr.* = val;
|
|
}
|
|
|
|
fn gicc_read(offset: usize) u32 {
|
|
const ptr: *volatile u32 = @ptrFromInt(GICC_BASE + offset);
|
|
return ptr.*;
|
|
}
|
|
|
|
fn gicc_write(offset: usize, val: u32) void {
|
|
const ptr: *volatile u32 = @ptrFromInt(GICC_BASE + offset);
|
|
ptr.* = val;
|
|
}
|
|
|
|
// =========================================================
|
|
// Public API
|
|
// =========================================================
|
|
|
|
/// Initialize GICv2 distributor and CPU interface.
|
|
pub fn gic_init() void {
|
|
// 1. Disable distributor during setup
|
|
gicd_write(GICD_CTLR, 0);
|
|
|
|
// 2. Set all SPIs to lowest priority (0xFF) and target CPU 0
|
|
// PPIs (0-31) are banked per-CPU, handled separately
|
|
const typer = gicd_read(GICD_TYPER);
|
|
const it_lines = (typer & 0x1F) + 1; // Number of 32-IRQ groups
|
|
var i: usize = 1; // Skip group 0 (SGIs/PPIs - banked)
|
|
while (i < it_lines) : (i += 1) {
|
|
// Disable all SPIs
|
|
gicd_write(GICD_ICENABLER + i * 4, 0xFFFFFFFF);
|
|
// Set priority to 0xA0 (low but not lowest)
|
|
var j: usize = 0;
|
|
while (j < 8) : (j += 1) {
|
|
gicd_write(GICD_IPRIORITYR + (i * 32 + j * 4), 0xA0A0A0A0);
|
|
}
|
|
// Target CPU 0 for all SPIs
|
|
j = 0;
|
|
while (j < 8) : (j += 1) {
|
|
gicd_write(GICD_ITARGETSR + (i * 32 + j * 4), 0x01010101);
|
|
}
|
|
}
|
|
|
|
// 3. Configure PPI priorities (group 0, banked)
|
|
// Timer IRQ 30: priority 0x20 (high)
|
|
const timer_prio_reg = GICD_IPRIORITYR + (TIMER_IRQ / 4) * 4;
|
|
const timer_prio_shift: u5 = @intCast((TIMER_IRQ % 4) * 8);
|
|
var prio_val = gicd_read(timer_prio_reg);
|
|
prio_val &= ~(@as(u32, 0xFF) << timer_prio_shift);
|
|
prio_val |= @as(u32, 0x20) << timer_prio_shift;
|
|
gicd_write(timer_prio_reg, prio_val);
|
|
|
|
// 4. Enable distributor (Group 0 + Group 1)
|
|
gicd_write(GICD_CTLR, 0x3);
|
|
|
|
// 5. Configure CPU interface
|
|
gicc_write(GICC_PMR, 0xFF); // Accept all priorities
|
|
gicc_write(GICC_CTLR, 0x1); // Enable CPU interface
|
|
}
|
|
|
|
/// Enable a specific interrupt in the distributor.
|
|
pub fn gic_enable_irq(irq: u32) void {
|
|
const reg = GICD_ISENABLER + (irq / 32) * 4;
|
|
const bit: u5 = @intCast(irq % 32);
|
|
gicd_write(reg, @as(u32, 1) << bit);
|
|
}
|
|
|
|
/// Disable a specific interrupt in the distributor.
|
|
pub fn gic_disable_irq(irq: u32) void {
|
|
const reg = GICD_ICENABLER + (irq / 32) * 4;
|
|
const bit: u5 = @intCast(irq % 32);
|
|
gicd_write(reg, @as(u32, 1) << bit);
|
|
}
|
|
|
|
/// Acknowledge an interrupt (read IAR). Returns IRQ number or SPURIOUS_IRQ.
|
|
pub fn gic_claim() u32 {
|
|
return gicc_read(GICC_IAR) & 0x3FF;
|
|
}
|
|
|
|
/// Signal end of interrupt processing.
|
|
pub fn gic_complete(irq: u32) void {
|
|
gicc_write(GICC_EOIR, irq);
|
|
}
|
|
|
|
/// Check if a claimed IRQ is spurious.
|
|
pub fn is_spurious(irq: u32) bool {
|
|
return irq >= SPURIOUS_IRQ;
|
|
}
|
|
|
|
/// Enable the NS Physical Timer interrupt (IRQ 30).
|
|
pub fn gic_enable_timer_irq() void {
|
|
gic_enable_irq(TIMER_IRQ);
|
|
}
|
|
|
|
/// Enable a VirtIO MMIO slot interrupt in the GIC.
|
|
pub fn gic_enable_virtio_mmio_irq(slot: u32) void {
|
|
gic_enable_irq(VIRTIO_MMIO_IRQ_BASE + slot);
|
|
}
|