27. Secure IRQ handling

The Armv8-M Architecture makes it possible to configure interrupts to target secure state.

TF-M makes it possible for secure partitions to get notified of secure interrupts.

By default TF-M sets up interrupts to target NS state. To configure an interrupt to target secure state and assign a handler to it, the manifest of the partition must be edited.

See the following example:

{
  "name": "...",
  "type": "...",
  "priority": "...",

  ...

  "irqs": [
    {
      "source": "5",
      "signal": "DUAL_TIMER"
    },
    {
      "source": "TFM_IRQ_LINE_TIMER_1",
      "signal": "TIMER_1"
      "tfm_irq_priority": 64,
    }
  ],

  ...

}

To set up a handler in a partition, the irqs node must be added. A single secure partition can have handlers registered for multiple IRQs, in this case the list irqs has multiple elements in it.

An IRQ handler is defined by the following nodes:

  • source: The IRQ number or the name of the IRQ line. With the name of the IRQ line, there must be defined a macro in tfm_peripherals_def.h which is substituted to the IRQ line num. The IRQ line nums and sources are defined by each platform: for example, they are defined in platform_irq.h for the Musca-S1 platform. When defining new macros in tfm_peripherals_def.h, it is important the macro name matches the platform’s handler function for that IRQ source.

  • signal: The name of the signal for this IRQ.

  • tfm_irq_priority: The priority of the IRQ. This number must be in the range [0-255] inclusive. Please note that some of the less significant bits of this value might be dropped based on the number of priority bits implemented in the platform.

Important

The name of the privileged interrupt handler is derived from the node specifying the IRQ line number.

  • In case source is IRQ number, the name of the handler becomes void irq_<number>_Handler(void).

  • In case source is defined IRQ macro, the name of the handler becomes void <macro>_Handler(void).

This is important, because the derived name has to be present in the vector table as the handler of the IRQ. The platform startup functions are specified in the vector table defined in the platform secure startup file. The user should verify the names of the generated handlers match for a given platform IRQ.

Note

signal and source are mandatory.

tfm_irq_priority is optional. If tfm_irq_priority is not set for an IRQ, the default is value is TFM_DEFAULT_SECURE_IRQ_PRIOTITY.

If an IRQ handler is registered, TF-M will:

  • Set the IRQ with number or macro to target secure state

  • Set the priority of IRQ with number or macro to tfm_irq_priority or to the default.

TF-M configures the interrupt lines to be disabled by default. Interrupts for a service can be enabled by the secure service by calling void tfm_enable_irq(psa_signal_t irq_signal). The function can be called in the service init function.

27.1. Library model

In Library model a function with the name derived from the value of the source property is generated. This function will be put in the vector table by the linker (as the handlers in the startup assembly are defined as weak symbols). The code generated for this function will forward the call to the function with the name of the value of the signal property post-fixed with _isr.

Hint

for a signal "signal": "DUAL_TIMER" the name of the handler function is DUAL_TIMER_isr

The signature of the IRQ handler in the partition must be the following:

void partition_irq_handler(void);

The detailed description on how secure interrupt handling works in the Library model see Secure Partition Interrupt Handling design document.

27.2. IPC model

The detailed description on how secure interrupt handling works in the IPC model, see the PSA Firmware Framework and RoT Services specification.

27.2.1. Implementation details

27.3. Library model implementation

As a result of the function call like behaviour of secure services in library model, some information that is critical for the SPM to keep track of partition states, is stored on the stack of the active partitions. When an interrupt happens, and a handler partition is set to running state, it has access to its whole stack, and could corrupt the data stacked by the SPM. To prevent this, a separate Context stack is introduced for each secure partition, that is used by the SPM to save this information before starting to execute secure partition code.

A stack frame to this context stack is pushed when the execution in the partition is interrupted, and when a handler in the partition interrupts another service. So the maximal stack usage can happen in the following situation:

Consider secure partition ‘A’. ‘A’ is running, and then it is interrupted by an other partition. Then the lowest priority interrupt of ‘A’ is triggered. Then before the handler returns, the partition is interrupted by another partition’s handler. Then before the running handler returns, the second lowest interrupt of ‘A’ is triggered. This can go until the highest priority interrupt of ‘A’ is triggered, and then this last handler is interrupted. At this point the context stack looks like this:

+------------+
| [intr_ctx] |
| [hndl_ctx] |
| .          |
| .          |
| .          |
| [intr_ctx] |
| [hndl_ctx] |
| [intr_ctx] |
+------------+

Legend:
  [intr_ctx]: Frame pushed when the partition is interrupted
  [hndl_ctx]: Frame pushed when the partition is handling an interrupt

So the max stack size can be calculated as a function of the IRQ count of ‘A’:

max_stack_size = intr_ctx_size + (IRQ_CNT * (intr_ctx_size + hndl_ctx_size))

Copyright (c) 2018-2021, Arm Limited. All rights reserved.