Merge pull request #1437 from jeenu-arm/ras-remaining

SDEI dispatch changes to enable RAS use cases
This commit is contained in:
Dimitris Papastamos 2018-06-22 09:36:59 +01:00 committed by GitHub
commit 9dfd755303
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 373 additions and 162 deletions

View File

@ -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

View File

@ -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

View File

@ -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
--------------------

View File

@ -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__ */

View File

@ -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__ */

65
lib/aarch64/setjmp.S Normal file
View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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__ */