GIC CPU Interface

Relevant source files

This document covers the GicCpuInterface implementation, which provides per-CPU interrupt handling functionality for ARM GICv2 controllers. The CPU interface handles interrupt acknowledgment, completion signaling, priority masking, and preemption control for individual processor cores.

For system-wide interrupt distribution and configuration, see GIC Distributor. For the overall interrupt classification system, see Interrupt System Architecture.

CPU Interface Register Structure

The GicCpuInterfaceRegs struct defines the memory-mapped register layout for the CPU interface, providing type-safe access to hardware registers that control per-CPU interrupt processing.

flowchart TD
subgraph subGraph1["Register Types"]
    RW["ReadWrite"]
    RO["ReadOnly"]
    WO["WriteOnly"]
end
subgraph subGraph0["GicCpuInterfaceRegs Structure"]
    CTLR["CTLR @ 0x0000CPU Interface Control"]
    PMR["PMR @ 0x0004Priority Mask Register"]
    BPR["BPR @ 0x0008Binary Point Register"]
    IAR["IAR @ 0x000cInterrupt Acknowledge"]
    EOIR["EOIR @ 0x0010End of Interrupt"]
    RPR["RPR @ 0x0014Running Priority"]
    HPPIR["HPPIR @ 0x0018Highest Priority Pending"]
    IIDR["IIDR @ 0x00fcInterface Identification"]
    DIR["DIR @ 0x1000Deactivate Interrupt"]
end

BPR --> RW
CTLR --> RW
DIR --> WO
EOIR --> WO
HPPIR --> RO
IAR --> RO
IIDR --> RO
PMR --> RW
RPR --> RO

Sources: src/gic_v2.rs(L50 - L76) 

Core Interface Implementation

The GicCpuInterface struct wraps the register structure and provides safe methods for interrupt handling operations.

ComponentTypePurpose
baseNonNullPointer to memory-mapped registers
Thread SafetySend + SyncSafe for multi-threaded access
flowchart TD
subgraph subGraph1["Register Operations"]
    IAR_read["IAR.get()"]
    EOIR_write["EOIR.set(iar)"]
    CTLR_enable["CTLR.set(1)"]
    PMR_unmask["PMR.set(0xff)"]
end
subgraph subGraph0["GicCpuInterface Methods"]
    new["new(base: *mut u8)"]
    regs["regs() -> &GicCpuInterfaceRegs"]
    iar["iar() -> u32"]
    eoi["eoi(iar: u32)"]
    handle_irq["handle_irq(handler: F)"]
    init["init()"]
end

eoi --> EOIR_write
handle_irq --> eoi
handle_irq --> iar
iar --> IAR_read
init --> CTLR_enable
init --> PMR_unmask
new --> regs

Sources: src/gic_v2.rs(L114 - L116)  src/gic_v2.rs(L214 - L222) 

Interrupt Acknowledgment and Completion

The CPU interface provides a two-phase interrupt handling protocol: acknowledgment via iar() and completion via eoi().

Interrupt Acknowledge Register (IAR)

The iar() method reads the GICC_IAR register to obtain the interrupt ID of the highest priority pending interrupt. This operation atomically acknowledges the interrupt and changes its state from pending to active.

#![allow(unused)]
fn main() {
pub fn iar(&self) -> u32 {
    self.regs().IAR.get()
}
}

Spurious Interrupt Handling: The method returns interrupt ID 1023 when no valid interrupt is pending, the distributor is disabled, or the CPU interface is disabled.

Sources: src/gic_v2.rs(L230 - L232) 

End of Interrupt (EOI)

The eoi() method writes to the GICC_EOIR register to signal completion of interrupt processing. The value written must match the value previously read from iar().

#![allow(unused)]
fn main() {
pub fn eoi(&self, iar: u32) {
    self.regs().EOIR.set(iar);
}
}

Sources: src/gic_v2.rs(L238 - L240) 

High-Level Interrupt Handling

The handle_irq() method provides a complete interrupt handling flow that combines acknowledgment, handler execution, and completion in a single operation.

flowchart TD
start["handle_irq() called"]
read_iar["Read IAR registeriar = self.iar()"]
extract_vector["Extract vector IDvector = iar & 0x3ff"]
check_spurious["vector < 1020?"]
call_handler["Call handler(vector)"]
write_eoir["Write EOIR registerself.eoi(iar)"]
spurious["Ignore spurious interrupt"]
end_flow["Return"]

call_handler --> write_eoir
check_spurious --> call_handler
check_spurious --> spurious
extract_vector --> check_spurious
read_iar --> extract_vector
spurious --> end_flow
start --> read_iar
write_eoir --> end_flow

The method masks the interrupt ID to 10 bits (iar & 0x3ff) to extract the vector number, as the upper bits of the IAR register contain additional status information.

Sources: src/gic_v2.rs(L250 - L262) 

CPU Interface Initialization

The init() method configures the CPU interface for operation by enabling interrupt delivery and setting priority masking.

flowchart TD
subgraph subGraph1["Register Effects"]
    ctrl_effect["Enables interrupt signalingto the processor"]
    pmr_effect["Allows interrupts atall priority levels"]
end
subgraph subGraph0["init() Operations"]
    enable_ctrl["Enable CPU InterfaceCTLR.set(1)"]
    unmask_prio["Unmask All PrioritiesPMR.set(0xff)"]
end

enable_ctrl --> ctrl_effect
unmask_prio --> pmr_effect

Initialization Requirements:

  • Must be called exactly once per CPU
  • Should be called after distributor initialization
  • Priority mask value 0xff represents the lowest priority threshold, allowing all interrupts

Sources: src/gic_v2.rs(L269 - L275) 

Thread Safety and Memory Management

The GicCpuInterface implements Send and Sync traits, enabling safe usage across thread boundaries in multi-core systems.

Safety AspectImplementation
Memory SafetyNonNullprevents null pointer dereference
Thread Safetyunsafe impl Send + Syncallows cross-thread usage
Register Accessconst fn regs()provides immutable reference to registers
Hardware SynchronizationHardware ensures atomic register operations

The unsafe implementations of Send and Sync are justified because:

  • Hardware registers are designed for concurrent access from multiple CPUs
  • Each CPU interface instance manages a separate set of memory-mapped registers
  • Register operations are atomic at the hardware level

Sources: src/gic_v2.rs(L121 - L122)