diff options
author | Dimitris Papastamos <dimitris.papastamos@arm.com> | 2018-06-22 09:36:59 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-22 09:36:59 +0100 |
commit | 9dfd755303abcea6b58849793367639a8531b6fd (patch) | |
tree | 948d2ffcd6869e1e6a4246698571002ac34c1e9f /services | |
parent | 826469bc65c62bf1b096333158091dcae44153dd (diff) | |
parent | a7055c5828fecaba80054348cc469b7e5d025937 (diff) |
Merge pull request #1437 from jeenu-arm/ras-remaining
SDEI dispatch changes to enable RAS use cases
Diffstat (limited to 'services')
-rw-r--r-- | services/std_svc/sdei/sdei_dispatch.S | 26 | ||||
-rw-r--r-- | services/std_svc/sdei/sdei_intr_mgmt.c | 172 | ||||
-rw-r--r-- | services/std_svc/sdei/sdei_main.c | 22 | ||||
-rw-r--r-- | services/std_svc/sdei/sdei_private.h | 16 |
4 files changed, 148 insertions, 88 deletions
diff --git a/services/std_svc/sdei/sdei_dispatch.S b/services/std_svc/sdei/sdei_dispatch.S new file mode 100644 index 00000000..a7a4a40f --- /dev/null +++ b/services/std_svc/sdei/sdei_dispatch.S @@ -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 diff --git a/services/std_svc/sdei/sdei_intr_mgmt.c b/services/std_svc/sdei/sdei_intr_mgmt.c index c0bd9de6..6acb1295 100644 --- a/services/std_svc/sdei/sdei_intr_mgmt.c +++ b/services/std_svc/sdei/sdei_intr_mgmt.c @@ -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; } diff --git a/services/std_svc/sdei/sdei_main.c b/services/std_svc/sdei/sdei_main.c index 9589a252..d6d092de 100644 --- a/services/std_svc/sdei/sdei_main.c +++ b/services/std_svc/sdei/sdei_main.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 */ @@ -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 diff --git a/services/std_svc/sdei/sdei_private.h b/services/std_svc/sdei/sdei_private.h index 44db4193..ea602878 100644 --- a/services/std_svc/sdei/sdei_private.h +++ b/services/std_svc/sdei/sdei_private.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 */ @@ -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__ */ |