Merge pull request #1437 from jeenu-arm/ras-remaining
SDEI dispatch changes to enable RAS use cases
This commit is contained in:
commit
9dfd755303
|
@ -18,15 +18,16 @@ include lib/psci/psci_lib.mk
|
|||
BL31_SOURCES += bl31/bl31_main.c \
|
||||
bl31/interrupt_mgmt.c \
|
||||
bl31/aarch64/bl31_entrypoint.S \
|
||||
bl31/aarch64/runtime_exceptions.S \
|
||||
bl31/aarch64/crash_reporting.S \
|
||||
bl31/aarch64/runtime_exceptions.S \
|
||||
bl31/bl31_context_mgmt.c \
|
||||
common/runtime_svc.c \
|
||||
lib/aarch64/setjmp.S \
|
||||
plat/common/aarch64/platform_mp_stack.S \
|
||||
services/arm_arch_svc/arm_arch_svc_setup.c \
|
||||
services/std_svc/std_svc_setup.c \
|
||||
${PSCI_LIB_SOURCES} \
|
||||
${SPM_SOURCES} \
|
||||
${SPM_SOURCES}
|
||||
|
||||
|
||||
ifeq (${ENABLE_PMF}, 1)
|
||||
|
@ -41,7 +42,8 @@ ifeq (${SDEI_SUPPORT},1)
|
|||
ifeq (${EL3_EXCEPTION_HANDLING},0)
|
||||
$(error EL3_EXCEPTION_HANDLING must be 1 for SDEI support)
|
||||
endif
|
||||
BL31_SOURCES += services/std_svc/sdei/sdei_event.c \
|
||||
BL31_SOURCES += services/std_svc/sdei/sdei_dispatch.S \
|
||||
services/std_svc/sdei/sdei_event.c \
|
||||
services/std_svc/sdei/sdei_intr_mgmt.c \
|
||||
services/std_svc/sdei/sdei_main.c \
|
||||
services/std_svc/sdei/sdei_state.c
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/'
|
||||
' Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
|
||||
' Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
||||
'
|
||||
' SPDX-License-Identifier: BSD-3-Clause
|
||||
'/
|
||||
|
@ -9,7 +9,8 @@
|
|||
autonumber "<b>[#]</b>"
|
||||
participant "SDEI client" as EL2
|
||||
participant EL3
|
||||
participant "Secure Partition" as SP
|
||||
participant SDEI
|
||||
participant "RAS Driver" as RAS
|
||||
|
||||
activate EL2
|
||||
EL2->EL3: **SDEI_EVENT_REGISTER**(ev, handler, ...)
|
||||
|
@ -24,21 +25,26 @@ EL3->EL2: 1
|
|||
EL3<--]: **CRITICAL EVENT**
|
||||
activate EL3 #red
|
||||
note over EL3: Critical event triage
|
||||
EL3->SP: dispatch
|
||||
activate SP #salmon
|
||||
note over SP: Critical event handling
|
||||
SP->EL3: done
|
||||
deactivate SP
|
||||
EL3-->EL3: sdei_dispatch_event(ev)
|
||||
note over EL3: Prepare SDEI dispatch
|
||||
EL3->EL2: dispatch
|
||||
EL3->RAS: dispatch to handle
|
||||
deactivate EL3
|
||||
activate RAS #salmon
|
||||
note over RAS: Critical event handling
|
||||
RAS-->SDEI: sdei_dispatch_event(ev)
|
||||
deactivate RAS
|
||||
activate SDEI #salmon
|
||||
note over SDEI: Prepare SDEI dispatch
|
||||
SDEI->EL2: dispatch
|
||||
activate EL2 #salmon
|
||||
note over EL2: SDEI handler
|
||||
EL2->EL3: **SDEI_EVENT_COMPLETE()**
|
||||
EL2->SDEI: **SDEI_EVENT_COMPLETE()**
|
||||
deactivate EL2
|
||||
note over EL3: Complete SDEI dispatch
|
||||
note over SDEI: Complete SDEI dispatch
|
||||
SDEI-->RAS: return
|
||||
deactivate SDEI
|
||||
activate RAS #salmon
|
||||
RAS->EL3: error handling done
|
||||
deactivate RAS
|
||||
EL3->EL2: resumes preempted execution
|
||||
deactivate EL3
|
||||
|
||||
... <<Normal execution resumes>> ...
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
|
@ -48,8 +48,10 @@ execute the registered handler [10]. The client terminates its execution with
|
|||
original EL2 execution [13]. Note that the SDEI interrupt remains active until
|
||||
the client handler completes, at which point EL3 does EOI [12].
|
||||
|
||||
SDEI events can be explicitly dispatched in response to other asynchronous
|
||||
exceptions. See `Explicit dispatch of events`_.
|
||||
Other than events bound to interrupts (as depicted in the sequence above, SDEI
|
||||
events can be explicitly dispatched in response to other exceptions, for
|
||||
example, upon receiving an *SError* or *Synchronous External Abort*. See
|
||||
`Explicit dispatch of events`_.
|
||||
|
||||
The remainder of this document only discusses the design and implementation of
|
||||
SDEI dispatcher in TF-A, and assumes that the reader is familiar with the SDEI
|
||||
|
@ -71,7 +73,8 @@ event descriptors. Both macros take 3 arguments:
|
|||
|
||||
- The event number: this must be a positive 32-bit integer.
|
||||
|
||||
- The interrupt number the event is bound to:
|
||||
- For an event that has a backing interrupt, the interrupt number the event is
|
||||
bound to:
|
||||
|
||||
- If it's not applicable to an event, this shall be left as ``0``.
|
||||
|
||||
|
@ -82,6 +85,17 @@ event descriptors. Both macros take 3 arguments:
|
|||
To define event 0, the macro ``SDEI_DEFINE_EVENT_0()`` should be used. This
|
||||
macro takes only one parameter: an SGI number to signal other PEs.
|
||||
|
||||
To define an event that's meant to be `explicitly dispatched`__ (i.e., not as a
|
||||
result of receiving an SDEI interrupt), the macro ``SDEI_EXPLICIT_EVENT()``
|
||||
should be used. It accepts two parameters:
|
||||
|
||||
.. __: `Explicit dispatch of events`_
|
||||
|
||||
- The event number (as above);
|
||||
|
||||
- Event priority: ``SDEI_MAPF_CRITICAL`` or ``SDEI_MAPF_NORMAL``, as described
|
||||
below.
|
||||
|
||||
Once the event descriptor arrays are defined, they should be exported to the
|
||||
SDEI dispatcher using the ``REGISTER_SDEI_MAP()`` macro, passing it the pointers
|
||||
to the private and shared event descriptor arrays, respectively. Note that the
|
||||
|
@ -99,6 +113,8 @@ Regarding event descriptors:
|
|||
|
||||
- Must be bound to a Secure SGI on the platform.
|
||||
|
||||
- Explicit events should only be used in the private array.
|
||||
|
||||
- Statically bound shared and private interrupts must be bound to shared and
|
||||
private interrupts on the platform, respectively. See the section on
|
||||
`interrupt configuration`__.
|
||||
|
@ -132,8 +148,10 @@ Event flags describe the properties of the event. They are bit maps that can be
|
|||
- ``SDEI_MAPF_BOUND``: Marks the event as statically bound to an interrupt.
|
||||
These events cannot be re-bound at runtime.
|
||||
|
||||
- ``SDEI_MAPF_NORMAL``: Marks the event as having *Normal* priority. This is
|
||||
the default priority.
|
||||
|
||||
- ``SDEI_MAPF_CRITICAL``: Marks the event as having *Critical* priority.
|
||||
Without this flag, the event is assumed to have *Normal* priority.
|
||||
|
||||
Event definition example
|
||||
------------------------
|
||||
|
@ -150,6 +168,10 @@ Event definition example
|
|||
/* Dynamic private events */
|
||||
SDEI_PRIVATE_EVENT(100, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC),
|
||||
SDEI_PRIVATE_EVENT(101, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC)
|
||||
|
||||
/* Events for explicit dispatch */
|
||||
SDEI_EXPLICIT_EVENT(2000, SDEI_MAPF_NORMAL);
|
||||
SDEI_EXPLICIT_EVENT(2000, SDEI_MAPF_CRITICAL);
|
||||
};
|
||||
|
||||
/* Shared event mappings */
|
||||
|
@ -211,14 +233,10 @@ this purpose. The API has the following signature:
|
|||
|
||||
::
|
||||
|
||||
int sdei_dispatch_event(int ev_num, unsigned int preempted_sec_state);
|
||||
int sdei_dispatch_event(int ev_num);
|
||||
|
||||
- The parameter ``ev_num`` is the event number to dispatch;
|
||||
|
||||
- The parameter ``preempted_sec_state`` indicates the context that was
|
||||
preempted. This must be either ``SECURE`` or ``NON_SECURE``.
|
||||
|
||||
The API returns ``0`` on success, or ``-1`` on failure.
|
||||
The parameter ``ev_num`` is the event number to dispatch. The API returns ``0``
|
||||
on success, or ``-1`` on failure.
|
||||
|
||||
The following figure depicts a scenario involving explicit dispatch of SDEI
|
||||
event. A commentary is provided below:
|
||||
|
@ -231,22 +249,18 @@ unlike in `general SDEI dispatch`_, this doesn't involve interrupt binding, as
|
|||
bound or dynamic events can't be explicitly dispatched (see the section below).
|
||||
|
||||
At a later point in time, a critical event [#critical-event]_ is trapped into
|
||||
EL3 [7]. EL3 performs a first-level triage of the event, and decides to dispatch
|
||||
to a Secure Partition [#secpart]_ for further handling [8]. The dispatch
|
||||
completes, but intends to involve Non-secure world in further handling, and
|
||||
therefore decides to explicitly dispatch an event [10] (which the client had
|
||||
already registered for [1]). The rest of the sequence is similar to that in the
|
||||
`general SDEI dispatch`_: the requested event is dispatched to the client
|
||||
(assuming all the conditions are met), and when the handler completes, the
|
||||
preempted execution resumes.
|
||||
EL3 [7]. EL3 performs a first-level triage of the event, and a RAS component
|
||||
assumes further handling [8]. The dispatch completes, but intends to involve
|
||||
Non-secure world in further handling, and therefore decides to explicitly
|
||||
dispatch an event [10] (which the client had already registered for [1]). The
|
||||
rest of the sequence is similar to that in the `general SDEI dispatch`_: the
|
||||
requested event is dispatched to the client (assuming all the conditions are
|
||||
met), and when the handler completes, the preempted execution resumes.
|
||||
|
||||
.. [#critical-event] Examples of critical event are *SError*, *Synchronous
|
||||
External Abort*, *Fault Handling interrupt*, or *Error
|
||||
Recovery interrupt* from one of RAS nodes in the system.
|
||||
|
||||
.. [#secpart] Dispatching to Secure Partition involves *Secure Partition
|
||||
Manager*, which isn't depicted in the sequence.
|
||||
|
||||
Conditions for event dispatch
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -258,7 +272,8 @@ event to be dispatched:
|
|||
|
||||
- Event 0 can't be dispatched.
|
||||
|
||||
- The event must neither be a dynamic event nor be bound to an interrupt.
|
||||
- The event must be declared using the ``SDEI_EXPLICIT_EVENT()`` macro
|
||||
described above.
|
||||
|
||||
- The event must be private to the PE.
|
||||
|
||||
|
@ -279,28 +294,22 @@ event to be dispatched:
|
|||
Further, the caller should be aware of the following assumptions made by the
|
||||
dispatcher:
|
||||
|
||||
- The caller of the API is a component running in EL3; for example, the *Secure
|
||||
Partition Manager*.
|
||||
- The caller of the API is a component running in EL3; for example, a RAS
|
||||
driver.
|
||||
|
||||
- The requested dispatch will be permitted by the Exception Handling Framework.
|
||||
I.e. the caller must make sure that the requested dispatch has sufficient
|
||||
priority so as not to cause priority level inversion within Exception
|
||||
Handling Framework.
|
||||
|
||||
- At the time of the call, the active context is Secure, and it has been saved.
|
||||
- The caller must be prepared for the SDEI dispatcher to restore the Non-secure
|
||||
context, and mark that the active context.
|
||||
|
||||
- Upon returning success, the Non-secure context will be restored and setup for
|
||||
the event dispatch, and it will be the active context. The Non-secure context
|
||||
should not be modified further by the caller.
|
||||
- The call will block until the SDEI client completes the event (i.e. when the
|
||||
client calls either ``SDEI_EVENT_COMPLETE`` or ``SDEI_COMPLETE_AND_RESUME``).
|
||||
|
||||
- The API returning success only means that the dispatch is scheduled at the
|
||||
next ``ERET``, and not immediately performed. Also, the caller must be
|
||||
prepared for this API to return failure and handle accordingly.
|
||||
|
||||
- Upon completing the event (i.e. when the client calls either
|
||||
``SDEI_EVENT_COMPLETE`` or ``SDEI_COMPLETE_AND_RESUME``), the preempted
|
||||
context is resumed (as indicated by the ``preempted_sec_state`` parameter of
|
||||
the API).
|
||||
- The caller must be prepared for this API to return failure and handle
|
||||
accordingly.
|
||||
|
||||
Porting requirements
|
||||
--------------------
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef __JMP_H__
|
||||
#define __JMP_H__
|
||||
|
||||
#define JMP_CTX_X19 0x0
|
||||
#define JMP_CTX_X21 0x10
|
||||
#define JMP_CTX_X23 0x20
|
||||
#define JMP_CTX_X25 0x30
|
||||
#define JMP_CTX_X27 0x40
|
||||
#define JMP_CTX_X29 0x50
|
||||
#define JMP_CTX_SP 0x60
|
||||
#define JMP_CTX_END 0x70
|
||||
|
||||
#define JMP_SIZE (JMP_CTX_END >> 3)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Jump buffer hosting x18 - x30 and sp_el0 registers */
|
||||
struct jmpbuf {
|
||||
uint64_t buf[JMP_SIZE];
|
||||
} __aligned(16);
|
||||
|
||||
|
||||
/*
|
||||
* Set a jump point, and populate the jump buffer with context information so
|
||||
* that longjmp() can jump later. The caller must adhere to the following
|
||||
* conditions:
|
||||
*
|
||||
* - After calling this function, the stack must not be shrunk. The contents of
|
||||
* the stack must not be changed either.
|
||||
*
|
||||
* - If the caller were to 'return', the buffer must be considered invalid, and
|
||||
* must not be used with longjmp().
|
||||
*
|
||||
* The caller will observe this function returning at two distinct
|
||||
* circumstances, each with different return values:
|
||||
*
|
||||
* - Zero, when the buffer is setup;
|
||||
*
|
||||
* - Non-zero, when a call to longjmp() is made (presumably by one of the
|
||||
* callee functions) with the same jump buffer.
|
||||
*/
|
||||
int setjmp(struct jmpbuf *buf);
|
||||
|
||||
/*
|
||||
* Reset execution to a jump point, and restore context information according to
|
||||
* the jump buffer populated by setjmp().
|
||||
*/
|
||||
void longjmp(struct jmpbuf *buf);
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __JMP_H__ */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
|
||||
* Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
@ -52,6 +52,7 @@
|
|||
#define _SDEI_MAPF_SIGNALABLE_SHIFT 3
|
||||
#define _SDEI_MAPF_PRIVATE_SHIFT 4
|
||||
#define _SDEI_MAPF_CRITICAL_SHIFT 5
|
||||
#define _SDEI_MAPF_EXPLICIT_SHIFT 6
|
||||
|
||||
/* SDEI event 0 */
|
||||
#define SDEI_EVENT_0 0
|
||||
|
@ -81,9 +82,12 @@
|
|||
*/
|
||||
#define SDEI_MAPF_DYNAMIC BIT(_SDEI_MAPF_DYNAMIC_SHIFT)
|
||||
#define SDEI_MAPF_BOUND BIT(_SDEI_MAPF_BOUND_SHIFT)
|
||||
#define SDEI_MAPF_EXPLICIT BIT(_SDEI_MAPF_EXPLICIT_SHIFT)
|
||||
|
||||
#define SDEI_MAPF_SIGNALABLE BIT(_SDEI_MAPF_SIGNALABLE_SHIFT)
|
||||
#define SDEI_MAPF_PRIVATE BIT(_SDEI_MAPF_PRIVATE_SHIFT)
|
||||
|
||||
#define SDEI_MAPF_NORMAL 0
|
||||
#define SDEI_MAPF_CRITICAL BIT(_SDEI_MAPF_CRITICAL_SHIFT)
|
||||
|
||||
/* Indices of private and shared mappings */
|
||||
|
@ -114,6 +118,9 @@
|
|||
#define SDEI_DEFINE_EVENT_0(_intr) \
|
||||
SDEI_PRIVATE_EVENT(SDEI_EVENT_0, _intr, SDEI_MAPF_SIGNALABLE)
|
||||
|
||||
#define SDEI_EXPLICIT_EVENT(_event, _pri) \
|
||||
SDEI_EVENT_MAP(_event, 0, _pri | SDEI_MAPF_EXPLICIT | SDEI_MAPF_PRIVATE)
|
||||
|
||||
/*
|
||||
* Declare shared and private entries for each core. Also declare a global
|
||||
* structure containing private and share entries.
|
||||
|
@ -176,6 +183,6 @@ uint64_t sdei_smc_handler(uint32_t smc_fid,
|
|||
void sdei_init(void);
|
||||
|
||||
/* Public API to dispatch an event to Normal world */
|
||||
int sdei_dispatch_event(int ev_num, unsigned int preempted_sec_state);
|
||||
int sdei_dispatch_event(int ev_num);
|
||||
|
||||
#endif /* __SDEI_H__ */
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <asm_macros.S>
|
||||
#include <assert_macros.S>
|
||||
#include <setjmp.h>
|
||||
|
||||
.globl setjmp
|
||||
.globl longjmp
|
||||
|
||||
/*
|
||||
* int setjmp(struct jmpbuf *buf);
|
||||
*
|
||||
* Sets a jump point in the buffer specified in x0. Returns 0 to the caller when
|
||||
* when setting up the jump, and 1 when returning from the jump.
|
||||
*/
|
||||
func setjmp
|
||||
mov x7, sp
|
||||
|
||||
stp x19, x20, [x0, #JMP_CTX_X19]
|
||||
stp x21, x22, [x0, #JMP_CTX_X21]
|
||||
stp x23, x24, [x0, #JMP_CTX_X23]
|
||||
stp x25, x26, [x0, #JMP_CTX_X25]
|
||||
stp x27, x28, [x0, #JMP_CTX_X27]
|
||||
stp x29, x30, [x0, #JMP_CTX_X29]
|
||||
stp x7, xzr, [x0, #JMP_CTX_SP]
|
||||
|
||||
mov x0, #0
|
||||
ret
|
||||
endfunc setjmp
|
||||
|
||||
|
||||
/*
|
||||
* void longjmp(struct jmpbuf *buf);
|
||||
*
|
||||
* Return to a jump point setup by setjmp()
|
||||
*/
|
||||
func longjmp
|
||||
ldp x7, xzr, [x0, #JMP_CTX_SP]
|
||||
|
||||
#if ENABLE_ASSERTIONS
|
||||
/*
|
||||
* Since we're unwinding the stack, assert that the stack being reset to
|
||||
* is shallower.
|
||||
*/
|
||||
mov x19, sp
|
||||
cmp x7, x19
|
||||
ASM_ASSERT(ge)
|
||||
#endif
|
||||
|
||||
ldp x19, x20, [x0, #JMP_CTX_X19]
|
||||
ldp x21, x22, [x0, #JMP_CTX_X21]
|
||||
ldp x23, x24, [x0, #JMP_CTX_X23]
|
||||
ldp x25, x26, [x0, #JMP_CTX_X25]
|
||||
ldp x27, x28, [x0, #JMP_CTX_X27]
|
||||
ldp x29, x30, [x0, #JMP_CTX_X29]
|
||||
|
||||
mov sp, x7
|
||||
|
||||
mov x0, #1
|
||||
ret
|
||||
endfunc longjmp
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <ras.h>
|
||||
|
||||
struct ras_interrupt fvp_ras_interrupts[] = {
|
||||
};
|
||||
|
||||
struct err_record_info fvp_err_records[] = {
|
||||
};
|
||||
|
||||
REGISTER_ERR_RECORD_INFO(fvp_err_records);
|
||||
REGISTER_RAS_INTERRUPTS(fvp_ras_interrupts);
|
|
@ -216,6 +216,10 @@ BL31_SOURCES += lib/cpus/aarch64/cortex_a75_pubsub.c \
|
|||
lib/cpus/aarch64/cpuamu_helpers.S
|
||||
endif
|
||||
|
||||
ifeq (${RAS_EXTENSION},1)
|
||||
BL31_SOURCES += plat/arm/board/fvp/aarch64/fvp_ras.c
|
||||
endif
|
||||
|
||||
ifneq (${ENABLE_STACK_PROTECTOR},0)
|
||||
PLAT_BL_COMMON_SOURCES += plat/arm/board/fvp/fvp_stack_protector.c
|
||||
endif
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <ras.h>
|
||||
|
||||
struct ras_interrupt arm_ras_interrupts[] = {
|
||||
};
|
||||
|
||||
struct err_record_info arm_err_records[] = {
|
||||
};
|
||||
|
||||
REGISTER_ERR_RECORD_INFO(arm_err_records);
|
||||
REGISTER_RAS_INTERRUPTS(arm_ras_interrupts);
|
|
@ -236,8 +236,7 @@ endif
|
|||
# RAS sources
|
||||
ifeq (${RAS_EXTENSION},1)
|
||||
BL31_SOURCES += lib/extensions/ras/std_err_record.c \
|
||||
lib/extensions/ras/ras_common.c \
|
||||
plat/arm/common/aarch64/arm_ras.c
|
||||
lib/extensions/ras/ras_common.c
|
||||
endif
|
||||
|
||||
ifneq (${TRUSTED_BOARD_BOOT},0)
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <asm_macros.S>
|
||||
|
||||
.globl begin_sdei_synchronous_dispatch
|
||||
|
||||
/*
|
||||
* void begin_sdei_synchronous_dispatch(struct jmpbuf *buffer);
|
||||
*
|
||||
* Begin SDEI dispatch synchronously by setting up a jump point, and exiting
|
||||
* EL3. This jump point is jumped to by the dispatcher after the event is
|
||||
* completed by the client.
|
||||
*/
|
||||
func begin_sdei_synchronous_dispatch
|
||||
stp x30, xzr, [sp, #-16]!
|
||||
bl setjmp
|
||||
cbz x0, 1f
|
||||
ldp x30, xzr, [sp], #16
|
||||
ret
|
||||
1:
|
||||
b el3_exit
|
||||
endfunc begin_sdei_synchronous_dispatch
|
|
@ -8,7 +8,6 @@
|
|||
#include <assert.h>
|
||||
#include <bl_common.h>
|
||||
#include <cassert.h>
|
||||
#include <context_mgmt.h>
|
||||
#include <debug.h>
|
||||
#include <ehf.h>
|
||||
#include <interrupt_mgmt.h>
|
||||
|
@ -32,9 +31,8 @@
|
|||
/* Structure to store information about an outstanding dispatch */
|
||||
typedef struct sdei_dispatch_context {
|
||||
sdei_ev_map_t *map;
|
||||
unsigned int sec_state;
|
||||
unsigned int intr_raw;
|
||||
uint64_t x[SDEI_SAVED_GPREGS];
|
||||
struct jmpbuf *dispatch_jmp;
|
||||
|
||||
/* Exception state registers */
|
||||
uint64_t elr_el3;
|
||||
|
@ -154,8 +152,8 @@ static sdei_dispatch_context_t *get_outstanding_dispatch(void)
|
|||
return &state->dispatch_stack[state->stack_top - 1];
|
||||
}
|
||||
|
||||
static void save_event_ctx(sdei_ev_map_t *map, void *tgt_ctx, int sec_state,
|
||||
unsigned int intr_raw)
|
||||
static sdei_dispatch_context_t *save_event_ctx(sdei_ev_map_t *map,
|
||||
void *tgt_ctx)
|
||||
{
|
||||
sdei_dispatch_context_t *disp_ctx;
|
||||
gp_regs_t *tgt_gpregs;
|
||||
|
@ -167,26 +165,14 @@ static void save_event_ctx(sdei_ev_map_t *map, void *tgt_ctx, int sec_state,
|
|||
|
||||
disp_ctx = push_dispatch();
|
||||
assert(disp_ctx);
|
||||
disp_ctx->sec_state = sec_state;
|
||||
disp_ctx->map = map;
|
||||
disp_ctx->intr_raw = intr_raw;
|
||||
|
||||
/* Save general purpose and exception registers */
|
||||
memcpy(disp_ctx->x, tgt_gpregs, sizeof(disp_ctx->x));
|
||||
disp_ctx->spsr_el3 = read_ctx_reg(tgt_el3, CTX_SPSR_EL3);
|
||||
disp_ctx->elr_el3 = read_ctx_reg(tgt_el3, CTX_ELR_EL3);
|
||||
|
||||
#if DYNAMIC_WORKAROUND_CVE_2018_3639
|
||||
cve_2018_3639_t *tgt_cve_2018_3639;
|
||||
tgt_cve_2018_3639 = get_cve_2018_3639_ctx(tgt_ctx);
|
||||
|
||||
/* Save CVE-2018-3639 mitigation state */
|
||||
disp_ctx->disable_cve_2018_3639 = read_ctx_reg(tgt_cve_2018_3639,
|
||||
CTX_CVE_2018_3639_DISABLE);
|
||||
|
||||
/* Force SDEI handler to execute with mitigation enabled by default */
|
||||
write_ctx_reg(tgt_cve_2018_3639, CTX_CVE_2018_3639_DISABLE, 0);
|
||||
#endif
|
||||
return disp_ctx;
|
||||
}
|
||||
|
||||
static void restore_event_ctx(sdei_dispatch_context_t *disp_ctx, void *tgt_ctx)
|
||||
|
@ -250,13 +236,12 @@ static cpu_context_t *restore_and_resume_ns_context(void)
|
|||
* SDEI client.
|
||||
*/
|
||||
static void setup_ns_dispatch(sdei_ev_map_t *map, sdei_entry_t *se,
|
||||
cpu_context_t *ctx, int sec_state_to_resume,
|
||||
unsigned int intr_raw)
|
||||
cpu_context_t *ctx, struct jmpbuf *dispatch_jmp)
|
||||
{
|
||||
el3_state_t *el3_ctx = get_el3state_ctx(ctx);
|
||||
sdei_dispatch_context_t *disp_ctx;
|
||||
|
||||
/* Push the event and context */
|
||||
save_event_ctx(map, ctx, sec_state_to_resume, intr_raw);
|
||||
disp_ctx = save_event_ctx(map, ctx);
|
||||
|
||||
/*
|
||||
* Setup handler arguments:
|
||||
|
@ -268,8 +253,8 @@ static void setup_ns_dispatch(sdei_ev_map_t *map, sdei_entry_t *se,
|
|||
*/
|
||||
SMC_SET_GP(ctx, CTX_GPREG_X0, map->ev_num);
|
||||
SMC_SET_GP(ctx, CTX_GPREG_X1, se->arg);
|
||||
SMC_SET_GP(ctx, CTX_GPREG_X2, read_ctx_reg(el3_ctx, CTX_ELR_EL3));
|
||||
SMC_SET_GP(ctx, CTX_GPREG_X3, read_ctx_reg(el3_ctx, CTX_SPSR_EL3));
|
||||
SMC_SET_GP(ctx, CTX_GPREG_X2, disp_ctx->elr_el3);
|
||||
SMC_SET_GP(ctx, CTX_GPREG_X3, disp_ctx->spsr_el3);
|
||||
|
||||
/*
|
||||
* Prepare for ERET:
|
||||
|
@ -280,6 +265,20 @@ static void setup_ns_dispatch(sdei_ev_map_t *map, sdei_entry_t *se,
|
|||
cm_set_elr_spsr_el3(NON_SECURE, (uintptr_t) se->ep,
|
||||
SPSR_64(sdei_client_el(), MODE_SP_ELX,
|
||||
DISABLE_ALL_EXCEPTIONS));
|
||||
|
||||
#if DYNAMIC_WORKAROUND_CVE_2018_3639
|
||||
cve_2018_3639_t *tgt_cve_2018_3639;
|
||||
tgt_cve_2018_3639 = get_cve_2018_3639_ctx(ctx);
|
||||
|
||||
/* Save CVE-2018-3639 mitigation state */
|
||||
disp_ctx->disable_cve_2018_3639 = read_ctx_reg(tgt_cve_2018_3639,
|
||||
CTX_CVE_2018_3639_DISABLE);
|
||||
|
||||
/* Force SDEI handler to execute with mitigation enabled by default */
|
||||
write_ctx_reg(tgt_cve_2018_3639, CTX_CVE_2018_3639_DISABLE, 0);
|
||||
#endif
|
||||
|
||||
disp_ctx->dispatch_jmp = dispatch_jmp;
|
||||
}
|
||||
|
||||
/* Handle a triggered SDEI interrupt while events were masked on this PE */
|
||||
|
@ -349,6 +348,7 @@ int sdei_intr_handler(uint32_t intr_raw, uint32_t flags, void *handle,
|
|||
unsigned int sec_state;
|
||||
sdei_cpu_state_t *state;
|
||||
uint32_t intr;
|
||||
struct jmpbuf dispatch_jmp;
|
||||
|
||||
/*
|
||||
* To handle an event, the following conditions must be true:
|
||||
|
@ -482,29 +482,60 @@ int sdei_intr_handler(uint32_t intr_raw, uint32_t flags, void *handle,
|
|||
ctx = restore_and_resume_ns_context();
|
||||
}
|
||||
|
||||
setup_ns_dispatch(map, se, ctx, sec_state, intr_raw);
|
||||
/* Synchronously dispatch event */
|
||||
setup_ns_dispatch(map, se, ctx, &dispatch_jmp);
|
||||
begin_sdei_synchronous_dispatch(&dispatch_jmp);
|
||||
|
||||
/*
|
||||
* End of interrupt is done in sdei_event_complete, when the client
|
||||
* signals completion.
|
||||
* We reach here when client completes the event.
|
||||
*
|
||||
* If the cause of dispatch originally interrupted the Secure world, and
|
||||
* if Non-secure world wasn't allowed to preempt Secure execution,
|
||||
* resume Secure.
|
||||
*
|
||||
* No need to save the Non-secure context ahead of a world switch: the
|
||||
* Non-secure context was fully saved before dispatch, and has been
|
||||
* returned to its pre-dispatch state.
|
||||
*/
|
||||
if ((sec_state == SECURE) && (ehf_is_ns_preemption_allowed() == 0))
|
||||
restore_and_resume_secure_context();
|
||||
|
||||
/*
|
||||
* The event was dispatched after receiving SDEI interrupt. With
|
||||
* the event handling completed, EOI the corresponding
|
||||
* interrupt.
|
||||
*/
|
||||
if ((map->ev_num != SDEI_EVENT_0) && is_map_bound(map)) {
|
||||
ERROR("Invalid SDEI mapping: ev=%u\n", map->ev_num);
|
||||
panic();
|
||||
}
|
||||
plat_ic_end_of_interrupt(intr_raw);
|
||||
|
||||
if (is_event_shared(map))
|
||||
sdei_map_unlock(map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Explicitly dispatch the given SDEI event */
|
||||
int sdei_dispatch_event(int ev_num, unsigned int preempted_sec_state)
|
||||
/*
|
||||
* Explicitly dispatch the given SDEI event.
|
||||
*
|
||||
* When calling this API, the caller must be prepared for the SDEI dispatcher to
|
||||
* restore and make Non-secure context as active. This call returns only after
|
||||
* the client has completed the dispatch. Then, the Non-secure context will be
|
||||
* active, and the following ERET will return to Non-secure.
|
||||
*
|
||||
* Should the caller require re-entry to Secure, it must restore the Secure
|
||||
* context and program registers for ERET.
|
||||
*/
|
||||
int sdei_dispatch_event(int ev_num)
|
||||
{
|
||||
sdei_entry_t *se;
|
||||
sdei_ev_map_t *map;
|
||||
cpu_context_t *ctx;
|
||||
cpu_context_t *ns_ctx;
|
||||
sdei_dispatch_context_t *disp_ctx;
|
||||
sdei_cpu_state_t *state;
|
||||
|
||||
/* Validate preempted security state */
|
||||
if ((preempted_sec_state != SECURE) &&
|
||||
(preempted_sec_state != NON_SECURE)) {
|
||||
return -1;
|
||||
}
|
||||
struct jmpbuf dispatch_jmp;
|
||||
|
||||
/* Can't dispatch if events are masked on this PE */
|
||||
state = sdei_get_this_pe_state();
|
||||
|
@ -520,15 +551,8 @@ int sdei_dispatch_event(int ev_num, unsigned int preempted_sec_state)
|
|||
if (!map)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Statically-bound or dynamic maps are dispatched only as a result of
|
||||
* interrupt, and not upon explicit request.
|
||||
*/
|
||||
if (is_map_dynamic(map) || is_map_bound(map))
|
||||
return -1;
|
||||
|
||||
/* The event must be private */
|
||||
if (is_event_shared(map))
|
||||
/* Only explicit events can be dispatched */
|
||||
if (!is_map_explicit(map))
|
||||
return -1;
|
||||
|
||||
/* Examine state of dispatch stack */
|
||||
|
@ -557,21 +581,31 @@ int sdei_dispatch_event(int ev_num, unsigned int preempted_sec_state)
|
|||
ehf_activate_priority(sdei_event_priority(map));
|
||||
|
||||
/*
|
||||
* We assume the current context is SECURE, and that it's already been
|
||||
* saved.
|
||||
* Prepare for NS dispatch by restoring the Non-secure context and
|
||||
* marking that as active.
|
||||
*/
|
||||
ctx = restore_and_resume_ns_context();
|
||||
ns_ctx = restore_and_resume_ns_context();
|
||||
|
||||
/* Dispatch event synchronously */
|
||||
setup_ns_dispatch(map, se, ns_ctx, &dispatch_jmp);
|
||||
begin_sdei_synchronous_dispatch(&dispatch_jmp);
|
||||
|
||||
/*
|
||||
* The caller has effectively terminated execution. Record to resume the
|
||||
* preempted context later when the event completes or
|
||||
* complete-and-resumes.
|
||||
* We reach here when client completes the event.
|
||||
*
|
||||
* Deactivate the priority level that was activated at the time of
|
||||
* explicit dispatch.
|
||||
*/
|
||||
setup_ns_dispatch(map, se, ctx, preempted_sec_state, 0);
|
||||
ehf_deactivate_priority(sdei_event_priority(map));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void end_sdei_explicit_dispatch(struct jmpbuf *buffer)
|
||||
{
|
||||
longjmp(buffer);
|
||||
}
|
||||
|
||||
int sdei_event_complete(int resume, uint64_t pc)
|
||||
{
|
||||
sdei_dispatch_context_t *disp_ctx;
|
||||
|
@ -644,38 +678,8 @@ int sdei_event_complete(int resume, uint64_t pc)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the cause of dispatch originally interrupted the Secure world, and
|
||||
* if Non-secure world wasn't allowed to preempt Secure execution,
|
||||
* resume Secure.
|
||||
*
|
||||
* No need to save the Non-secure context ahead of a world switch: the
|
||||
* Non-secure context was fully saved before dispatch, and has been
|
||||
* returned to its pre-dispatch state.
|
||||
*/
|
||||
if ((disp_ctx->sec_state == SECURE) &&
|
||||
(ehf_is_ns_preemption_allowed() == 0)) {
|
||||
restore_and_resume_secure_context();
|
||||
}
|
||||
|
||||
if ((map->ev_num == SDEI_EVENT_0) || is_map_bound(map)) {
|
||||
/*
|
||||
* The event was dispatched after receiving SDEI interrupt. With
|
||||
* the event handling completed, EOI the corresponding
|
||||
* interrupt.
|
||||
*/
|
||||
plat_ic_end_of_interrupt(disp_ctx->intr_raw);
|
||||
} else {
|
||||
/*
|
||||
* An unbound event must have been dispatched explicitly.
|
||||
* Deactivate the priority level that was activated at the time
|
||||
* of explicit dispatch.
|
||||
*/
|
||||
ehf_deactivate_priority(sdei_event_priority(map));
|
||||
}
|
||||
|
||||
if (is_event_shared(map))
|
||||
sdei_map_unlock(map);
|
||||
/* End the outstanding dispatch */
|
||||
end_sdei_explicit_dispatch(disp_ctx->dispatch_jmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
|
||||
* Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
@ -10,7 +10,6 @@
|
|||
#include <bl_common.h>
|
||||
#include <cassert.h>
|
||||
#include <context.h>
|
||||
#include <context_mgmt.h>
|
||||
#include <debug.h>
|
||||
#include <ehf.h>
|
||||
#include <interrupt_mgmt.h>
|
||||
|
@ -111,6 +110,9 @@ void sdei_class_init(sdei_class_t class)
|
|||
|
||||
/* No shared mapping should have signalable property */
|
||||
assert(!is_event_signalable(map));
|
||||
|
||||
/* Shared mappings can't be explicit */
|
||||
assert(!is_map_explicit(map));
|
||||
#endif
|
||||
|
||||
/* Skip initializing the wrong priority */
|
||||
|
@ -162,6 +164,16 @@ void sdei_class_init(sdei_class_t class)
|
|||
|
||||
/* Make sure it's a private event */
|
||||
assert(is_event_private(map));
|
||||
|
||||
/*
|
||||
* Other than priority, explicit events can only have explicit
|
||||
* and private flags set.
|
||||
*/
|
||||
if (is_map_explicit(map)) {
|
||||
assert((map->map_flags | SDEI_MAPF_CRITICAL) ==
|
||||
(SDEI_MAPF_EXPLICIT | SDEI_MAPF_PRIVATE
|
||||
| SDEI_MAPF_CRITICAL));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Skip initializing the wrong priority */
|
||||
|
@ -174,6 +186,12 @@ void sdei_class_init(sdei_class_t class)
|
|||
assert(map->intr == SDEI_DYN_IRQ);
|
||||
assert(is_event_normal(map));
|
||||
num_dyn_priv_slots++;
|
||||
} else if (is_map_explicit(map)) {
|
||||
/*
|
||||
* Explicit mappings don't have a backing
|
||||
* SDEI interrupt, but verify that anyway.
|
||||
*/
|
||||
assert(map->intr == SDEI_DYN_IRQ);
|
||||
} else {
|
||||
/*
|
||||
* Private mappings must be bound to private
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
|
||||
* Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
@ -8,11 +8,13 @@
|
|||
#define __SDEI_PRIVATE_H__
|
||||
|
||||
#include <arch_helpers.h>
|
||||
#include <context_mgmt.h>
|
||||
#include <debug.h>
|
||||
#include <errno.h>
|
||||
#include <interrupt_mgmt.h>
|
||||
#include <platform.h>
|
||||
#include <sdei.h>
|
||||
#include <setjmp.h>
|
||||
#include <spinlock.h>
|
||||
#include <stdbool.h>
|
||||
#include <types.h>
|
||||
|
@ -137,6 +139,11 @@ static inline void set_map_bound(sdei_ev_map_t *map)
|
|||
map->map_flags |= BIT(_SDEI_MAPF_BOUND_SHIFT);
|
||||
}
|
||||
|
||||
static inline int is_map_explicit(sdei_ev_map_t *map)
|
||||
{
|
||||
return ((map->map_flags & BIT(_SDEI_MAPF_EXPLICIT_SHIFT)) != 0);
|
||||
}
|
||||
|
||||
static inline void clr_map_bound(sdei_ev_map_t *map)
|
||||
{
|
||||
map->map_flags &= ~(BIT(_SDEI_MAPF_BOUND_SHIFT));
|
||||
|
@ -154,7 +161,11 @@ static inline int is_secure_sgi(unsigned int intr)
|
|||
*/
|
||||
static inline unsigned int sdei_client_el(void)
|
||||
{
|
||||
return read_scr_el3() & SCR_HCE_BIT ? MODE_EL2 : MODE_EL1;
|
||||
cpu_context_t *ns_ctx = cm_get_context(NON_SECURE);
|
||||
el3_state_t *el3_ctx = get_el3state_ctx(ns_ctx);
|
||||
|
||||
return read_ctx_reg(el3_ctx, CTX_SPSR_EL3) & SCR_HCE_BIT ? MODE_EL2 :
|
||||
MODE_EL1;
|
||||
}
|
||||
|
||||
static inline unsigned int sdei_event_priority(sdei_ev_map_t *map)
|
||||
|
@ -230,5 +241,6 @@ unsigned int sdei_pe_mask(void);
|
|||
int sdei_intr_handler(uint32_t intr, uint32_t flags, void *handle,
|
||||
void *cookie);
|
||||
bool can_sdei_state_trans(sdei_entry_t *se, sdei_action_t act);
|
||||
void begin_sdei_synchronous_dispatch(struct jmpbuf *buffer);
|
||||
|
||||
#endif /* __SDEI_PRIVATE_H__ */
|
||||
|
|
Loading…
Reference in New Issue