Virtual GIC Controller (Vgic)
Relevant source files
This document covers the Vgic struct and its associated components, which form the core of the virtual interrupt controller implementation in the arm_vgic crate. The Vgic provides virtualized access to ARM Generic Interrupt Controller (GIC) functionality for guest virtual machines within the ArceOS hypervisor ecosystem.
For information about the CPU-specific interface components, see CPU Interface (Vgicc). For details about the device framework integration, see Device Operations Interface.
Core Structure and Components
The Virtual GIC Controller is implemented through two primary structures that work together to manage interrupt virtualization state and provide thread-safe access to interrupt controller resources.
Primary Data Structures
The Vgic implementation follows a layered approach with an outer wrapper providing thread-safe access to the internal state:
flowchart TD
subgraph subGraph4["Control State"]
ctrlr["ctrlr: u32"]
typer["typer: u32"]
iidr["iidr: u32"]
end
subgraph subGraph3["GIC Registers"]
gicd_igroupr["gicd_igroupr[SPI_ID_MAX/32]"]
gicd_isenabler["gicd_isenabler[SPI_ID_MAX/32]"]
gicd_ipriorityr["gicd_ipriorityr[SPI_ID_MAX]"]
gicd_itargetsr["gicd_itargetsr[SPI_ID_MAX]"]
gicd_icfgr["gicd_icfgr[SPI_ID_MAX/16]"]
end
subgraph subGraph2["State Arrays"]
used_irq["used_irq[SPI_ID_MAX/32]"]
ptov["ptov[SPI_ID_MAX]"]
vtop["vtop[SPI_ID_MAX]"]
gicc_vec["gicc: Vec"]
end
subgraph subGraph1["Protected State"]
VgicInner["VgicInner"]
Mutex["Mutex"]
end
subgraph subGraph0["Public Interface"]
Vgic["Vgic"]
end
Mutex --> VgicInner
Vgic --> Mutex
VgicInner --> ctrlr
VgicInner --> gicc_vec
VgicInner --> gicd_icfgr
VgicInner --> gicd_igroupr
VgicInner --> gicd_ipriorityr
VgicInner --> gicd_isenabler
VgicInner --> gicd_itargetsr
VgicInner --> iidr
VgicInner --> ptov
VgicInner --> typer
VgicInner --> used_irq
VgicInner --> vtop
Sources: src/vgic.rs(L15 - L34)
State Management Arrays
The VgicInner struct maintains several key data structures for interrupt management:
| Array | Type | Size | Purpose |
|---|---|---|---|
| used_irq | [u32; SPI_ID_MAX/32] | 16 words | Tracks which interrupt IDs are allocated |
| ptov | [u32; SPI_ID_MAX] | 512 entries | Physical-to-virtual interrupt ID mapping |
| vtop | [u32; SPI_ID_MAX] | 512 entries | Virtual-to-physical interrupt ID mapping |
| gicd_igroupr | [u32; SPI_ID_MAX/32] | 16 words | Interrupt group register state |
| gicd_isenabler | [u32; SPI_ID_MAX/32] | 16 words | Interrupt enable register state |
| gicd_ipriorityr | [u8; SPI_ID_MAX] | 512 bytes | Interrupt priority values |
| gicd_itargetsr | [u8; SPI_ID_MAX] | 512 bytes | Interrupt target CPU mapping |
| gicd_icfgr | [u32; SPI_ID_MAX/16] | 32 words | Interrupt configuration register state |
Sources: src/vgic.rs(L15 - L30) src/consts.rs(L1 - L4)
Memory Access Handling
The Vgic provides width-specific handlers for processing virtual machine memory accesses to the interrupt controller's memory-mapped registers. These handlers implement the virtualization layer between guest VM register accesses and the underlying interrupt controller state.
Handler Method Dispatch
flowchart TD
subgraph subGraph2["Register Processing"]
addr_decode["Address Decoding"]
register_logic["Register-Specific Logic"]
state_update["VgicInner State Update"]
end
subgraph subGraph1["Handler Methods"]
handle_read8["handle_read8()"]
handle_read16["handle_read16()"]
handle_read32["handle_read32()"]
handle_write8["handle_write8()"]
handle_write16["handle_write16()"]
handle_write32["handle_write32()"]
end
subgraph subGraph0["VM Memory Access"]
vm_read8["VM 8-bit Read"]
vm_read16["VM 16-bit Read"]
vm_read32["VM 32-bit Read"]
vm_write8["VM 8-bit Write"]
vm_write16["VM 16-bit Write"]
vm_write32["VM 32-bit Write"]
end
addr_decode --> register_logic
handle_read16 --> addr_decode
handle_read32 --> addr_decode
handle_read8 --> addr_decode
handle_write16 --> addr_decode
handle_write32 --> addr_decode
handle_write8 --> addr_decode
register_logic --> state_update
vm_read16 --> handle_read16
vm_read32 --> handle_read32
vm_read8 --> handle_read8
vm_write16 --> handle_write16
vm_write32 --> handle_write32
vm_write8 --> handle_write8
Sources: src/vgic.rs(L56 - L133)
Read Handler Implementation
The read handlers currently provide basic functionality with placeholder implementations:
handle_read8(): Returns 0 for all 8-bit register readshandle_read16(): Returns 0 for all 16-bit register readshandle_read32(): Returns 0 for all 32-bit register reads
These methods use the signature fn handle_readX(&self, addr: usize) -> AxResult<usize> and are marked as pub(crate) for internal crate access.
Sources: src/vgic.rs(L56 - L66)
Register Handling and Interrupt Control
The write handlers implement the core interrupt virtualization logic by processing writes to specific GIC distributor registers and coordinating with the physical interrupt controller.
Control Register Handling
The VGICD_CTLR register controls the overall enable/disable state of the interrupt distributor:
flowchart TD
subgraph subGraph2["Physical GIC Operations"]
scan_used["Scan used_irq array"]
set_enable["GicInterface::set_enable()"]
set_priority["GicInterface::set_priority()"]
end
subgraph subGraph1["Conditional Processing"]
check_enable["ctrlr > 0?"]
enable_path["Enable Path"]
disable_path["Disable Path"]
end
subgraph subGraph0["Control Register Write Flow"]
write_ctlr["Write to VGICD_CTLR"]
extract_bits["Extract bits [1:0]"]
lock_state["Lock VgicInner"]
update_ctrlr["Update ctrlr field"]
end
check_enable --> disable_path
check_enable --> enable_path
disable_path --> scan_used
enable_path --> scan_used
enable_path --> set_priority
extract_bits --> lock_state
lock_state --> update_ctrlr
scan_used --> set_enable
update_ctrlr --> check_enable
write_ctlr --> extract_bits
Sources: src/vgic.rs(L70 - L95)
Enable Register Handling
The interrupt enable registers (VGICD_ISENABLER_SGI_PPI and VGICD_ISENABLER_SPI) are handled through delegation to wider access methods:
- 8-bit writes to enable registers delegate to
handle_write32() - 16-bit writes to enable registers delegate to
handle_write32() - 32-bit writes to enable registers are handled directly but currently contain placeholder logic
Sources: src/vgic.rs(L96 - L132)
Physical Hardware Integration
The Vgic coordinates with the physical ARM GIC hardware through the arm_gicv2::GicInterface to ensure that virtual interrupt state changes are reflected in the underlying hardware configuration.
Hardware Interface Operations
| Operation | Method | Purpose |
|---|---|---|
| Enable/Disable | GicInterface::set_enable(irq_id, enabled) | Control interrupt enable state |
| Priority Setting | GicInterface::set_priority(irq_id, priority) | Set interrupt priority level |
The integration logic scans the used_irq bitmap to identify allocated interrupts and applies configuration changes only to interrupts that are actively managed by the virtual controller.
Sources: src/vgic.rs(L5) src/vgic.rs(L82 - L92)
Initialization and Construction
The Vgic::new() constructor initializes all internal state arrays to zero values and creates an empty vector for CPU interface objects:
flowchart TD
subgraph subGraph1["Default Values"]
zero_ctrlr["ctrlr = 0"]
zero_arrays["All arrays = [0; SIZE]"]
empty_gicc["gicc = Vec::new()"]
end
subgraph subGraph0["Constructor Process"]
new_call["Vgic::new()"]
create_mutex["Create Mutex"]
init_arrays["Initialize State Arrays"]
empty_vec["Empty gicc Vec"]
end
create_mutex --> init_arrays
empty_vec --> empty_gicc
init_arrays --> empty_vec
init_arrays --> zero_arrays
init_arrays --> zero_ctrlr
new_call --> create_mutex
The constructor ensures that the virtual interrupt controller starts in a clean, disabled state with no allocated interrupts or configured CPU interfaces.
Sources: src/vgic.rs(L37 - L54)