Handling interrupts

This section describes what happens when an interrupt occurs: how the interrupt is routed to a PE, how interrupts are prioritized against each other, and what happens at the end of the interrupt, for example.

Routing a pending interrupt to a PE

The Interrupt state machine section describes how an interrupt transitions from the inactive to the pending state when the source of the interrupt is asserted. When an interrupt becomes pending, the interrupt controller decides whether to send the interrupt to one of the connected PEs based on the following tests. These tests determine which PE, if any, to send the interrupt to:

  • Check that the group associated with the interrupt is enabled.
    The Security model section described how each INTID is assigned to a Group: Group 0, Secure Group 1, or Non-secure Group 1. For each Group, there is a Group enable bit in both the Distributor and in each CPU Interface.
    The interrupt controller checks that the Group enable bit is set for the Group associated with the INTID for that interrupt.
    An interrupt that is a member of a disabled Group cannot be signaled to a PE. These interrupts remain in the pending state until the group is enabled.
  • Check that the interrupt is enabled.
    Individually disabled interrupts can become pending but will not be forwarded to a PE.
  • Check the routing controls to decide which PEs can receive the interrupt.
    Which PEs can receive an interrupt depends on what type of interrupt is being sent:
    • For Shared Peripheral Interrupts (SPIs), routing is controlled by GICD_IROUTERn. An SPI can target one specific PE, or any one of the connected PEs.
    • For Locality-specific Peripheral Interrupts (LPIs), the routing information comes from the ITS.
    • Private Peripheral Interrupts (PPIs) are specific to one PE and can only be handled by that PE.
    • For Software Generated Interrupts (SGIs), the originating PE defines the list of target PEs. This is described further in Sending and receiving Software Generated Interrupts.
  • Check the interrupt priority and priority mask to decide which PEs are suitable to handle the interrupt.
    Each PE has a Priority Mask register, ICC_PMR_EL1, in its CPU interface. This register sets the minimum priority that is required for an interrupt to be forwarded to that PE. Only interrupts with a higher priority than the mask are signaled to the PE.
  • Check the running priority to decide which PEs are available to handle the interrupt.
    Running priority and preemption covers running priority, and how this affects preemption. If the PE is not already handling an interrupt, the running priority is the idle priority: 0xFF. Only an interrupt with a higher priority than the running priority can preempt the current interrupt.

If the interrupt passes all these tests, it is forwarded to the appropriate core as an IRQ or FIQ exception. To learn more, see Setting the target PE for SPIs.

Taking an interrupt

When entering the exception handler, software does not know which interrupt it has taken. The handler must read one of the Interrupt Acknowledge Registers (IARs) to get the INTID of the interrupt.

There are two IARs:

Register Use
ICC_IAR0_EL1 Used to acknowledge Group 0 interrupts. Typically read in FIQ handlers.
ICC_IAR1_EL1 Used to acknowledge Group 1 interrupts. Typically used in IRQ handlers.

Reading an IAR returns the INTID of the taken interrupt and advances the state machine of the interrupt. Typically, the IARs are read on entry to an interrupt handler. However, software is free to read the registers at any time.

Sometimes, the IAR cannot return a valid INTID. For example, software reads ICC_IAR0_EL1, acknowledge Group 0 interrupts, but the pending interrupt belongs to Group 1. In this case, the read returns one of the reserved INTIDs, as shown in the following table:

ID Meaning Example scenario
1020 Only returned by reads of ICC_IAR0_EL1.
Highest pending interrupt is Secure Group 1.
Only seen in EL3.
An interrupt for the Trusted OS was signaled while the PE was executing in Non-secure state. This is taken as an FIQ to EL3, so that the Secure Monitor could context switch to the Trusted OS.
1021 Only returned by reads of ICC_IAR0_EL1.
Highest pending interrupt is Non-secure Group 1.
Only seen in EL3.
An interrupt for the rich OS was signaled while the PE was executing in Secure state. This would be taken as a FIQ to EL3, so that the Secure Monitor could context switch to the rich OS.
1022 Used only for legacy operation.
Legacy operation is beyond the scope of this guide.
1023 Spurious interrupt.
There are no enabled INTIDs in the pending state, or all INTIDs in that pending are of insufficient priority to be taken.
When polling the IARs, this value indicates that there are no interrupts to available to acknowledge.

A read of an IAR that returns one of these reserved values does not acknowledge an interrupt, if one is present.

Example of interrupt handling

The following diagram shows an example of a mobile system with a modem interrupt which signals an incoming phone call. This interrupt is intended to be handled by the Rich OS in the Non-secure state.

The steps involved in handling the interrupt are as follows:

  1. The modem interrupt becomes pending while the PE is executing the Trusted OS at Secure EL1. As the modem interrupt is configured as Non-secure Group 1, it will be signaled as an FIQ. With SCR_EL3.FIQ==1, the exception is taken to EL3.
  2. Secure Monitor software executing at EL3 reads the IAR, which returns 1021. This value indicates that the interrupt is expected to be handled in Non-secure state. The Secure Monitor then performs the necessary context switching operations.
  3. Now that the PE is in Non-secure state, the interrupt is re-signaled as an IRQ and taken to Non-secure EL1 to be handled by the Rich OS.

In this example, the Non-secure Group 1 interrupt caused an immediate exit from the Secure OS. This might not always be required or wanted. An alternative model for this example is shown in the following diagram, where the interrupt is initially taken to Secure EL1:

The steps involved in handling the interrupt are now as follows:

  1. The modem interrupt becomes pending while the PE is executing the Trusted OS at Secure EL1. Because the modem interrupt is configured as Non-secure Group 1, it will be signaled as an FIQ. With SCR_EL3.FIQ==0, the exception is taken to Secure EL1.
  2. The Trusted OS performs actions to tidy up its internal state. When it is ready, the Trusted OS uses an SMC instruction to yield to Non-secure state.
  3. The SMC exception is taken to EL3. The Secure Monitor software executing at EL3 performs the necessary context switching operations.
  4. Now that the PE is in Non-secure state, the interrupt is signaled as an IRQ and taken to Non-secure EL1 to be handled by the Rich OS.

Running priority and preemption

The Priority Mask register sets the minimum priority that an interrupt must have to be forwarded to the PE. The GICv3 architecture also has the concept of a running priority. When a PE acknowledges an interrupt, its running priority becomes the same as the priority of the interrupt. The running priority returns to its former value when the PE writes to one of the End of Interrupt (EOI) registers. The following diagram shows an example the running priority of a PE over time:

The current running priority is reported in the Running Priority register in the CPU interface: ICC_RPR_EL1.

The concept of running priority is important when considering preemption. Preemption occurs when a high priority interrupt is signaled to a PE that is already handling a lower priority interrupt. Preemption introduces some additional complexity for software, but it can prevent a low priority interrupt from blocking the handling of a higher priority interrupt.

The following diagram shows what would happen if preemption was not allowed:

The high priority interrupt is blocked until the previously signaled low priority interrupt is taken. Now consider the same situation, but with preemption enabled:

When the higher priority interrupt becomes pending, it preempts the previously signaled low priority interrupt. The preceding diagram shows one level of preemption. However, it is possible to have multiple levels of preemption.

The Arm CoreLink GICv3 architecture allows software to control preemption by specifying the difference in priority required for preemption to occur. This is controlled through the Binary Point registers: ICC_BPRn_EL1.

The Binary Point registers split the priority into two fields, group priority and sub-priority, as you can see here:

For preemption, only the group priority bits are considered. The sub-priority bits are ignored.

For example, consider the following three interrupts:

  • INTID A has priority 0x10.
  • INTID B has priority 0x20.
  • INTID C has priority 0x21.

In this example, we decided that:

  • A can preempt B or C.
  • B cannot preempt C, because B and C have similar priorities.

To achieve this, the split between Group and sub-priority could be set at N=4, as you can see here:

With this split, B and C now have the same priority for the purpose of preemption. However, A still has a higher priority, which means that it can preempt either B or C.

The Binary Point registers only affect preemption, that is, whether an interrupt should be signaled while already handling a different interrupt. When choosing between pending interrupts, the Binary Point registers are not used.

Note: Preemption requires that the interrupt handler, or handlers, are written to support nesting. Details of how to write such an interrupt handler are outside of the scope of this guide. To learn more, see the Learn the Architecture: Exception model guide.

End of interrupt

Once the interrupt has been handled, software must inform the interrupt controller that the interrupt has been handled, so that the state machine can transition to the next state. The Arm CoreLink GICv3 architecture treats this as two tasks:

  • Priority drop
    This means dropping the running priority back to the value that it had before the interrupt was taken.
  • Deactivation
    This means updating the state machine of the interrupt that is currently being handled. Typically, this will be a transition from the Active state to the Inactive state.

In the GICv3 architecture, priority drop and deactivation can happen together or separately. This is determined by the settings of ICC_CTLR_ELn.EOImode:

  • EOImode = 0
    A write to ICC_EOIR0_EL1 for Group 0 interrupts, or ICC_EOIR1_EL1 for Group 1 interrupts, performs both the priority drop and deactivation. This mode is often used for a simple bare metal environment.
  • EOImode = 1
    A write to ICC_EOIR0_EL1 for Group 0 interrupts, or ICC_EOIR1_EL1 for Group 1 interrupts results in a priority drop. A separate write to ICC_DIR_EL1 is required for deactivation. This mode is often used for virtualization purposes.

Most software will use EOIMode==0. EOImode==1 is most often used by hypervisors.

Checking the current state of the system

Highest priority pending interrupt and running priority

As their names suggest, the Highest Priority Pending Interrupt registers, ICC_HPPIR0_EL1 and ICC_HPPIR1_EL1, report the INTID of the highest priority pending interrupt for a PE.

Running priority was introduced in Running priority and preemption, and is reported by the Running Priority register (ICC_RPR_EL1).

State of individual INTIDs

The Distributor provides registers that indicate the current state of each SPI. Similarly, the Redistributors provide registers that indicate the state of PPIs and SGIs for their connected PEs.

These registers can also move an interrupt to a specific state. This can be useful, for example, for testing that the configuration is correct without requiring the peripheral to assert the interrupt.

There are separate registers to report the active state and the pending state. The following table lists the active state registers. The pending state registers have the same format:

Register Description
GICD_ISACTIVERn Sets the active state for SPIs.
One bit for each INTID.
Reads of a bit return the current state of the INTID:
  • 1 – The INTID is active.
  • 0 – The INTID is not active.

Writing 1 to a bit activates the corresponding INTID.
Writing 0 to a bit has not effect.
GICD_ICACTIVERn Clears the active state for SPIs.
One bit for each INTID.
Reads of a bit return the current state of the interrupt:
  • 1 – The INTID is active.
  • 0 – The INTID is not active.

Writing 1 to a bit deactivates the corresponding INTID.
Writing 0 to a bit has not effect.
GICR_ISACTIVERn Sets the active state for SGIs and PPIs.
One bit for each INTID. This register covers INTIDs 0 to 31, which are private to each PE.
Reads of a bit return the current state of the interrupt:
  • 1 – The INTID is active.
  • 0 – The INTID is not active.

Writing 1 to a bit activates the corresponding INTID.
Writing 0 to a bit has not effect.
GICR_ICACTIVERn Clears the active state for SGIs and PPIs.
One bit for each INTID. This register covers INTIDs 0 to 31, which are private to each PE.
Reads of a bit return the current state of the interrupt
  • 1 – The INTID is active.
  • 0 – The INTID is not active.

Writing 1 to a bit deactivates the corresponding INTID.
Writing 0 to a bit has not effect.

Note: Software executing in Non-secure state cannot see the state of Group 0 or Secure Group 1 interrupts, unless access is permitted by GICD_NASCRn or GICR_NASCRn.

Previous Next