You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
620 lines
26 KiB
620 lines
26 KiB
Exception Handling Framework
|
|
============================
|
|
|
|
This document describes various aspects of handling exceptions by Runtime
|
|
Firmware (BL31) that are targeted at EL3, other than SMCs. The |EHF| takes care
|
|
of the following exceptions when targeted at EL3:
|
|
|
|
- Interrupts
|
|
- Synchronous External Aborts
|
|
- Asynchronous External Aborts
|
|
|
|
|TF-A|'s handling of synchronous ``SMC`` exceptions raised from lower ELs is
|
|
described in the :ref:`Firmware Design document <handling-an-smc>`. However, the
|
|
|EHF| changes the semantics of `Interrupt handling`_ and :ref:`synchronous
|
|
exceptions <Effect on SMC calls>` other than SMCs.
|
|
|
|
The |EHF| is selected by setting the build option ``EL3_EXCEPTION_HANDLING`` to
|
|
``1``, and is only available for AArch64 systems.
|
|
|
|
Introduction
|
|
------------
|
|
|
|
Through various control bits in the ``SCR_EL3`` register, the Arm architecture
|
|
allows for asynchronous exceptions to be routed to EL3. As described in the
|
|
:ref:`Interrupt Management Framework` document, depending on the chosen
|
|
interrupt routing model, TF-A appropriately sets the ``FIQ`` and ``IRQ`` bits of
|
|
``SCR_EL3`` register to effect this routing. For most use cases, other than for
|
|
the purpose of facilitating context switch between Normal and Secure worlds,
|
|
FIQs and IRQs routed to EL3 are not required to be handled in EL3.
|
|
|
|
However, the evolving system and standards landscape demands that various
|
|
exceptions are targeted at and handled in EL3. For instance:
|
|
|
|
- Starting with ARMv8.2 architecture extension, many RAS features have been
|
|
introduced to the Arm architecture. With RAS features implemented, various
|
|
components of the system may use one of the asynchronous exceptions to signal
|
|
error conditions to PEs. These error conditions are of critical nature, and
|
|
it's imperative that corrective or remedial actions are taken at the earliest
|
|
opportunity. Therefore, a *Firmware-first Handling* approach is generally
|
|
followed in response to RAS events in the system.
|
|
|
|
- The Arm `SDEI specification`_ defines interfaces through which Normal world
|
|
interacts with the Runtime Firmware in order to request notification of
|
|
system events. The |SDEI| specification requires that these events are
|
|
notified even when the Normal world executes with the exceptions masked. This
|
|
too implies that firmware-first handling is required, where the events are
|
|
first received by the EL3 firmware, and then dispatched to Normal world
|
|
through purely software mechanism.
|
|
|
|
For |TF-A|, firmware-first handling means that asynchronous exceptions are
|
|
suitably routed to EL3, and the Runtime Firmware (BL31) is extended to include
|
|
software components that are capable of handling those exceptions that target
|
|
EL3. These components—referred to as *dispatchers* [#spd]_ in general—may
|
|
choose to:
|
|
|
|
.. _delegation-use-cases:
|
|
|
|
- Receive and handle exceptions entirely in EL3, meaning the exceptions
|
|
handling terminates in EL3.
|
|
|
|
- Receive exceptions, but handle part of the exception in EL3, and delegate the
|
|
rest of the handling to a dedicated software stack running at lower Secure
|
|
ELs. In this scheme, the handling spans various secure ELs.
|
|
|
|
- Receive exceptions, but handle part of the exception in EL3, and delegate
|
|
processing of the error to dedicated software stack running at lower secure
|
|
ELs (as above); additionally, the Normal world may also be required to
|
|
participate in the handling, or be notified of such events (for example, as
|
|
an |SDEI| event). In this scheme, exception handling potentially and
|
|
maximally spans all ELs in both Secure and Normal worlds.
|
|
|
|
On any given system, all of the above handling models may be employed
|
|
independently depending on platform choice and the nature of the exception
|
|
received.
|
|
|
|
.. [#spd] Not to be confused with :ref:`Secure Payload Dispatcher
|
|
<firmware_design_sel1_spd>`, which is an EL3 component that operates in EL3
|
|
on behalf of Secure OS.
|
|
|
|
The role of Exception Handling Framework
|
|
----------------------------------------
|
|
|
|
Corollary to the use cases cited above, the primary role of the |EHF| is to
|
|
facilitate firmware-first handling of exceptions on Arm systems. The |EHF| thus
|
|
enables multiple exception dispatchers in runtime firmware to co-exist, register
|
|
for, and handle exceptions targeted at EL3. This section outlines the basics,
|
|
and the rest of this document expands the various aspects of the |EHF|.
|
|
|
|
In order to arbitrate exception handling among dispatchers, the |EHF| operation
|
|
is based on a priority scheme. This priority scheme is closely tied to how the
|
|
Arm GIC architecture defines it, although it's applied to non-interrupt
|
|
exceptions too (SErrors, for example).
|
|
|
|
The platform is required to `partition`__ the Secure priority space into
|
|
priority levels as applicable for the Secure software stack. It then assigns the
|
|
dispatchers to one or more priority levels. The dispatchers then register
|
|
handlers for the priority levels at runtime. A dispatcher can register handlers
|
|
for more than one priority level.
|
|
|
|
.. __: `Partitioning priority levels`_
|
|
|
|
|
|
.. _ehf-figure:
|
|
|
|
.. image:: ../resources/diagrams/draw.io/ehf.svg
|
|
|
|
A priority level is *active* when a handler at that priority level is currently
|
|
executing in EL3, or has delegated the execution to a lower EL. For interrupts,
|
|
this is implicit when an interrupt is targeted and acknowledged at EL3, and the
|
|
priority of the acknowledged interrupt is used to match its registered handler.
|
|
The priority level is likewise implicitly deactivated when the interrupt
|
|
handling concludes by EOIing the interrupt.
|
|
|
|
Non-interrupt exceptions (SErrors, for example) don't have a notion of priority.
|
|
In order for the priority arbitration to work, the |EHF| provides APIs in order
|
|
for these non-interrupt exceptions to assume a priority, and to interwork with
|
|
interrupts. Dispatchers handling such exceptions must therefore explicitly
|
|
activate and deactivate the respective priority level as and when they're
|
|
handled or delegated.
|
|
|
|
Because priority activation and deactivation for interrupt handling is implicit
|
|
and involves GIC priority masking, it's impossible for a lower priority
|
|
interrupt to preempt a higher priority one. By extension, this means that a
|
|
lower priority dispatcher cannot preempt a higher-priority one. Priority
|
|
activation and deactivation for non-interrupt exceptions, however, has to be
|
|
explicit. The |EHF| therefore disallows for lower priority level to be activated
|
|
whilst a higher priority level is active, and would result in a panic.
|
|
Likewise, a panic would result if it's attempted to deactivate a lower priority
|
|
level when a higher priority level is active.
|
|
|
|
In essence, priority level activation and deactivation conceptually works like a
|
|
stack—priority levels stack up in strictly increasing fashion, and need to be
|
|
unstacked in strictly the reverse order. For interrupts, the GIC ensures this is
|
|
the case; for non-interrupts, the |EHF| monitors and asserts this. See
|
|
`Transition of priority levels`_.
|
|
|
|
.. _interrupt-handling:
|
|
|
|
Interrupt handling
|
|
------------------
|
|
|
|
The |EHF| is a client of *Interrupt Management Framework*, and registers the
|
|
top-level handler for interrupts that target EL3, as described in the
|
|
:ref:`Interrupt Management Framework` document. This has the following
|
|
implications:
|
|
|
|
- On GICv3 systems, when executing in S-EL1, pending Non-secure interrupts of
|
|
sufficient priority are signalled as FIQs, and therefore will be routed to
|
|
EL3. As a result, S-EL1 software cannot expect to handle Non-secure
|
|
interrupts at S-EL1. Essentially, this deprecates the routing mode described
|
|
as :ref:`CSS=0, TEL3=0 <EL3 interrupts>`.
|
|
|
|
In order for S-EL1 software to handle Non-secure interrupts while having
|
|
|EHF| enabled, the dispatcher must adopt a model where Non-secure interrupts
|
|
are received at EL3, but are then :ref:`synchronously <sp-synchronous-int>`
|
|
handled over to S-EL1.
|
|
|
|
- On GICv2 systems, it's required that the build option ``GICV2_G0_FOR_EL3`` is
|
|
set to ``1`` so that *Group 0* interrupts target EL3.
|
|
|
|
- While executing in Secure world, |EHF| sets GIC Priority Mask Register to the
|
|
lowest Secure priority. This means that no Non-secure interrupts can preempt
|
|
Secure execution. See `Effect on SMC calls`_ for more details.
|
|
|
|
As mentioned above, with |EHF|, the platform is required to partition *Group 0*
|
|
interrupts into distinct priority levels. A dispatcher that chooses to receive
|
|
interrupts can then *own* one or more priority levels, and register interrupt
|
|
handlers for them. A given priority level can be assigned to only one handler. A
|
|
dispatcher may register more than one priority level.
|
|
|
|
Dispatchers are assigned interrupt priority levels in two steps:
|
|
|
|
.. _Partitioning priority levels:
|
|
|
|
Partitioning priority levels
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Interrupts are associated to dispatchers by way of grouping and assigning
|
|
interrupts to a priority level. In other words, all interrupts that are to
|
|
target a particular dispatcher should fall in a particular priority level. For
|
|
priority assignment:
|
|
|
|
- Of the 8 bits of priority that Arm GIC architecture permits, bit 7 must be 0
|
|
(secure space).
|
|
|
|
- Depending on the number of dispatchers to support, the platform must choose
|
|
to use the top *n* of the 7 remaining bits to identify and assign interrupts
|
|
to individual dispatchers. Choosing *n* bits supports up to 2\ :sup:`n`
|
|
distinct dispatchers. For example, by choosing 2 additional bits (i.e., bits
|
|
6 and 5), the platform can partition into 4 secure priority ranges: ``0x0``,
|
|
``0x20``, ``0x40``, and ``0x60``. See `Interrupt handling example`_.
|
|
|
|
.. note::
|
|
|
|
The Arm GIC architecture requires that a GIC implementation that supports two
|
|
security states must implement at least 32 priority levels; i.e., at least 5
|
|
upper bits of the 8 bits are writeable. In the scheme described above, when
|
|
choosing *n* bits for priority range assignment, the platform must ensure
|
|
that at least ``n+1`` top bits of GIC priority are writeable.
|
|
|
|
The priority thus assigned to an interrupt is also used to determine the
|
|
priority of delegated execution in lower ELs. Delegated execution in lower EL is
|
|
associated with a priority level chosen with ``ehf_activate_priority()`` API
|
|
(described `later`__). The chosen priority level also determines the interrupts
|
|
masked while executing in a lower EL, therefore controls preemption of delegated
|
|
execution.
|
|
|
|
.. __: `ehf-apis`_
|
|
|
|
The platform expresses the chosen priority levels by declaring an array of
|
|
priority level descriptors. Each entry in the array is of type
|
|
``ehf_pri_desc_t``, and declares a priority level, and shall be populated by the
|
|
``EHF_PRI_DESC()`` macro.
|
|
|
|
.. warning::
|
|
|
|
The macro ``EHF_PRI_DESC()`` installs the descriptors in the array at a
|
|
computed index, and not necessarily where the macro is placed in the array.
|
|
The size of the array might therefore be larger than what it appears to be.
|
|
The ``ARRAY_SIZE()`` macro therefore should be used to determine the size of
|
|
array.
|
|
|
|
Finally, this array of descriptors is exposed to |EHF| via the
|
|
``EHF_REGISTER_PRIORITIES()`` macro.
|
|
|
|
Refer to the `Interrupt handling example`_ for usage. See also: `Interrupt
|
|
Prioritisation Considerations`_.
|
|
|
|
Programming priority
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The text in `Partitioning priority levels`_ only describes how the platform
|
|
expresses the required levels of priority. It however doesn't choose interrupts
|
|
nor program the required priority in GIC.
|
|
|
|
The :ref:`Firmware Design guide<configuring-secure-interrupts>` explains methods
|
|
for configuring secure interrupts. |EHF| requires the platform to enumerate
|
|
interrupt properties (as opposed to just numbers) of Secure interrupts. The
|
|
priority of secure interrupts must match that as determined in the
|
|
`Partitioning priority levels`_ section above.
|
|
|
|
See `Limitations`_, and also refer to `Interrupt handling example`_ for
|
|
illustration.
|
|
|
|
Registering handler
|
|
-------------------
|
|
|
|
Dispatchers register handlers for their priority levels through the following
|
|
API:
|
|
|
|
.. code:: c
|
|
|
|
int ehf_register_priority_handler(int pri, ehf_handler_t handler)
|
|
|
|
The API takes two arguments:
|
|
|
|
- The priority level for which the handler is being registered;
|
|
|
|
- The handler to be registered. The handler must be aligned to 4 bytes.
|
|
|
|
If a dispatcher owns more than one priority levels, it has to call the API for
|
|
each of them.
|
|
|
|
The API will succeed, and return ``0``, only if:
|
|
|
|
- There exists a descriptor with the priority level requested.
|
|
|
|
- There are no handlers already registered by a previous call to the API.
|
|
|
|
Otherwise, the API returns ``-1``.
|
|
|
|
The interrupt handler should have the following signature:
|
|
|
|
.. code:: c
|
|
|
|
typedef int (*ehf_handler_t)(uint32_t intr_raw, uint32_t flags, void *handle,
|
|
void *cookie);
|
|
|
|
The parameters are as obtained from the top-level :ref:`EL3 interrupt handler
|
|
<el3-runtime-firmware>`.
|
|
|
|
The :ref:`SDEI dispatcher<SDEI: Software Delegated Exception Interface>`, for
|
|
example, expects the platform to allocate two different priority levels—
|
|
``PLAT_SDEI_CRITICAL_PRI``, and ``PLAT_SDEI_NORMAL_PRI`` —and registers the
|
|
same handler to handle both levels.
|
|
|
|
Interrupt handling example
|
|
--------------------------
|
|
|
|
The following annotated snippet demonstrates how a platform might choose to
|
|
assign interrupts to fictitious dispatchers:
|
|
|
|
.. code:: c
|
|
|
|
#include <common/interrupt_props.h>
|
|
#include <drivers/arm/gic_common.h>
|
|
#include <exception_mgmt.h>
|
|
|
|
...
|
|
|
|
/*
|
|
* This platform uses 2 bits for interrupt association. In total, 3 upper
|
|
* bits are in use.
|
|
*
|
|
* 7 6 5 3 0
|
|
* .-.-.-.----------.
|
|
* |0|b|b| ..0.. |
|
|
* '-'-'-'----------'
|
|
*/
|
|
#define PLAT_PRI_BITS 2
|
|
|
|
/* Priorities for individual dispatchers */
|
|
#define DISP0_PRIO 0x00 /* Not used */
|
|
#define DISP1_PRIO 0x20
|
|
#define DISP2_PRIO 0x40
|
|
#define DISP3_PRIO 0x60
|
|
|
|
/* Install priority level descriptors for each dispatcher */
|
|
ehf_pri_desc_t plat_exceptions[] = {
|
|
EHF_PRI_DESC(PLAT_PRI_BITS, DISP1_PRIO),
|
|
EHF_PRI_DESC(PLAT_PRI_BITS, DISP2_PRIO),
|
|
EHF_PRI_DESC(PLAT_PRI_BITS, DISP3_PRIO),
|
|
};
|
|
|
|
/* Expose priority descriptors to Exception Handling Framework */
|
|
EHF_REGISTER_PRIORITIES(plat_exceptions, ARRAY_SIZE(plat_exceptions),
|
|
PLAT_PRI_BITS);
|
|
|
|
...
|
|
|
|
/* List interrupt properties for GIC driver. All interrupts target EL3 */
|
|
const interrupt_prop_t plat_interrupts[] = {
|
|
/* Dispatcher 1 owns interrupts d1_0 and d1_1, so assigns priority DISP1_PRIO */
|
|
INTR_PROP_DESC(d1_0, DISP1_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
|
|
INTR_PROP_DESC(d1_1, DISP1_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
|
|
|
|
/* Dispatcher 2 owns interrupts d2_0 and d2_1, so assigns priority DISP2_PRIO */
|
|
INTR_PROP_DESC(d2_0, DISP2_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
|
|
INTR_PROP_DESC(d2_1, DISP2_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
|
|
|
|
/* Dispatcher 3 owns interrupts d3_0 and d3_1, so assigns priority DISP3_PRIO */
|
|
INTR_PROP_DESC(d3_0, DISP3_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
|
|
INTR_PROP_DESC(d3_1, DISP3_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
|
|
};
|
|
|
|
...
|
|
|
|
/* Dispatcher 1 registers its handler */
|
|
ehf_register_priority_handler(DISP1_PRIO, disp1_handler);
|
|
|
|
/* Dispatcher 2 registers its handler */
|
|
ehf_register_priority_handler(DISP2_PRIO, disp2_handler);
|
|
|
|
/* Dispatcher 3 registers its handler */
|
|
ehf_register_priority_handler(DISP3_PRIO, disp3_handler);
|
|
|
|
...
|
|
|
|
See also the `Build-time flow`_ and the `Run-time flow`_.
|
|
|
|
.. _Activating and Deactivating priorities:
|
|
|
|
Activating and Deactivating priorities
|
|
--------------------------------------
|
|
|
|
A priority level is said to be *active* when an exception of that priority is
|
|
being handled: for interrupts, this is implied when the interrupt is
|
|
acknowledged; for non-interrupt exceptions, such as SErrors or :ref:`SDEI
|
|
explicit dispatches <explicit-dispatch-of-events>`, this has to be done via
|
|
calling ``ehf_activate_priority()``. See `Run-time flow`_.
|
|
|
|
Conversely, when the dispatcher has reached a logical resolution for the cause
|
|
of the exception, the corresponding priority level ought to be deactivated. As
|
|
above, for interrupts, this is implied when the interrupt is EOId in the GIC;
|
|
for other exceptions, this has to be done via calling
|
|
``ehf_deactivate_priority()``.
|
|
|
|
Thanks to `different provisions`__ for exception delegation, there are
|
|
potentially more than one work flow for deactivation:
|
|
|
|
.. __: `delegation-use-cases`_
|
|
|
|
.. _deactivation workflows:
|
|
|
|
- The dispatcher has addressed the cause of the exception, and decided to take
|
|
no further action. In this case, the dispatcher's handler deactivates the
|
|
priority level before returning to the |EHF|. Runtime firmware, upon exit
|
|
through an ``ERET``, resumes execution before the interrupt occurred.
|
|
|
|
- The dispatcher has to delegate the execution to lower ELs, and the cause of
|
|
the exception can be considered resolved only when the lower EL returns
|
|
signals complete (via an ``SMC``) at a future point in time. The following
|
|
sequence ensues:
|
|
|
|
#. The dispatcher calls ``setjmp()`` to setup a jump point, and arranges to
|
|
enter a lower EL upon the next ``ERET``.
|
|
|
|
#. Through the ensuing ``ERET`` from runtime firmware, execution is delegated
|
|
to a lower EL.
|
|
|
|
#. The lower EL completes its execution, and signals completion via an
|
|
``SMC``.
|
|
|
|
#. The ``SMC`` is handled by the same dispatcher that handled the exception
|
|
previously. Noticing the conclusion of exception handling, the dispatcher
|
|
does ``longjmp()`` to resume beyond the previous jump point.
|
|
|
|
As mentioned above, the |EHF| provides the following APIs for activating and
|
|
deactivating interrupt:
|
|
|
|
.. _ehf-apis:
|
|
|
|
- ``ehf_activate_priority()`` activates the supplied priority level, but only
|
|
if the current active priority is higher than the given one; otherwise
|
|
panics. Also, to prevent interruption by physical interrupts of lower
|
|
priority, the |EHF| programs the *Priority Mask Register* corresponding to
|
|
the PE to the priority being activated. Dispatchers typically only need to
|
|
call this when handling exceptions other than interrupts, and it needs to
|
|
delegate execution to a lower EL at a desired priority level.
|
|
|
|
- ``ehf_deactivate_priority()`` deactivates a given priority, but only if the
|
|
current active priority is equal to the given one; otherwise panics. |EHF|
|
|
also restores the *Priority Mask Register* corresponding to the PE to the
|
|
priority before the call to ``ehf_activate_priority()``. Dispatchers
|
|
typically only need to call this after handling exceptions other than
|
|
interrupts.
|
|
|
|
The calling of APIs are subject to allowed `transitions`__. See also the
|
|
`Run-time flow`_.
|
|
|
|
.. __: `Transition of priority levels`_
|
|
|
|
Transition of priority levels
|
|
-----------------------------
|
|
|
|
The |EHF| APIs ``ehf_activate_priority()`` and ``ehf_deactivate_priority()`` can
|
|
be called to transition the current priority level on a PE. A given sequence of
|
|
calls to these APIs are subject to the following conditions:
|
|
|
|
- For activation, the |EHF| only allows for the priority to increase (i.e.
|
|
numeric value decreases);
|
|
|
|
- For deactivation, the |EHF| only allows for the priority to decrease (i.e.
|
|
numeric value increases). Additionally, the priority being deactivated is
|
|
required to be the current priority.
|
|
|
|
If these are violated, a panic will result.
|
|
|
|
.. _Effect on SMC calls:
|
|
|
|
Effect on SMC calls
|
|
-------------------
|
|
|
|
In general, Secure execution is regarded as more important than Non-secure
|
|
execution. As discussed elsewhere in this document, EL3 execution, and any
|
|
delegated execution thereafter, has the effect of raising GIC's priority
|
|
mask—either implicitly by acknowledging Secure interrupts, or when dispatchers
|
|
call ``ehf_activate_priority()``. As a result, Non-secure interrupts cannot
|
|
preempt any Secure execution.
|
|
|
|
SMCs from Non-secure world are synchronous exceptions, and are mechanisms for
|
|
Non-secure world to request Secure services. They're broadly classified as
|
|
*Fast* or *Yielding* (see `SMCCC`__).
|
|
|
|
.. __: https://developer.arm.com/docs/den0028/latest
|
|
|
|
- *Fast* SMCs are atomic from the caller's point of view. I.e., they return
|
|
to the caller only when the Secure world has finished serving the request.
|
|
Any Non-secure interrupts that become pending meanwhile cannot preempt Secure
|
|
execution.
|
|
|
|
- *Yielding* SMCs carry the semantics of a preemptible, lower-priority request.
|
|
A pending Non-secure interrupt can preempt Secure execution handling a
|
|
Yielding SMC. I.e., the caller might observe a Yielding SMC returning when
|
|
either:
|
|
|
|
#. Secure world completes the request, and the caller would find ``SMC_OK``
|
|
as the return code.
|
|
|
|
#. A Non-secure interrupt preempts Secure execution. Non-secure interrupt is
|
|
handled, and Non-secure execution resumes after ``SMC`` instruction.
|
|
|
|
The dispatcher handling a Yielding SMC must provide a different return code
|
|
to the Non-secure caller to distinguish the latter case. This return code,
|
|
however, is not standardised (unlike ``SMC_UNKNOWN`` or ``SMC_OK``, for
|
|
example), so will vary across dispatchers that handle the request.
|
|
|
|
For the latter case above, dispatchers before |EHF| expect Non-secure interrupts
|
|
to be taken to S-EL1 [#irq]_, so would get a chance to populate the designated
|
|
preempted error code before yielding to Non-secure world.
|
|
|
|
The introduction of |EHF| changes the behaviour as described in `Interrupt
|
|
handling`_.
|
|
|
|
When |EHF| is enabled, in order to allow Non-secure interrupts to preempt
|
|
Yielding SMC handling, the dispatcher must call ``ehf_allow_ns_preemption()``
|
|
API. The API takes one argument, the error code to be returned to the Non-secure
|
|
world upon getting preempted.
|
|
|
|
.. [#irq] In case of GICv2, Non-secure interrupts while in S-EL1 were signalled
|
|
as IRQs, and in case of GICv3, FIQs.
|
|
|
|
Build-time flow
|
|
---------------
|
|
|
|
Please refer to the `figure`__ above.
|
|
|
|
.. __: `ehf-figure`_
|
|
|
|
The build-time flow involves the following steps:
|
|
|
|
#. Platform assigns priorities by installing priority level descriptors for
|
|
individual dispatchers, as described in `Partitioning priority levels`_.
|
|
|
|
#. Platform provides interrupt properties to GIC driver, as described in
|
|
`Programming priority`_.
|
|
|
|
#. Dispatcher calling ``ehf_register_priority_handler()`` to register an
|
|
interrupt handler.
|
|
|
|
Also refer to the `Interrupt handling example`_.
|
|
|
|
Run-time flow
|
|
-------------
|
|
|
|
.. _interrupt-flow:
|
|
|
|
The following is an example flow for interrupts:
|
|
|
|
#. The GIC driver, during initialization, iterates through the platform-supplied
|
|
interrupt properties (see `Programming priority`_), and configures the
|
|
interrupts. This programs the appropriate priority and group (Group 0) on
|
|
interrupts belonging to different dispatchers.
|
|
|
|
#. The |EHF|, during its initialisation, registers a top-level interrupt handler
|
|
with the :ref:`Interrupt Management Framework<el3-runtime-firmware>` for EL3
|
|
interrupts. This also results in setting the routing bits in ``SCR_EL3``.
|
|
|
|
#. When an interrupt belonging to a dispatcher fires, GIC raises an EL3/Group 0
|
|
interrupt, and is taken to EL3.
|
|
|
|
#. The top-level EL3 interrupt handler executes. The handler acknowledges the
|
|
interrupt, reads its *Running Priority*, and from that, determines the
|
|
dispatcher handler.
|
|
|
|
#. The |EHF| programs the *Priority Mask Register* of the PE to the priority of
|
|
the interrupt received.
|
|
|
|
#. The |EHF| marks that priority level *active*, and jumps to the dispatcher
|
|
handler.
|
|
|
|
#. Once the dispatcher handler finishes its job, it has to immediately
|
|
*deactivate* the priority level before returning to the |EHF|. See
|
|
`deactivation workflows`_.
|
|
|
|
.. _non-interrupt-flow:
|
|
|
|
The following is an example flow for exceptions that targets EL3 other than
|
|
interrupt:
|
|
|
|
#. The platform provides handlers for the specific kind of exception.
|
|
|
|
#. The exception arrives, and the corresponding handler is executed.
|
|
|
|
#. The handler calls ``ehf_activate_priority()`` to activate the required
|
|
priority level. This also has the effect of raising GIC priority mask, thus
|
|
preventing interrupts of lower priority from preempting the handling. The
|
|
handler may choose to do the handling entirely in EL3 or delegate to a lower
|
|
EL.
|
|
|
|
#. Once exception handling concludes, the handler calls
|
|
``ehf_deactivate_priority()`` to deactivate the priority level activated
|
|
earlier. This also has the effect of lowering GIC priority mask to what it
|
|
was before.
|
|
|
|
Interrupt Prioritisation Considerations
|
|
---------------------------------------
|
|
|
|
The GIC priority scheme, by design, prioritises Secure interrupts over Normal
|
|
world ones. The platform further assigns relative priorities amongst Secure
|
|
dispatchers through |EHF|.
|
|
|
|
As mentioned in `Partitioning priority levels`_, interrupts targeting distinct
|
|
dispatchers fall in distinct priority levels. Because they're routed via the
|
|
GIC, interrupt delivery to the PE is subject to GIC prioritisation rules. In
|
|
particular, when an interrupt is being handled by the PE (i.e., the interrupt is
|
|
in *Active* state), only interrupts of higher priority are signalled to the PE,
|
|
even if interrupts of same or lower priority are pending. This has the side
|
|
effect of one dispatcher being starved of interrupts by virtue of another
|
|
dispatcher handling its (higher priority) interrupts.
|
|
|
|
The |EHF| doesn't enforce a particular prioritisation policy, but the platform
|
|
should carefully consider the assignment of priorities to dispatchers integrated
|
|
into runtime firmware. The platform should sensibly delineate priority to
|
|
various dispatchers according to their nature. In particular, dispatchers of
|
|
critical nature (RAS, for example) should be assigned higher priority than
|
|
others (|SDEI|, for example); and within |SDEI|, Critical priority
|
|
|SDEI| should be assigned higher priority than Normal ones.
|
|
|
|
Limitations
|
|
-----------
|
|
|
|
The |EHF| has the following limitations:
|
|
|
|
- Although there could be up to 128 Secure dispatchers supported by the GIC
|
|
priority scheme, the size of descriptor array exposed with
|
|
``EHF_REGISTER_PRIORITIES()`` macro is currently limited to 32. This serves most
|
|
expected use cases. This may be expanded in the future, should use cases
|
|
demand so.
|
|
|
|
- The platform must ensure that the priority assigned to the dispatcher in the
|
|
exception descriptor and the programmed priority of interrupts handled by the
|
|
dispatcher match. The |EHF| cannot verify that this has been followed.
|
|
|
|
--------------
|
|
|
|
*Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved.*
|
|
|
|
.. _SDEI specification: http://infocenter.arm.com/help/topic/com.arm.doc.den0054a/ARM_DEN0054A_Software_Delegated_Exception_Interface.pdf
|