Generic Interrupt Controller
The Generic Interrupt Controller is a standardized component of modern ARM boards, and it provides a solid interrupt handling scheme for embedded systems. Various different versions of the GIC specification exist. The purpose of this article is to provide a quick reference for the ARM Generic Interrupt Controller's behaviour.
This article is current with version 2 of the specification and has not been updated to match version 3.
- IRQs are divided into secure and non-secure IRQs, pertaining to the secure and non-secure worlds.
- By default all IRQs belong to the secure world, in the same manner that by default, SCR.NS is set to 0. Essentially if the running kernel is not secure-world aware, it will not observe any effects from the fact that it is running in secure world.
- GIC supports three types of IRQs: external, "internal" (more later) and inter-processor.
- Both version 1 and 2 GICs support an optional Security Extension feature set.
- Only version 2 GICs support an optional Virtualization Extension feature set.
Differences between a version 1 GIC and a version 2 GIC
For a GIC version 1 implementation:
- For a version 1 GIC, "Interrupt grouping" refers only to the grouping of IRQs as being either secure (group0) or non-secure (group1). By default, on #reset of the GIC, if a GIC supports IRQ grouping, all IRQs are assigned to group0, and the FIQ exception request is disabled (section 3.4.4).
- There is no support for GIC virtualization. If the GIC on your board is version 1, then you must trap and emulate all accesses to the GIC from the guest. With GIC version 2, the GIC handles a portion of the emulation in hardware.
- There is no builtin support for disabling IRQ signal bypass in the GIC. If legacy IRQs are connected to the CPU in a bypass or redundant arrangement, they must be disabled using some implementation defined fashion.
Both version 1 and version 2 support an optional Security Extensions feature. Only version 2 GICs support an optional Virtualization Extensions feature.
For a GIC version 2 implementation:
- Version 2 GIC implementations have a fuller set of features and capabilities added to the "Interrupt grouping" feature. Of note, group0 IRQs can be routed to the CPU as both IRQs and FIQs, whereas group1 IRQs can only be delivered as IRQs.
- Version 2 GICs support an optional Virtualization Extensions feature. This feature supports hardware emulation of the GIC for guest VMs. Along with these extensions comes support for hardware save and restore of GIC state (for supporting multiple guest VMs).
- Version 2 GICs support splitting the handling of IRQs into two stages: "Priority Drop" and "Interrupt Deactivation". The purpose of this split is to allow a Hypervisor to receive a greater number of actual physical IRQs, and dispatch them to guest VMs, and drop the priority of those IRQs the hypervisor has dispatched, without completely deactivating them. See the EOI section below.
- Support for disabling IRQ bypass signals at the GIC CPU Interface.
According to section 5.1, the ARM model is designed to assume that secure IRQs are delivered as FIQ-signals to the processor, and non-secure IRQs are delivered as IRQ-signals to the processor: <pr> In the ARM model for virtualizing Non-secure operation of a processor that implements the ARM Virtualization Extensions, Secure software on the processor must configure the system as described in Using IRQs and FIQs to provide Non-secure and Secure interrupts on page 3-68, so that FIQs are used for Secure interrupts, and IRQs for Non-secure interrupts. </pr>
The specification defines IRQs as being in one of the following states:
- Inactive: not asserted.
- Pending: This IRQ signal has been asserted by its hardware device, but not delivered to a CPU as yet.
- Active: This IRQ signal has been asserted and delivered to a processor, but the processor has not yet finished servicing it.
- Active and pending: This IRQ signal has been asserted and delivered to a processor, but the processor has not yet finished servicing it, and the signal has been re-asserted by a device.
For SGIs, PPIs and SPIs, when a CPU acknowledges an IRQ (Reads from GICC_IAR/GICC_AIAR), the Distributor changes the status of the IRQ from active to pending if it is a level-triggered IRQ and the device has deasserted the level on the line. The Distributor changes the status of the IRQ from active to "active and pending" if it is a level-triggered IRQ and the device has not deasserted the level on the line.
For SGIs, PPIs and SPIs, when a CPU acknowledges an IRQ (Reads from GICC_IAR/GICC_AIAR), the Distributor changes the status of the IRQ from active to pending if it is an edge-triggered IRQ and the Distributor has not detected another edge on the line. The Distributor changes the status of the IRQ from active to "active and pending" if it is a level-triggered IRQ and the Distributor has detected another edge on the line.
IRQ types and properties
Four categories of IRQs exist:
These IRQs are asserted by external or internal devices. External devices are those which exist on the chipset and are not part of the processor itself. Internal devices are devices such as the ARM Generic Timers which are part of the ARM processor specification itself.
External IRQs are called Shared Peripheral Interrupts by the specification, and internally generated IRQs are called Private Peripheral Interrupts by the specification.
Both types of peripheral IRQ can be either level or edge sensitive.
Software Generated IRQs
These are ARM's notion of Inter-processor Interrupts (IPIs). They are always edge sensitive.
When an SGI occurs, the "CPUID" field of the GIC Interrupt Acknowledge Register, or the GIC Aliased Interrupt Acknowledge Register contains the CPUID of the processor that requested the IRQ.
These are IRQs that have been injected by a hypervisor into a guest VM's Virtual-GIC.
GIC Maintenance IRQs
These are used to signal changes to the hypervisor, when the guest VM changes the state of its VGIC. Maintenance IRQs are always level-sensitive.
GIC implementations enable the chipset to connect connect IRQs directly to the CPU when the GICC is disabled. In this setup, nIRQ and nFIQ can be directly tied to some other IRQ controller or signal. In order to disable the GIC, the chipset must drive GICCDISABLE to high. This is somewhat analogous to ExtINT on x86 CPUs.
For SGIs, an IRQ is identified by the combination of the source CPUID, target CPUID and the IRQ number (0-15 are the IDs that can be used for an SGI). The GIC ignores accesses to its registers from any CPU in the system that is not connected to a CPU interface. In a uniprocessor system, the source of an SGI will always be 0. ARM recommends that system software splits the SGI IRQ IDs into two ranges: 0-7 for secure SGIs and 8-15 for non-secure SGIs.
An SGI can be generated such that it targets multiple processors, but presents itself with the same IRQ ID to each processor it targets. The SGI IRQ configuration registers are banked at the Distributor.
A spurious IRQ is one where the GIC determines that an IRQ it has delivered to a processor does not need to be (and should not be) EOId. If this occurs, the GIC will return the value 1023 as the IRQ ID, when the GICC_IAR register is read.
When using the 1-N model for an IRQ, the version 2 GIC must ensure (Section 3.2.3) that only the first CPU to read GICC_IAR gets the IRQ's correct ID, and the rest of the CPUs, if any others got the IRQ, must get the spurious IRQ value. The version 1 GIC does not have this constraint specified, and it may return the correct ID to more than one processor.
A spurious IRQ ID may also be returned when IRQ grouping is being used, and the highest priority pending IRQ is a group0 IRQ, but secure-world software reads GICC_AIAR instead of GICC_IAR. Additionally, in general, any read of the GICC_IAR from a world that does not match the security of the highest priority active IRQ "does not acknowledge any interrupt", and will return:
- 1022 for a Secure read when the highest priority interrupt is Non-secure
- 1023 for a Non-secure read when the highest priority interrupt is Secure.
So if by chance non-secure software gets an IRQ which should be delivered to secure-world, the non-secure software will see it as a spurious IRQ (1023). If secure world gets an IRQ that pertains to non-secure world, it will read a special value (1022) to indicate to it that the IRQ is not for it; If the secure software still wishes to handle the IRQ, it can then acknowledge it by instead reading GICC_AIAR.
ARM "strongly recommends" (section 3.4.3) using a software IRQ handling model where GICC_CTLR.AckCtl == 0. In such a setup, every secure world IRQ must be higher priority than every non-secure IRQ.
An IRQ is taken from inactive to pending when it is asserted by its device. The IRQ is then taken from pending to active when the CPU reads the Interrupt Acknowledge Register. Finally, the IRQ is fully retired and the EOI signal is sent, when the CPU writes to the End of Interrupt register.
These steps apply also for Virtual GICs, which behave, from the guest VM's perspective, as if they are no different from a physical GIC. However, for a VGIC, each of these steps may also update the List registers. Additionally, for a virtual IRQ that was delivered to a guest VM through an update of the List registers of the virtual Distributor, the EOI that the guest VM does at the VGIC automatically propagates through to the physical Distributor (Section 5.2):
<quote> When the virtual machine handles a virtual interrupt, it writes to the virtual CPU interface to indicate when it has finished this processing. The virtual CPU interface signals this completion to the physical Distributor and the physical Distributor then deactivates the interrupt. </quote>
The split IRQ handling feature splits IRQ handling into two stages:
- Priority drop: when a hypervisor receives a physical IRQ, it immediately dispatches a virtual IRQ to the VGIC of the guest VM. Then it sends an EOI by writing to the GICC_EOIR or GICC_AEOIR. The running priority of the physical GIC is lowered by the EOI, so that if the IRQ's priority was high, the IRQ won't continue to block lower priority IRQs from being received by the hypervisor.
- The guest VM then handles the virtual IRQ and sends its own EOI by writing to GICV_EOIR or GICV_AEOIR. If GICV_CTRLR.EOIMode == 0, this will automatically cause a write to GICV_DIR as well, which will then cascade a write to GICC_DIR. If GICV_CTRLR.EOIMode != 0, the guest VM will have to also issue a write to GICV_DIR to deactivate the virtual IRQ, and this will then also automatically cascade a write to GICC_DIR.
IRQ splitting is enabled by setting GICH_LRn.HW to 1. If GICH_LRn.HW is not set to 1, the hypervisor must manually deactivate the physical IRQ when it has been handled by the guest VM. This can be done with the assistance of an EOI Maintenance IRQ.
The Hypervisor must maintain a set "List" registers in the GICH.
CPU Interface register access
In a GIC that supports the Virtualization Extensions, access to the VGIC CPU Interface for each CPU is mandated by the specification via both the following mechanisms:
- Common base address: Each processor can access its VGIC at the same fixed physical address, and the GIC hardware logic will determine which VGIC register set should be accessed based on the CPUID of the requesting processor.
- Processor-specific base address: Each VGIC is also mapped at its own physical address, and this allows one CPU to access the VGIC of any other CPU.