Interrupt controller kernel module
The interrupt controller kernel module transfers logic from startup-provided callouts to the kernel, allowing the kernel to make better interrupt handling decisions. This module allows for less contention on concurrent interrupt handling on multiple cores. This is in part due to the ability of the kernel module to make smarter decisions (e.g., when serializing access to the interrupt controller's resources) than the stock kernel that invokes opaque callouts provided by startup. A prime example is the clock interrupt, which can easily be triggered at the same time on multiple cores.
The interrupt controller modules depend on startup to set up the controller hardware and provide the matching intrinfo entries for the root controller and cascades. Currently, the modules only exist for aarch64 processors with a GICv2 or GICv3. GICv4 is backwards compatible with GICv3 and treated as such.
GICv2
The GICv2 module supports the following interrupts as separate entries:
-
private peripheral interrupts (PPI) (which include software generated interrupts (SGI))
-
shared peripheral interrupts (SPI)
The module depends on a startup that splits the PPIs and SPIs into different intrinfo entries. You can use the latest libstartup to build the startup.
To use the GICv2 module, add the following attribute to the procnto line in your buildfile:
[ module=gicv2 ]
GICv3
The GICv3 module supports the following interrupts as separate entries:
-
private peripheral interrupts (PPI) (which include software generated interrupts (SGI))
-
shared peripheral interrupts (SPI)
- external SPIs
- external PPIs
-
locality-specific peripheral interrupts (LPI) via a translation table (ITS). In other words, the GICv3 module doesn't support direct LPIs.
The CPU interface (GICC) is only supported via system registers
(ICC_xxx
), not via memory-mapped
registers.
For LPIs, startup must provide the callouts for mask and unmask operations. LPI mask and unmask operation must use the ITS command queue for invalidating the configuration cache, which is a resource shared between cores.
For interprocessor interrupts (IPIs), startup must provide a value in the
SYSPAGE_CPU_ENTRY(aarch64, gic_map)->gic_cpu[]
array for each
target CPU that the source CPU can write to icc_sgi1r_el1
. This
triggers the IPI on the target CPU.
To use the GICv3 module, add the following attribute to the procnto line in your buildfile:
[ module=gicv3 ]
Is your GIC module loaded?
To check if you have a GIC module loaded, enter:
# cat /proc/config
[...]
intrctlr:gicv2
Your system will display intrctlr:gicv3
if a GICv3 module is loaded,
or intrctlr:N/A
if no module is loaded.
# cat /proc/ker/intr
gicv2 /* module name */
2025/01/23-14:27:09EST /* date of build */
spurious: 0 /* number of spurious interrupts */
Cascades
The modules see child controllers as opaque, and the controllers rely on startup to provide the callouts and configuration. Therefore, the modules treat these controllers as a shared resource when delivering their interrupts, and masking and unmasking them. For both GICv2 and GICv3 modules, child controllers should be hooked to an SPI.
The module supports all GENFLAG flags for child controllers, except for INTR_GENFLAG_ID_LOOP.
Caveats
The GIC modules only replaces the root controller, which matches several intrinfo entries (e.g., PPIs and SPIs). Each type of interrupt must be its own intrinfo entry.
Legacy GICv2 support in startup from libstartup bundles PPIs and SPIs in a single intrinfo. Ensure that your startup uses the latest libstartup to split them. For example:
Section:intrinfo offset:0x00000e68 size:0x000000c0 elsize:0x00000060
0) vector_base:00000000, #vectors:32, cascade_vector:7fffffff <--- PPIs: base: 0, 32 vectors
cpu_intr_base:00000000, cpu_intr_stride:0, flags:0000, local_stride:32768
id => flags:0000, size:0030, rtn:ffffff80402050a8
eoi => flags:0000, size:0018, rtn:ffffff80402050d8
mask:ffffff80402050f0, unmask:ffffff8040205120, config:ffffff8040205150
1) vector_base:00000020, #vectors:128, cascade_vector:7fffffff <--- SPIs: base: 32, n vectors
cpu_intr_base:00000000, cpu_intr_stride:0, flags:0000, local_stride:0
id => flags:0000, size:0010, rtn:ffffff8040205160
eoi => flags:0000, size:0018, rtn:ffffff8040205170
mask:ffffff8040205188, unmask:ffffff80402051bc, config:0000000000000000
GICv3 startups typically split the different interrupt types.
The INTR_GENFLAG_ID_LOOP flag isn't supported for cascades and the root controller won't use the flag for itself either.