Document design of the Interrupt Mangement Framework

This patch adds documentation that describes the design of the Interrupt
management framework in the ARM Trusted Firmware. The porting-guide.md has also
been updated to describe the interface that should be implemented by each
platform to support this framework.

Change-Id: I3eda48e5c9456e6a9516956bee16a29e366633b7
Co-Authored-By: Soby Mathew <soby.mathew@arm.com>
This commit is contained in:
Achin Gupta 2014-06-02 22:27:36 +01:00 committed by Dan Handley
parent eab932bf6a
commit a4fa3cb159
5 changed files with 989 additions and 3 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

View File

@ -32,6 +32,10 @@ Trusted Firmware runtime services via the ARM SMC (Secure Monitor Call)
instruction. The SMC instruction must be used as mandated by the [SMC Calling
Convention PDD][SMCCC] [3].
The ARM Trusted Firmware implements a framework for configuring and managing
interrupts generated in either security state. The details of the interrupt
management framework and its design can be found in [ARM Trusted
Firmware Interrupt Management Design guide][INTRG] [4].
2. Cold boot
-------------
@ -1296,6 +1300,7 @@ kernel at boot time. These can be found in the `fdts` directory.
3. [SMC Calling Convention PDD (ARM DEN 0028A)][SMCCC].
4. [ARM Trusted Firmware Interrupt Management Design guide][INTRG].
- - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1306,4 +1311,5 @@ _Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved._
[PSCI]: http://infocenter.arm.com/help/topic/com.arm.doc.den0022b/index.html "Power State Coordination Interface PDD (ARM DEN 0022B.b)"
[SMCCC]: http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html "SMC Calling Convention PDD (ARM DEN 0028A)"
[UUID]: https://tools.ietf.org/rfc/rfc4122.txt "A Universally Unique IDentifier (UUID) URN Namespace"
[User Guide]: user-guide.md
[User Guide]: ./user-guide.md
[INTRG]: ./interrupt-framework-design.md

View File

@ -0,0 +1,848 @@
ARM Trusted Firmware Interrupt Management Design guide
======================================================
Contents :
1. Introduction
* Assumptions
* Concepts
- Interrupt Types
- Routing Model
- Valid Routing Models
+ Secure-EL1 Interrupts
+ Non-secure Interrupts
- Mapping of Interrupt Type to Signal
2. Interrupt Management
* Software Components
* Interrupt Registration
- EL3 Runtime Firmware
- Secure Payload Dispatcher
+ Test Secure Payload Dispatcher behavior
- Secure Payload
+ Secure Payload IHF design w.r.t Secure-EL1 interrupts
+ Secure Payload IHF design w.r.t Non-secure interrupts
+ Test Secure Payload behavior
* Interrupt Handling
- EL3 Runtime Firmware
- Secure Payload Dispatcher
+ Interrupt Entry
+ Interrupt Exit
+ Test Secure Payload Dispatcher behavior
- Secure Payload
+ Test Secure Payload behavior
1. Introduction
----------------
This document describes the design of the Interrupt management framework in ARM
Trusted Firmware. This section briefly describes the requirements from this
framework. It also briefly explains some concepts and assumptions. They will
help in understanding the implementation of the framework explained in
subsequent sections.
This framework is responsible for managing interrupts routed to EL3. It also
allows EL3 software to configure the interrupt routing behavior. Its main
objective is to implement the following two requirements.
1. It should be possible to route interrupts meant to be handled by secure
software (Secure interrupts) to EL3, when execution is in non-secure state
(normal world). The framework should then take care of handing control of
the interrupt to either software in EL3 or Secure-EL1 depending upon the
software configuration and the GIC implementation. This requirement ensures
that secure interrupts are under the control of the secure software with
respect to their delivery and handling without the possibility of
intervention from non-secure software.
2. It should be possible to route interrupts meant to be handled by
non-secure software (Non-secure interrupts) to the last executed exception
level in the normal world when the execution is in secure world at
exception levels lower than EL3. This could be done with or without the
knowledge of software executing in Secure-EL1/Secure-EL0. The choice of
approach should be governed by the secure software. This requirement
ensures that non-secure software is able to execute in tandem with the
secure software without overriding it.
### 1.1 Assumptions
The framework makes the following assumptions to simplify its implementation.
1. All secure interrupts are handled in Secure-EL1. They can be delivered to
Secure-EL1 via EL3 but they cannot be handled in EL3. It will be possible
to extend the framework to handle secure interrupts in EL3 in the future.
2. Interrupt exceptions (`PSTATE.I` and `F` bits) are masked during execution
in EL3.
### 1.2 Concepts
#### 1.2.1 Interrupt types
The framework categorises an interrupt to be one of the following depending upon
the exception level(s) it is handled in.
1. Secure EL1 interrupt. This type of interrupt can be routed to EL3 or
Secure-EL1 depending upon the security state of the current execution
context. It is always handled in Secure-EL1.
2. Non-secure interrupt. This type of interrupt can be routed to EL3,
Secure-EL1, Non-secure EL1 or EL2 depending upon the security state of the
current execution context. It is always handled in either Non-secure EL1
or EL2.
3. EL3 interrupt. This type of interrupt can be routed to EL3 or Secure-EL1
depending upon the security state of the current execution context. It is
always handled in EL3.
In the current implementation of the framework, all secure interrupts are
treated as Secure EL1 interrupts. It will be possible for EL3 software to
configure a secure interrupt as an EL3 interrupt in future implementations. The
following constants define the various interrupt types in the framework
implementation.
#define INTR_TYPE_S_EL1 0
#define INTR_TYPE_EL3 1
#define INTR_TYPE_NS 2
#### 1.2.2 Routing model
A type of interrupt can be either generated as an FIQ or an IRQ. The target
exception level of an interrupt type is configured through the FIQ and IRQ bits
in the Secure Configuration Register at EL3 (`SCR_EL3.FIQ` and `SCR_EL3.IRQ`
bits). When `SCR_EL3.FIQ`=1, FIQs are routed to EL3. Otherwise they are routed
to the First Exception Level (FEL) capable of handling interrupts. When
`SCR_EL3.IRQ`=1, IRQs are routed to EL3. Otherwise they are routed to the
FEL. This register is configured independently by EL3 software for each security
state prior to entry into a lower exception level in that security state.
A routing model for a type of interrupt (generated as FIQ or IRQ) is defined as
its target exception level for each security state. It is represented by a
single bit for each security state. A value of `0` means that the interrupt
should be routed to the FEL. A value of `1` means that the interrupt should be
routed to EL3. A routing model is applicable only when execution is not in EL3.
The default routing model for an interrupt type is to route it to the FEL in
either security state.
#### 1.2.3 Valid routing models
The framework considers certain routing models for each type of interrupt to be
incorrect as they conflict with the requirements mentioned in Section 1. The
following sub-sections describe all the possible routing models and specify
which ones are valid or invalid. Only the Secure-EL1 and Non-secure interrupt
types are considered as EL3 interrupts are currently unsupported (See 1.1). The
terminology used in the following sub-sections is explained below.
1. __CSS__. Current Security State. `0` when secure and `1` when non-secure
2. __TEL3__. Target Exception Level 3. `0` when targeted to the FEL. `1` when
targeted to EL3.
##### 1.2.3.1 Secure-EL1 interrupts
1. __CSS=0, TEL3=0__. Interrupt is routed to the FEL when execution is in
secure state. This is a valid routing model as secure software is in
control of handling secure interrupts.
2. __CSS=0, TEL3=1__. Interrupt is routed to EL3 when execution is in secure
state. This is a valid routing model as secure software in EL3 can
handover the interrupt to Secure-EL1 for handling.
3. __CSS=1, TEL3=0__. Interrupt is routed to the FEL when execution is in
non-secure state. This is an invalid routing model as a secure interrupt
is not visible to the secure software which violates the motivation behind
the ARM Security Extensions.
4. __CSS=1, TEL3=1__. Interrupt is routed to EL3 when execution is in secure
state. This is a valid routing model as secure software in EL3 can
handover the interrupt to Secure-EL1 for handling.
##### 1.2.3.2 Non-secure interrupts
1. __CSS=0, TEL3=0__. Interrupt is routed to the FEL when execution is in
secure state. This allows the secure software to trap non-secure
interrupts, perform its bookeeping and hand the interrupt to the
non-secure software through EL3. This is a valid routing model as secure
software is in control of how its execution is pre-empted by non-secure
interrupts.
2. __CSS=0, TEL3=1__. Interrupt is routed to EL3 when execution is in secure
state. This is a valid routing model as secure software in EL3 can save
the state of software in Secure-EL1/Secure-EL0 before handing the
interrupt to non-secure software. This model requires additional
coordination between Secure-EL1 and EL3 software to ensure that the
former's state is correctly saved by the latter.
3. __CSS=1, TEL3=0__. Interrupt is routed to FEL when execution is in
non-secure state. This is an valid routing model as a non-secure interrupt
is handled by non-secure software.
4. __CSS=1, TEL3=1__. Interrupt is routed to EL3 when execution is in
non-secure state. This is an invalid routing model as there is no valid
reason to route the interrupt to EL3 software and then hand it back to
non-secure software for handling.
#### 1.2.4 Mapping of interrupt type to signal
The framework is meant to work with any interrupt controller implemented by a
platform. A interrupt controller could generate a type of interrupt as either an
FIQ or IRQ signal to the CPU depending upon the current security state.The
mapping between the type and signal is known only to the platform. The framework
uses this information to determine whether the IRQ or the FIQ bit should be
programmed in `SCR_EL3` while applying the routing model for a type of
interrupt. The platform provides this information through the
`plat_interrupt_type_to_line()` API (described in the [Porting
Guide]). For example, on the FVP port when the platform uses an ARM GICv2
interrupt controller, Secure-EL1 interrupts are signalled through the FIQ signal
while Non-secure interrupts are signalled through the IRQ signal. This applies
when execution is in either security state.
2. Interrupt management
-----------------------
The following sections describe how interrupts are managed by the interrupt
handling framework. This entails:
1. Providing an interface to allow registration of a handler and specification
of the routing model for a type of interrupt.
2. Implementing support to hand control of an interrupt type to its registered
handler when the interrupt is generated.
Both aspects of interrupt management involve various components in the secure
software stack spanning from EL3 to Secure-EL1. These components are described
in the section 2.1. The framework stores information associated with each type
of interrupt in the following data structure.
```
typedef struct intr_type_desc {
interrupt_type_handler_t handler;
uint32_t flags;
uint32_t scr_el3[2];
} intr_type_desc_t;
```
The `flags` field stores the routing model for the interrupt type in
bits[1:0]. Bit[0] stores the routing model when execution is in the secure
state. Bit[1] stores the routing model when execution is in the non-secure
state. As mentioned in Section 1.2.2, a value of `0` implies that the interrupt
should be targeted to the FEL. A value of `1` implies that it should be targeted
to EL3. The remaining bits are reserved and SBZ. The helper macro
`set_interrupt_rm_flag()` should be used to set the bits in the `flags`
parameter.
The `scr_el3[2]` field also stores the routing model but as a mapping of the
model in the `flags` field to the corresponding bit in the `SCR_EL3` for each
security state.
The framework also depends upon the platform port to configure the interrupt
controller to distinguish between secure and non-secure interrupts. The platform
is expected to be aware of the secure devices present in the system and their
associated interrupt numbers. It should configure the interrupt controller to
enable the secure interrupts, ensure that their priority is always higher than
the non-secure interrupts and target them to the primary CPU. It should also
export the interface described in the the [Porting Guide][PRTG] to enable
handling of interrupts.
In the remainder of this document, for the sake of simplicity it is assumed that
the FIQ signal is used to generate Secure-EL1 interrupts and the IRQ signal is
used to generate non-secure interrupts in either security state.
### 2.1 Software components
Roles and responsibilities for interrupt management are sub-divided between the
following components of software running in EL3 and Secure-EL1. Each component is
briefly described below.
1. EL3 Runtime Firmware. This component is common to all ports of the ARM
Trusted Firmware.
2. Secure Payload Dispatcher (SPD) service. This service interfaces with the
Secure Payload (SP) software which runs in exception levels lower than EL3
i.e. Secure-EL1/Secure-EL0. It is responsible for switching execution
between software running in secure and non-secure states at exception
levels lower than EL3. A switch is triggered by a Secure Monitor Call from
either state. It uses the APIs exported by the Context management library
to implement this functionality. Switching execution between the two
security states is a requirement for interrupt management as well. This
results in a significant dependency on the SPD service. ARM Trusted
firmware implements an example Test Secure Payload Dispatcher (TSPD)
service.
An SPD service plugs into the EL3 runtime firmware and could be common to
some ports of the ARM Trusted Firmware.
3. Secure Payload (SP). On a production system, the Secure Payload corresponds
to a Secure OS which runs in Secure-EL1/Secure-EL0. It interfaces with the
SPD service to manage communication with non-secure software. ARM Trusted
Firmware implements an example secure payload called Test Secure Payload
(TSP) which runs only in Secure-EL1.
A Secure payload implementation could be common to some ports of the ARM
Trusted Firmware just like the SPD service.
### 2.2 Interrupt registration
This section describes in detail the role of each software component (see 2.1)
during the registration of a handler for an interrupt type.
#### 2.2.1 EL3 runtime firmware
This component declares the following prototype for a handler of an interrupt type.
typedef uint64_t (*interrupt_type_handler_t)(uint32_t id,
uint32_t flags,
void *handle,
void *cookie);
The value of the `id` parameter depends upon the definition of the
`IMF_READ_INTERRUPT_ID` build time flag. When the flag is defined, `id` contains
the number of the highest priority pending interrupt of the type that this
handler was registered for. When the flag is not defined `id` contains
`INTR_ID_UNAVAILABLE`.
The `flags` parameter contains miscellaneous information as follows.
1. Security state, bit[0]. This bit indicates the security state of the lower
exception level when the interrupt was generated. A value of `1` means
that it was in the non-secure state. A value of `0` indicates that it was
in the secure state. This bit can be used by the handler to ensure that
interrupt was generated and routed as per the routing model specified
during registration.
2. Reserved, bits[31:1]. The remaining bits are reserved for future use.
The `handle` parameter points to the `cpu_context` structure of the current CPU
for the security state specified in the `flags` parameter.
Once the handler routine completes, execution will return to either the secure
or non-secure state. The handler routine should return a pointer to
`cpu_context` structure of the current CPU for the the target security state. It
should treat all error conditions as critical errors and take appropriate action
within its implementation e.g. use assertion failures.
The runtime firmware provides the following API for registering a handler for a
particular type of interrupt. A Secure Payload Dispatcher service should use
this API to register a handler for Secure-EL1 and optionally for non-secure
interrupts. This API also requires the caller to specify the routing model for
the type of interrupt.
int32_t register_interrupt_type_handler(uint32_t type,
interrupt_type_handler handler,
uint64_t flags);
The `type` parameter can be one of the three interrupt types listed above i.e.
`INTR_TYPE_S_EL1`, `INTR_TYPE_NS` & `INTR_TYPE_EL3` (currently unimplemented).
The `flags` parameter is as described in Section 2.
The function will return `0` upon a successful registration. It will return
`-EALREADY` in case a handler for the interrupt type has already been
registered. If the `type` is unrecognised or the `flags` or the `handler` are
invalid it will return `-EINVAL`. It will return `-ENOTSUP` if the specified
`type` is not supported by the framework i.e. `INTR_TYPE_EL3`.
Interrupt routing is governed by the configuration of the `SCR_EL3.FIQ/IRQ` bits
prior to entry into a lower exception level in either security state. The
context management library maintains a copy of the `SCR_EL3` system register for
each security state in the `cpu_context` structure of each CPU. It exports the
following APIs to let EL3 Runtime Firmware program and retrieve the routing
model for each security state for the current CPU. The value of `SCR_EL3` stored
in the `cpu_context` is used by the `el3_exit()` function to program the
`SCR_EL3` register prior to returning from the EL3 exception level.
uint32_t cm_get_scr_el3(uint32_t security_state);
void cm_write_scr_el3_bit(uint32_t security_state,
uint32_t bit_pos,
uint32_t value);
`cm_get_scr_el3()` returns the value of the `SCR_EL3` register for the specified
security state of the current CPU. `cm_write_scr_el3()` writes a `0` or `1` to
the bit specified by `bit_pos`. `register_interrupt_type_handler()` invokes
`set_routing_model()` API which programs the `SCR_EL3` according to the routing
model using the `cm_get_scr_el3()` and `cm_write_scr_el3_bit()` APIs.
It is worth noting that in the current implementation of the framework, the EL3
runtime firmware is responsible for programming the routing model. The SPD is
responsible for ensuring that the routing model has been adhered to upon
receiving an interrupt.
#### 2.2.2 Secure payload dispatcher
A SPD service is responsible for determining and maintaining the interrupt
routing model supported by itself and the Secure Payload. It is also responsible
for ferrying interrupts between secure and non-secure software depending upon
the routing model. It could determine the routing model at build time or at
runtime. It must use this information to register a handler for each interrupt
type using the `register_interrupt_type_handler()` API in EL3 runtime firmware.
If the routing model is not known to the SPD service at build time, then it must
be provided by the SP as the result of its initialisation. The SPD should
program the routing model only after SP initialisation has completed e.g. in the
SPD initialisation function pointed to by the `bl32_init` variable.
The SPD should determine the mechanism to pass control to the Secure Payload
after receiving an interrupt from the EL3 runtime firmware. This information
could either be provided to the SPD service at build time or by the SP at
runtime.
#### 2.2.2.1 Test secure payload dispatcher behavior
The TSPD only handles Secure-EL1 interrupts and is provided with the following
routing model at build time.
* Secure-EL1 interrupts are routed to EL3 when execution is in non-secure
state and are routed to the FEL when execution is in the secure state
i.e __CSS=0, TEL3=0__ & __CSS=1, TEL3=1__ for Secure-EL1 interrupts
* The default routing model is used for non-secure interrupts i.e they are
routed to the FEL in either security state i.e __CSS=0, TEL3=0__ &
__CSS=1, TEL3=0__ for Non-secure interrupts
It performs the following actions in the `tspd_init()` function to fulfill the
requirements mentioned earlier.
1. It passes control to the Test Secure Payload to perform its
initialisation. The TSP provides the address of the vector table
`tsp_vectors` in the SP which also includes the handler for Secure-EL1
interrupts in the `fiq_entry` field. The TSPD passes control to the TSP at
this address when it receives a Secure-EL1 interrupt.
The handover agreement between the TSP and the TSPD requires that the TSPD
masks all interrupts (`PSTATE.DAIF` bits) when it calls
`tsp_fiq_entry()`. The TSP has to preserve the callee saved general
purpose, SP_EL1/Secure-EL0, LR, VFP and system registers. It can use
`x0-x18` to enable its C runtime.
2. The TSPD implements a handler function for Secure-EL1 interrupts. It
registers it with the EL3 runtime firmware using the
`register_interrupt_type_handler()` API as follows
/* Forward declaration */
interrupt_type_handler tspd_secure_el1_interrupt_handler;
int32_t rc, flags = 0;
set_interrupt_rm_flag(flags, NON_SECURE);
rc = register_interrupt_type_handler(INTR_TYPE_S_EL1,
tspd_secure_el1_interrupt_handler,
flags);
assert(rc == 0);
#### 2.2.3 Secure payload
A Secure Payload must implement an interrupt handling framework at Secure-EL1
(Secure-EL1 IHF) to support its chosen interrupt routing model. Secure payload
execution will alternate between the below cases.
1. In the code where IRQ, FIQ or both interrupts are enabled, if an interrupt
type is targeted to the FEL, then it will be routed to the Secure-EL1
exception vector table. This is defined as the asynchronous model of
handling interrupts. This mode applies to both Secure-EL1 and non-secure
interrupts.
2. In the code where both interrupts are disabled, if an interrupt type is
targeted to the FEL, then execution will eventually migrate to the
non-secure state. Any non-secure interrupts will be handled as described
in the routing model where __CSS=1 and TEL3=0__. Secure-EL1 interrupts
will be routed to EL3 (as per the routing model where __CSS=1 and
TEL3=1__) where the SPD service will hand them to the SP. This is defined
as the synchronous mode of handling interrupts.
The interrupt handling framework implemented by the SP should support one or
both these interrupt handling models depending upon the chosen routing model.
The following list briefly describes how the choice of a valid routing model
(See 1.2.3) effects the implementation of the Secure-EL1 IHF. If the choice of
the interrupt routing model is not known to the SPD service at compile time,
then the SP should pass this information to the SPD service at runtime during
its initialisation phase.
As mentioned earlier, it is assumed that the FIQ signal is used to generate
Secure-EL1 interrupts and the IRQ signal is used to generate non-secure
interrupts in either security state.
##### 2.2.3.1 Secure payload IHF design w.r.t secure-EL1 interrupts
1. __CSS=0, TEL3=0__. If `PSTATE.F=0`, Secure-EL1 interrupts will be
trigerred at one of the Secure-EL1 FIQ exception vectors. The Secure-EL1
IHF should implement support for handling FIQ interrupts asynchronously.
If `PSTATE.F=1` then Secure-EL1 interrupts will be handled as per the
synchronous interrupt handling model. The SP could implement this scenario
by exporting a seperate entrypoint for Secure-EL1 interrupts to the SPD
service during the registration phase. The SPD service would also need to
know the state of the system, general purpose and the `PSTATE` registers
in which it should arrange to return execution to the SP. The SP should
provide this information in an implementation defined way during the
registration phase if it is not known to the SPD service at build time.
2. __CSS=1, TEL3=1__. Interrupts are routed to EL3 when execution is in
non-secure state. They should be handled through the synchronous interrupt
handling model as described in 1. above.
3. __CSS=0, TEL3=1__. Secure interrupts are routed to EL3 when execution is in
secure state. They will not be visible to the SP. The `PSTATE.F` bit in
Secure-EL1/Secure-EL0 will not mask FIQs. The EL3 runtime firmware will
call the handler registered by the SPD service for Secure-EL1
interrupts. Secure-EL1 IHF should then handle all Secure-EL1 interrupt
through the synchronous interrupt handling model described in 1. above.
##### 2.2.3.2 Secure payload IHF design w.r.t non-secure interrupts
1. __CSS=0, TEL3=0__. If `PSTATE.I=0`, non-secure interrupts will be
trigerred at one of the Secure-EL1 IRQ exception vectors . The Secure-EL1
IHF should co-ordinate with the SPD service to transfer execution to the
non-secure state where the interrupt should be handled e.g the SP could
allocate a function identifier to issue a SMC64 or SMC32 to the SPD
service which indicates that the SP execution has been pre-empted by a
non-secure interrupt. If this function identifier is not known to the SPD
service at compile time then the SP could provide it during the
registration phase.
If `PSTATE.I=1` then the non-secure interrupt will pend until execution
resumes in the non-secure state.
2. __CSS=0, TEL3=1__. Non-secure interrupts are routed to EL3. They will not
be visible to the SP. The `PSTATE.I` bit in Secure-EL1/Secure-EL0 will
have not effect. The SPD service should register a non-secure interrupt
handler which should save the SP state correctly and resume execution in
the non-secure state where the interrupt will be handled. The Secure-EL1
IHF does not need to take any action.
3. __CSS=1, TEL3=0__. Non-secure interrupts are handled in the FEL in
non-secure state (EL1/EL2) and are not visible to the SP. This routing
model does not affect the SP behavior.
A Secure Payload must also ensure that all Secure-EL1 interrupts are correctly
configured at the interrupt controller by the platform port of the EL3 runtime
firmware. It should configure any additional Secure-EL1 interrupts which the EL3
runtime firmware is not aware of through its platform port.
#### 2.2.3.3 Test secure payload behavior
The routing model for Secure-EL1 and non-secure interrupts chosen by the TSP is
described in Section 2.2.2. It is known to the TSPD service at build time.
The TSP implements an entrypoint (`tsp_fiq_entry()`) for handling Secure-EL1
interrupts taken in non-secure state and routed through the TSPD service
(synchronous handling model). It passes the reference to this entrypoint via
`tsp_vectors` to the TSPD service.
The TSP also replaces the default exception vector table referenced through the
`early_exceptions` variable, with a vector table capable of handling FIQ and IRQ
exceptions taken at the same (Secure-EL1) exception level. This table is
referenced through the `tsp_exceptions` variable and programmed into the
VBAR_EL1. It caters for the asynchronous handling model.
The TSP also programs the Secure Physical Timer in the ARM Generic Timer block
to raise a periodic interrupt (every half a second) for the purpose of testing
interrupt management across all the software components listed in 2.1
### 2.3 Interrupt handling
This section describes in detail the role of each software component (see
Section 2.1) in handling an interrupt of a particular type.
#### 2.3.1 EL3 runtime firmware
The EL3 runtime firmware populates the IRQ and FIQ exception vectors referenced
by the `runtime_exceptions` variable as follows.
1. IRQ and FIQ exceptions taken from the current exception level with
`SP_EL0` or `SP_EL3` are reported as irrecoverable error conditions. As
mentioned earlier, EL3 runtime firmware always executes with the
`PSTATE.I` and `PSTATE.F` bits set.
2. The following text describes how the IRQ and FIQ exceptions taken from a
lower exception level using AArch64 or AArch32 are handled.
When an interrupt is generated, the vector for each interrupt type is
responsible for:
1. Saving the entire general purpose register context (x0-x30) immediately
upon exception entry. The registers are saved in the per-cpu `cpu_context`
data structure referenced by the `SP_EL3`register.
2. Saving the `ELR_EL3`, `SP_EL0` and `SPSR_EL3` system registers in the
per-cpu `cpu_context` data structure referenced by the `SP_EL3` register.
3. Switching to the C runtime stack by restoring the `CTX_RUNTIME_SP` value
from the per-cpu `cpu_context` data structure in `SP_EL0` and
executing the `msr spsel, #0` instruction.
4. Determining the type of interrupt. Secure-EL1 interrupts will be signalled
at the FIQ vector. Non-secure interrupts will be signalled at the IRQ
vector. The platform should implement the following API to determine the
type of the pending interrupt.
uint32_t plat_ic_get_interrupt_type(void);
It should return either `INTR_TYPE_S_EL1` or `INTR_TYPE_NS`.
5. Determining the handler for the type of interrupt that has been generated.
The following API has been added for this purpose.
interrupt_type_handler get_interrupt_type_handler(uint32_t interrupt_type);
It returns the reference to the registered handler for this interrupt
type. The `handler` is retrieved from the `intr_type_desc_t` structure as
described in Section 2. `NULL` is returned if no handler has been
registered for this type of interrupt. This scenario is reported as an
irrecoverable error condition.
6. Calling the registered handler function for the interrupt type generated.
The firmware also determines the interrupt id if the IMF_READ_INTERRUPT_ID
build time flag is set. The id is set to `INTR_ID_UNAVAILABLE` if the flag
is not set. The id along with the current security state and a reference to
the `cpu_context_t` structure for the current security state are passed to
the handler function as its arguments.
The handler function returns a reference to the per-cpu `cpu_context_t`
structure for the target security state.
7. Calling `el3_exit()` to return from EL3 into a lower exception level in
the security state determined by the handler routine. The `el3_exit()`
function is responsible for restoring the register context from the
`cpu_context_t` data structure for the target security state.
#### 2.3.2 Secure payload dispatcher
##### 2.3.2.1 Interrupt entry
The SPD service begins handling an interrupt when the EL3 runtime firmware calls
the handler function for that type of interrupt. The SPD service is responsible
for the following:
1. Validating the interrupt. This involves ensuring that the interrupt was
generating according to the interrupt routing model specified by the SPD
service during registration. It should use the interrupt id and the
security state of the exception level (passed in the `flags` parameter of
the handler) where the interrupt was taken from to determine this. If the
interrupt is not recognised then the handler should treat it as an
irrecoverable error condition.
A SPD service can register a handler for Secure-EL1 and/or Non-secure
interrupts. The following text describes further error scenarios keeping
this in mind:
1. __SPD service has registered a handler for Non-secure interrupts__:
When an interrupt is received by the handler, it could check its id
to ensure it has been configured as a non-secure interrupt at the
interrupt controller. A secure interrupt should never be handed to
the non-secure interrupt handler. A non-secure interrupt should
never be routed to EL3 when execution is in non-secure state. The
handler could check the security state flag to ensure this.
2. __SPD service has registered a handler for Secure-EL1 interrupts__:
When an interrupt is received by the handler, it could check its id
to ensure it has been configured as a secure interrupt at the
interrupt controller. A non-secure interrupt should never be handed
to the secure interrupt handler. If the routing model chosen is such
that Secure-EL1 interrupts are not routed to EL3 when execution is
in non-secure state, then a Secure-EL1 interrupt generated in the
secure state would be invalid. The handler could use the security
state flag to check this.
The SPD service should use the platform API:
`plat_ic_get_interrupt_type()` to determine the type of interrupt for the
specified id.
2. Determining whether the security state of the exception level for handling
the interrupt is the same as the security state of the exception level
where the interrupt was generated. This depends upon the routing model and
type of the interrupt. The SPD should use this information to determine if
a context switch is required. The following two cases would require a
context switch from secure to non-secure or vice-versa.
1. A Secure-EL1 interrupt taken from the non-secure state should be
routed to the Secure Payload.
2. A non-secure interrupt taken from the secure state should be routed
to the last known non-secure exception level.
The SPD service must save the system register context of the current
security state. It must then restore the system register context of the
target security state. It should use the `cm_set_next_eret_context()` API
to ensure that the next `cpu_context` to be restored is of the target
security state.
If the target state is secure then execution should be handed to the SP as
per the synchronous interrupt handling model it implements. A Secure-EL1
interrupt can be routed to EL3 while execution is in the SP. This implies
that SP execution can be preempted while handling an interrupt by a
another higher priority Secure-EL1 interrupt (or a EL3 interrupt in the
future). The SPD service should manage secure interrupt priorities before
handing control to the SP to prevent this type of preemption which can
leave the system in an inconsistent state.
3. Setting the return value of the handler to the per-cpu `cpu_context` if
the interrupt has been successfully validated and ready to be handled at a
lower exception level.
The routing model allows non-secure interrupts to be taken to Secure-EL1 when in
secure state. The SPD service and the SP should implement a mechanism for
routing these interrupts to the last known exception level in the non-secure
state. The former should save the SP context, restore the non-secure context and
arrange for entry into the non-secure state so that the interrupt can be
handled.
##### 2.3.2.2 Interrupt exit
When the Secure Payload has finished handling a Secure-EL1 interrupt, it could
return control back to the SPD service through a SMC32 or SMC64. The SPD service
should handle this secure monitor call so that execution resumes in the
exception level and the security state from where the Secure-EL1 interrupt was
originally taken.
##### 2.3.2.1 Test secure payload dispatcher behavior
The example TSPD service registers a handler for Secure-EL1 interrupts taken
from the non-secure state. Its handler `tspd_secure_el1_interrupt_handler()`
takes the following actions upon being invoked.
1. It uses the `id` parameter to query the interrupt controller to ensure
that the interrupt is a Secure-EL1 interrupt. It asserts if this is not
the case.
2. It uses the security state provided in the `flags` parameter to ensure
that the secure interrupt originated from the non-secure state. It asserts
if this is not the case.
3. It saves the system register context for the non-secure state by calling
`cm_el1_sysregs_context_save(NON_SECURE);`.
4. It sets the `ELR_EL3` system register to `tsp_fiq_entry` and sets the
`SPSR_EL3.DAIF` bits in the secure CPU context. It sets `x0` to
`TSP_HANDLE_FIQ_AND_RETURN`. If the TSP was in the middle of handling a
standard SMC, then the `ELR_EL3` and `SPSR_EL3` registers in the secure CPU
context are saved first.
5. It restores the system register context for the secure state by calling
`cm_el1_sysregs_context_restore(SECURE);`.
6. It ensures that the secure CPU context is used to program the next
exception return from EL3 by calling `cm_set_next_eret_context(SECURE);`.
7. It returns the per-cpu `cpu_context` to indicate that the interrupt can
now be handled by the SP. `x1` is written with the value of `elr_el3`
register for the non-secure state. This information is used by the SP for
debugging purposes.
The figure below describes how the interrupt handling is implemented by the TSPD
when a Secure-EL1 interrupt is generated when execution is in the non-secure
state.
![Image 1](diagrams/sec-int-handling.png?raw=true)
The TSP issues an SMC with `TSP_HANDLED_S_EL1_FIQ` as the function identifier to
signal completion of interrupt handling.
The TSP issues an SMC with `TSP_PREEMPTED` as the function identifier to signal
generation of a non-secure interrupt in Secure-EL1.
The TSPD service takes the following actions in `tspd_smc_handler()` function
upon receiving an SMC with `TSP_HANDLED_S_EL1_FIQ` and `TSP_PREEMPTED` as the
function identifiers:
1. It ensures that the call originated from the secure state otherwise
execution returns to the non-secure state with `SMC_UNK` in `x0`.
2. If the function identifier is `TSP_HANDLED_S_EL1_FIQ`, it restores the
saved `ELR_EL3` and `SPSR_EL3` system registers back to the secure CPU
context (see step 4 above) in case the TSP had been preempted by a non
secure interrupt earlier. It does not save the secure context since the
TSP is expected to preserve it (see Section 2.2.2.1)
3. If the function identifier is `TSP_PREEMPTED`, it saves the system
register context for the secure state by calling
`cm_el1_sysregs_context_save(SECURE)`.
4. It restores the system register context for the non-secure state by
calling `cm_el1_sysregs_context_restore(NON_SECURE)`. It sets `x0` to
`SMC_PREEMPTED` if the incoming function identifier is
`TSP_PREEMPTED`. The Normal World is expected to resume the TSP after the
non-secure interrupt handling by issuing an SMC with `TSP_FID_RESUME` as
the function identifier.
5. It ensures that the non-secure CPU context is used to program the next
exception return from EL3 by calling
`cm_set_next_eret_context(NON_SECURE)`.
6. `tspd_smc_handler()` returns a reference to the non-secure `cpu_context`
as the return value.
As mentioned in 4. above, if a non-secure interrupt preempts the TSP execution
then the non-secure software issues an SMC with `TSP_FID_RESUME` as the function
identifier to resume TSP execution. The TSPD service takes the following actions
in `tspd_smc_handler()` function upon receiving this SMC:
1. It ensures that the call originated from the non secure state. An
assertion is raised otherwise.
2. Checks whether the TSP needs a resume i.e check if it was preempted. It
then saves the system register context for the secure state by calling
`cm_el1_sysregs_context_save(NON_SECURE)`.
3. Restores the secure context by calling
`cm_el1_sysregs_context_restore(SECURE)`
4. It ensures that the secure CPU context is used to program the next
exception return from EL3 by calling `cm_set_next_eret_context(SECURE)`.
5. `tspd_smc_handler()` returns a reference to the secure `cpu_context` as the
return value.
The figure below describes how the TSP/TSPD handle a non-secure interrupt when
it is generated during execution in the TSP with `PSTATE.I` = 0.
![Image 2](diagrams/non-sec-int-handling.png?raw=true)
#### 2.3.3 Secure payload
The SP should implement one or both of the synchronous and asynchronous
interrupt handling models depending upon the interrupt routing model it has
chosen (as described in 2.2.3).
In the synchronous model, it should begin handling a Secure-EL1 interrupt after
receiving control from the SPD service at an entrypoint agreed upon during build
time or during the registration phase. Before handling the interrupt, the SP
should save any Secure-EL1 system register context which is needed for resuming
normal execution in the SP later e.g. `SPSR_EL1, `ELR_EL1`. After handling the
interrupt, the SP could return control back to the exception level and security
state where the interrupt was originally taken from. The SP should use an SMC32
or SMC64 to ask the SPD service to do this.
In the asynchronous model, the Secure Payload is responsible for handling
non-secure and Secure-EL1 interrupts at the IRQ and FIQ vectors in its exception
vector table when `PSTATE.I` and `PSTATE.F` bits are 0. As described earlier,
when a non-secure interrupt is generated, the SP should coordinate with the SPD
service to pass control back to the non-secure state in the last known exception
level. This will allow the non-secure interrupt to be handled in the non-secure
state.
##### 2.3.3.1 Test secure payload behavior
The TSPD hands control of a Secure-EL1 interrupt to the TSP at the
`tsp_fiq_entry()`. The TSP handles the interrupt while ensuring that the
handover agreement described in Section 2.2.2.1 is maintained. It updates some
statistics by calling `tsp_update_sync_fiq_stats()`. It then calls
`tsp_fiq_handler()` which.
1. Checks whether the interrupt is the secure physical timer interrupt. It
uses the platform API `plat_ic_get_pending_interrupt_id()` to get the
interrupt number.
2. Handles the interrupt by acknowledging it using the
`plat_ic_acknowledge_interrupt()` platform API, calling
`tsp_generic_timer_handler()` to reprogram the secure physical generic
timer and calling the `plat_ic_end_of_interrupt()` platform API to signal
end of interrupt processing.
The TSP passes control back to the TSPD by issuing an SMC64 with
`TSP_HANDLED_S_EL1_FIQ` as the function identifier.
The TSP handles interrupts under the asynchronous model as follows.
1. Secure-EL1 interrupts are handled by calling the `tsp_fiq_handler()`
function. The function has been described above.
2. Non-secure interrupts are handled by issuing an SMC64 with `TSP_PREEMPTED`
as the function identifier. Execution resumes at the instruction that
follows this SMC instruction when the TSPD hands control to the TSP in
response to an SMC with `TSP_FID_RESUME` as the function identifier from
the non-secure state (see section 2.3.2.1).
- - - - - - - - - - - - - - - - - - - - - - - - - -
_Copyright (c) 2014, ARM Limited and Contributors. All rights reserved._
[Porting Guide]: ./porting-guide.md

View File

@ -14,6 +14,7 @@ Contents
* Boot Loader stage 2 (BL2)
* Boot Loader stage 3-1 (BL3-1)
* PSCI implementation (in BL3-1)
* Interrupt Management framework (in BL3-1)
4. C Library
5. Storage abstraction layer
@ -1135,6 +1136,135 @@ critical for the PSCI runtime service to function correctly. More details are
provided in the description of the `plat_get_aff_count()` and
`plat_get_aff_state()` functions above.
3.4 Interrupt Management framework (in BL3-1)
----------------------------------------------
BL3-1 implements an Interrupt Management Framework (IMF) to manage interrupts
generated in either security state and targeted to EL1 or EL2 in the non-secure
state or EL3/S-EL1 in the secure state. The design of this framework is
described in the [IMF Design Guide]
A platform should export the following APIs to support the IMF. The following
text briefly describes each api and its implementation on the FVP port. The API
implementation depends upon the type of interrupt controller present in the
platform. The FVP implements an ARM Generic Interrupt Controller (ARM GIC) as
per the version 2.0 of the [ARM GIC Architecture Specification]
### Function : plat_interrupt_type_to_line() [mandatory]
Argument : uint32_t, uint32_t
Return : uint32_t
The ARM processor signals an interrupt exception either through the IRQ or FIQ
interrupt line. The specific line that is signaled depends on how the interrupt
controller (IC) reports different interrupt types from an execution context in
either security state. The IMF uses this API to determine which interrupt line
the platform IC uses to signal each type of interrupt supported by the framework
from a given security state.
The first parameter will be one of the `INTR_TYPE_*` values (see [IMF Design
Guide]) indicating the target type of the interrupt, the second parameter is the
security state of the originating execution context. The return result is the
bit position in the `SCR_EL3` register of the respective interrupt trap: IRQ=1,
FIQ=2.
The FVP port configures the ARM GIC to signal S-EL1 interrupts as FIQs and
Non-secure interrupts as IRQs from either security state.
### Function : plat_ic_get_pending_interrupt_type() [mandatory]
Argument : void
Return : uint32_t
This API returns the type of the highest priority pending interrupt at the
platform IC. The IMF uses the interrupt type to retrieve the corresponding
handler function. `INTR_TYPE_INVAL` is returned when there is no interrupt
pending. The valid interrupt types that can be returned are `INTR_TYPE_EL3`,
`INTR_TYPE_S_EL1` and `INTR_TYPE_NS`.
The FVP port reads the _Highest Priority Pending Interrupt Register_
(`GICC_HPPIR`) to determine the id of the pending interrupt. The type of interrupt
depends upon the id value as follows.
1. id < 1022 is reported as a S-EL1 interrupt
2. id = 1022 is reported as a Non-secure interrupt.
3. id = 1023 is reported as an invalid interrupt type.
### Function : plat_ic_get_pending_interrupt_id() [mandatory]
Argument : void
Return : uint32_t
This API returns the id of the highest priority pending interrupt at the
platform IC. The IMF passes the id returned by this API to the registered
handler for the pending interrupt if the `IMF_READ_INTERRUPT_ID` build time flag
is set. INTR_ID_UNAVAILABLE is returned when there is no interrupt pending.
The FVP port reads the _Highest Priority Pending Interrupt Register_
(`GICC_HPPIR`) to determine the id of the pending interrupt. The id that is
returned by API depends upon the value of the id read from the interrupt
controller as follows.
1. id < 1022. id is returned as is.
2. id = 1022. The _Aliased Highest Priority Pending Interrupt Register_
(`GICC_AHPPIR`) is read to determine the id of the non-secure interrupt. This
id is returned by the API.
3. id = 1023. `INTR_ID_UNAVAILABLE` is returned.
### Function : plat_ic_acknowledge_interrupt() [mandatory]
Argument : void
Return : uint32_t
This API is used by the CPU to indicate to the platform IC that processing of
the highest pending interrupt has begun. It should return the id of the
interrupt which is being processed.
The FVP port reads the _Interrupt Acknowledge Register_ (`GICC_IAR`). This
changes the state of the highest priority pending interrupt from pending to
active in the interrupt controller. It returns the value read from the
`GICC_IAR`. This value is the id of the interrupt whose state has been changed.
The TSP uses this API to start processing of the secure physical timer
interrupt.
### Function : plat_ic_end_of_interrupt() [mandatory]
Argument : uint32_t
Return : void
This API is used by the CPU to indicate to the platform IC that processing of
the interrupt corresponding to the id (passed as the parameter) has
finished. The id should be the same as the id returned by the
`plat_ic_acknowledge_interrupt()` API.
The FVP port writes the id to the _End of Interrupt Register_
(`GICC_EOIR`). This deactivates the corresponding interrupt in the interrupt
controller.
The TSP uses this API to finish processing of the secure physical timer
interrupt.
### Function : plat_ic_get_interrupt_type() [mandatory]
Argument : uint32_t
Return : uint32_t
This API returns the type of the interrupt id passed as the parameter.
`INTR_TYPE_INVAL` is returned if the id is invalid. If the id is valid, a valid
interrupt type (one of `INTR_TYPE_EL3`, `INTR_TYPE_S_EL1` and `INTR_TYPE_NS`) is
returned depending upon how the interrupt has been configured by the platform
IC.
The FVP port configures S-EL1 interrupts as Group0 interrupts and Non-secure
interrupts as Group1 interrupts. It reads the group value corresponding to the
interrupt id from the relevant _Interrupt Group Register_ (`GICD_IGROUPRn`). It
uses the group value to determine the type of interrupt.
4. C Library
-------------
@ -1227,8 +1357,10 @@ amount of open resources per driver.
_Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved._
[User Guide]: user-guide.md
[FreeBSD]: http://www.freebsd.org
[ARM GIC Architecture Specification]: http://arminfo.emea.arm.com/help/topic/com.arm.doc.ihi0048b/IHI0048B_gic_architecture_specification.pdf
[IMF Design Guide]: interrupt-framework-design.md
[User Guide]: user-guide.md
[FreeBSD]: http://www.freebsd.org
[plat/common/aarch64/platform_mp_stack.S]: ../plat/common/aarch64/platform_mp_stack.S
[plat/common/aarch64/platform_up_stack.S]: ../plat/common/aarch64/platform_up_stack.S