#############################
Non-Secure Interrupt Handling
#############################

:Author: Mate Toth-Pal
:Organization: Arm Limited
:Contact: Mate Toth-Pal <mate.toth-pal@arm.com>
:Status: Accepted


*****
Terms
*****

==========  ================================================
Term        Meaning
==========  ================================================
AIRCR       Application Interrupt and Reset Control Register
AIRCR.PRIS  PRIoritize Secure exceptions
ISR         Interrupt Service Routine
NS          Non-Secure
NSPM        Non-Secure Partition Manager
SAU         Security Attribution Unit
SPM         Secure Partition Manager
TF-M        Trusted Firmware-M
==========  ================================================

************
Introduction
************

In the current design it is possible to use Non-secure interrupts, however the
Non-secure interrupts cannot pre-empt Secure service execution. TF-M core
achieves this by making the following configurations:

1. ``AIRCR.PRIS`` is set to 1 during TF-M core initialisation. This
   de-prioritizes Non-secure exceptions compared to Secure exceptions, so that
   they cannot interrupt Secure Handler mode. the ``AIRCR.PRIS`` bit remains set
   during TF-M run. The bit is set in the function

.. code-block:: c

    static void tfm_arch_set_secure_exception_priorities(void);

.. Note::

    Setting ``AIRCR.PRIS`` in itself doesn't prevent NS interrupts to pre-empt
    Secure Thread mode when it runs on normal priority i.e., 256.

2. On Secure service entry ``PRIMASK_NS`` is set, to boost the Non-secure
   execution priority so that all NS interrupts are masked. This is done in the
   ``TFM_NS_EXC_DISABLE()`` macro called from

.. code-block:: c

    static int32_t tfm_start_partition(struct tfm_sfn_req_s *desc_ptr, uint32_t excReturn)

.. Note::

    '2.' is only in Library model.

In the below chapters a design is proposed to enable Non-secure interrupts to
pre-empt Secure Thread mode.

**********************************
Limitations of the proposed design
**********************************

Library model
=============

The proposed design keeps the Secure lock in place, which means Non-secure code
can do a single Secure service call, all further calls to Secure services will
be rejected until the first Secure service call returns.

IPC model
=========

The PSA client API can only be entered once. All the functions in the client API
(``psa_framework_version``, ``psa_version``, ``psa_connect``, ``psa_call``,
``psa_close``) are part of the same critical section.

Non-secure software
===================

The Non-secure API functions that wraps the Secure service veneers (either
Library or IPC model) should continue to use the NS locking mechanism currently
implemented by calling ``tfm_ns_lock_dispatch(...)``.

If any of the Non-secure software decides to bypass the locking mechanism, then
concurrent access of veneer functions is detected by TF-M, and NS software
execution is halted.

.. Note::

    This makes denial of service attack possible by a malicious NS application

***************************************************
Enabling NS interrupts to pre-empt Secure execution
***************************************************

To enable NS interrupts, 2) described in chapter 'Current design' must be turned
off. (For details see implementation notes)

When a Non-secure interrupt is triggered during Secure code execution, and the
ISR have to be executed based on the priority settings, the hardware saves the
current execution context on the current Secure stack, and clears the general
purpose registers, to prevents data leakage. After that the NS ISR starts
execution.

When the Non-secure ISR returns with the EXC_RETURN value provided to it in the
link register, the context is fetched from the Secure stack, and the Secure code
continues execution.

If TF-M is used with a single threaded NS software, the mechanisms provided by
the HW is enough to maintain the consistency of the system, and keep the
secrets.

However if the NS software is allowed to change execution context during an
interrupt (e.g an NS operating system schedules another thread during a SysTick
interrupt), then the Secure code can return execution to an NS thread, with the
context of a different thread. So extra measures needs to be introduced in TF-M
to prevent this.

For IPC model
=============

In the current implementation there is no locking mechanism on the Secure side
that would prevent the Non-secure code to enter psa_client functions multiple
times. (For the Library model the ``tfm_secure_lock global`` variable is used
for this purpose). Note, that the ``tfm_ns_lock_dispatch(...)`` function that
is used by the NS service API implementations to prevent Secure services to be
called simultaneously can be bypassed by a malicious Non-secure application, so
a Secure side locking mechanism have to be implemented.

When an NS client calls a PSA client API function, the client ID of the calling
NS context have to be saved, and execution can only return to NS if the current
scheduled NS thread is the one that did the call.

For Library model
=================

As currently there is no scheduling in the Library model, the calls follow each
other just like in an ordinary function call scheme. Then when the original
Secure service that was called from the NS code is about to return, it has to
check for the current NS client ID, and only return if it is the same as the one
saved on Secure service entry from NS. If the ID's don't match, the Secure side
waits so that NS OS can do context switch.

Common measures
===============

Exception priorities
--------------------

The priority of the Secure SVC and the Secure faults must be higher than any
Secure exception in the system.

.. note::

    The priority of PendSV Is set to be the lowest priority Secure interrupt,
    but still higher than the maximum possible NS execution priority when
    ``AIRCR.PRIS`` is set.

NSPM
----

If the Non-secure software allows the use of multiple threads, it needs to use
the NSPM feature of TF-M. It is expected, that all the NS context that use
Secure services have a unique client ID, and the other contexts, that don't use
Secure service need to have a client ID that doesn't match with any of the
client IDs of the Secure service calling contexts.

In other words, for all the *cs(0)*, *cs(1)*, ..., *cs(n)* NS contexts that use
Secure services and for all *cn(0)*, *cn(1)*, ..., *cn(m)* NS contexts that
don't use Secure service (where *n* > 0, *m* >= 0):

- *cs(i).client_id* != *cs(j).client_id* (where 0 <= *i* < *j* <= *n*)
- *cs(i).client_id* != *cn(j).client_id* (where 0 <= *i* <= *n* and 0 <= *j*
  <= *m*)

Entering from Non-secure to Secure
----------------------------------

The Secure code can be entered through the following gateways:

1. NSPM related functions (``TZ_<operation>(...)``,
   ``tfm_register_client_id(...)``)

   These functions are expected to be called from Handler mode. The execution
   priority, after the execution crosses the security boundary will be the same
   as it was during NS execution. This means a malicious Non-secure application,
   can set up Non-secure interrupt priorities in a way that it can enter one or
   more of the NSPM APIs simultaneously.

   This might leave the NSPM database in an inconsistent state, however if the
   attacker has influence over the interrupt priorities, they can gain no
   additional privilege by this.

   .. Note::
       The NS software is able to consume the main stack of the Secure software.
       The Main Secure stack have to be protected by MSPLIM, to prevent stack
       overflow. However a denial of service attack is still possible.

2. PSA Client API, Library model service veneers

   When a veneer is called from Non-secure, the Secure code have to check
   whether the veneer is only entered by a single NS thread. This can be done by
   checking the veneer stack usage. It can only contain the locals of the veneer
   implementation. If the veneer has been entered from multiple NS threads,
   there is at least one extra context stack frame that was created by the
   hardware when the veneer execution had been interrupted by the NS systick.

********************
Implementation notes
********************

IPC model
=========

Save NS client ID on Secure service veneer entry
------------------------------------------------

As long as the Secure lock is in place, a single client ID have to be stored, so
it can be done in a global variable.

The caller client ID can be saved in the function
``void tfm_psa_ipc_request_handler(uint32_t svc_ctx[])`` depending on the return
value of the PSA API function. (Doesn't execute any Secure service code, only
sets signals, and triggers scheduling. If the return value is success, that
means a scheduling is to happen, and a secure service is about to be entered.)

Check client ID on Secure service return
----------------------------------------

The saved client ID can be compared with the current client ID in the function
``tfm_core_ns_ipc_request``, after the SVC return. Before doing the comparison,
``BASEPRI_NS`` must be set to 1.

The original ``BASEPRI_NS`` value can be stored in a global variable (because of
the single context).

If the client ID's don't match, ``BASEPRI_NS`` must be reset, WFI to be issued,
and start the checking sequence from the beginning.

Library model
=============

Save NS client ID on Secure veneer entry
----------------------------------------

As long as the Secure lock is in place, only a single client ID have to be
stored, so it can be done in a global variable.

The caller client ID can be saved in the function
``uint32_t tfm_core_partition_request_svc_handler(uint32_t *svc_args, uint32_t excReturn)``.

Check client ID on SP return
----------------------------

The saved client ID can be compared with the current client ID in the function
``tfm_core_partition_request``, after the ``tfm_core_sfn_request return``.

If the client ID's don't match, WFI to be issued, and the checking sequence have
to be started from the beginning.

Common
======

Enforce single NS entry to Secure
---------------------------------

On Secure service entry (from the SVC implementation) check that (pseudocode)

.. code-block:: c

    svc_handler()
    {
        /* If there are multiple context stacked in veneer stack, hang NSPE */
        expected_sp_top = veneer_stack_addr -
                            sizeof(svc_state_context) + sizeof(locals);
        if (__get_PSP() != expected_sp_top) {
            /* Multiple frames are existing, panic */
            panic();
        }
    }

*******
Testing
*******

Basic scenario
==============

Basic testing of the feature is possible, by adding a new scenario to the
existing IRQ test. The flow of the test would be something like this:

============  =====================  ===========================================
IRQ_TEST_1    prepare test scenario  Do nothing
CORE_TEST_2   prepare test scenario  Do nothing
NS            prepare test scenario  Initialise and start timer
IRQ_TEST_1    execute test scenario  Do nothing
CORE_TEST_2   execute test scenario  Busy wait until NS interrupt is
                                     acknowledged (a flag in non Secure data is
                                     set) set flag CORE_TEST_2 waits on
NS            execute test scenario  Do nothing
============  =====================  ===========================================

The test is successful if NS execute test scenario returns.

Advanced scenarios
==================

Testing advanced scenarios (that involves NS scheduling during secure execution,
NS interrupting Secure interrupt, Secure interrupting NS interrupt) would
require more advanced test framework and are not covered in this proposal.

*Copyright (c) 2019-2020, Arm Limited. All rights reserved.*
