灵符lingfu

ling-lang

↓ 0 downloads

Ling - The Omniglot Systems Language

Keywords systems, polyglot, ai, crypto, game
lingfu install ling-lang
LicenseApache-2.0 OR MIT
AuthorsLing Lin, Sanny Lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (995)

crates/ling-kernel/src/arch/aarch64/gic.rs
//! GIC-400 (ARM Generic Interrupt Controller v2) driver for the boards that
//! have one: the Pi 4 (BCM2711) and the Pi 5 (BCM2712). The Pi 3's legacy
//! VC / QA7 controllers stay in `intc.rs`.
//!
//! Only what this kernel needs: the distributor and CPU interface are enabled,
//! the generic timer's non-secure physical PPI (interrupt 30, `CNTPNSIRQ`) is
//! unmasked, and the IRQ vector acknowledges / ends interrupts through the
//! CPU interface. Shared peripheral interrupts are left masked; the UART and
//! USB keep polling.
//!
//! The firmware's ARM stub hands the kernel over in the non-secure world with
//! every interrupt already in group 1, which is what lets `GICC_IAR` return
//! the timer's id here. The addresses are the SoC's:
//! Pi 4 GICD 0xFF84_1000 / GICC 0xFF84_2000, Pi 5 GICD 0x10_7FFF_9000 /
//! GICC 0x10_7FFF_A000 (inside the device windows the page tables map).
use crate::arch::mmio::{board, read32, write32, RpiBoard};

const GICD_CTLR: usize = 0x000;
const GICD_ISENABLER: usize = 0x100;
const GICD_ICENABLER: usize = 0x180;
const GICD_ICPENDR: usize = 0x280;
const GICD_IPRIORITYR: usize = 0x400;
const GICD_IGROUPR: usize = 0x080;
const GICC_CTLR: usize = 0x000;
const GICC_PMR: usize = 0x004;
const GICC_BPR: usize = 0x008;
const GICC_IAR: usize = 0x00C;
const GICC_EOIR: usize = 0x010;

/// The generic timer's non-secure physical interrupt (`CNTP_*_EL0`).
pub const IRQ_TIMER_PHYS_NS: u32 = 30;
/// The secure physical timer interrupt (`CNTPS`): what `CNTP_*_EL0` raises when
/// the kernel was entered in the secure world (QEMU without an ARM stub).
pub const IRQ_TIMER_PHYS_S: u32 = 29;

fn bases() -> Option<(usize, usize)> {
    match board() {
        RpiBoard::Pi4 => Some((0xFF84_1000, 0xFF84_2000)),
        RpiBoard::Pi5 => Some((0x10_7FFF_9000, 0x10_7FFF_A000)),
        _ => None,
    }
}

/// This board routes interrupts through a GIC (the legacy controllers are absent).
#[inline]
pub fn active() -> bool {
    bases().is_some()
}

/// Enable the distributor and this core's CPU interface and unmask the timer PPI.
/// IRQs stay masked at the core (DAIF) until the caller's `sti`.
pub fn init() {
    let Some((gicd, gicc)) = bases() else { return };
    unsafe {
        write32(gicd + GICD_CTLR, 0);
        // The banked private interrupts 0..31: mask all, clear pending, then
        // (where the write is honoured) make them group 1 so a non-secure core
        // sees them, and give the timer a middle priority.
        write32(gicd + GICD_ICENABLER, 0xFFFF_FFFF);
        write32(gicd + GICD_ICPENDR, 0xFFFF_FFFF);
        write32(gicd + GICD_IGROUPR, 0xFFFF_FFFF);
        for irq in [IRQ_TIMER_PHYS_NS, IRQ_TIMER_PHYS_S, 27, 26] {
            let pri_reg = gicd + GICD_IPRIORITYR + (irq as usize / 4) * 4;
            let shift = (irq % 4) * 8;
            let cur = read32(pri_reg) & !(0xFF << shift);
            write32(pri_reg, cur | (0x80 << shift));
        }
        write32(gicd + GICD_ISENABLER, (1 << IRQ_TIMER_PHYS_NS) | (1 << IRQ_TIMER_PHYS_S) | (1 << 27) | (1 << 26));
        // Bits 0 | 1: EnableGrp0 | EnableGrp1. Through the firmware's
        // non-secure view (real Pi 4 / 5) bit 0 is the group-1 enable and
        // bit 1 is ignored; on a GIC without security extensions (QEMU) the
        // interrupts made group 1 above need bit 1. Both worlds get IRQs.
        write32(gicd + GICD_CTLR, 3);
        write32(gicc + GICC_PMR, 0xFF);
        write32(gicc + GICC_BPR, 0);
        // bit 2 AckCtl: a secure-world read of IAR returns the real id of a
        // group-1 interrupt instead of 1022 (QEMU); ignored by the NS view
        write32(gicc + GICC_CTLR, 7);
    }
}

/// What the controller accepted, for the boot log: `(GICD_ISENABLER0, GICD_CTLR,
/// GICC_CTLR, CurrentEL)`. A zero timer bit in the enable mask means the
/// firmware left the private interrupts in group 0 and this (non-secure)
/// kernel cannot unmask them: no timer IRQs on that firmware.
pub fn status() -> (u32, u32, u32, u64) {
    let Some((gicd, gicc)) = bases() else { return (0, 0, 0, 0) };
    let el: u64;
    unsafe {
        core::arch::asm!("mrs {}, CurrentEL", out(reg) el, options(nomem, nostack));
        (read32(gicd + GICD_ISENABLER), read32(gicd + GICD_CTLR), read32(gicc + GICC_CTLR), el >> 2)
    }
}

/// `GICD_IGROUPR0` as this world reads it (all ones from the firmware's
/// non-secure view, whatever this kernel wrote on a GIC without security).
pub fn group0() -> u32 {
    match bases() {
        Some((gicd, _)) => unsafe { read32(gicd + GICD_IGROUPR) },
        None => 0,
    }
}

/// Whether the timer interrupt is really unmasked at the distributor.
pub fn timer_enabled() -> bool {
    status().0 & ((1 << IRQ_TIMER_PHYS_NS) | (1 << IRQ_TIMER_PHYS_S)) != 0
}

/// Acknowledge the highest-priority pending interrupt: the raw `GICC_IAR`
/// value (pass it back to [`eoi`]); `None` when nothing is pending (1023).
pub fn ack() -> Option<u32> {
    let (_, gicc) = bases()?;
    let iar = unsafe { read32(gicc + GICC_IAR) };
    if iar & 0x3FF >= 1020 {
        None
    } else {
        Some(iar)
    }
}

/// Signal the end of the interrupt `ack` returned.
pub fn eoi(iar: u32) {
    if let Some((_, gicc)) = bases() {
        unsafe { write32(gicc + GICC_EOIR, iar) };
    }
}

← Back to index