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.
Component | Type | Purpose |
---|---|---|
base | NonNull | Pointer to memory-mapped registers |
Thread Safety | Send + Sync | Safe 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 Aspect | Implementation |
---|---|
Memory Safety | NonNull |
Thread Safety | unsafe impl Send + Syncallows cross-thread usage |
Register Access | const fn regs()provides immutable reference to registers |
Hardware Synchronization | Hardware 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)