diff options
94 files changed, 5920 insertions, 233 deletions
@@ -197,6 +197,7 @@ INCLUDES += -Iinclude/bl1 \ -Iinclude/lib/cpus/${ARCH} \ -Iinclude/lib/el3_runtime \ -Iinclude/lib/el3_runtime/${ARCH} \ + -Iinclude/lib/extensions \ -Iinclude/lib/pmf \ -Iinclude/lib/psci \ -Iinclude/lib/xlat_tables \ @@ -512,6 +513,7 @@ $(eval $(call add_define,PROGRAMMABLE_RESET_ADDRESS)) $(eval $(call add_define,PSCI_EXTENDED_STATE_ID)) $(eval $(call add_define,RESET_TO_BL31)) $(eval $(call add_define,SEPARATE_CODE_AND_RODATA)) +$(eval $(call add_define,ENABLE_SPM)) $(eval $(call add_define,SPD_${SPD})) $(eval $(call add_define,SPIN_ON_BL1_EXIT)) $(eval $(call add_define,TRUSTED_BOARD_BOOT)) diff --git a/bl31/aarch64/crash_reporting.S b/bl31/aarch64/crash_reporting.S index 34e4dcdd..cf32b31d 100644 --- a/bl31/aarch64/crash_reporting.S +++ b/bl31/aarch64/crash_reporting.S @@ -46,8 +46,7 @@ non_el3_sys_regs: "tpidrro_el0", "dacr32_el2", "ifsr32_el2", "par_el1",\ "mpidr_el1", "afsr0_el1", "afsr1_el1", "contextidr_el1",\ "vbar_el1", "cntp_ctl_el0", "cntp_cval_el0", "cntv_ctl_el0",\ - "cntv_cval_el0", "cntkctl_el1", "fpexc32_el2", "sp_el0",\ - "isr_el1", "" + "cntv_cval_el0", "cntkctl_el1", "sp_el0", "isr_el1", "" panic_msg: .asciz "PANIC in EL3 at x30 = 0x" @@ -313,9 +312,8 @@ func do_crash_reporting mrs x15, cntv_cval_el0 bl str_in_crash_buf_print mrs x8, cntkctl_el1 - mrs x9, fpexc32_el2 - mrs x10, sp_el0 - mrs x11, isr_el1 + mrs x9, sp_el0 + mrs x10, isr_el1 bl str_in_crash_buf_print /* Get the cpu specific registers to report */ diff --git a/bl31/bl31.ld.S b/bl31/bl31.ld.S index 9ff774b6..7f442d00 100644 --- a/bl31/bl31.ld.S +++ b/bl31/bl31.ld.S @@ -118,6 +118,23 @@ SECTIONS ASSERT(__CPU_OPS_END__ > __CPU_OPS_START__, "cpu_ops not defined for this platform.") +#if ENABLE_SPM + /* + * Exception vectors of the SPM shim layer. They must be aligned to a 2K + * address, but we need to place them in a separate page so that we can set + * individual permissions to them, so the actual alignment needed is 4K. + * + * There's no need to include this into the RO section of BL31 because it + * doesn't need to be accessed by BL31. + */ + spm_shim_exceptions : ALIGN(4096) { + __SPM_SHIM_EXCEPTIONS_START__ = .; + *(.spm_shim_exceptions) + . = NEXT(4096); + __SPM_SHIM_EXCEPTIONS_END__ = .; + } >RAM +#endif + /* * Define a linker symbol to mark start of the RW memory area for this * image. @@ -202,6 +219,13 @@ SECTIONS * the .bss section and eliminates the unecessary zero init */ xlat_table (NOLOAD) : { +#if ENABLE_SPM + __SP_IMAGE_XLAT_TABLES_START__ = .; + *secure_partition*.o(xlat_table) + /* Make sure that the rest of the page is empty. */ + . = NEXT(4096); + __SP_IMAGE_XLAT_TABLES_END__ = .; +#endif *(xlat_table) } >RAM diff --git a/bl31/bl31.mk b/bl31/bl31.mk index 0c9e3939..fccdc8a0 100644 --- a/bl31/bl31.mk +++ b/bl31/bl31.mk @@ -4,6 +4,15 @@ # SPDX-License-Identifier: BSD-3-Clause # +################################################################################ +# Include SPM Makefile +################################################################################ +ifeq (${ENABLE_SPM},1) +$(info Including SPM makefile) +include services/std_svc/spm/spm.mk +endif + + include lib/psci/psci_lib.mk BL31_SOURCES += bl31/bl31_main.c \ @@ -15,12 +24,32 @@ BL31_SOURCES += bl31/bl31_main.c \ common/runtime_svc.c \ plat/common/aarch64/platform_mp_stack.S \ services/std_svc/std_svc_setup.c \ - ${PSCI_LIB_SOURCES} + ${PSCI_LIB_SOURCES} \ + ${SPM_SOURCES} \ + ifeq (${ENABLE_PMF}, 1) BL31_SOURCES += lib/pmf/pmf_main.c endif +ifeq (${EL3_EXCEPTION_HANDLING},1) +BL31_SOURCES += bl31/ehf.c +endif + +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 \ + services/std_svc/sdei/sdei_intr_mgmt.c \ + services/std_svc/sdei/sdei_main.c \ + services/std_svc/sdei/sdei_state.c +endif + +ifeq (${ENABLE_SPE_FOR_LOWER_ELS},1) +BL31_SOURCES += lib/extensions/spe/spe.c +endif + BL31_LINKERFILE := bl31/bl31.ld.S # Flag used to indicate if Crash reporting via console should be included @@ -30,4 +59,9 @@ CRASH_REPORTING := $(DEBUG) endif $(eval $(call assert_boolean,CRASH_REPORTING)) +$(eval $(call assert_boolean,EL3_EXCEPTION_HANDLING)) +$(eval $(call assert_boolean,SDEI_SUPPORT)) + $(eval $(call add_define,CRASH_REPORTING)) +$(eval $(call add_define,EL3_EXCEPTION_HANDLING)) +$(eval $(call add_define,SDEI_SUPPORT)) diff --git a/bl31/bl31_main.c b/bl31/bl31_main.c index 4a88bd7b..a34cf86d 100644 --- a/bl31/bl31_main.c +++ b/bl31/bl31_main.c @@ -12,6 +12,7 @@ #include <console.h> #include <context_mgmt.h> #include <debug.h> +#include <ehf.h> #include <platform.h> #include <pmf.h> #include <runtime_instr.h> @@ -79,6 +80,11 @@ void bl31_main(void) /* Initialise helper libraries */ bl31_lib_init(); +#if EL3_EXCEPTION_HANDLING + INFO("BL31: Initialising Exception Handling Framework\n"); + ehf_init(); +#endif + /* Initialize the runtime services e.g. psci. */ INFO("BL31: Initializing runtime services\n"); runtime_svc_init(); diff --git a/bl31/ehf.c b/bl31/ehf.c new file mode 100644 index 00000000..65f2df52 --- /dev/null +++ b/bl31/ehf.c @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Exception handlers at EL3, their priority levels, and management. + */ + +#include <assert.h> +#include <cpu_data.h> +#include <debug.h> +#include <ehf.h> +#include <gic_common.h> +#include <interrupt_mgmt.h> +#include <platform.h> +#include <pubsub_events.h> + +/* Output EHF logs as verbose */ +#define EHF_LOG(...) VERBOSE("EHF: " __VA_ARGS__) + +#define EHF_INVALID_IDX (-1) + +/* For a valid handler, return the actual function pointer; otherwise, 0. */ +#define RAW_HANDLER(h) \ + ((ehf_handler_t) ((h & _EHF_PRI_VALID) ? (h & ~_EHF_PRI_VALID) : 0)) + +#define PRI_BIT(idx) (((ehf_pri_bits_t) 1) << idx) + +/* + * Convert index into secure priority using the platform-defined priority bits + * field. + */ +#define IDX_TO_PRI(idx) \ + ((idx << (7 - exception_data.pri_bits)) & 0x7f) + +/* Check whether a given index is valid */ +#define IS_IDX_VALID(idx) \ + ((exception_data.ehf_priorities[idx].ehf_handler & _EHF_PRI_VALID) != 0) + +/* Returns whether given priority is in secure priority range */ +#define IS_PRI_SECURE(pri) ((pri & 0x80) == 0) + +/* To be defined by the platform */ +extern const ehf_priorities_t exception_data; + +/* Translate priority to the index in the priority array */ +static int pri_to_idx(unsigned int priority) +{ + int idx; + + idx = EHF_PRI_TO_IDX(priority, exception_data.pri_bits); + assert((idx >= 0) && (idx < exception_data.num_priorities)); + assert(IS_IDX_VALID(idx)); + + return idx; +} + +/* Return whether there are outstanding priority activation */ +static int has_valid_pri_activations(pe_exc_data_t *pe_data) +{ + return pe_data->active_pri_bits != 0; +} + +static pe_exc_data_t *this_cpu_data(void) +{ + return &get_cpu_data(ehf_data); +} + +/* + * Return the current priority index of this CPU. If no priority is active, + * return EHF_INVALID_IDX. + */ +static int get_pe_highest_active_idx(pe_exc_data_t *pe_data) +{ + if (!has_valid_pri_activations(pe_data)) + return EHF_INVALID_IDX; + + /* Current priority is the right-most bit */ + return __builtin_ctz(pe_data->active_pri_bits); +} + +/* + * Mark priority active by setting the corresponding bit in active_pri_bits and + * programming the priority mask. + * + * This API is to be used as part of delegating to lower ELs other than for + * interrupts; e.g. while handling synchronous exceptions. + * + * This API is expected to be invoked before restoring context (Secure or + * Non-secure) in preparation for the respective dispatch. + */ +void ehf_activate_priority(unsigned int priority) +{ + int idx, cur_pri_idx; + unsigned int old_mask, run_pri; + pe_exc_data_t *pe_data = this_cpu_data(); + + /* + * Query interrupt controller for the running priority, or idle priority + * if no interrupts are being handled. The requested priority must be + * less (higher priority) than the active running priority. + */ + run_pri = plat_ic_get_running_priority(); + if (priority >= run_pri) { + ERROR("Running priority higher (0x%x) than requested (0x%x)\n", + run_pri, priority); + panic(); + } + + /* + * If there were priority activations already, the requested priority + * must be less (higher priority) than the current highest priority + * activation so far. + */ + cur_pri_idx = get_pe_highest_active_idx(pe_data); + idx = pri_to_idx(priority); + if ((cur_pri_idx != EHF_INVALID_IDX) && (idx >= cur_pri_idx)) { + ERROR("Activation priority mismatch: req=0x%x current=0x%x\n", + priority, IDX_TO_PRI(cur_pri_idx)); + panic(); + } + + /* Set the bit corresponding to the requested priority */ + pe_data->active_pri_bits |= PRI_BIT(idx); + + /* + * Program priority mask for the activated level. Check that the new + * priority mask is setting a higher priority level than the existing + * mask. + */ + old_mask = plat_ic_set_priority_mask(priority); + if (priority >= old_mask) { + ERROR("Requested priority (0x%x) lower than Priority Mask (0x%x)\n", + priority, old_mask); + panic(); + } + + /* + * If this is the first activation, save the priority mask. This will be + * restored after the last deactivation. + */ + if (cur_pri_idx == EHF_INVALID_IDX) + pe_data->init_pri_mask = old_mask; + + EHF_LOG("activate prio=%d\n", get_pe_highest_active_idx(pe_data)); +} + +/* + * Mark priority inactive by clearing the corresponding bit in active_pri_bits, + * and programming the priority mask. + * + * This API is expected to be used as part of delegating to to lower ELs other + * than for interrupts; e.g. while handling synchronous exceptions. + * + * This API is expected to be invoked after saving context (Secure or + * Non-secure), having concluded the respective dispatch. + */ +void ehf_deactivate_priority(unsigned int priority) +{ + int idx, cur_pri_idx; + pe_exc_data_t *pe_data = this_cpu_data(); + unsigned int old_mask, run_pri; + + /* + * Query interrupt controller for the running priority, or idle priority + * if no interrupts are being handled. The requested priority must be + * less (higher priority) than the active running priority. + */ + run_pri = plat_ic_get_running_priority(); + if (priority >= run_pri) { + ERROR("Running priority higher (0x%x) than requested (0x%x)\n", + run_pri, priority); + panic(); + } + + /* + * Deactivation is allowed only when there are priority activations, and + * the deactivation priority level must match the current activated + * priority. + */ + cur_pri_idx = get_pe_highest_active_idx(pe_data); + idx = pri_to_idx(priority); + if ((cur_pri_idx == EHF_INVALID_IDX) || (idx != cur_pri_idx)) { + ERROR("Deactivation priority mismatch: req=0x%x current=0x%x\n", + priority, IDX_TO_PRI(cur_pri_idx)); + panic(); + } + + /* Clear bit corresponding to highest priority */ + pe_data->active_pri_bits &= (pe_data->active_pri_bits - 1); + + /* + * Restore priority mask corresponding to the next priority, or the + * one stashed earlier if there are no more to deactivate. + */ + idx = get_pe_highest_active_idx(pe_data); + if (idx == EHF_INVALID_IDX) + old_mask = plat_ic_set_priority_mask(pe_data->init_pri_mask); + else + old_mask = plat_ic_set_priority_mask(priority); + + if (old_mask >= priority) { + ERROR("Deactivation priority (0x%x) lower than Priority Mask (0x%x)\n", + priority, old_mask); + panic(); + } + + EHF_LOG("deactivate prio=%d\n", get_pe_highest_active_idx(pe_data)); +} + +/* + * After leaving Non-secure world, stash current Non-secure Priority Mask, and + * set Priority Mask to the highest Non-secure priority so that Non-secure + * interrupts cannot preempt Secure execution. + * + * If the current running priority is in the secure range, or if there are + * outstanding priority activations, this function does nothing. + * + * This function subscribes to the 'cm_exited_normal_world' event published by + * the Context Management Library. + */ +static void *ehf_exited_normal_world(const void *arg) +{ + unsigned int run_pri; + pe_exc_data_t *pe_data = this_cpu_data(); + + /* If the running priority is in the secure range, do nothing */ + run_pri = plat_ic_get_running_priority(); + if (IS_PRI_SECURE(run_pri)) + return 0; + + /* Do nothing if there are explicit activations */ + if (has_valid_pri_activations(pe_data)) + return 0; + + assert(pe_data->ns_pri_mask == 0); + + pe_data->ns_pri_mask = + plat_ic_set_priority_mask(GIC_HIGHEST_NS_PRIORITY); + + /* The previous Priority Mask is not expected to be in secure range */ + if (IS_PRI_SECURE(pe_data->ns_pri_mask)) { + ERROR("Priority Mask (0x%x) already in secure range\n", + pe_data->ns_pri_mask); + panic(); + } + + EHF_LOG("Priority Mask: 0x%x => 0x%x\n", pe_data->ns_pri_mask, + GIC_HIGHEST_NS_PRIORITY); + + return 0; +} + +/* + * Conclude Secure execution and prepare for return to Non-secure world. Restore + * the Non-secure Priority Mask previously stashed upon leaving Non-secure + * world. + * + * If there the current running priority is in the secure range, or if there are + * outstanding priority activations, this function does nothing. + * + * This function subscribes to the 'cm_entering_normal_world' event published by + * the Context Management Library. + */ +static void *ehf_entering_normal_world(const void *arg) +{ + unsigned int old_pmr, run_pri; + pe_exc_data_t *pe_data = this_cpu_data(); + + /* If the running priority is in the secure range, do nothing */ + run_pri = plat_ic_get_running_priority(); + if (IS_PRI_SECURE(run_pri)) + return 0; + + /* + * If there are explicit activations, do nothing. The Priority Mask will + * be restored upon the last deactivation. + */ + if (has_valid_pri_activations(pe_data)) + return 0; + + /* Do nothing if we don't have a valid Priority Mask to restore */ + if (pe_data->ns_pri_mask == 0) + return 0; + + old_pmr = plat_ic_set_priority_mask(pe_data->ns_pri_mask); + + /* + * When exiting secure world, the current Priority Mask must be + * GIC_HIGHEST_NS_PRIORITY (as set during entry), or the Non-secure + * priority mask set upon calling ehf_allow_ns_preemption() + */ + if ((old_pmr != GIC_HIGHEST_NS_PRIORITY) && + (old_pmr != pe_data->ns_pri_mask)) { + ERROR("Invalid Priority Mask (0x%x) restored\n", old_pmr); + panic(); + } + + EHF_LOG("Priority Mask: 0x%x => 0x%x\n", old_pmr, pe_data->ns_pri_mask); + + pe_data->ns_pri_mask = 0; + + return 0; +} + +/* + * Program Priority Mask to the original Non-secure priority such that + * Non-secure interrupts may preempt Secure execution, viz. during Yielding SMC + * calls. + * + * This API is expected to be invoked before delegating a yielding SMC to Secure + * EL1. I.e. within the window of secure execution after Non-secure context is + * saved (after entry into EL3) and Secure context is restored (before entering + * Secure EL1). + */ +void ehf_allow_ns_preemption(void) +{ + unsigned int old_pmr __unused; + pe_exc_data_t *pe_data = this_cpu_data(); + + /* + * We should have been notified earlier of entering secure world, and + * therefore have stashed the Non-secure priority mask. + */ + assert(pe_data->ns_pri_mask != 0); + + /* Make sure no priority levels are active when requesting this */ + if (has_valid_pri_activations(pe_data)) { + ERROR("PE %lx has priority activations: 0x%x\n", + read_mpidr_el1(), pe_data->active_pri_bits); + panic(); + } + + old_pmr = plat_ic_set_priority_mask(pe_data->ns_pri_mask); + + EHF_LOG("Priority Mask: 0x%x => 0x%x\n", old_pmr, pe_data->ns_pri_mask); + + pe_data->ns_pri_mask = 0; +} + +/* + * Return whether Secure execution has explicitly allowed Non-secure interrupts + * to preempt itself, viz. during Yielding SMC calls. + */ +unsigned int ehf_is_ns_preemption_allowed(void) +{ + unsigned int run_pri; + pe_exc_data_t *pe_data = this_cpu_data(); + + /* If running priority is in secure range, return false */ + run_pri = plat_ic_get_running_priority(); + if (IS_PRI_SECURE(run_pri)) + return 0; + + /* + * If Non-secure preemption was permitted by calling + * ehf_allow_ns_preemption() earlier: + * + * - There wouldn't have been priority activations; + * - We would have cleared the stashed the Non-secure Priority Mask. + */ + if (has_valid_pri_activations(pe_data)) + return 0; + if (pe_data->ns_pri_mask != 0) + return 0; + + return 1; +} + +/* + * Top-level EL3 interrupt handler. + */ +static uint64_t ehf_el3_interrupt_handler(uint32_t id, uint32_t flags, + void *handle, void *cookie) +{ + int pri, idx, intr, intr_raw, ret = 0; + ehf_handler_t handler; + + /* + * Top-level interrupt type handler from Interrupt Management Framework + * doesn't acknowledge the interrupt; so the interrupt ID must be + * invalid. + */ + assert(id == INTR_ID_UNAVAILABLE); + + /* + * Acknowledge interrupt. Proceed with handling only for valid interrupt + * IDs. This situation may arise because of Interrupt Management + * Framework identifying an EL3 interrupt, but before it's been + * acknowledged here, the interrupt was either deasserted, or there was + * a higher-priority interrupt of another type. + */ + intr_raw = plat_ic_acknowledge_interrupt(); + intr = plat_ic_get_interrupt_id(intr_raw); + if (intr == INTR_ID_UNAVAILABLE) + return 0; + + /* Having acknowledged the interrupt, get the running priority */ + pri = plat_ic_get_running_priority(); + + /* Check EL3 interrupt priority is in secure range */ + assert(IS_PRI_SECURE(pri)); + + /* + * Translate the priority to a descriptor index. We do this by masking + * and shifting the running priority value (platform-supplied). + */ + idx = pri_to_idx(pri); + + /* Validate priority */ + assert(pri == IDX_TO_PRI(idx)); + + handler = RAW_HANDLER(exception_data.ehf_priorities[idx].ehf_handler); + if (!handler) { + ERROR("No EL3 exception handler for priority 0x%x\n", + IDX_TO_PRI(idx)); + panic(); + } + + /* + * Call registered handler. Pass the raw interrupt value to registered + * handlers. + */ + ret = handler(intr_raw, flags, handle, cookie); + + return ret; +} + +/* + * Initialize the EL3 exception handling. + */ +void ehf_init(void) +{ + unsigned int flags = 0; + int ret __unused; + + /* Ensure EL3 interrupts are supported */ + assert(plat_ic_has_interrupt_type(INTR_TYPE_EL3)); + + /* + * Make sure that priority water mark has enough bits to represent the + * whole priority array. + */ + assert(exception_data.num_priorities <= (sizeof(ehf_pri_bits_t) * 8)); + + assert(exception_data.ehf_priorities); + + /* + * Bit 7 of GIC priority must be 0 for secure interrupts. This means + * platforms must use at least 1 of the remaining 7 bits. + */ + assert((exception_data.pri_bits >= 1) || (exception_data.pri_bits < 8)); + + /* Route EL3 interrupts when in Secure and Non-secure. */ + set_interrupt_rm_flag(flags, NON_SECURE); + set_interrupt_rm_flag(flags, SECURE); + + /* Register handler for EL3 interrupts */ + ret = register_interrupt_type_handler(INTR_TYPE_EL3, + ehf_el3_interrupt_handler, flags); + assert(ret == 0); +} + +/* + * Register a handler at the supplied priority. Registration is allowed only if + * a handler hasn't been registered before, or one wasn't provided at build + * time. The priority for which the handler is being registered must also accord + * with the platform-supplied data. + */ +void ehf_register_priority_handler(unsigned int pri, ehf_handler_t handler) +{ + int idx; + + /* Sanity check for handler */ + assert(handler != NULL); + + /* Handler ought to be 4-byte aligned */ + assert((((uintptr_t) handler) & 3) == 0); + + /* Ensure we register for valid priority */ + idx = pri_to_idx(pri); + assert(idx < exception_data.num_priorities); + assert(IDX_TO_PRI(idx) == pri); + + /* Return failure if a handler was already registered */ + if (exception_data.ehf_priorities[idx].ehf_handler != _EHF_NO_HANDLER) { + ERROR("Handler already registered for priority 0x%x\n", pri); + panic(); + } + + /* + * Install handler, and retain the valid bit. We assume that the handler + * is 4-byte aligned, which is usually the case. + */ + exception_data.ehf_priorities[idx].ehf_handler = + (((uintptr_t) handler) | _EHF_PRI_VALID); + + EHF_LOG("register pri=0x%x handler=%p\n", pri, handler); +} + +SUBSCRIBE_TO_EVENT(cm_entering_normal_world, ehf_entering_normal_world); +SUBSCRIBE_TO_EVENT(cm_exited_normal_world, ehf_exited_normal_world); diff --git a/docs/firmware-design.rst b/docs/firmware-design.rst index 93c13d25..405964d2 100644 --- a/docs/firmware-design.rst +++ b/docs/firmware-design.rst @@ -1144,7 +1144,6 @@ The sample crash output is shown below. cntv_ctl_el0 :0x0000000000000000 cntv_cval_el0 :0x0000000000000000 cntkctl_el1 :0x0000000000000000 - fpexc32_el2 :0x0000000004000700 sp_el0 :0x0000000004010780 Guidelines for Reset Handlers diff --git a/docs/plantuml/plantuml_to_svg.sh b/docs/plantuml/plantuml_to_svg.sh new file mode 100755 index 00000000..0bf8588b --- /dev/null +++ b/docs/plantuml/plantuml_to_svg.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Convert all PlantUML files in this directory to SVG files. The plantuml_jar +# environment variable must be set to the path to PlantUML JAR file. + +if [ -z "$plantuml_jar" ]; then + echo "Usage: plantuml_jar=/path/to/plantuml.jar $0 *.puml" >&2 + exit 1 +fi + +java -jar "$plantuml_jar" -nometadata -tsvg "$@" + +# vim:set noet sts=8 tw=80: diff --git a/docs/plantuml/sdei_explicit_dispatch.puml b/docs/plantuml/sdei_explicit_dispatch.puml new file mode 100644 index 00000000..c80fcd11 --- /dev/null +++ b/docs/plantuml/sdei_explicit_dispatch.puml @@ -0,0 +1,45 @@ +/' + ' Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + ' + ' SPDX-License-Identifier: BSD-3-Clause + '/ + +@startuml + +autonumber "<b>[#]</b>" +participant "SDEI client" as EL2 +participant EL3 +participant "Secure Partition" as SP + +activate EL2 +EL2->EL3: **SDEI_EVENT_REGISTER**(ev, handler, ...) +EL3->EL2: success +EL2->EL3: **SDEI_EVENT_ENABLE**(ev) +EL3->EL2: success +EL2->EL3: **SDEI_PE_UNMASK**() +EL3->EL2: 1 + +... <<Business as usual>> ... + +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 +activate EL2 #salmon +note over EL2: SDEI handler +EL2->EL3: **SDEI_EVENT_COMPLETE()** +deactivate EL2 +note over EL3: Complete SDEI dispatch +EL3->EL2: resumes preempted execution +deactivate EL3 + +... <<Normal execution resumes>> ... + +@enduml diff --git a/docs/plantuml/sdei_explicit_dispatch.svg b/docs/plantuml/sdei_explicit_dispatch.svg new file mode 100644 index 00000000..182df0af --- /dev/null +++ b/docs/plantuml/sdei_explicit_dispatch.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="767px" preserveAspectRatio="none" style="width:692px;height:767px;" version="1.1" viewBox="0 0 692 767" width="692px" zoomAndPan="magnify"><defs><filter height="300%" id="fueepysa066oi" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><rect fill="#FFFFFF" filter="url(#fueepysa066oi)" height="174.7969" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="48.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="48.2969" y2="223.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="48.2969" y2="223.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="66.5" y1="48.2969" y2="48.2969"/><rect fill="#FFFFFF" filter="url(#fueepysa066oi)" height="412.5938" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="263.8984"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="263.8984" y2="676.4922"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="263.8984" y2="676.4922"/><rect fill="#FFFFFF" filter="url(#fueepysa066oi)" height="1" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="717.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="717.2969" y2="718.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="717.2969" y2="718.2969"/><rect fill="#FA8072" filter="url(#fueepysa066oi)" height="68.2656" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="61.5" y="531.8281"/><rect fill="#FF0000" filter="url(#fueepysa066oi)" height="383.4609" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="375.5" y="284.8984"/><rect fill="#FA8072" filter="url(#fueepysa066oi)" height="68.2656" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="588.5" y="353.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="61" x2="61" y1="38.2969" y2="223.0938"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="61" x2="61" y1="223.0938" y2="263.8984"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="61" x2="61" y1="263.8984" y2="676.4922"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="61" x2="61" y1="676.4922" y2="717.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="61" x2="61" y1="717.2969" y2="727.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="380" x2="380" y1="38.2969" y2="223.0938"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="380" x2="380" y1="223.0938" y2="263.8984"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="380" x2="380" y1="263.8984" y2="676.4922"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="380" x2="380" y1="676.4922" y2="717.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="380" x2="380" y1="717.2969" y2="727.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="593" x2="593" y1="38.2969" y2="223.0938"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="593" x2="593" y1="223.0938" y2="263.8984"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="593" x2="593" y1="263.8984" y2="676.4922"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="593" x2="593" y1="676.4922" y2="717.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="593" x2="593" y1="717.2969" y2="727.2969"/><rect fill="#FEFECE" filter="url(#fueepysa066oi)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="87" x="16" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="73" x="23" y="22.9951">SDEI client</text><rect fill="#FEFECE" filter="url(#fueepysa066oi)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="87" x="16" y="726.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="73" x="23" y="746.292">SDEI client</text><rect fill="#FEFECE" filter="url(#fueepysa066oi)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="39" x="359" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="25" x="366" y="22.9951">EL3</text><rect fill="#FEFECE" filter="url(#fueepysa066oi)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="39" x="359" y="726.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="25" x="366" y="746.292">EL3</text><rect fill="#FEFECE" filter="url(#fueepysa066oi)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="123" x="530" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="109" x="537" y="22.9951">Secure Partition</text><rect fill="#FEFECE" filter="url(#fueepysa066oi)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="123" x="530" y="726.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="109" x="537" y="746.292">Secure Partition</text><rect fill="#FFFFFF" filter="url(#fueepysa066oi)" height="174.7969" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="48.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="48.2969" y2="223.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="48.2969" y2="223.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="66.5" y1="48.2969" y2="48.2969"/><rect fill="#FFFFFF" filter="url(#fueepysa066oi)" height="412.5938" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="263.8984"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="263.8984" y2="676.4922"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="263.8984" y2="676.4922"/><rect fill="#FFFFFF" filter="url(#fueepysa066oi)" height="1" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="717.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="717.2969" y2="718.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="717.2969" y2="718.2969"/><rect fill="#FA8072" filter="url(#fueepysa066oi)" height="68.2656" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="61.5" y="531.8281"/><rect fill="#FF0000" filter="url(#fueepysa066oi)" height="383.4609" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="375.5" y="284.8984"/><rect fill="#FA8072" filter="url(#fueepysa066oi)" height="68.2656" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="588.5" y="353.1641"/><polygon fill="#A80036" points="368.5,65.2969,378.5,69.2969,368.5,73.2969,372.5,69.2969" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="374.5" y1="69.2969" y2="69.2969"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="73.5" y="64.3638">[1]</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="164" x="98.5" y="64.3638">SDEI_EVENT_REGISTER</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="101" x="262.5" y="64.3638">(ev, handler, ...)</text><polygon fill="#A80036" points="77.5,94.4297,67.5,98.4297,77.5,102.4297,73.5,98.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="71.5" x2="379.5" y1="98.4297" y2="98.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="83.5" y="93.4966">[2]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="51" x="108.5" y="93.4966">success</text><polygon fill="#A80036" points="368.5,123.5625,378.5,127.5625,368.5,131.5625,372.5,127.5625" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="374.5" y1="127.5625" y2="127.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="73.5" y="122.6294">[3]</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="151" x="98.5" y="122.6294">SDEI_EVENT_ENABLE</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="25" x="249.5" y="122.6294">(ev)</text><polygon fill="#A80036" points="77.5,152.6953,67.5,156.6953,77.5,160.6953,73.5,156.6953" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="71.5" x2="379.5" y1="156.6953" y2="156.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="83.5" y="151.7622">[4]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="51" x="108.5" y="151.7622">success</text><polygon fill="#A80036" points="368.5,181.8281,378.5,185.8281,368.5,189.8281,372.5,185.8281" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="374.5" y1="185.8281" y2="185.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="73.5" y="180.895">[5]</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="129" x="98.5" y="180.895">SDEI_PE_UNMASK</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="10" x="227.5" y="180.895">()</text><polygon fill="#A80036" points="77.5,210.9609,67.5,214.9609,77.5,218.9609,73.5,214.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="71.5" x2="379.5" y1="214.9609" y2="214.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="83.5" y="210.0278">[6]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="8" x="108.5" y="210.0278">1</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="142" x="256.5" y="247.3042"><<Business as usual>></text><polygon fill="#A80036" points="396.5,280.8984,386.5,284.8984,396.5,288.8984,392.5,284.8984" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 2.0,2.0;" x1="390.5" x2="680" y1="284.8984" y2="284.8984"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="402.5" y="279.9653">[7]</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="116" x="427.5" y="279.9653">CRITICAL EVENT</text><polygon fill="#FBFB77" filter="url(#fueepysa066oi)" points="306,298.0313,306,323.0313,451,323.0313,451,308.0313,441,298.0313,306,298.0313" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="441" x2="441" y1="298.0313" y2="308.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="451" x2="441" y1="308.0313" y2="308.0313"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="124" x="312" y="315.0981">Critical event triage</text><polygon fill="#A80036" points="576.5,349.1641,586.5,353.1641,576.5,357.1641,580.5,353.1641" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="385.5" x2="582.5" y1="353.1641" y2="353.1641"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="392.5" y="348.231">[8]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="54" x="417.5" y="348.231">dispatch</text><polygon fill="#FBFB77" filter="url(#fueepysa066oi)" points="510,366.2969,510,391.2969,672,391.2969,672,376.2969,662,366.2969,510,366.2969" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="662" x2="662" y1="366.2969" y2="376.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="672" x2="662" y1="376.2969" y2="376.2969"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="141" x="516" y="383.3638">Critical event handling</text><polygon fill="#A80036" points="396.5,417.4297,386.5,421.4297,396.5,425.4297,392.5,421.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="390.5" x2="592.5" y1="421.4297" y2="421.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="402.5" y="416.4966">[9]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="32" x="427.5" y="416.4966">done</text><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 2.0,2.0;" x1="385.5" x2="427.5" y1="450.6953" y2="450.6953"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 2.0,2.0;" x1="427.5" x2="427.5" y1="450.6953" y2="463.6953"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 2.0,2.0;" x1="386.5" x2="427.5" y1="463.6953" y2="463.6953"/><polygon fill="#A80036" points="396.5,459.6953,386.5,463.6953,396.5,467.6953,392.5,463.6953" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="30" x="392.5" y="445.6294">[10]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="155" x="426.5" y="445.6294">sdei_dispatch_event(ev)</text><polygon fill="#FBFB77" filter="url(#fueepysa066oi)" points="297,476.6953,297,501.6953,460,501.6953,460,486.6953,450,476.6953,297,476.6953" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="450" x2="450" y1="476.6953" y2="486.6953"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="460" x2="450" y1="486.6953" y2="486.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="142" x="303" y="493.7622">Prepare SDEI dispatch</text><polygon fill="#A80036" points="82.5,527.8281,72.5,531.8281,82.5,535.8281,78.5,531.8281" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="76.5" x2="374.5" y1="531.8281" y2="531.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="30" x="88.5" y="526.895">[11]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="54" x="122.5" y="526.895">dispatch</text><polygon fill="#FBFB77" filter="url(#fueepysa066oi)" points="8,544.9609,8,569.9609,111,569.9609,111,554.9609,101,544.9609,8,544.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="101" x2="101" y1="544.9609" y2="554.9609"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="111" x2="101" y1="554.9609" y2="554.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="82" x="14" y="562.0278">SDEI handler</text><polygon fill="#A80036" points="363.5,596.0938,373.5,600.0938,363.5,604.0938,367.5,600.0938" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="369.5" y1="600.0938" y2="600.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="30" x="73.5" y="595.1606">[12]</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="184" x="107.5" y="595.1606">SDEI_EVENT_COMPLETE()</text><polygon fill="#FBFB77" filter="url(#fueepysa066oi)" points="291,613.2266,291,638.2266,466,638.2266,466,623.2266,456,613.2266,291,613.2266" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="456" x2="456" y1="613.2266" y2="623.2266"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="466" x2="456" y1="623.2266" y2="623.2266"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="154" x="297" y="630.2935">Complete SDEI dispatch</text><polygon fill="#A80036" points="77.5,664.3594,67.5,668.3594,77.5,672.3594,73.5,668.3594" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="71.5" x2="379.5" y1="668.3594" y2="668.3594"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="30" x="83.5" y="663.4263">[13]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="197" x="117.5" y="663.4263">resumes preempted execution</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="190" x="232.5" y="700.7026"><<Normal execution resumes>></text></g></svg>
\ No newline at end of file diff --git a/docs/plantuml/sdei_general.puml b/docs/plantuml/sdei_general.puml new file mode 100644 index 00000000..ab6929ab --- /dev/null +++ b/docs/plantuml/sdei_general.puml @@ -0,0 +1,43 @@ +/' + ' Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + ' + ' SPDX-License-Identifier: BSD-3-Clause + '/ + +@startuml + +autonumber "<b>[#]</b>" +participant "SDEI client" as EL2 +participant EL3 +participant "SDEI interrupt source" as SDEI + +activate EL2 +EL2->EL3: **SDEI_INTERRUPT_BIND**(irq) +EL3->EL2: event number: ev +EL2->EL3: **SDEI_EVENT_REGISTER**(ev, handler, ...) +EL3->EL2: success +EL2->EL3: **SDEI_EVENT_ENABLE**(ev) +EL3->EL2: success +EL2->EL3: **SDEI_PE_UNMASK**() +EL3->EL2: 1 + +... <<Business as usual>> ... + +SDEI-->EL3: SDEI interrupt +activate SDEI #salmon +activate EL3 #red +note over EL3: Prepare SDEI dispatch +EL3->EL2: dispatch +activate EL2 #salmon +note over EL2: SDEI handler +EL2->EL3: **SDEI_EVENT_COMPLETE()** +deactivate EL2 +note over EL3: Complete SDEI dispatch +EL3-->SDEI: EOI +deactivate SDEI +EL3->EL2: resumes preempted execution +deactivate EL3 + +... <<Normal execution resumes>> ... + +@enduml diff --git a/docs/plantuml/sdei_general.svg b/docs/plantuml/sdei_general.svg new file mode 100644 index 00000000..e1721122 --- /dev/null +++ b/docs/plantuml/sdei_general.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="676px" preserveAspectRatio="none" style="width:608px;height:676px;" version="1.1" viewBox="0 0 608 676" width="608px" zoomAndPan="magnify"><defs><filter height="300%" id="fvds2ijrtbp5u" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><rect fill="#FFFFFF" filter="url(#fvds2ijrtbp5u)" height="233.0625" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="48.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="48.2969" y2="281.3594"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="48.2969" y2="281.3594"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="66.5" y1="48.2969" y2="48.2969"/><rect fill="#FFFFFF" filter="url(#fvds2ijrtbp5u)" height="263.0625" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="322.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="322.1641" y2="585.2266"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="322.1641" y2="585.2266"/><rect fill="#FFFFFF" filter="url(#fvds2ijrtbp5u)" height="1" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="626.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="626.0313" y2="627.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="626.0313" y2="627.0313"/><rect fill="#FA8072" filter="url(#fvds2ijrtbp5u)" height="68.2656" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="61.5" y="411.4297"/><rect fill="#FF0000" filter="url(#fvds2ijrtbp5u)" height="233.9297" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="375.5" y="343.1641"/><rect fill="#FA8072" filter="url(#fvds2ijrtbp5u)" height="204.7969" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="513.5" y="343.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="61" x2="61" y1="38.2969" y2="281.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="61" x2="61" y1="281.3594" y2="322.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="61" x2="61" y1="322.1641" y2="585.2266"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="61" x2="61" y1="585.2266" y2="626.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="61" x2="61" y1="626.0313" y2="636.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="380" x2="380" y1="38.2969" y2="281.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="380" x2="380" y1="281.3594" y2="322.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="380" x2="380" y1="322.1641" y2="585.2266"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="380" x2="380" y1="585.2266" y2="626.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="380" x2="380" y1="626.0313" y2="636.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="518" x2="518" y1="38.2969" y2="281.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="518" x2="518" y1="281.3594" y2="322.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="518" x2="518" y1="322.1641" y2="585.2266"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="518" x2="518" y1="585.2266" y2="626.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="518" x2="518" y1="626.0313" y2="636.0313"/><rect fill="#FEFECE" filter="url(#fvds2ijrtbp5u)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="87" x="16" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="73" x="23" y="22.9951">SDEI client</text><rect fill="#FEFECE" filter="url(#fvds2ijrtbp5u)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="87" x="16" y="635.0313"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="73" x="23" y="655.0264">SDEI client</text><rect fill="#FEFECE" filter="url(#fvds2ijrtbp5u)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="39" x="359" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="25" x="366" y="22.9951">EL3</text><rect fill="#FEFECE" filter="url(#fvds2ijrtbp5u)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="39" x="359" y="635.0313"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="25" x="366" y="655.0264">EL3</text><rect fill="#FEFECE" filter="url(#fvds2ijrtbp5u)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="161" x="436" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="147" x="443" y="22.9951">SDEI interrupt source</text><rect fill="#FEFECE" filter="url(#fvds2ijrtbp5u)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="161" x="436" y="635.0313"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="147" x="443" y="655.0264">SDEI interrupt source</text><rect fill="#FFFFFF" filter="url(#fvds2ijrtbp5u)" height="233.0625" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="48.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="48.2969" y2="281.3594"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="48.2969" y2="281.3594"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="66.5" y1="48.2969" y2="48.2969"/><rect fill="#FFFFFF" filter="url(#fvds2ijrtbp5u)" height="263.0625" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="322.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="322.1641" y2="585.2266"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="322.1641" y2="585.2266"/><rect fill="#FFFFFF" filter="url(#fvds2ijrtbp5u)" height="1" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="56.5" y="626.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="56.5" x2="56.5" y1="626.0313" y2="627.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="66.5" y1="626.0313" y2="627.0313"/><rect fill="#FA8072" filter="url(#fvds2ijrtbp5u)" height="68.2656" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="61.5" y="411.4297"/><rect fill="#FF0000" filter="url(#fvds2ijrtbp5u)" height="233.9297" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="375.5" y="343.1641"/><rect fill="#FA8072" filter="url(#fvds2ijrtbp5u)" height="204.7969" style="stroke: #A80036; stroke-width: 1.0;" width="10" x="513.5" y="343.1641"/><polygon fill="#A80036" points="368.5,65.2969,378.5,69.2969,368.5,73.2969,372.5,69.2969" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="374.5" y1="69.2969" y2="69.2969"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="73.5" y="64.3638">[1]</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="163" x="98.5" y="64.3638">SDEI_INTERRUPT_BIND</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="26" x="261.5" y="64.3638">(irq)</text><polygon fill="#A80036" points="77.5,94.4297,67.5,98.4297,77.5,102.4297,73.5,98.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="71.5" x2="379.5" y1="98.4297" y2="98.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="83.5" y="93.4966">[2]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="113" x="108.5" y="93.4966">event number: ev</text><polygon fill="#A80036" points="368.5,123.5625,378.5,127.5625,368.5,131.5625,372.5,127.5625" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="374.5" y1="127.5625" y2="127.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="73.5" y="122.6294">[3]</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="164" x="98.5" y="122.6294">SDEI_EVENT_REGISTER</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="101" x="262.5" y="122.6294">(ev, handler, ...)</text><polygon fill="#A80036" points="77.5,152.6953,67.5,156.6953,77.5,160.6953,73.5,156.6953" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="71.5" x2="379.5" y1="156.6953" y2="156.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="83.5" y="151.7622">[4]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="51" x="108.5" y="151.7622">success</text><polygon fill="#A80036" points="368.5,181.8281,378.5,185.8281,368.5,189.8281,372.5,185.8281" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="374.5" y1="185.8281" y2="185.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="73.5" y="180.895">[5]</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="151" x="98.5" y="180.895">SDEI_EVENT_ENABLE</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="25" x="249.5" y="180.895">(ev)</text><polygon fill="#A80036" points="77.5,210.9609,67.5,214.9609,77.5,218.9609,73.5,214.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="71.5" x2="379.5" y1="214.9609" y2="214.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="83.5" y="210.0278">[6]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="51" x="108.5" y="210.0278">success</text><polygon fill="#A80036" points="368.5,240.0938,378.5,244.0938,368.5,248.0938,372.5,244.0938" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="374.5" y1="244.0938" y2="244.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="73.5" y="239.1606">[7]</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="129" x="98.5" y="239.1606">SDEI_PE_UNMASK</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="10" x="227.5" y="239.1606">()</text><polygon fill="#A80036" points="77.5,269.2266,67.5,273.2266,77.5,277.2266,73.5,273.2266" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="71.5" x2="379.5" y1="273.2266" y2="273.2266"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="83.5" y="268.2935">[8]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="8" x="108.5" y="268.2935">1</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="142" x="219" y="305.5698"><<Business as usual>></text><polygon fill="#A80036" points="396.5,339.1641,386.5,343.1641,396.5,347.1641,392.5,343.1641" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 2.0,2.0;" x1="390.5" x2="512.5" y1="343.1641" y2="343.1641"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="21" x="402.5" y="338.231">[9]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="89" x="427.5" y="338.231">SDEI interrupt</text><polygon fill="#FBFB77" filter="url(#fvds2ijrtbp5u)" points="297,356.2969,297,381.2969,460,381.2969,460,366.2969,450,356.2969,297,356.2969" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="450" x2="450" y1="356.2969" y2="366.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="460" x2="450" y1="366.2969" y2="366.2969"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="142" x="303" y="373.3638">Prepare SDEI dispatch</text><polygon fill="#A80036" points="82.5,407.4297,72.5,411.4297,82.5,415.4297,78.5,411.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="76.5" x2="374.5" y1="411.4297" y2="411.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="30" x="88.5" y="406.4966">[10]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="54" x="122.5" y="406.4966">dispatch</text><polygon fill="#FBFB77" filter="url(#fvds2ijrtbp5u)" points="8,424.5625,8,449.5625,111,449.5625,111,434.5625,101,424.5625,8,424.5625" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="101" x2="101" y1="424.5625" y2="434.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="111" x2="101" y1="434.5625" y2="434.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="82" x="14" y="441.6294">SDEI handler</text><polygon fill="#A80036" points="363.5,475.6953,373.5,479.6953,363.5,483.6953,367.5,479.6953" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="66.5" x2="369.5" y1="479.6953" y2="479.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="30" x="73.5" y="474.7622">[11]</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="184" x="107.5" y="474.7622">SDEI_EVENT_COMPLETE()</text><polygon fill="#FBFB77" filter="url(#fvds2ijrtbp5u)" points="291,492.8281,291,517.8281,466,517.8281,466,502.8281,456,492.8281,291,492.8281" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="456" x2="456" y1="492.8281" y2="502.8281"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="466" x2="456" y1="502.8281" y2="502.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="154" x="297" y="509.895">Complete SDEI dispatch</text><polygon fill="#A80036" points="506.5,543.9609,516.5,547.9609,506.5,551.9609,510.5,547.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 2.0,2.0;" x1="385.5" x2="512.5" y1="547.9609" y2="547.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="30" x="392.5" y="543.0278">[12]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="21" x="426.5" y="543.0278">EOI</text><polygon fill="#A80036" points="77.5,573.0938,67.5,577.0938,77.5,581.0938,73.5,577.0938" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="71.5" x2="379.5" y1="577.0938" y2="577.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="30" x="83.5" y="572.1606">[13]</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="197" x="117.5" y="572.1606">resumes preempted execution</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="190" x="195" y="609.437"><<Normal execution resumes>></text></g></svg>
\ No newline at end of file diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index 795c0856..c14f0053 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -292,6 +292,22 @@ inserts to order memory updates before updating mask, then writes to the GIC *Priority Mask Register*, and make sure memory updates are visible before potential trigger due to mask update. +Function: unsigned int plat_ic_get_interrupt_id(unsigned int raw); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : unsigned int + +This API should extract and return the interrupt number from the raw value +obtained by the acknowledging the interrupt (read using +``plat_ic_acknowledge_interrupt()``). If the interrupt ID is invalid, this API +should return ``INTR_ID_UNAVAILABLE``. + +In case of ARM standard platforms using GIC, the implementation of the API +masks out the interrupt ID field from the acknowledged value from GIC. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst index f0a8aaf3..f020ec97 100644 --- a/docs/porting-guide.rst +++ b/docs/porting-guide.rst @@ -1904,6 +1904,74 @@ calculated by the linker then a link time assertion is raised. A compile time assertion is raised if the value of the constant is not aligned to the cache line boundary. +SDEI porting requirements +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The SDEI dispatcher requires the platform to provide the following macros +and functions, of which some are optional, and some others mandatory. + +Macros +...... + +Macro: PLAT_SDEI_NORMAL_PRI [mandatory] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This macro must be defined to the EL3 exception priority level associated with +Normal SDEI events on the platform. This must have a higher value (therefore of +lower priority) than ``PLAT_SDEI_CRITICAL_PRI``. + +Macro: PLAT_SDEI_CRITICAL_PRI [mandatory] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This macro must be defined to the EL3 exception priority level associated with +Critical SDEI events on the platform. This must have a lower value (therefore of +higher priority) than ``PLAT_SDEI_NORMAL_PRI``. + +It's recommended that SDEI exception priorities in general are assigned the +lowest among Secure priorities. Among the SDEI exceptions, Critical SDEI +priority must be higher than Normal SDEI priority. + +Functions +......... + +Function: int plat_sdei_validate_entry_point(uintptr_t ep) [optional] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + Argument: uintptr_t + Return: int + +This function validates the address of client entry points provided for both +event registration and *Complete and Resume* SDEI calls. The function takes one +argument, which is the address of the handler the SDEI client requested to +register. The function must return ``0`` for successful validation, or ``-1`` +upon failure. + +The default implementation always returns ``0``. On ARM platforms, this function +is implemented to translate the entry point to physical address, and further to +ensure that the address is located in Non-secure DRAM. + +Function: void plat_sdei_handle_masked_trigger(uint64_t mpidr, unsigned int intr) [optional] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + Argument: uint64_t + Argument: unsigned int + Return: void + +SDEI specification requires that a PE comes out of reset with the events masked. +The client therefore is expected to call ``PE_UNMASK`` to unmask SDEI events on +the PE. No SDEI events can be dispatched until such time. + +Should a PE receive an interrupt that was bound to an SDEI event while the +events are masked on the PE, the dispatcher implementation invokes the function +``plat_sdei_handle_masked_trigger``. The MPIDR of the PE that received the +interrupt and the interrupt ID are passed as parameters. + +The default implementation only prints out a warning message. + Power State Coordination Interface (in BL31) -------------------------------------------- @@ -2479,14 +2547,17 @@ Function : plat\_ic\_acknowledge\_interrupt() [mandatory] Return : uint32_t This API is used by the CPU to indicate to the platform IC that processing of -the highest pending interrupt has begun. It should return the id of the -interrupt which is being processed. +the highest pending interrupt has begun. It should return the raw, unmodified +value obtained from the interrupt controller when acknowledging an interrupt. +The actual interrupt number shall be extracted from this raw value using the API +`plat_ic_get_interrupt_id()`__. + +.. __: platform-interrupt-controller-API.rst#function-unsigned-int-plat-ic-get-interrupt-id-unsigned-int-raw-optional This function in ARM standard platforms using GICv2, reads the *Interrupt Acknowledge Register* (``GICC_IAR``). This changes the state of the highest priority pending interrupt from pending to active in the interrupt controller. -It returns the value read from the ``GICC_IAR``. This value is the id of the -interrupt whose state has been changed. +It returns the value read from the ``GICC_IAR``, unmodified. In the case of ARM standard platforms using GICv3, if the API is invoked from EL3, the function reads the system register ``ICC_IAR0_EL1``, *Interrupt @@ -2494,7 +2565,7 @@ Acknowledge Register group 0*. If the API is invoked from S-EL1, the function reads the system register ``ICC_IAR1_EL1``, *Interrupt Acknowledge Register group 1*. The read changes the state of the highest pending interrupt from pending to active in the interrupt controller. The value read is returned -and is the id of the interrupt whose state has been changed. +unmodified. The TSP uses this API to start processing of the secure physical timer interrupt. diff --git a/docs/sdei.rst b/docs/sdei.rst new file mode 100644 index 00000000..a67b7248 --- /dev/null +++ b/docs/sdei.rst @@ -0,0 +1,370 @@ +Software Delegated Exception Interface +====================================== + + +.. section-numbering:: + :suffix: . + +.. contents:: + :depth: 2 + +This document provides an overview of the SDEI dispatcher implementation in ARM +Trusted Firmware. + +Introduction +------------ + +`Software Delegated Exception Interface`_ (SDEI) is an ARM specification for +Non-secure world to register handlers with firmware to receive notifications +about system events. Firmware will first receive the system events by way of +asynchronous exceptions and, in response, arranges for the registered handler to +execute in the Non-secure EL. + +Normal world software that interacts with the SDEI dispatcher (makes SDEI +requests and receives notifications) is referred to as the *SDEI Client*. A +client receives the event notification at the registered handler even when it +was executing with exceptions masked. The list of SDEI events available to the +client are specific to the platform [#std-event]_. See also `Determining client +EL`_. + +.. _general SDEI dispatch: + +The following figure depicts a general sequence involving SDEI client executing +at EL2 and an event dispatch resulting from the triggering of a bound interrupt. +A commentary is provided below: + +.. image:: plantuml/sdei_general.svg + +As part of initialisation, the SDEI client binds a Non-secure interrupt [1], and +the SDEI dispatcher returns a platform dynamic event number [2]. The client then +registers a handler for that event [3], enables the event [5], and unmasks all +events on the current PE [7]. This sequence is typical of an SDEI client, but it +may involve additional SDEI calls. + +At a later point in time, when the bound interrupt triggers [9], it's trapped to +EL3. The interrupt is handed over to the SDEI dispatcher, which then arranges to +execute the registered handler [10]. The client terminates its execution with +``SDEI_EVENT_COMPLETE`` [11], following which the dispatcher resumes the +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`_. + +The remainder of this document only discusses the design and implementation of +SDEI dispatcher in ARM Trusted Firmware, and assumes that the reader is familiar +with the SDEI specification, the interfaces, and their requirements. + +.. [#std-event] Except event 0, which is defined by the SDEI specification as a + standard event. + +Defining events +--------------- + +A platform choosing to include the SDEI dispatcher must also define the events +available on the platform, along with their attributes. + +The platform is expected to provide two arrays of event descriptors: one for +private events, and another for shared events. The SDEI dispatcher provides +``SDEI_PRIVATE_EVENT()`` and ``SDEI_SHARED_EVENT()`` macros to populate the +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: + + - If it's not applicable to an event, this shall be left as ``0``. + + - If the event is dynamic, this should be specified as ``SDEI_DYN_IRQ``. + +- A bit map of `Event flags`_. + +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. + +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 +``REGISTER_SDEI_MAP()`` macro must be used in the same file where the arrays are +defined. + +Regarding event descriptors: + +- For Event 0: + + - There must be exactly one descriptor in the private array, and none in the + shared array. + + - The event should be defined using ``SDEI_DEFINE_EVENT_0()``. + + - Must be bound to a Secure SGI on the platform. + +- Statically bound shared and private interrupts must be bound to shared and + private interrupts on the platform, respectively. See the section on + `interrupt configuration`__. + + .. __: `Configuration within Exception Handling Framework`_ + +- Both arrays should be one-dimensional. The ``REGISTER_SDEI_MAP()`` macro + takes care of replicating private events for each PE on the platform. + +- Both arrays must be sorted in the increasing order of event number. + +The SDEI specification doesn't have provisions for discovery of available events +on the platform. The list of events made available to the client, along with +their semantics, have to be communicated out of band; for example, through +Device Trees or firmware configuration tables. + +See also `Event definition example`_. + +Event flags +~~~~~~~~~~~ + +Event flags describe the properties of the event. They are bit maps that can be +``OR``\ ed to form parameters to macros that `define events`__. + +.. __: `Defining events`_ + +- ``SDEI_MAPF_DYNAMIC``: Marks the event as dynamic. Dynamic events can be + bound to (or released from) any Non-secure interrupt at runtime via. the + ``SDEI_INTERRUPT_BIND`` and ``SDEI_INTERRUPT_RELEASE`` calls. + +- ``SDEI_MAPF_BOUND``: Marks the event as statically bound to an interrupt. + These events cannot be re-bound at runtime. + +- ``SDEI_MAPF_CRITICAL``: Marks the event as having *Critical* priority. + Without this flag, the event is assumed to have *Normal* priority. + +Event definition example +------------------------ + +.. code:: c + + static sdei_ev_map_t plat_private_sdei[] = { + /* Event 0 definition */ + SDEI_DEFINE_EVENT_0(8), + + /* PPI */ + SDEI_PRIVATE_EVENT(8, 23, SDEI_MAPF_BOUND), + + /* Dynamic private events */ + SDEI_PRIVATE_EVENT(100, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), + SDEI_PRIVATE_EVENT(101, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC) + }; + + /* Shared event mappings */ + static sdei_ev_map_t plat_shared_sdei[] = { + SDEI_SHARED_EVENT(804, 0, SDEI_MAPF_DYNAMIC), + + /* Dynamic shared events */ + SDEI_SHARED_EVENT(3000, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), + SDEI_SHARED_EVENT(3001, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC) + }; + + /* Export SDEI events */ + REGISTER_SDEI_MAP(plat_private_sdei, plat_shared_sdei); + +Configuration within Exception Handling Framework +------------------------------------------------- + +The SDEI dispatcher functions alongside the Exception Handling Framework. This +means that the platform must assign priorities to both Normal and Critical SDEI +interrupts for the platform: + +- Install priority descriptors for Normal and Critical SDEI interrupts. + +- For those interrupts that are statically bound (i.e. events defined as having + the ``SDEI_MAPF_BOUND`` property), enumerate their properties for the GIC + driver to configure interrupts accordingly. + + The interrupts must be configured to target EL3. This means that they should + be configured as *Group 0*. Additionally, on GICv2 systems, the build option + ``GICV2_G0_FOR_EL3`` must be set to ``1``. + +See also `SDEI porting requirements`_. + +Determining client EL +--------------------- + +The SDEI specification requires that the *physical* SDEI client executes in the +highest Non-secure EL implemented on the system. This means that the dispatcher +will only allow SDEI calls to be made from: + +- EL2, if EL2 is implemented. The Hypervisor is expected to implement a + *virtual* SDEI dispatcher to support SDEI clients in Guest Operating Systems + executing in Non-secure EL1. + +- Non-secure EL1, if EL2 is not implemented or disabled. + +See the function ``sdei_client_el()`` in ``sdei_private.h``. + +Explicit dispatch of events +--------------------------- + +Typically, an SDEI event dispatch is caused by the PE receiving interrupts that +are bound to an SDEI event. However, there are cases where the Secure world +requires dispatch of an SDEI event as a direct or indirect result of a past +activity, viz. receiving a Secure interrupt or an exception. + +The SDEI dispatcher implementation provides ``sdei_dispatch_event()`` API for +this purpose. The API has the following signature: + +:: + + int sdei_dispatch_event(int ev_num, unsigned int preempted_sec_state); + +- 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 following figure depicts a scenario involving explicit dispatch of SDEI +event. A commentary is provided below: + +.. image:: plantuml/sdei_explicit_dispatch.svg + +As part of initialisation, the SDEI client registers a handler for a platform +event [1], enables the event [3], and unmasks the current PE [5]. Note that, +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. + +.. [#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 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All of the following requirements must be met for the API to return ``0`` and +event to be dispatched: + +- SDEI events must be unmasked on the PE. I.e. the client must have called + ``PE_UNMASK`` beforehand. + +- Event 0 can't be dispatched. + +- The event must neither be a dynamic event nor be bound to an interrupt. + +- The event must be private to the PE. + +- The event must have been registered for and enabled. + +- A dispatch for the same event must not be outstanding. I.e. it hasn't already + been dispatched and is yet to be completed. + +- The priority of the event (either Critical or Normal, as configured by the + platform at build-time) shouldn't cause priority inversion. This means: + + - If it's of Normal priority, neither Normal nor Critical priority dispatch + must be outstanding on the PE. + + - If it's of a Critical priority, no Critical priority dispatch must be + outstanding on the PE. + +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 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. + +- 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 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). + +Porting requirements +-------------------- + +The porting requirements of the SDEI dispatcher are outlined in the `porting +guide`__. + +.. __: `SDEI porting requirements`_ + +Note on writing SDEI event handlers +----------------------------------- + +*This section pertains to SDEI event handlers in general, not just when using +ARM Trusted Firmware SDEI dispatcher.* + +The SDEI specification requires that event handlers preserve the contents of all +registers except ``x0`` to ``x17``. This has significance if event handler is +written in C: compilers typically adjust the stack frame at the beginning and +end of C functions. For example, AArch64 GCC typically produces the following +function prologue and epilogue: + +:: + + c_event_handler: + stp x29, x30, [sp,#-32]! + mov x29, sp + + ... + + bl ... + + ... + + ldp x29, x30, [sp],#32 + ret + +The register ``x29`` is used as frame pointer in the prologue. Because neither a +valid ``SDEI_EVENT_COMPLETE`` nor ``SDEI_EVENT_COMPLETE_AND_RESUME`` calls +return to the handler, the epilogue never gets executed, and registers ``x29`` +and ``x30`` (in the case above) are inadvertently corrupted. This violates the +SDEI specification, and the normal execution thereafter will result in +unexpected behaviour. + +To work this around, it's advised that the top-level event handlers are +implemented in assembly, following a similar pattern as below: + +:: + + asm_event_handler: + /* Save link register whilst maintaining stack alignment */ + stp xzr, x30, [sp, #-16]! + bl c_event_handler + + /* Restore link register */ + ldp xzr, x30, [sp], #16 + + /* Complete call */ + ldr x0, =SDEI_EVENT_COMPLETE + smc #0 + b . + +---- + +*Copyright (c) 2017, 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 +.. _SDEI porting requirements: porting-guide.rst#sdei-porting-requirements diff --git a/docs/spm-user-guide.rst b/docs/spm-user-guide.rst new file mode 100644 index 00000000..a3b64d93 --- /dev/null +++ b/docs/spm-user-guide.rst @@ -0,0 +1,59 @@ +ARM Trusted Firmware - SPM User Guide +===================================== + +.. section-numbering:: + :suffix: . + +.. contents:: + + +This document briefly presents the Secure Partition Management (SPM) support in +the Arm Trusted Firmware (TF), specifically focusing on how to build Arm TF with +SPM support. + +Overview of the SPM software stack +---------------------------------- + +SPM is supported on the Arm FVP exclusively at the moment. + +It is not currently possible for BL31 to integrate SPM support and a Secure +Payload Dispatcher (SPD) at the same time; they are mutually exclusive. In the +SPM bootflow, a Secure Partition (SP) image executing at Secure-EL0 replaces the +Secure Payload image executing at Secure-EL1 (e.g. a Trusted OS). Both are +referred to as BL32. + +A working prototype of a SP has been implemented by repurposing the EDK2 code +and tools, leveraging the concept of the *Standalone Management Mode (MM)* in +the UEFI specification (see the PI v1.6 Volume 4: Management Mode Core +Interface). This will be referred to as the *Standalone MM Secure Partition* in +the rest of this document. + + +Building TF with SPM support +---------------------------- + +To enable SPM support in the TF, the source code must be compiled with the build +flag ``ENABLE_SPM=1``. On Arm platforms the build option ``ARM_BL31_IN_DRAM`` +can be used to select the location of BL31, both SRAM and DRAM are supported. + + +Using the Standalone MM SP +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First, build the Standalone MM Secure Partition. To build it, refer to the +`instructions in the EDK2 repository`_. + +Then build TF with SPM support and include the Standalone MM Secure Partition +image in the FIP: + +:: + + BL32=path/to/standalone/mm/sp BL33=path/to/bl33.bin \ + make PLAT=fvp ENABLE_SPM=1 fip all + + +-------------- + +*Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* + +.. _instructions in the EDK2 repository: https://github.com/tianocore/edk2-staging/blob/AArch64StandaloneMm/HowtoBuild.MD diff --git a/docs/user-guide.rst b/docs/user-guide.rst index d175ebd1..95fa22e0 100644 --- a/docs/user-guide.rst +++ b/docs/user-guide.rst @@ -345,9 +345,9 @@ Common build options the ``ENABLE_PMF`` build option as well. Default is 0. - ``ENABLE_SPE_FOR_LOWER_ELS`` : Boolean option to enable Statistical Profiling - extensions. This is an optional architectural feature available only for - AArch64 8.2 onwards. This option defaults to 1 but is automatically - disabled when the target architecture is AArch32 or AArch64 8.0/8.1. + extensions. This is an optional architectural feature for AArch64. + The default is 1 but is automatically disabled when the target architecture + is AArch32. - ``ENABLE_STACK_PROTECTOR``: String option to enable the stack protection checks in GCC. Allowed values are "all", "strong" and "0" (default). @@ -362,6 +362,11 @@ Common build options Firmware as error. It can take the value 1 (flag the use of deprecated APIs as error) or 0. The default is 0. +- ``EL3_EXCEPTION_HANDLING``: When set to ``1``, enable handling of exceptions + targeted at EL3. When set ``0`` (default), no exceptions are expected or + handled at EL3, and a panic will result. This is supported only for AArch64 + builds. + - ``FIP_NAME``: This is an optional build option which specifies the FIP filename for the ``fip`` target. Default is ``fip.bin``. @@ -421,11 +426,15 @@ Common build options - ``KEY_ALG``: This build flag enables the user to select the algorithm to be used for generating the PKCS keys and subsequent signing of the certificate. - It accepts 3 values viz ``rsa``, ``rsa_1_5``, ``ecdsa``. The ``rsa_1_5`` is + It accepts 3 values viz. ``rsa``, ``rsa_1_5``, ``ecdsa``. The ``rsa_1_5`` is the legacy PKCS#1 RSA 1.5 algorithm which is not TBBR compliant and is retained only for compatibility. The default value of this flag is ``rsa`` which is the TBBR compliant PKCS#1 RSA 2.1 scheme. +- ``HASH_ALG``: This build flag enables the user to select the secure hash + algorithm. It accepts 3 values viz. ``sha256``, ``sha384``, ``sha512``. + The default value of this flag is ``sha256``. + - ``LDFLAGS``: Extra user options appended to the linkers' command line in addition to the one set by the build system. @@ -530,6 +539,12 @@ Common build options optional. It is only needed if the platform makefile specifies that it is required in order to build the ``fwu_fip`` target. +- ``SDEI_SUPPORT``: Setting this to ``1`` enables support for Software + Delegated Exception Interface to BL31 image. This defaults to ``0``. + + When set to ``1``, the build option ``EL3_EXCEPTION_HANDLING`` must also be + set to ``1``. + - ``SEPARATE_CODE_AND_RODATA``: Whether code and read-only data should be isolated on separate memory pages. This is a trade-off between security and memory usage. See "Isolating code and read-only data on separate memory diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index 1d963bae..87986594 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -72,6 +72,8 @@ void gicv2_cpuif_disable(void) ******************************************************************************/ void gicv2_pcpu_distif_init(void) { + unsigned int ctlr; + assert(driver_data); assert(driver_data->gicd_base); @@ -89,6 +91,13 @@ void gicv2_pcpu_distif_init(void) driver_data->g0_interrupt_array); } #endif + + /* Enable G0 interrupts if not already */ + ctlr = gicd_read_ctlr(driver_data->gicd_base); + if ((ctlr & CTLR_ENABLE_G0_BIT) == 0) { + gicd_write_ctlr(driver_data->gicd_base, + ctlr | CTLR_ENABLE_G0_BIT); + } } /******************************************************************************* @@ -320,9 +329,26 @@ void gicv2_set_pe_target_mask(unsigned int proc_num) if (driver_data->target_masks[proc_num]) return; - /* Read target register corresponding to this CPU */ - driver_data->target_masks[proc_num] = - gicv2_get_cpuif_id(driver_data->gicd_base); + /* + * Update target register corresponding to this CPU and flush for it to + * be visible to other CPUs. + */ + if (driver_data->target_masks[proc_num] == 0) { + driver_data->target_masks[proc_num] = + gicv2_get_cpuif_id(driver_data->gicd_base); +#if !HW_ASSISTED_COHERENCY + /* + * PEs only update their own masks. Primary updates it with + * caches on. But because secondaries does it with caches off, + * all updates go to memory directly, and there's no danger of + * secondaries overwriting each others' mask, despite + * target_masks[] not being cache line aligned. + */ + flush_dcache_range((uintptr_t) + &driver_data->target_masks[proc_num], + sizeof(driver_data->target_masks[proc_num])); +#endif + } } /******************************************************************************* diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c index 25226956..dee63f18 100644 --- a/drivers/arm/gic/v3/gicv3_helpers.c +++ b/drivers/arm/gic/v3/gicv3_helpers.c @@ -541,12 +541,13 @@ void gicv3_secure_ppi_sgi_configure(uintptr_t gicr_base, /******************************************************************************* * Helper function to configure properties of secure G0 and G1S PPIs and SGIs. ******************************************************************************/ -void gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base, +unsigned int gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base, const interrupt_prop_t *interrupt_props, unsigned int interrupt_props_num) { unsigned int i; const interrupt_prop_t *current_prop; + unsigned int ctlr_enable = 0; /* Make sure there's a valid property array */ assert(interrupt_props != NULL); @@ -564,10 +565,13 @@ void gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base, /* Configure this interrupt as G0 or a G1S interrupt */ assert((current_prop->intr_grp == INTR_GROUP0) || (current_prop->intr_grp == INTR_GROUP1S)); - if (current_prop->intr_grp == INTR_GROUP1S) + if (current_prop->intr_grp == INTR_GROUP1S) { gicr_set_igrpmodr0(gicr_base, current_prop->intr_num); - else + ctlr_enable |= CTLR_ENABLE_G1S_BIT; + } else { gicr_clr_igrpmodr0(gicr_base, current_prop->intr_num); + ctlr_enable |= CTLR_ENABLE_G0_BIT; + } /* Set the priority of this interrupt */ gicr_set_ipriorityr(gicr_base, current_prop->intr_num, @@ -586,4 +590,6 @@ void gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base, /* Enable this interrupt */ gicr_set_isenabler0(gicr_base, current_prop->intr_num); } + + return ctlr_enable; } diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 8c4f5084..8de5be3f 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -224,12 +224,16 @@ void gicv3_distif_init(void) void gicv3_rdistif_init(unsigned int proc_num) { uintptr_t gicr_base; + unsigned int bitmap = 0; + uint32_t ctlr; assert(gicv3_driver_data); assert(proc_num < gicv3_driver_data->rdistif_num); assert(gicv3_driver_data->rdistif_base_addrs); assert(gicv3_driver_data->gicd_base); - assert(gicd_read_ctlr(gicv3_driver_data->gicd_base) & CTLR_ARE_S_BIT); + + ctlr = gicd_read_ctlr(gicv3_driver_data->gicd_base); + assert(ctlr & CTLR_ARE_S_BIT); assert(IS_IN_EL3()); @@ -244,7 +248,7 @@ void gicv3_rdistif_init(unsigned int proc_num) #if !ERROR_DEPRECATED if (gicv3_driver_data->interrupt_props != NULL) { #endif - gicv3_secure_ppi_sgi_configure_props(gicr_base, + bitmap = gicv3_secure_ppi_sgi_configure_props(gicr_base, gicv3_driver_data->interrupt_props, gicv3_driver_data->interrupt_props_num); #if !ERROR_DEPRECATED @@ -258,6 +262,7 @@ void gicv3_rdistif_init(unsigned int proc_num) gicv3_driver_data->g1s_interrupt_num, gicv3_driver_data->g1s_interrupt_array, INTR_GROUP1S); + bitmap |= CTLR_ENABLE_G1S_BIT; } /* Configure the G0 SGIs/PPIs */ @@ -266,9 +271,14 @@ void gicv3_rdistif_init(unsigned int proc_num) gicv3_driver_data->g0_interrupt_num, gicv3_driver_data->g0_interrupt_array, INTR_GROUP0); + bitmap |= CTLR_ENABLE_G0_BIT; } } #endif + + /* Enable interrupt groups as required, if not already */ + if ((ctlr & bitmap) != bitmap) + gicd_set_ctlr(gicv3_driver_data->gicd_base, bitmap, RWP_TRUE); } /******************************************************************************* diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h index a5093d0c..52039074 100644 --- a/drivers/arm/gic/v3/gicv3_private.h +++ b/drivers/arm/gic/v3/gicv3_private.h @@ -95,7 +95,7 @@ void gicv3_secure_ppi_sgi_configure(uintptr_t gicr_base, const unsigned int *sec_intr_list, unsigned int int_grp); #endif -void gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base, +unsigned int gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base, const interrupt_prop_t *interrupt_props, unsigned int interrupt_props_num); unsigned int gicv3_secure_spis_configure_props(uintptr_t gicd_base, diff --git a/drivers/auth/mbedtls/mbedtls_crypto.c b/drivers/auth/mbedtls/mbedtls_crypto.c index d8810d6d..bc9ed3a8 100644 --- a/drivers/auth/mbedtls/mbedtls_crypto.c +++ b/drivers/auth/mbedtls/mbedtls_crypto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,7 @@ #include <crypto_mod.h> #include <debug.h> #include <mbedtls_common.h> +#include <mbedtls_config.h> #include <stddef.h> #include <string.h> diff --git a/drivers/auth/mbedtls/mbedtls_crypto.mk b/drivers/auth/mbedtls/mbedtls_crypto.mk index d6fc7eb5..8eb4873d 100644 --- a/drivers/auth/mbedtls/mbedtls_crypto.mk +++ b/drivers/auth/mbedtls/mbedtls_crypto.mk @@ -37,9 +37,30 @@ MBEDTLS_CRYPTO_SOURCES := drivers/auth/mbedtls/mbedtls_crypto.c \ pk_wrap.c \ pkparse.c \ pkwrite.c \ - sha256.c \ ) +ifeq (${HASH_ALG}, sha384) + MBEDTLS_CRYPTO_SOURCES += \ + $(addprefix ${MBEDTLS_DIR}/library/, \ + sha256.c \ + sha512.c \ + ) + TF_MBEDTLS_HASH_ALG_ID := TF_MBEDTLS_SHA384 +else ifeq (${HASH_ALG}, sha512) + MBEDTLS_CRYPTO_SOURCES += \ + $(addprefix ${MBEDTLS_DIR}/library/, \ + sha256.c \ + sha512.c \ + ) + TF_MBEDTLS_HASH_ALG_ID := TF_MBEDTLS_SHA512 +else + MBEDTLS_CRYPTO_SOURCES += \ + $(addprefix ${MBEDTLS_DIR}/library/, \ + sha256.c \ + ) + TF_MBEDTLS_HASH_ALG_ID := TF_MBEDTLS_SHA256 +endif + # Key algorithm specific files MBEDTLS_ECDSA_CRYPTO_SOURCES += $(addprefix ${MBEDTLS_DIR}/library/, \ ecdsa.c \ @@ -67,6 +88,7 @@ endif # Needs to be set to drive mbed TLS configuration correctly $(eval $(call add_define,TF_MBEDTLS_KEY_ALG_ID)) +$(eval $(call add_define,TF_MBEDTLS_HASH_ALG_ID)) BL1_SOURCES += ${MBEDTLS_CRYPTO_SOURCES} BL2_SOURCES += ${MBEDTLS_CRYPTO_SOURCES} diff --git a/drivers/auth/tbbr/tbbr_cot.c b/drivers/auth/tbbr/tbbr_cot.c index 4aaab390..01d6fb5a 100644 --- a/drivers/auth/tbbr/tbbr_cot.c +++ b/drivers/auth/tbbr/tbbr_cot.c @@ -19,7 +19,7 @@ * Maximum key and hash sizes (in DER format) */ #define PK_DER_LEN 294 -#define HASH_DER_LEN 51 +#define HASH_DER_LEN 83 /* * The platform must allocate buffers to store the authentication parameters diff --git a/include/bl31/ehf.h b/include/bl31/ehf.h new file mode 100644 index 00000000..be8c957c --- /dev/null +++ b/include/bl31/ehf.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __EHF_H__ +#define __EHF_H__ + +#ifndef __ASSEMBLY__ + +#include <stdint.h> +#include <utils_def.h> + +/* Valid priorities set bit 0 of the priority handler. */ +#define _EHF_PRI_VALID (((uintptr_t) 1) << 0) + +/* Marker for no handler registered for a valid priority */ +#define _EHF_NO_HANDLER (0 | _EHF_PRI_VALID) + +/* Extract the specified number of top bits from 7 lower bits of priority */ +#define EHF_PRI_TO_IDX(pri, plat_bits) \ + ((pri & 0x7f) >> (7 - plat_bits)) + +/* Install exception priority descriptor at a suitable index */ +#define EHF_PRI_DESC(plat_bits, priority) \ + [EHF_PRI_TO_IDX(priority, plat_bits)] = { \ + .ehf_handler = _EHF_NO_HANDLER, \ + } + +/* Macro for platforms to regiter its exception priorities */ +#define EHF_REGISTER_PRIORITIES(priorities, num, bits) \ + const ehf_priorities_t exception_data = { \ + .num_priorities = num, \ + .ehf_priorities = priorities, \ + .pri_bits = bits, \ + } + +/* + * Priority stack, managed as a bitmap. + * + * Currently only supports 32 priority levels, allowing platforms to use up to 5 + * top bits of priority. But the type can be changed to uint64_t should need + * arise to support 64 priority levels, allowing platforms to use up to 6 top + * bits of priority. + */ +typedef uint32_t ehf_pri_bits_t; + +/* + * Per-PE exception data. The data for each PE is kept as a per-CPU data field. + * See cpu_data.h. + */ +typedef struct { + ehf_pri_bits_t active_pri_bits; + + /* Priority mask value before any priority levels were active */ + uint8_t init_pri_mask; + + /* Non-secure priority mask value stashed during Secure execution */ + uint8_t ns_pri_mask; +} __aligned(sizeof(uint64_t)) pe_exc_data_t; + +typedef int (*ehf_handler_t)(uint32_t intr_raw, uint32_t flags, void *handle, + void *cookie); + +typedef struct ehf_pri_desc { + /* + * 4-byte-aligned exception handler. Bit 0 indicates the corresponding + * priority level is valid. This is effectively of ehf_handler_t type, + * but left as uintptr_t in order to make pointer arithmetic convenient. + */ + uintptr_t ehf_handler; +} ehf_pri_desc_t; + +typedef struct ehf_priorities { + ehf_pri_desc_t *ehf_priorities; + unsigned int num_priorities; + int pri_bits; +} ehf_priorities_t; + +void ehf_init(void); +void ehf_activate_priority(unsigned int priority); +void ehf_deactivate_priority(unsigned int priority); +void ehf_register_priority_handler(unsigned int pri, ehf_handler_t handler); +void ehf_allow_ns_preemption(void); +unsigned int ehf_is_ns_preemption_allowed(void); + +#endif /* __ASSEMBLY__ */ + +#endif /* __EHF_H__ */ diff --git a/include/common/aarch64/asm_macros.S b/include/common/aarch64/asm_macros.S index 6d6989c2..94a9df92 100644 --- a/include/common/aarch64/asm_macros.S +++ b/include/common/aarch64/asm_macros.S @@ -51,8 +51,8 @@ * so that it inserts illegal AArch64 instructions. This increases * security, robustness and potentially facilitates debugging. */ - .macro vector_base label - .section .vectors, "ax" + .macro vector_base label, section_name=.vectors + .section \section_name, "ax" .align 11, 0 \label: .endm @@ -64,9 +64,9 @@ * so that it inserts illegal AArch64 instructions. This increases * security, robustness and potentially facilitates debugging. */ - .macro vector_entry label + .macro vector_entry label, section_name=.vectors .cfi_sections .debug_frame - .section .vectors, "ax" + .section \section_name, "ax" .align 7, 0 .type \label, %function .func \label diff --git a/include/common/aarch64/el3_common_macros.S b/include/common/aarch64/el3_common_macros.S index 34fdaee9..ed35df82 100644 --- a/include/common/aarch64/el3_common_macros.S +++ b/include/common/aarch64/el3_common_macros.S @@ -95,10 +95,6 @@ * MDCR_EL3.SPD32: Set to 0b10 to disable AArch32 Secure self-hosted * privileged debug from S-EL1. * - * MDCR_EL3.NSPB (ARM v8.2): SPE enabled in non-secure state and - * disabled in secure state. Accesses to SPE registers at SEL1 generate - * trap exceptions to EL3. - * * MDCR_EL3.TDOSA: Set to zero so that EL2 and EL2 System register * access to the powerdown debug registers do not trap to EL3. * @@ -112,19 +108,6 @@ */ mov_imm x0, ((MDCR_EL3_RESET_VAL | MDCR_SDD_BIT | MDCR_SPD32(MDCR_SPD32_DISABLE)) \ & ~(MDCR_TDOSA_BIT | MDCR_TDA_BIT | MDCR_TPM_BIT)) - -#if ENABLE_SPE_FOR_LOWER_ELS - /* Detect if SPE is implemented */ - mrs x1, id_aa64dfr0_el1 - ubfx x1, x1, #ID_AA64DFR0_PMS_SHIFT, #ID_AA64DFR0_PMS_LENGTH - cmp x1, #0x1 - b.ne 1f - - /* Enable SPE for use by normal world */ - orr x0, x0, #MDCR_NSPB(MDCR_NSPB_EL1) -1: -#endif - msr mdcr_el3, x0 /* --------------------------------------------------------------------- diff --git a/include/common/param_header.h b/include/common/param_header.h index 90d59b3a..c982fc90 100644 --- a/include/common/param_header.h +++ b/include/common/param_header.h @@ -8,12 +8,13 @@ #define __PARAM_HEADER_H__ /* Param header types */ -#define PARAM_EP 0x01 -#define PARAM_IMAGE_BINARY 0x02 -#define PARAM_BL31 0x03 -#define PARAM_BL_LOAD_INFO 0x04 -#define PARAM_BL_PARAMS 0x05 -#define PARAM_PSCI_LIB_ARGS 0x06 +#define PARAM_EP 0x01 +#define PARAM_IMAGE_BINARY 0x02 +#define PARAM_BL31 0x03 +#define PARAM_BL_LOAD_INFO 0x04 +#define PARAM_BL_PARAMS 0x05 +#define PARAM_PSCI_LIB_ARGS 0x06 +#define PARAM_SP_IMAGE_BOOT_INFO 0x07 /* Param header version */ #define VERSION_1 0x01 diff --git a/include/drivers/arm/gic_common.h b/include/drivers/arm/gic_common.h index 67d4a28b..001f5736 100644 --- a/include/drivers/arm/gic_common.h +++ b/include/drivers/arm/gic_common.h @@ -34,10 +34,10 @@ #define GIC_INTR_CFG_EDGE 1 /* Constants to categorise priorities */ -#define GIC_HIGHEST_SEC_PRIORITY 0 -#define GIC_LOWEST_SEC_PRIORITY 127 -#define GIC_HIGHEST_NS_PRIORITY 128 -#define GIC_LOWEST_NS_PRIORITY 254 /* 255 would disable an interrupt */ +#define GIC_HIGHEST_SEC_PRIORITY 0x0 +#define GIC_LOWEST_SEC_PRIORITY 0x7f +#define GIC_HIGHEST_NS_PRIORITY 0x80 +#define GIC_LOWEST_NS_PRIORITY 0xfe /* 0xff would disable all interrupts */ /******************************************************************************* * GIC Distributor interface register offsets that are common to GICv3 & GICv2 diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index b2e4d4c5..f2a53712 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -245,6 +245,9 @@ #define GICR_NUM_REGS(reg_name) \ DIV_ROUND_UP_2EVAL(TOTAL_PCPU_INTR_NUM, (1 << reg_name ## _SHIFT)) +/* Interrupt ID mask for HPPIR, AHPPIR, IAR and AIAR CPU Interface registers */ +#define INT_ID_MASK 0xffffff + /******************************************************************************* * This structure describes some of the implementation defined attributes of the * GICv3 IP. It is used by the platform port to specify these attributes in order diff --git a/include/drivers/auth/mbedtls/mbedtls_config.h b/include/drivers/auth/mbedtls/mbedtls_config.h index 96587aca..f8f26080 100644 --- a/include/drivers/auth/mbedtls/mbedtls_config.h +++ b/include/drivers/auth/mbedtls/mbedtls_config.h @@ -14,6 +14,13 @@ #define TF_MBEDTLS_RSA_AND_ECDSA 3 /* + * Hash algorithms currently supported on mbed TLS libraries + */ +#define TF_MBEDTLS_SHA256 1 +#define TF_MBEDTLS_SHA384 2 +#define TF_MBEDTLS_SHA512 3 + +/* * Configuration file to build mbed TLS with the required features for * Trusted Boot */ @@ -66,6 +73,9 @@ #endif #define MBEDTLS_SHA256_C +#if (TF_MBEDTLS_HASH_ALG_ID != TF_MBEDTLS_SHA256) +#define MBEDTLS_SHA512_C +#endif #define MBEDTLS_VERSION_C diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h index 997e3a22..4b31f168 100644 --- a/include/lib/aarch64/arch.h +++ b/include/lib/aarch64/arch.h @@ -126,6 +126,7 @@ #define ID_AA64PFR0_GIC_MASK ((U(1) << ID_AA64PFR0_GIC_WIDTH) - 1) /* ID_AA64MMFR0_EL1 definitions */ +#define ID_AA64MMFR0_EL1_PARANGE_SHIFT U(0) #define ID_AA64MMFR0_EL1_PARANGE_MASK U(0xf) #define PARANGE_0000 U(32) @@ -134,6 +135,22 @@ #define PARANGE_0011 U(42) #define PARANGE_0100 U(44) #define PARANGE_0101 U(48) +#define PARANGE_0110 U(52) + +#define ID_AA64MMFR0_EL1_TGRAN4_SHIFT U(28) +#define ID_AA64MMFR0_EL1_TGRAN4_MASK U(0xf) +#define ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED U(0x0) +#define ID_AA64MMFR0_EL1_TGRAN4_NOT_SUPPORTED U(0xf) + +#define ID_AA64MMFR0_EL1_TGRAN64_SHIFT U(24) +#define ID_AA64MMFR0_EL1_TGRAN64_MASK U(0xf) +#define ID_AA64MMFR0_EL1_TGRAN64_SUPPORTED U(0x0) +#define ID_AA64MMFR0_EL1_TGRAN64_NOT_SUPPORTED U(0xf) + +#define ID_AA64MMFR0_EL1_TGRAN16_SHIFT U(20) +#define ID_AA64MMFR0_EL1_TGRAN16_MASK U(0xf) +#define ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED U(0x1) +#define ID_AA64MMFR0_EL1_TGRAN16_NOT_SUPPORTED U(0x0) /* ID_PFR1_EL1 definitions */ #define ID_PFR1_VIRTEXT_SHIFT U(12) @@ -160,12 +177,25 @@ #define SCTLR_A_BIT (U(1) << 1) #define SCTLR_C_BIT (U(1) << 2) #define SCTLR_SA_BIT (U(1) << 3) +#define SCTLR_SA0_BIT (U(1) << 4) #define SCTLR_CP15BEN_BIT (U(1) << 5) +#define SCTLR_ITD_BIT (U(1) << 7) +#define SCTLR_SED_BIT (U(1) << 8) +#define SCTLR_UMA_BIT (U(1) << 9) #define SCTLR_I_BIT (U(1) << 12) +#define SCTLR_V_BIT (U(1) << 13) +#define SCTLR_DZE_BIT (U(1) << 14) +#define SCTLR_UCT_BIT (U(1) << 15) #define SCTLR_NTWI_BIT (U(1) << 16) #define SCTLR_NTWE_BIT (U(1) << 18) #define SCTLR_WXN_BIT (U(1) << 19) +#define SCTLR_UWXN_BIT (U(1) << 20) +#define SCTLR_E0E_BIT (U(1) << 24) #define SCTLR_EE_BIT (U(1) << 25) +#define SCTLR_UCI_BIT (U(1) << 26) +#define SCTLR_TRE_BIT (U(1) << 28) +#define SCTLR_AFE_BIT (U(1) << 29) +#define SCTLR_TE_BIT (U(1) << 30) #define SCTLR_RESET_VAL SCTLR_EL3_RES1 /* CPACR_El1 definitions */ @@ -350,6 +380,13 @@ #define TCR_SH_OUTER_SHAREABLE (U(0x2) << 12) #define TCR_SH_INNER_SHAREABLE (U(0x3) << 12) +#define TCR_TG0_SHIFT U(14) +#define TCR_TG0_MASK U(3) +#define TCR_TG0_4K (ULL(0) << TCR_TG0_SHIFT) +#define TCR_TG0_64K (ULL(1) << TCR_TG0_SHIFT) +#define TCR_TG0_16K (ULL(2) << TCR_TG0_SHIFT) + +#define TCR_EPD0_BIT (U(1) << 7) #define TCR_EPD1_BIT (U(1) << 23) #define MODE_SP_SHIFT U(0x0) @@ -562,4 +599,15 @@ #define MAKE_MAIR_NORMAL_MEMORY(inner, outer) ((inner) | ((outer) << MAIR_NORM_OUTER_SHIFT)) +/* PAR_EL1 fields */ +#define PAR_F_SHIFT 0 +#define PAR_F_MASK 1 +#define PAR_ADDR_SHIFT 12 +#define PAR_ADDR_MASK (BIT(40) - 1) /* 40-bits-wide page address */ + +/******************************************************************************* + * Definitions for system register interface to SPE + ******************************************************************************/ +#define PMBLIMITR_EL1 S3_0_C9_C10_0 + #endif /* __ARCH_H__ */ diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h index 9c022ab5..46d9a1c0 100644 --- a/include/lib/aarch64/arch_helpers.h +++ b/include/lib/aarch64/arch_helpers.h @@ -155,6 +155,7 @@ DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e1r) DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e1w) DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e0r) DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e0w) +DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s1e2r) void flush_dcache_range(uintptr_t addr, size_t size); void clean_dcache_range(uintptr_t addr, size_t size); @@ -196,6 +197,7 @@ DEFINE_SYSOP_TYPE_FUNC(dmb, sy) DEFINE_SYSOP_TYPE_FUNC(dmb, st) DEFINE_SYSOP_TYPE_FUNC(dmb, ld) DEFINE_SYSOP_TYPE_FUNC(dsb, ish) +DEFINE_SYSOP_TYPE_FUNC(dsb, nsh) DEFINE_SYSOP_TYPE_FUNC(dsb, ishst) DEFINE_SYSOP_TYPE_FUNC(dmb, ish) DEFINE_SYSOP_TYPE_FUNC(dmb, ishst) @@ -300,6 +302,7 @@ DEFINE_SYSREG_READ_FUNC(isr_el1) DEFINE_SYSREG_READ_FUNC(ctr_el0) DEFINE_SYSREG_RW_FUNCS(mdcr_el2) +DEFINE_SYSREG_RW_FUNCS(mdcr_el3) DEFINE_SYSREG_RW_FUNCS(hstr_el2) DEFINE_SYSREG_RW_FUNCS(cnthp_ctl_el2) DEFINE_SYSREG_RW_FUNCS(pmcr_el0) @@ -319,6 +322,7 @@ DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir0_el1, ICC_EOIR0_EL1) DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir1_el1, ICC_EOIR1_EL1) DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_sgi0r_el1, ICC_SGI0R_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(pmblimitr_el1, PMBLIMITR_EL1) #define IS_IN_EL(x) \ (GET_EL(read_CurrentEl()) == MODE_EL##x) diff --git a/include/lib/el3_runtime/aarch64/context.h b/include/lib/el3_runtime/aarch64/context.h index a89468d4..58899049 100644 --- a/include/lib/el3_runtime/aarch64/context.h +++ b/include/lib/el3_runtime/aarch64/context.h @@ -100,8 +100,7 @@ #define CTX_SPSR_FIQ U(0xd8) #define CTX_DACR32_EL2 U(0xe0) #define CTX_IFSR32_EL2 U(0xe8) -#define CTX_FP_FPEXC32_EL2 U(0xf0) -#define CTX_TIMER_SYSREGS_OFF U(0x100) /* Align to the next 16 byte boundary */ +#define CTX_TIMER_SYSREGS_OFF U(0xf0) /* Align to the next 16 byte boundary */ #else #define CTX_TIMER_SYSREGS_OFF U(0xc0) /* Align to the next 16 byte boundary */ #endif /* __CTX_INCLUDE_AARCH32_REGS__ */ @@ -161,7 +160,12 @@ #define CTX_FP_Q31 U(0x1f0) #define CTX_FP_FPSR U(0x200) #define CTX_FP_FPCR U(0x208) -#define CTX_FPREGS_END U(0x210) +#if CTX_INCLUDE_AARCH32_REGS +#define CTX_FP_FPEXC32_EL2 U(0x210) +#define CTX_FPREGS_END U(0x220) /* Align to the next 16 byte boundary */ +#else +#define CTX_FPREGS_END U(0x210) /* Align to the next 16 byte boundary */ +#endif #endif #ifndef __ASSEMBLY__ @@ -309,7 +313,6 @@ CASSERT(CTX_EL3STATE_OFFSET == __builtin_offsetof(cpu_context_t, el3state_ctx), * Function prototypes ******************************************************************************/ void el1_sysregs_context_save(el1_sys_regs_t *regs); -void el1_sysregs_context_save_post_ops(void); void el1_sysregs_context_restore(el1_sys_regs_t *regs); #if CTX_INCLUDE_FPREGS void fpregs_context_save(fp_regs_t *regs); diff --git a/include/lib/el3_runtime/cpu_data.h b/include/lib/el3_runtime/cpu_data.h index bd787ce9..3f48de5e 100644 --- a/include/lib/el3_runtime/cpu_data.h +++ b/include/lib/el3_runtime/cpu_data.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,7 @@ #ifndef __CPU_DATA_H__ #define __CPU_DATA_H__ +#include <ehf.h> #include <platform_def.h> /* CACHE_WRITEBACK_GRANULE required */ #ifdef AARCH32 @@ -96,6 +97,9 @@ typedef struct cpu_data { #if PLAT_PCPU_DATA_SIZE uint8_t platform_cpu_data[PLAT_PCPU_DATA_SIZE]; #endif +#if defined(IMAGE_BL31) && EL3_EXCEPTION_HANDLING + pe_exc_data_t ehf_data; +#endif } __aligned(CACHE_WRITEBACK_GRANULE) cpu_data_t; #if CRASH_REPORTING diff --git a/include/lib/extensions/spe.h b/include/lib/extensions/spe.h new file mode 100644 index 00000000..8a74127a --- /dev/null +++ b/include/lib/extensions/spe.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __SPE_H__ +#define __SPE_H__ + +void spe_enable(int el2_unused); +void spe_disable(void); + +#endif /* __SPE_H__ */ diff --git a/include/lib/xlat_tables/aarch64/xlat_tables_aarch64.h b/include/lib/xlat_tables/aarch64/xlat_tables_aarch64.h index 7381bc82..6021e407 100644 --- a/include/lib/xlat_tables/aarch64/xlat_tables_aarch64.h +++ b/include/lib/xlat_tables/aarch64/xlat_tables_aarch64.h @@ -16,6 +16,11 @@ #endif /* + * Encode a Physical Address Space size for its use in TCR_ELx. + */ +unsigned long long tcr_physical_addr_size_bits(unsigned long long max_addr); + +/* * In AArch64 state, the MMU may support 4 KB, 16 KB and 64 KB page * granularity. For 4KB granularity, a level 0 table descriptor doesn't support * block translation. For 16KB, the same thing happens to levels 0 and 1. For diff --git a/include/plat/arm/board/common/board_arm_def.h b/include/plat/arm/board/common/board_arm_def.h index 7a4594cc..97a1af4b 100644 --- a/include/plat/arm/board/common/board_arm_def.h +++ b/include/plat/arm/board/common/board_arm_def.h @@ -52,7 +52,17 @@ * They are also used for the dynamically mapped regions in the images that * enable dynamic memory mapping. */ -#if defined(IMAGE_BL31) || defined(IMAGE_BL32) +#if defined(IMAGE_BL31) +# if ENABLE_SPM +# define PLAT_ARM_MMAP_ENTRIES 9 +# define MAX_XLAT_TABLES 7 +# define PLAT_SP_IMAGE_MMAP_REGIONS 7 +# define PLAT_SP_IMAGE_MAX_XLAT_TABLES 10 +# else +# define PLAT_ARM_MMAP_ENTRIES 7 +# define MAX_XLAT_TABLES 5 +# endif +#elif defined(IMAGE_BL32) # define PLAT_ARM_MMAP_ENTRIES 7 # define MAX_XLAT_TABLES 5 #else @@ -80,7 +90,11 @@ * PLAT_ARM_MAX_BL31_SIZE is calculated using the current BL31 debug size plus a * little space for growth. */ +#if ENABLE_SPM +#define PLAT_ARM_MAX_BL31_SIZE 0x28000 +#else #define PLAT_ARM_MAX_BL31_SIZE 0x1D000 +#endif #endif /* ARM_BOARD_OPTIMISE_MEM */ diff --git a/include/plat/arm/board/common/v2m_def.h b/include/plat/arm/board/common/v2m_def.h index 364b7803..e5f53732 100644 --- a/include/plat/arm/board/common/v2m_def.h +++ b/include/plat/arm/board/common/v2m_def.h @@ -121,6 +121,11 @@ V2M_IOFPGA_SIZE, \ MT_DEVICE | MT_RW | MT_SECURE) +/* Region equivalent to V2M_MAP_IOFPGA suitable for mapping at EL0 */ +#define V2M_MAP_IOFPGA_EL0 MAP_REGION_FLAT( \ + V2M_IOFPGA_BASE, \ + V2M_IOFPGA_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) #endif /* __V2M_DEF_H__ */ diff --git a/include/plat/arm/common/arm_def.h b/include/plat/arm/common/arm_def.h index 6cab91fe..b8955afc 100644 --- a/include/plat/arm/common/arm_def.h +++ b/include/plat/arm/common/arm_def.h @@ -202,7 +202,7 @@ GIC_INTR_CFG_EDGE) #define ARM_G0_IRQ_PROPS(grp) \ - INTR_PROP_DESC(ARM_IRQ_SEC_SGI_0, GIC_HIGHEST_SEC_PRIORITY, grp, \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_0, PLAT_SDEI_NORMAL_PRI, grp, \ GIC_INTR_CFG_EDGE), \ INTR_PROP_DESC(ARM_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY, grp, \ GIC_INTR_CFG_EDGE) @@ -378,7 +378,13 @@ * Trusted DRAM (if available) or the DRAM region secured by the TrustZone * controller. */ -#if ARM_BL31_IN_DRAM +#if ENABLE_SPM +# define TSP_SEC_MEM_BASE (ARM_AP_TZC_DRAM1_BASE + ULL(0x200000)) +# define TSP_SEC_MEM_SIZE (ARM_AP_TZC_DRAM1_SIZE - ULL(0x200000)) +# define BL32_BASE (ARM_AP_TZC_DRAM1_BASE + ULL(0x200000)) +# define BL32_LIMIT (ARM_AP_TZC_DRAM1_BASE + \ + ARM_AP_TZC_DRAM1_SIZE) +#elif ARM_BL31_IN_DRAM # define TSP_SEC_MEM_BASE (ARM_AP_TZC_DRAM1_BASE + \ PLAT_ARM_MAX_BL31_SIZE) # define TSP_SEC_MEM_SIZE (ARM_AP_TZC_DRAM1_SIZE - \ @@ -409,11 +415,14 @@ # error "Unsupported ARM_TSP_RAM_LOCATION_ID value" #endif -/* BL32 is mandatory in AArch32 */ +/* + * BL32 is mandatory in AArch32. In AArch64, undefine BL32_BASE if there is no + * SPD and no SPM, as they are the only ones that can be used as BL32. + */ #ifndef AARCH32 -#ifdef SPD_none -#undef BL32_BASE -#endif /* SPD_none */ +# if defined(SPD_none) && !ENABLE_SPM +# undef BL32_BASE +# endif #endif /******************************************************************************* @@ -445,5 +454,24 @@ */ #define PLAT_PERCPU_BAKERY_LOCK_SIZE (1 * CACHE_WRITEBACK_GRANULE) +/* Priority levels for ARM platforms */ +#define PLAT_SDEI_CRITICAL_PRI 0x60 +#define PLAT_SDEI_NORMAL_PRI 0x70 + +/* ARM platforms use 3 upper bits of secure interrupt priority */ +#define ARM_PRI_BITS 3 + +/* SGI used for SDEI signalling */ +#define ARM_SDEI_SGI ARM_IRQ_SEC_SGI_0 + +/* ARM SDEI dynamic private event numbers */ +#define ARM_SDEI_DP_EVENT_0 1000 +#define ARM_SDEI_DP_EVENT_1 1001 +#define ARM_SDEI_DP_EVENT_2 1002 + +/* ARM SDEI dynamic shared event numbers */ +#define ARM_SDEI_DS_EVENT_0 2000 +#define ARM_SDEI_DS_EVENT_1 2001 +#define ARM_SDEI_DS_EVENT_2 2002 #endif /* __ARM_DEF_H__ */ diff --git a/include/plat/arm/common/arm_spm_def.h b/include/plat/arm/common/arm_spm_def.h new file mode 100644 index 00000000..83277a6c --- /dev/null +++ b/include/plat/arm/common/arm_spm_def.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __ARM_SPM_DEF_H__ +#define __ARM_SPM_DEF_H__ + +#include <arm_def.h> +#include <platform_def.h> +#include <utils_def.h> +#include <xlat_tables_defs.h> + +/* + * If BL31 is placed in DRAM, place the Secure Partition in DRAM right after the + * region used by BL31. If BL31 it is placed in SRAM, put the Secure Partition + * at the base of DRAM. + */ +#define ARM_SP_IMAGE_BASE BL32_BASE +#define ARM_SP_IMAGE_LIMIT BL32_LIMIT +/* The maximum size of the S-EL0 payload can be 3MB */ +#define ARM_SP_IMAGE_SIZE ULL(0x300000) + +#ifdef IMAGE_BL2 +/* SPM Payload memory. Mapped as RW in BL2. */ +#define ARM_SP_IMAGE_MMAP MAP_REGION_FLAT( \ + ARM_SP_IMAGE_BASE, \ + ARM_SP_IMAGE_SIZE, \ + MT_MEMORY | MT_RW | MT_SECURE) +#endif +#ifdef IMAGE_BL31 +/* SPM Payload memory. Mapped as code in S-EL1 */ +#define ARM_SP_IMAGE_MMAP MAP_REGION2( \ + ARM_SP_IMAGE_BASE, \ + ARM_SP_IMAGE_BASE, \ + ARM_SP_IMAGE_SIZE, \ + MT_CODE | MT_SECURE | MT_USER, \ + PAGE_SIZE) +#endif + +/* + * Memory shared between EL3 and S-EL0. It is used by EL3 to push data into + * S-EL0, so it is mapped with RW permission from EL3 and with RO permission + * from S-EL0. Placed after SPM Payload memory. + */ +#define PLAT_SPM_BUF_BASE (ARM_SP_IMAGE_BASE + ARM_SP_IMAGE_SIZE) +#define PLAT_SPM_BUF_SIZE ULL(0x100000) + +#define ARM_SPM_BUF_EL3_MMAP MAP_REGION_FLAT( \ + PLAT_SPM_BUF_BASE, \ + PLAT_SPM_BUF_SIZE, \ + MT_RW_DATA | MT_SECURE) +#define ARM_SPM_BUF_EL0_MMAP MAP_REGION2( \ + PLAT_SPM_BUF_BASE, \ + PLAT_SPM_BUF_BASE, \ + PLAT_SPM_BUF_SIZE, \ + MT_RO_DATA | MT_SECURE | MT_USER,\ + PAGE_SIZE) + +/* + * Memory shared between Normal world and S-EL0 for passing data during service + * requests. Mapped as RW and NS. Placed after the shared memory between EL3 and + * S-EL0. + */ +#define ARM_SP_IMAGE_NS_BUF_BASE (PLAT_SPM_BUF_BASE + PLAT_SPM_BUF_SIZE) +#define ARM_SP_IMAGE_NS_BUF_SIZE ULL(0x10000) +#define ARM_SP_IMAGE_NS_BUF_MMAP MAP_REGION2( \ + ARM_SP_IMAGE_NS_BUF_BASE, \ + ARM_SP_IMAGE_NS_BUF_BASE, \ + ARM_SP_IMAGE_NS_BUF_SIZE, \ + MT_RW_DATA | MT_NS | MT_USER, \ + PAGE_SIZE) + +/* + * RW memory, which uses the remaining Trusted DRAM. Placed after the memory + * shared between Secure and Non-secure worlds. First there is the stack memory + * for all CPUs and then there is the common heap memory. Both are mapped with + * RW permissions. + */ +#define PLAT_SP_IMAGE_STACK_BASE (ARM_SP_IMAGE_NS_BUF_BASE + \ + ARM_SP_IMAGE_NS_BUF_SIZE) +#define PLAT_SP_IMAGE_STACK_PCPU_SIZE ULL(0x2000) +#define ARM_SP_IMAGE_STACK_TOTAL_SIZE (PLATFORM_CORE_COUNT * \ + PLAT_SP_IMAGE_STACK_PCPU_SIZE) + +#define ARM_SP_IMAGE_HEAP_BASE (PLAT_SP_IMAGE_STACK_BASE + \ + ARM_SP_IMAGE_STACK_TOTAL_SIZE) +#define ARM_SP_IMAGE_HEAP_SIZE (ARM_SP_IMAGE_LIMIT - ARM_SP_IMAGE_HEAP_BASE) + +#define ARM_SP_IMAGE_RW_MMAP MAP_REGION2( \ + PLAT_SP_IMAGE_STACK_BASE, \ + PLAT_SP_IMAGE_STACK_BASE, \ + (ARM_SP_IMAGE_LIMIT - \ + PLAT_SP_IMAGE_STACK_BASE), \ + MT_RW_DATA | MT_SECURE | MT_USER,\ + PAGE_SIZE) + +/* Total number of memory regions with distinct properties */ +#define ARM_SP_IMAGE_NUM_MEM_REGIONS 6 + +/* Cookies passed to the Secure Partition at boot. Not used by ARM platforms. */ +#define PLAT_SPM_COOKIE_0 ULL(0) +#define PLAT_SPM_COOKIE_1 ULL(0) + +#endif /* __ARM_SPM_DEF_H__ */ diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index 33d951c2..abd73953 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -119,6 +119,7 @@ void arm_configure_sys_timer(void); /* PM utility functions */ int arm_validate_power_state(unsigned int power_state, psci_power_state_t *req_state); +int arm_validate_psci_entrypoint(uintptr_t entrypoint); int arm_validate_ns_entrypoint(uintptr_t entrypoint); void arm_system_pwr_domain_save(void); void arm_system_pwr_domain_resume(void); @@ -226,7 +227,4 @@ int arm_execution_state_switch(unsigned int smc_fid, uint32_t cookie_lo, void *handle); -/* Disable Statistical Profiling Extensions helper */ -void arm_disable_spe(void); - #endif /* __PLAT_ARM_H__ */ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index e2bfa505..f11bee9f 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -23,6 +23,8 @@ struct bl31_params; struct image_desc; struct bl_load_info; struct bl_params; +struct mmap_region; +struct secure_partition_boot_info; /******************************************************************************* * plat_get_rotpk_info() flags @@ -88,6 +90,7 @@ void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, void plat_ic_set_interrupt_pending(unsigned int id); void plat_ic_clear_interrupt_pending(unsigned int id); unsigned int plat_ic_set_priority_mask(unsigned int mask); +unsigned int plat_ic_get_interrupt_id(unsigned int raw); /******************************************************************************* * Optional common functions (may be overridden) @@ -111,6 +114,16 @@ void bl1_plat_arch_setup(void); void bl1_platform_setup(void); struct meminfo *bl1_plat_sec_mem_layout(void); +/******************************************************************************* + * Optional EL3 component functions in BL31 + ******************************************************************************/ + +/* SDEI platform functions */ +#if SDEI_SUPPORT +int plat_sdei_validate_entry_point(uintptr_t ep, unsigned int client_mode); +void plat_sdei_handle_masked_trigger(uint64_t mpidr, unsigned int intr); +#endif + /* * The following function is mandatory when the * firmware update feature is used. @@ -293,6 +306,13 @@ int plat_set_nv_ctr(void *cookie, unsigned int nv_ctr); int plat_set_nv_ctr2(void *cookie, const struct auth_img_desc_s *img_desc, unsigned int nv_ctr); +/******************************************************************************* + * Secure Partitions functions + ******************************************************************************/ +const struct mmap_region *plat_get_secure_partition_mmap(void *cookie); +const struct secure_partition_boot_info *plat_get_secure_partition_boot_info( + void *cookie); + #if LOAD_IMAGE_V2 /******************************************************************************* * Mandatory BL image load functions(may be overridden). diff --git a/include/services/sdei.h b/include/services/sdei.h new file mode 100644 index 00000000..ce9a008c --- /dev/null +++ b/include/services/sdei.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __SDEI_H__ +#define __SDEI_H__ + +#include <spinlock.h> +#include <utils_def.h> + +/* Range 0xC4000020 - 0xC400003F reserved for SDE 64bit smc calls */ +#define SDEI_VERSION 0xC4000020 +#define SDEI_EVENT_REGISTER 0xC4000021 +#define SDEI_EVENT_ENABLE 0xC4000022 +#define SDEI_EVENT_DISABLE 0xC4000023 +#define SDEI_EVENT_CONTEXT 0xC4000024 +#define SDEI_EVENT_COMPLETE 0xC4000025 +#define SDEI_EVENT_COMPLETE_AND_RESUME 0xC4000026 + +#define SDEI_EVENT_UNREGISTER 0xC4000027 +#define SDEI_EVENT_STATUS 0xC4000028 +#define SDEI_EVENT_GET_INFO 0xC4000029 +#define SDEI_EVENT_ROUTING_SET 0xC400002A +#define SDEI_PE_MASK 0xC400002B +#define SDEI_PE_UNMASK 0xC400002C + +#define SDEI_INTERRUPT_BIND 0xC400002D +#define SDEI_INTERRUPT_RELEASE 0xC400002E +#define SDEI_EVENT_SIGNAL 0xC400002F +#define SDEI_FEATURES 0xC4000030 +#define SDEI_PRIVATE_RESET 0xC4000031 +#define SDEI_SHARED_RESET 0xC4000032 + +/* SDEI_EVENT_REGISTER flags */ +#define SDEI_REGF_RM_ANY 0 +#define SDEI_REGF_RM_PE 1 + +/* SDEI_EVENT_COMPLETE status flags */ +#define SDEI_EV_HANDLED 0 +#define SDEI_EV_FAILED 1 + +/* SDE event status values in bit position */ +#define SDEI_STATF_REGISTERED 0 +#define SDEI_STATF_ENABLED 1 +#define SDEI_STATF_RUNNING 2 + +/* Internal: SDEI flag bit positions */ +#define _SDEI_MAPF_DYNAMIC_SHIFT 1 +#define _SDEI_MAPF_BOUND_SHIFT 2 +#define _SDEI_MAPF_SIGNALABLE_SHIFT 3 +#define _SDEI_MAPF_PRIVATE_SHIFT 4 +#define _SDEI_MAPF_CRITICAL_SHIFT 5 + +/* SDEI event 0 */ +#define SDEI_EVENT_0 0 + +/* Placeholder interrupt for dynamic mapping */ +#define SDEI_DYN_IRQ 0 + +/* SDEI flags */ + +/* + * These flags determine whether or not an event can be associated with an + * interrupt. Static events are permanently associated with an interrupt, and + * can't be changed at runtime. Association of dynamic events with interrupts + * can be changed at run time using the SDEI_INTERRUPT_BIND and + * SDEI_INTERRUPT_RELEASE calls. + * + * SDEI_MAPF_DYNAMIC only indicates run time configurability, where as + * SDEI_MAPF_BOUND indicates interrupt association. For example: + * + * - Calling SDEI_INTERRUPT_BIND on a dynamic event will have both + * SDEI_MAPF_DYNAMIC and SDEI_MAPF_BOUND set. + * + * - Statically-bound events will always have SDEI_MAPF_BOUND set, and neither + * SDEI_INTERRUPT_BIND nor SDEI_INTERRUPT_RELEASE can be called on them. + * + * See also the is_map_bound() macro. + */ +#define SDEI_MAPF_DYNAMIC BIT(_SDEI_MAPF_DYNAMIC_SHIFT) +#define SDEI_MAPF_BOUND BIT(_SDEI_MAPF_BOUND_SHIFT) + +#define SDEI_MAPF_SIGNALABLE BIT(_SDEI_MAPF_SIGNALABLE_SHIFT) +#define SDEI_MAPF_PRIVATE BIT(_SDEI_MAPF_PRIVATE_SHIFT) +#define SDEI_MAPF_CRITICAL BIT(_SDEI_MAPF_CRITICAL_SHIFT) + +/* Indices of private and shared mappings */ +#define _SDEI_MAP_IDX_PRIV 0 +#define _SDEI_MAP_IDX_SHRD 1 +#define _SDEI_MAP_IDX_MAX 2 + +/* The macros below are used to identify SDEI calls from the SMC function ID */ +#define SDEI_FID_MASK U(0xffe0) +#define SDEI_FID_VALUE U(0x20) +#define is_sdei_fid(_fid) \ + ((((_fid) & SDEI_FID_MASK) == SDEI_FID_VALUE) && \ + (((_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_64)) + +#define SDEI_EVENT_MAP(_event, _intr, _flags) \ + { \ + .ev_num = _event, \ + .intr = _intr, \ + .map_flags = _flags \ + } + +#define SDEI_SHARED_EVENT(_event, _intr, _flags) \ + SDEI_EVENT_MAP(_event, _intr, _flags) + +#define SDEI_PRIVATE_EVENT(_event, _intr, _flags) \ + SDEI_EVENT_MAP(_event, _intr, _flags | SDEI_MAPF_PRIVATE) + +#define SDEI_DEFINE_EVENT_0(_intr) \ + SDEI_PRIVATE_EVENT(SDEI_EVENT_0, _intr, SDEI_MAPF_SIGNALABLE) + +/* + * Declare shared and private entries for each core. Also declare a global + * structure containing private and share entries. + * + * This macro must be used in the same file as the platform SDEI mappings are + * declared. Only then would ARRAY_SIZE() yield a meaningful value. + */ +#define REGISTER_SDEI_MAP(_private, _shared) \ + sdei_entry_t sdei_private_event_table \ + [PLATFORM_CORE_COUNT * ARRAY_SIZE(_private)]; \ + sdei_entry_t sdei_shared_event_table[ARRAY_SIZE(_shared)]; \ + const sdei_mapping_t sdei_global_mappings[] = { \ + [_SDEI_MAP_IDX_PRIV] = { \ + .map = _private, \ + .num_maps = ARRAY_SIZE(_private) \ + }, \ + [_SDEI_MAP_IDX_SHRD] = { \ + .map = _shared, \ + .num_maps = ARRAY_SIZE(_shared) \ + }, \ + } + +typedef uint8_t sdei_state_t; + +/* Runtime data of SDEI event */ +typedef struct sdei_entry { + uint64_t ep; /* Entry point */ + uint64_t arg; /* Entry point argument */ + uint64_t affinity; /* Affinity of shared event */ + unsigned int reg_flags; /* Registration flags */ + + /* Event handler states: registered, enabled, running */ + sdei_state_t state; +} sdei_entry_t; + +/* Mapping of SDEI events to interrupts, and associated data */ +typedef struct sdei_ev_map { + int32_t ev_num; /* Event number */ + unsigned int intr; /* Physical interrupt number for a bound map */ + unsigned int map_flags; /* Mapping flags, see SDEI_MAPF_* */ + int reg_count; /* Registration count */ + spinlock_t lock; /* Per-event lock */ +} sdei_ev_map_t; + +typedef struct sdei_mapping { + sdei_ev_map_t *map; + size_t num_maps; +} sdei_mapping_t; + +/* Handler to be called to handle SDEI smc calls */ +uint64_t sdei_smc_handler(uint32_t smc_fid, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags); + +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); + +#endif /* __SDEI_H__ */ diff --git a/include/services/secure_partition.h b/include/services/secure_partition.h new file mode 100644 index 00000000..334f7610 --- /dev/null +++ b/include/services/secure_partition.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __SECURE_PARTITION_H__ +#define __SECURE_PARTITION_H__ + +#include <bl_common.h> +#include <types.h> +#include <utils_def.h> + +/* Linker symbols */ +extern uintptr_t __SP_IMAGE_XLAT_TABLES_START__; +extern uintptr_t __SP_IMAGE_XLAT_TABLES_END__; + +/* Definitions */ +#define SP_IMAGE_XLAT_TABLES_START \ + (uintptr_t)(&__SP_IMAGE_XLAT_TABLES_START__) +#define SP_IMAGE_XLAT_TABLES_END \ + (uintptr_t)(&__SP_IMAGE_XLAT_TABLES_END__) +#define SP_IMAGE_XLAT_TABLES_SIZE \ + (SP_IMAGE_XLAT_TABLES_END - SP_IMAGE_XLAT_TABLES_START) + +/* + * Flags used by the secure_partition_mp_info structure to describe the + * characteristics of a cpu. Only a single flag is defined at the moment to + * indicate the primary cpu. + */ +#define MP_INFO_FLAG_PRIMARY_CPU U(0x00000001) + +/* + * This structure is used to provide information required to initialise a S-EL0 + * partition. + */ +typedef struct secure_partition_mp_info { + u_register_t mpidr; + unsigned int linear_id; + unsigned int flags; +} secure_partition_mp_info_t; + +typedef struct secure_partition_boot_info { + param_header_t h; + uintptr_t sp_mem_base; + uintptr_t sp_mem_limit; + uintptr_t sp_image_base; + uintptr_t sp_stack_base; + uintptr_t sp_heap_base; + uintptr_t sp_ns_comm_buf_base; + uintptr_t sp_shared_buf_base; + size_t sp_image_size; + size_t sp_pcpu_stack_size; + size_t sp_heap_size; + size_t sp_ns_comm_buf_size; + size_t sp_shared_buf_size; + unsigned int num_sp_mem_regions; + unsigned int num_cpus; + secure_partition_mp_info_t *mp_info; +} secure_partition_boot_info_t; + +/* Setup function for secure partitions context. */ + +void secure_partition_setup(void); + +#endif /* __SECURE_PARTITION_H__ */ diff --git a/include/services/spm_svc.h b/include/services/spm_svc.h new file mode 100644 index 00000000..2c8c7cd8 --- /dev/null +++ b/include/services/spm_svc.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __SPM_SVC_H__ +#define __SPM_SVC_H__ + +#include <utils_def.h> + +#define SPM_VERSION_MAJOR U(0) +#define SPM_VERSION_MINOR U(1) +#define SPM_VERSION_FORM(major, minor) ((major << 16) | (minor)) +#define SPM_VERSION_COMPILED SPM_VERSION_FORM(SPM_VERSION_MAJOR, SPM_VERSION_MINOR) + +#define SP_VERSION_MAJOR U(1) +#define SP_VERSION_MINOR U(0) +#define SP_VERSION_FORM(major, minor) ((major << 16) | (minor)) +#define SP_VERSION_COMPILED SP_VERSION_FORM(SP_VERSION_MAJOR, SP_VERSION_MINOR) + +/* The macros below are used to identify SPM calls from the SMC function ID */ +#define SPM_FID_MASK U(0xffff) +#define SPM_FID_MIN_VALUE U(0x40) +#define SPM_FID_MAX_VALUE U(0x7f) +#define is_spm_fid(_fid) \ + ((((_fid) & SPM_FID_MASK) >= SPM_FID_MIN_VALUE) && \ + (((_fid) & SPM_FID_MASK) <= SPM_FID_MAX_VALUE)) + +/* + * SMC IDs defined for accessing services implemented by the Secure Partition + * Manager from the Secure Partition(s). These services enable a partition to + * handle delegated events and request privileged operations from the manager. + */ +#define SPM_VERSION_AARCH32 U(0x84000060) +#define SP_EVENT_COMPLETE_AARCH64 U(0xC4000061) +#define SP_MEM_ATTRIBUTES_GET_AARCH64 U(0xC4000064) +#define SP_MEM_ATTRIBUTES_SET_AARCH64 U(0xC4000065) + +/* + * Macros used by SP_MEM_ATTRIBUTES_SET_AARCH64. + */ + +#define SP_MEM_ATTR_ACCESS_NOACCESS U(0) +#define SP_MEM_ATTR_ACCESS_RW U(1) +/* Value U(2) is reserved. */ +#define SP_MEM_ATTR_ACCESS_RO U(3) +#define SP_MEM_ATTR_ACCESS_MASK U(3) +#define SP_MEM_ATTR_ACCESS_SHIFT 0 + +#define SP_MEM_ATTR_EXEC (U(0) << 2) +#define SP_MEM_ATTR_NON_EXEC (U(1) << 2) + +/* + * SMC IDs defined in [1] for accessing secure partition services from the + * Non-secure world. These FIDs occupy the range 0x40 - 0x5f + * [1] DEN0060A_ARM_MM_Interface_Specification.pdf + */ +#define SP_VERSION_AARCH64 U(0xC4000040) +#define SP_VERSION_AARCH32 U(0x84000040) + +#define SP_COMMUNICATE_AARCH64 U(0xC4000041) +#define SP_COMMUNICATE_AARCH32 U(0x84000041) + +/* SPM error codes. */ +#define SPM_SUCCESS 0 +#define SPM_NOT_SUPPORTED -1 +#define SPM_INVALID_PARAMETER -2 +#define SPM_DENIED -3 +#define SPM_NO_MEMORY -5 + +#ifndef __ASSEMBLY__ + +#include <stdint.h> + +int32_t spm_setup(void); + +uint64_t spm_smc_handler(uint32_t smc_fid, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags); + +#endif /* __ASSEMBLY__ */ + +#endif /* __SPM_SVC_H__ */ diff --git a/lib/el3_runtime/aarch32/context_mgmt.c b/lib/el3_runtime/aarch32/context_mgmt.c index 3e7a5b73..a8672d6c 100644 --- a/lib/el3_runtime/aarch32/context_mgmt.c +++ b/lib/el3_runtime/aarch32/context_mgmt.c @@ -125,6 +125,17 @@ static void cm_init_context_common(cpu_context_t *ctx, const entry_point_info_t } /******************************************************************************* + * Enable architecture extensions on first entry to Non-secure world. + * When EL2 is implemented but unused `el2_unused` is non-zero, otherwise + * it is zero. + ******************************************************************************/ +static void enable_extensions_nonsecure(int el2_unused) +{ +#if IMAGE_BL32 +#endif +} + +/******************************************************************************* * The following function initializes the cpu_context for a CPU specified by * its `cpu_idx` for first use, and sets the initial entrypoint state as * specified by the entry_point_info structure. @@ -161,6 +172,7 @@ void cm_prepare_el3_exit(uint32_t security_state) { uint32_t hsctlr, scr; cpu_context_t *ctx = cm_get_context(security_state); + int el2_unused = 0; assert(ctx); @@ -185,6 +197,8 @@ void cm_prepare_el3_exit(uint32_t security_state) isb(); } else if (read_id_pfr1() & (ID_PFR1_VIRTEXT_MASK << ID_PFR1_VIRTEXT_SHIFT)) { + el2_unused = 1; + /* * Set the NS bit to access NS copies of certain banked * registers @@ -283,5 +297,6 @@ void cm_prepare_el3_exit(uint32_t security_state) write_scr(read_scr() & ~SCR_NS_BIT); isb(); } + enable_extensions_nonsecure(el2_unused); } } diff --git a/lib/el3_runtime/aarch64/context.S b/lib/el3_runtime/aarch64/context.S index db16a9f0..620ec16f 100644 --- a/lib/el3_runtime/aarch64/context.S +++ b/lib/el3_runtime/aarch64/context.S @@ -9,7 +9,6 @@ #include <context.h> .global el1_sysregs_context_save - .global el1_sysregs_context_save_post_ops .global el1_sysregs_context_restore #if CTX_INCLUDE_FPREGS .global fpregs_context_save @@ -90,9 +89,6 @@ func el1_sysregs_context_save mrs x15, dacr32_el2 mrs x16, ifsr32_el2 stp x15, x16, [x0, #CTX_DACR32_EL2] - - mrs x17, fpexc32_el2 - str x17, [x0, #CTX_FP_FPEXC32_EL2] #endif /* Save NS timer registers if the build has instructed so */ @@ -115,36 +111,6 @@ endfunc el1_sysregs_context_save /* ----------------------------------------------------- * The following function strictly follows the AArch64 * PCS to use x9-x17 (temporary caller-saved registers) - * to do post operations after saving the EL1 system - * register context. - * ----------------------------------------------------- - */ -func el1_sysregs_context_save_post_ops -#if ENABLE_SPE_FOR_LOWER_ELS - /* Detect if SPE is implemented */ - mrs x9, id_aa64dfr0_el1 - ubfx x9, x9, #ID_AA64DFR0_PMS_SHIFT, #ID_AA64DFR0_PMS_LENGTH - cmp x9, #0x1 - b.ne 1f - - /* - * Before switching from normal world to secure world - * the profiling buffers need to be drained out to memory. This is - * required to avoid an invalid memory access when TTBR is switched - * for entry to SEL1. - */ - .arch armv8.2-a+profile - psb csync - dsb nsh - .arch armv8-a -1: -#endif - ret -endfunc el1_sysregs_context_save_post_ops - -/* ----------------------------------------------------- - * The following function strictly follows the AArch64 - * PCS to use x9-x17 (temporary caller-saved registers) * to restore EL1 system register context. It assumes * that 'x0' is pointing to a 'el1_sys_regs' structure * from where the register context will be restored @@ -212,9 +178,6 @@ func el1_sysregs_context_restore ldp x15, x16, [x0, #CTX_DACR32_EL2] msr dacr32_el2, x15 msr ifsr32_el2, x16 - - ldr x17, [x0, #CTX_FP_FPEXC32_EL2] - msr fpexc32_el2, x17 #endif /* Restore NS timer registers if the build has instructed so */ #if NS_TIMER_SWITCH @@ -275,6 +238,10 @@ func fpregs_context_save mrs x10, fpcr str x10, [x0, #CTX_FP_FPCR] +#if CTX_INCLUDE_AARCH32_REGS + mrs x11, fpexc32_el2 + str x11, [x0, #CTX_FP_FPEXC32_EL2] +#endif ret endfunc fpregs_context_save @@ -318,6 +285,10 @@ func fpregs_context_restore ldr x10, [x0, #CTX_FP_FPCR] msr fpcr, x10 +#if CTX_INCLUDE_AARCH32_REGS + ldr x11, [x0, #CTX_FP_FPEXC32_EL2] + msr fpexc32_el2, x11 +#endif /* * No explict ISB required here as ERET to * switch to secure EL1 or non-secure world diff --git a/lib/el3_runtime/aarch64/context_mgmt.c b/lib/el3_runtime/aarch64/context_mgmt.c index c8232df9..8f1523f0 100644 --- a/lib/el3_runtime/aarch64/context_mgmt.c +++ b/lib/el3_runtime/aarch64/context_mgmt.c @@ -15,6 +15,7 @@ #include <platform_def.h> #include <pubsub_events.h> #include <smcc_helpers.h> +#include <spe.h> #include <string.h> #include <utils.h> @@ -209,6 +210,20 @@ static void cm_init_context_common(cpu_context_t *ctx, const entry_point_info_t } /******************************************************************************* + * Enable architecture extensions on first entry to Non-secure world. + * When EL2 is implemented but unused `el2_unused` is non-zero, otherwise + * it is zero. + ******************************************************************************/ +static void enable_extensions_nonsecure(int el2_unused) +{ +#if IMAGE_BL31 +#if ENABLE_SPE_FOR_LOWER_ELS + spe_enable(el2_unused); +#endif +#endif +} + +/******************************************************************************* * The following function initializes the cpu_context for a CPU specified by * its `cpu_idx` for first use, and sets the initial entrypoint state as * specified by the entry_point_info structure. @@ -245,6 +260,7 @@ void cm_prepare_el3_exit(uint32_t security_state) { uint32_t sctlr_elx, scr_el3, mdcr_el2; cpu_context_t *ctx = cm_get_context(security_state); + int el2_unused = 0; assert(ctx); @@ -258,6 +274,8 @@ void cm_prepare_el3_exit(uint32_t security_state) sctlr_elx |= SCTLR_EL2_RES1; write_sctlr_el2(sctlr_elx); } else if (EL_IMPLEMENTED(2)) { + el2_unused = 1; + /* * EL2 present but unused, need to disable safely. * SCTLR_EL2 can be ignored in this case. @@ -340,13 +358,6 @@ void cm_prepare_el3_exit(uint32_t security_state) * relying on hw. Some fields are architecturally * UNKNOWN on reset. * - * MDCR_EL2.TPMS (ARM v8.2): Do not trap statistical - * profiling controls to EL2. - * - * MDCR_EL2.E2PB (ARM v8.2): SPE enabled in non-secure - * state. Accesses to profiling buffer controls at - * non-secure EL1 are not trapped to EL2. - * * MDCR_EL2.TDRA: Set to zero so that Non-secure EL0 and * EL1 System register accesses to the Debug ROM * registers are not trapped to EL2. @@ -383,22 +394,6 @@ void cm_prepare_el3_exit(uint32_t security_state) | MDCR_EL2_HPME_BIT | MDCR_EL2_TPM_BIT | MDCR_EL2_TPMCR_BIT)); -#if ENABLE_SPE_FOR_LOWER_ELS - uint64_t id_aa64dfr0_el1; - - /* Detect if SPE is implemented */ - id_aa64dfr0_el1 = read_id_aa64dfr0_el1() >> - ID_AA64DFR0_PMS_SHIFT; - if ((id_aa64dfr0_el1 & ID_AA64DFR0_PMS_MASK) == 1) { - /* - * Make sure traps to EL2 are not generated if - * EL2 is implemented but not used. - */ - mdcr_el2 &= ~MDCR_EL2_TPMS; - mdcr_el2 |= MDCR_EL2_E2PB(MDCR_EL2_E2PB_EL1); - } -#endif - write_mdcr_el2(mdcr_el2); /* @@ -420,6 +415,7 @@ void cm_prepare_el3_exit(uint32_t security_state) write_cnthp_ctl_el2(CNTHP_CTL_RESET_VAL & ~(CNTHP_CTL_ENABLE_BIT)); } + enable_extensions_nonsecure(el2_unused); } cm_el1_sysregs_context_restore(security_state); @@ -439,7 +435,6 @@ void cm_el1_sysregs_context_save(uint32_t security_state) assert(ctx); el1_sysregs_context_save(get_sysregs_ctx(ctx)); - el1_sysregs_context_save_post_ops(); #if IMAGE_BL31 if (security_state == SECURE) diff --git a/lib/extensions/spe/spe.c b/lib/extensions/spe/spe.c new file mode 100644 index 00000000..3b297f21 --- /dev/null +++ b/lib/extensions/spe/spe.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <arch_helpers.h> +#include <pubsub.h> + +/* + * The assembler does not yet understand the psb csync mnemonic + * so use the equivalent hint instruction. + */ +#define psb_csync() asm volatile("hint #17") + +void spe_enable(int el2_unused) +{ + uint64_t features; + + features = read_id_aa64dfr0_el1() >> ID_AA64DFR0_PMS_SHIFT; + if ((features & ID_AA64DFR0_PMS_MASK) == 1) { + uint64_t v; + + if (el2_unused) { + /* + * MDCR_EL2.TPMS (ARM v8.2): Do not trap statistical + * profiling controls to EL2. + * + * MDCR_EL2.E2PB (ARM v8.2): SPE enabled in Non-secure + * state. Accesses to profiling buffer controls at + * Non-secure EL1 are not trapped to EL2. + */ + v = read_mdcr_el2(); + v &= ~MDCR_EL2_TPMS; + v |= MDCR_EL2_E2PB(MDCR_EL2_E2PB_EL1); + write_mdcr_el2(v); + } + + /* + * MDCR_EL2.NSPB (ARM v8.2): SPE enabled in Non-secure state + * and disabled in secure state. Accesses to SPE registers at + * S-EL1 generate trap exceptions to EL3. + */ + v = read_mdcr_el3(); + v |= MDCR_NSPB(MDCR_NSPB_EL1); + write_mdcr_el3(v); + } +} + +void spe_disable(void) +{ + uint64_t features; + + features = read_id_aa64dfr0_el1() >> ID_AA64DFR0_PMS_SHIFT; + if ((features & ID_AA64DFR0_PMS_MASK) == 1) { + uint64_t v; + + /* Drain buffered data */ + psb_csync(); + dsbnsh(); + + /* Disable profiling buffer */ + v = read_pmblimitr_el1(); + v &= ~(1ULL << 0); + write_pmblimitr_el1(v); + isb(); + } +} + +static void *spe_drain_buffers_hook(const void *arg) +{ + uint64_t features; + + features = read_id_aa64dfr0_el1() >> ID_AA64DFR0_PMS_SHIFT; + if ((features & ID_AA64DFR0_PMS_MASK) == 1) { + /* Drain buffered data */ + psb_csync(); + dsbnsh(); + } + + return 0; +} + +SUBSCRIBE_TO_EVENT(cm_entering_secure_world, spe_drain_buffers_hook); diff --git a/lib/psci/psci_common.c b/lib/psci/psci_common.c index 4502c24b..2220a745 100644 --- a/lib/psci/psci_common.c +++ b/lib/psci/psci_common.c @@ -767,12 +767,12 @@ void psci_warmboot_entrypoint(void) psci_acquire_pwr_domain_locks(end_pwrlvl, cpu_idx); + psci_get_target_local_pwr_states(end_pwrlvl, &state_info); + #if ENABLE_PSCI_STAT plat_psci_stat_accounting_stop(&state_info); #endif - psci_get_target_local_pwr_states(end_pwrlvl, &state_info); - /* * This CPU could be resuming from suspend or it could have just been * turned on. To distinguish between these 2 cases, we examine the diff --git a/lib/psci/psci_main.c b/lib/psci/psci_main.c index 4105e63b..8e41cf02 100644 --- a/lib/psci/psci_main.c +++ b/lib/psci/psci_main.c @@ -220,6 +220,23 @@ int psci_affinity_info(u_register_t target_affinity, if (target_idx == -1) return PSCI_E_INVALID_PARAMS; + /* + * Generic management: + * Perform cache maintanence ahead of reading the target CPU state to + * ensure that the data is not stale. + * There is a theoretical edge case where the cache may contain stale + * data for the target CPU data - this can occur under the following + * conditions: + * - the target CPU is in another cluster from the current + * - the target CPU was the last CPU to shutdown on its cluster + * - the cluster was removed from coherency as part of the CPU shutdown + * + * In this case the cache maintenace that was performed as part of the + * target CPUs shutdown was not seen by the current CPU's cluster. And + * so the cache may contain stale data for the target CPU. + */ + flush_cpu_data_by_index(target_idx, psci_svc_cpu_data.aff_info_state); + return psci_get_aff_info_state_by_idx(target_idx); } diff --git a/lib/psci/psci_suspend.c b/lib/psci/psci_suspend.c index 40ecdeea..d9490672 100644 --- a/lib/psci/psci_suspend.c +++ b/lib/psci/psci_suspend.c @@ -37,6 +37,11 @@ static void psci_suspend_to_standby_finisher(unsigned int cpu_idx, */ psci_get_target_local_pwr_states(end_pwrlvl, &state_info); +#if ENABLE_PSCI_STAT + plat_psci_stat_accounting_stop(&state_info); + psci_stats_update_pwr_up(end_pwrlvl, &state_info); +#endif + /* * Plat. management: Allow the platform to do operations * on waking up from retention. @@ -236,10 +241,6 @@ exit: PMF_NO_CACHE_MAINT); #endif -#if ENABLE_PSCI_STAT - plat_psci_stat_accounting_start(state_info); -#endif - /* * We will reach here if only retention/standby states have been * requested at multiple power levels. This means that the cpu @@ -247,11 +248,6 @@ exit: */ wfi(); -#if ENABLE_PSCI_STAT - plat_psci_stat_accounting_stop(state_info); - psci_stats_update_pwr_up(end_pwrlvl, state_info); -#endif - #if ENABLE_RUNTIME_INSTRUMENTATION PMF_CAPTURE_TIMESTAMP(rt_instr_svc, RT_INSTR_EXIT_HW_LOW_PWR, diff --git a/lib/xlat_tables/aarch64/xlat_tables.c b/lib/xlat_tables/aarch64/xlat_tables.c index 28ae1f73..eabc3df9 100644 --- a/lib/xlat_tables/aarch64/xlat_tables.c +++ b/lib/xlat_tables/aarch64/xlat_tables.c @@ -60,7 +60,10 @@ static unsigned long long calc_physical_addr_size_bits( /* Physical Address ranges supported in the AArch64 Memory Model */ static const unsigned int pa_range_bits_arr[] = { PARANGE_0000, PARANGE_0001, PARANGE_0010, PARANGE_0011, PARANGE_0100, - PARANGE_0101 + PARANGE_0101, +#if ARM_ARCH_AT_LEAST(8, 2) + PARANGE_0110, +#endif }; static unsigned long long get_max_supported_pa(void) diff --git a/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c b/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c index eda38d34..aa5b9e54 100644 --- a/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c +++ b/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c @@ -16,8 +16,7 @@ #include <xlat_tables_v2.h> #include "../xlat_tables_private.h" -static unsigned long long calc_physical_addr_size_bits( - unsigned long long max_addr) +unsigned long long tcr_physical_addr_size_bits(unsigned long long max_addr) { /* Physical address can't exceed 48 bits */ assert((max_addr & ADDR_MASK_48_TO_63) == 0); @@ -49,7 +48,10 @@ static unsigned long long calc_physical_addr_size_bits( /* Physical Address ranges supported in the AArch64 Memory Model */ static const unsigned int pa_range_bits_arr[] = { PARANGE_0000, PARANGE_0001, PARANGE_0010, PARANGE_0011, PARANGE_0100, - PARANGE_0101 + PARANGE_0101, +#if ARM_ARCH_AT_LEAST(8, 2) + PARANGE_0110, +#endif }; unsigned long long xlat_arch_get_max_supported_pa(void) @@ -252,7 +254,7 @@ void enable_mmu_arch(unsigned int flags, * It is safer to restrict the max physical address accessible by the * hardware as much as possible. */ - unsigned long long tcr_ps_bits = calc_physical_addr_size_bits(max_pa); + unsigned long long tcr_ps_bits = tcr_physical_addr_size_bits(max_pa); #if IMAGE_EL == 1 assert(IS_IN_EL(1)); diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index 412c3b72..b7ce051b 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -62,6 +62,9 @@ ENABLE_RUNTIME_INSTRUMENTATION := 0 # Flag to enable stack corruption protection ENABLE_STACK_PROTECTOR := 0 +# Flag to enable exception handling in EL3 +EL3_EXCEPTION_HANDLING := 0 + # Build flag to treat usage of deprecated platform and framework APIs as error. ERROR_DEPRECATED := 0 @@ -111,6 +114,9 @@ RESET_TO_BL31 := 0 # For Chain of Trust SAVE_KEYS := 0 +# Software Delegated Exception support +SDEI_SUPPORT := 0 + # Whether code and read-only data should be put on separate memory pages. The # platform Makefile is free to override this value. SEPARATE_CODE_AND_RODATA := 0 @@ -118,6 +124,9 @@ SEPARATE_CODE_AND_RODATA := 0 # SPD choice SPD := none +# For including the Secure Partition Manager +ENABLE_SPM := 0 + # Flag to introduce an infinite loop in BL1 just before it exits into the next # image. This is meant to help debugging the post-BL2 phase. SPIN_ON_BL1_EXIT := 0 @@ -140,19 +149,10 @@ V := 0 # platforms). WARMBOOT_ENABLE_DCACHE_EARLY := 0 -# By default, enable Statistical Profiling Extensions. -# The top level Makefile will disable this feature depending on -# the target architecture and version number. +# Build option to enable/disable the Statistical Profiling Extensions ENABLE_SPE_FOR_LOWER_ELS := 1 -# SPE is enabled by default but only supported on AArch64 8.2 onwards. -# Disable it in all other cases. +# SPE is only supported on AArch64 so disable it on AArch32. ifeq (${ARCH},aarch32) override ENABLE_SPE_FOR_LOWER_ELS := 0 -else - ifeq (${ARM_ARCH_MAJOR},8) - ifeq ($(ARM_ARCH_MINOR),$(filter $(ARM_ARCH_MINOR),0 1)) - ENABLE_SPE_FOR_LOWER_ELS := 0 - endif - endif endif diff --git a/make_helpers/tbbr/tbbr_tools.mk b/make_helpers/tbbr/tbbr_tools.mk index 712fa6f6..b13afe48 100644 --- a/make_helpers/tbbr/tbbr_tools.mk +++ b/make_helpers/tbbr/tbbr_tools.mk @@ -54,6 +54,7 @@ $(eval $(call FWU_CERT_ADD_CMD_OPT,${FWU_CERT},--fwu-cert)) # packed in the FIP). Developers can use their own keys by specifying the proper # build option in the command line when building the Trusted Firmware $(if ${KEY_ALG},$(eval $(call CERT_ADD_CMD_OPT,${KEY_ALG},--key-alg))) +$(if ${HASH_ALG},$(eval $(call CERT_ADD_CMD_OPT,${HASH_ALG},--hash-alg))) $(if ${ROT_KEY},$(eval $(call CERT_ADD_CMD_OPT,${ROT_KEY},--rot-key))) $(if ${ROT_KEY},$(eval $(call FWU_CERT_ADD_CMD_OPT,${ROT_KEY},--rot-key))) $(if ${TRUSTED_WORLD_KEY},$(eval $(call CERT_ADD_CMD_OPT,${TRUSTED_WORLD_KEY},--trusted-world-key))) diff --git a/plat/arm/board/fvp/fvp_common.c b/plat/arm/board/fvp/fvp_common.c index 57cc3d51..6729863d 100644 --- a/plat/arm/board/fvp/fvp_common.c +++ b/plat/arm/board/fvp/fvp_common.c @@ -6,6 +6,7 @@ #include <arm_config.h> #include <arm_def.h> +#include <arm_spm_def.h> #include <assert.h> #include <cci.h> #include <ccn.h> @@ -13,6 +14,7 @@ #include <gicv2.h> #include <mmio.h> #include <plat_arm.h> +#include <secure_partition.h> #include <v2m_def.h> #include "../fvp_def.h" @@ -89,6 +91,9 @@ const mmap_region_t plat_arm_mmap[] = { /* To access the Root of Trust Public Key registers. */ MAP_DEVICE2, #endif +#if ENABLE_SPM + ARM_SP_IMAGE_MMAP, +#endif #if ARM_BL31_IN_DRAM ARM_MAP_BL31_SEC_DRAM, #endif @@ -114,9 +119,23 @@ const mmap_region_t plat_arm_mmap[] = { MAP_DEVICE0, MAP_DEVICE1, ARM_V2M_MAP_MEM_PROTECT, +#if ENABLE_SPM + ARM_SPM_BUF_EL3_MMAP, +#endif + {0} +}; + +#if ENABLE_SPM && defined(IMAGE_BL31) +const mmap_region_t plat_arm_secure_partition_mmap[] = { + V2M_MAP_IOFPGA_EL0, /* for the UART */ + ARM_SP_IMAGE_MMAP, + ARM_SP_IMAGE_NS_BUF_MMAP, + ARM_SP_IMAGE_RW_MMAP, + ARM_SPM_BUF_EL0_MMAP, {0} }; #endif +#endif #ifdef IMAGE_BL32 const mmap_region_t plat_arm_mmap[] = { #ifdef AARCH32 @@ -156,6 +175,57 @@ static unsigned int get_interconnect_master(void) } #endif +#if ENABLE_SPM && defined(IMAGE_BL31) +/* + * Boot information passed to a secure partition during initialisation. Linear + * indices in MP information will be filled at runtime. + */ +static secure_partition_mp_info_t sp_mp_info[] = { + [0] = {0x80000000, 0}, + [1] = {0x80000001, 0}, + [2] = {0x80000002, 0}, + [3] = {0x80000003, 0}, + [4] = {0x80000100, 0}, + [5] = {0x80000101, 0}, + [6] = {0x80000102, 0}, + [7] = {0x80000103, 0}, +}; + +const secure_partition_boot_info_t plat_arm_secure_partition_boot_info = { + .h.type = PARAM_SP_IMAGE_BOOT_INFO, + .h.version = VERSION_1, + .h.size = sizeof(secure_partition_boot_info_t), + .h.attr = 0, + .sp_mem_base = ARM_SP_IMAGE_BASE, + .sp_mem_limit = ARM_SP_IMAGE_LIMIT, + .sp_image_base = ARM_SP_IMAGE_BASE, + .sp_stack_base = PLAT_SP_IMAGE_STACK_BASE, + .sp_heap_base = ARM_SP_IMAGE_HEAP_BASE, + .sp_ns_comm_buf_base = ARM_SP_IMAGE_NS_BUF_BASE, + .sp_shared_buf_base = PLAT_SPM_BUF_BASE, + .sp_image_size = ARM_SP_IMAGE_SIZE, + .sp_pcpu_stack_size = PLAT_SP_IMAGE_STACK_PCPU_SIZE, + .sp_heap_size = ARM_SP_IMAGE_HEAP_SIZE, + .sp_ns_comm_buf_size = ARM_SP_IMAGE_NS_BUF_SIZE, + .sp_shared_buf_size = PLAT_SPM_BUF_SIZE, + .num_sp_mem_regions = ARM_SP_IMAGE_NUM_MEM_REGIONS, + .num_cpus = PLATFORM_CORE_COUNT, + .mp_info = &sp_mp_info[0], +}; + +const struct mmap_region *plat_get_secure_partition_mmap(void *cookie) +{ + return plat_arm_secure_partition_mmap; +} + +const struct secure_partition_boot_info *plat_get_secure_partition_boot_info( + void *cookie) +{ + return &plat_arm_secure_partition_boot_info; +} + +#endif + /******************************************************************************* * A single boot loader stack is expected to work on both the Foundation FVP * models and the two flavours of the Base FVP models (AEMv8 & Cortex). The diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index faeb1b77..13bd8f23 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -14,6 +14,7 @@ #include <plat_arm.h> #include <platform.h> #include <psci.h> +#include <spe.h> #include <v2m_def.h> #include "drivers/pwrc/fvp_pwrc.h" #include "fvp_def.h" @@ -57,7 +58,7 @@ static void fvp_cluster_pwrdwn_common(void) * On power down we need to disable statistical profiling extensions * before exiting coherency. */ - arm_disable_spe(); + spe_disable(); #endif /* Disable coherency if this cluster is to be turned off */ @@ -398,7 +399,7 @@ plat_psci_ops_t plat_arm_psci_pm_ops = { .system_off = fvp_system_off, .system_reset = fvp_system_reset, .validate_power_state = fvp_validate_power_state, - .validate_ns_entrypoint = arm_validate_ns_entrypoint, + .validate_ns_entrypoint = arm_validate_psci_entrypoint, .translate_power_state_by_mpidr = fvp_translate_power_state_by_mpidr, .get_node_hw_state = fvp_node_hw_state, .get_sys_suspend_power_state = fvp_get_sys_suspend_power_state, diff --git a/plat/arm/common/aarch64/arm_ehf.c b/plat/arm/common/aarch64/arm_ehf.c new file mode 100644 index 00000000..785b7bb5 --- /dev/null +++ b/plat/arm/common/aarch64/arm_ehf.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <ehf.h> +#include <platform_def.h> + +/* + * Enumeration of priority levels on ARM platforms. + */ +ehf_pri_desc_t arm_exceptions[] = { +#if SDEI_SUPPORT + /* Critical priority SDEI */ + EHF_PRI_DESC(ARM_PRI_BITS, PLAT_SDEI_CRITICAL_PRI), + + /* Normal priority SDEI */ + EHF_PRI_DESC(ARM_PRI_BITS, PLAT_SDEI_NORMAL_PRI), +#endif +}; + +/* Plug in ARM exceptions to Exception Handling Framework. */ +EHF_REGISTER_PRIORITIES(arm_exceptions, ARRAY_SIZE(arm_exceptions), ARM_PRI_BITS); diff --git a/plat/arm/common/aarch64/arm_helpers.S b/plat/arm/common/aarch64/arm_helpers.S index b53e60db..9d3a1081 100644 --- a/plat/arm/common/aarch64/arm_helpers.S +++ b/plat/arm/common/aarch64/arm_helpers.S @@ -12,7 +12,6 @@ .globl plat_crash_console_putc .globl plat_crash_console_flush .globl platform_mem_init - .globl arm_disable_spe /* ----------------------------------------------------- @@ -88,34 +87,6 @@ func platform_mem_init ret endfunc platform_mem_init - /* ----------------------------------------------------- - * void arm_disable_spe (void); - * ----------------------------------------------------- - */ -#if ENABLE_SPE_FOR_LOWER_ELS -func arm_disable_spe - /* Detect if SPE is implemented */ - mrs x0, id_aa64dfr0_el1 - ubfx x0, x0, #ID_AA64DFR0_PMS_SHIFT, #ID_AA64DFR0_PMS_LENGTH - cmp x0, #0x1 - b.ne 1f - - /* Drain buffered data */ - .arch armv8.2-a+profile - psb csync - dsb nsh - - /* Disable Profiling Buffer */ - mrs x0, pmblimitr_el1 - bic x0, x0, #1 - msr pmblimitr_el1, x0 - isb - .arch armv8-a -1: - ret -endfunc arm_disable_spe -#endif - /* * Need to use coherent stack when ARM Cryptocell is used to autheticate images * since Cryptocell uses DMA to transfer data and it is not coherent with the diff --git a/plat/arm/common/aarch64/arm_sdei.c b/plat/arm/common/aarch64/arm_sdei.c new file mode 100644 index 00000000..514800c3 --- /dev/null +++ b/plat/arm/common/aarch64/arm_sdei.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* SDEI configuration for ARM platforms */ + +#include <ehf.h> +#include <platform_def.h> +#include <sdei.h> + +/* Private event mappings */ +static sdei_ev_map_t arm_private_sdei[] = { + /* Event 0 */ + SDEI_DEFINE_EVENT_0(ARM_SDEI_SGI), + + /* Dynamic private events */ + SDEI_PRIVATE_EVENT(ARM_SDEI_DP_EVENT_0, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), + SDEI_PRIVATE_EVENT(ARM_SDEI_DP_EVENT_1, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), + SDEI_PRIVATE_EVENT(ARM_SDEI_DP_EVENT_2, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), +}; + +/* Shared event mappings */ +static sdei_ev_map_t arm_shared_sdei[] = { + /* Dynamic shared events */ + SDEI_SHARED_EVENT(ARM_SDEI_DS_EVENT_0, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), + SDEI_SHARED_EVENT(ARM_SDEI_DS_EVENT_1, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), + SDEI_SHARED_EVENT(ARM_SDEI_DS_EVENT_2, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), +}; + +/* Export ARM SDEI events */ +REGISTER_SDEI_MAP(arm_private_sdei, arm_shared_sdei); diff --git a/plat/arm/common/arm_common.c b/plat/arm/common/arm_common.c index 420a3865..bf639737 100644 --- a/plat/arm/common/arm_common.c +++ b/plat/arm/common/arm_common.c @@ -11,6 +11,7 @@ #include <mmio.h> #include <plat_arm.h> #include <platform_def.h> +#include <secure_partition.h> extern const mmap_region_t plat_arm_mmap[]; @@ -79,6 +80,14 @@ void arm_setup_page_tables(uintptr_t total_base, MT_DEVICE | MT_RW | MT_SECURE); #endif +#if ENABLE_SPM && defined(IMAGE_BL31) + /* The address of the following region is calculated by the linker. */ + mmap_add_region(SP_IMAGE_XLAT_TABLES_START, + SP_IMAGE_XLAT_TABLES_START, + SP_IMAGE_XLAT_TABLES_SIZE, + MT_MEMORY | MT_RW | MT_SECURE); +#endif + /* Now (re-)map the platform-specific memory regions */ mmap_add(plat_arm_get_mmap()); @@ -195,3 +204,51 @@ unsigned int plat_get_syscnt_freq2(void) } #endif /* ARM_SYS_CNTCTL_BASE */ + +#if SDEI_SUPPORT +/* + * Translate SDEI entry point to PA, and perform standard ARM entry point + * validation on it. + */ +int plat_sdei_validate_entry_point(uintptr_t ep, unsigned int client_mode) +{ + uint64_t par, pa; + uint32_t scr_el3; + + /* Doing Non-secure address translation requires SCR_EL3.NS set */ + scr_el3 = read_scr_el3(); + write_scr_el3(scr_el3 | SCR_NS_BIT); + isb(); + + assert((client_mode == MODE_EL2) || (client_mode == MODE_EL1)); + if (client_mode == MODE_EL2) { + /* + * Translate entry point to Physical Address using the EL2 + * translation regime. + */ + ats1e2r(ep); + } else { + /* + * Translate entry point to Physical Address using the EL1&0 + * translation regime, including stage 2. + */ + ats12e1r(ep); + } + isb(); + par = read_par_el1(); + + /* Restore original SCRL_EL3 */ + write_scr_el3(scr_el3); + isb(); + + /* If the translation resulted in fault, return failure */ + if ((par & PAR_F_MASK) != 0) + return -1; + + /* Extract Physical Address from PAR */ + pa = (par & (PAR_ADDR_MASK << PAR_ADDR_SHIFT)); + + /* Perform NS entry point validation on the physical address */ + return arm_validate_ns_entrypoint(pa); +} +#endif diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index 44eb43f6..17acae52 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -184,6 +184,14 @@ BL31_SOURCES += plat/arm/common/arm_sip_svc.c \ lib/pmf/pmf_smc.c endif +ifeq (${EL3_EXCEPTION_HANDLING},1) +BL31_SOURCES += plat/arm/common/aarch64/arm_ehf.c +endif + +ifeq (${SDEI_SUPPORT},1) +BL31_SOURCES += plat/arm/common/aarch64/arm_sdei.c +endif + ifneq (${TRUSTED_BOARD_BOOT},0) # Include common TBB sources diff --git a/plat/arm/common/arm_gicv2.c b/plat/arm/common/arm_gicv2.c index b081fa8d..5644c604 100644 --- a/plat/arm/common/arm_gicv2.c +++ b/plat/arm/common/arm_gicv2.c @@ -51,6 +51,7 @@ void plat_arm_gic_init(void) { gicv2_distif_init(); gicv2_pcpu_distif_init(); + gicv2_set_pe_target_mask(plat_my_core_pos()); gicv2_cpuif_enable(); } diff --git a/plat/arm/common/arm_pm.c b/plat/arm/common/arm_pm.c index 5e7e047a..44ac5b5d 100644 --- a/plat/arm/common/arm_pm.c +++ b/plat/arm/common/arm_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -112,7 +112,7 @@ int arm_validate_power_state(unsigned int power_state, /******************************************************************************* * ARM standard platform handler called to check the validity of the non secure - * entrypoint. + * entrypoint. Returns 0 if the entrypoint is valid, or -1 otherwise. ******************************************************************************/ int arm_validate_ns_entrypoint(uintptr_t entrypoint) { @@ -121,15 +121,23 @@ int arm_validate_ns_entrypoint(uintptr_t entrypoint) * secure DRAM. */ if ((entrypoint >= ARM_NS_DRAM1_BASE) && (entrypoint < - (ARM_NS_DRAM1_BASE + ARM_NS_DRAM1_SIZE))) - return PSCI_E_SUCCESS; + (ARM_NS_DRAM1_BASE + ARM_NS_DRAM1_SIZE))) { + return 0; + } #ifndef AARCH32 if ((entrypoint >= ARM_DRAM2_BASE) && (entrypoint < - (ARM_DRAM2_BASE + ARM_DRAM2_SIZE))) - return PSCI_E_SUCCESS; + (ARM_DRAM2_BASE + ARM_DRAM2_SIZE))) { + return 0; + } #endif - return PSCI_E_INVALID_ADDRESS; + return -1; +} + +int arm_validate_psci_entrypoint(uintptr_t entrypoint) +{ + return arm_validate_ns_entrypoint(entrypoint) == 0 ? PSCI_E_SUCCESS : + PSCI_E_INVALID_ADDRESS; } /****************************************************************************** diff --git a/plat/arm/common/arm_tzc400.c b/plat/arm/common/arm_tzc400.c index e19ca673..23c03173 100644 --- a/plat/arm/common/arm_tzc400.c +++ b/plat/arm/common/arm_tzc400.c @@ -1,10 +1,11 @@ /* - * Copyright (c) 2014-2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <arm_def.h> +#include <arm_spm_def.h> #include <debug.h> #include <platform_def.h> #include <tzc400.h> @@ -56,9 +57,26 @@ void arm_tzc400_setup(void) ARM_DRAM2_BASE, ARM_DRAM2_END, ARM_TZC_NS_DRAM_S_ACCESS, PLAT_ARM_TZC_NS_DEV_ACCESS); -#else + +#if ENABLE_SPM + /* + * Region 4 set to cover Non-Secure access to the communication buffer + * shared with the Secure world. + */ + tzc400_configure_region(PLAT_ARM_TZC_FILTERS, + 4, + ARM_SP_IMAGE_NS_BUF_BASE, + (ARM_SP_IMAGE_NS_BUF_BASE + + ARM_SP_IMAGE_NS_BUF_SIZE) - 1, + TZC_REGION_S_NONE, + PLAT_ARM_TZC_NS_DEV_ACCESS); +#endif + +#else /* if defined(EL3_PAYLOAD_BASE) */ + /* Allow secure access only to DRAM for EL3 payloads. */ tzc400_configure_region0(TZC_REGION_S_RDWR, 0); + #endif /* EL3_PAYLOAD_BASE */ /* diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 4104dd73..4a615e1c 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -299,7 +299,7 @@ plat_psci_ops_t plat_arm_psci_pm_ops = { .system_off = css_system_off, .system_reset = css_system_reset, .validate_power_state = css_validate_power_state, - .validate_ns_entrypoint = arm_validate_ns_entrypoint, + .validate_ns_entrypoint = arm_validate_psci_entrypoint, .translate_power_state_by_mpidr = css_translate_power_state_by_mpidr, .get_node_hw_state = css_node_hw_state, .get_sys_suspend_power_state = css_get_sys_suspend_power_state, diff --git a/plat/common/aarch64/plat_common.c b/plat/common/aarch64/plat_common.c index 05084e19..a87e7c67 100644 --- a/plat/common/aarch64/plat_common.c +++ b/plat/common/aarch64/plat_common.c @@ -3,6 +3,8 @@ * * SPDX-License-Identifier: BSD-3-Clause */ + +#include <arch_helpers.h> #include <assert.h> #include <console.h> #include <platform.h> @@ -20,6 +22,11 @@ #pragma weak plat_get_syscnt_freq2 #endif /* ERROR_DEPRECATED */ +#if SDEI_SUPPORT +#pragma weak plat_sdei_handle_masked_trigger +#pragma weak plat_sdei_validate_entry_point +#endif + void bl31_plat_enable_mmu(uint32_t flags) { enable_mmu_el3(flags); @@ -64,3 +71,22 @@ unsigned int plat_get_syscnt_freq2(void) return (unsigned int)freq; } #endif /* ERROR_DEPRECATED */ + +#if SDEI_SUPPORT +/* + * Function that handles spurious SDEI interrupts while events are masked. + */ +void plat_sdei_handle_masked_trigger(uint64_t mpidr, unsigned int intr) +{ + WARN("Spurious SDEI interrupt %u on masked PE %lx\n", intr, mpidr); +} + +/* + * Default Function to validate SDEI entry point, which returns success. + * Platforms may override this with their own validation mechanism. + */ +int plat_sdei_validate_entry_point(uintptr_t ep, unsigned int client_mode) +{ + return 0; +} +#endif diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index 05fabcab..38e1a61e 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -277,3 +277,13 @@ unsigned int plat_ic_set_priority_mask(unsigned int mask) { return gicv2_set_pmr(mask); } + +unsigned int plat_ic_get_interrupt_id(unsigned int raw) +{ + unsigned int id = (raw & INT_ID_MASK); + + if (id == GIC_SPURIOUS_INTERRUPT) + id = INTR_ID_UNAVAILABLE; + + return id; +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index 52ceb6a7..030eea72 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -271,6 +271,14 @@ unsigned int plat_ic_set_priority_mask(unsigned int mask) { return gicv3_set_pmr(mask); } + +unsigned int plat_ic_get_interrupt_id(unsigned int raw) +{ + unsigned int id = (raw & INT_ID_MASK); + + return (gicv3_is_intr_id_special_identifier(id) ? + INTR_ID_UNAVAILABLE : id); +} #endif #ifdef IMAGE_BL32 diff --git a/plat/common/plat_psci_common.c b/plat/common/plat_psci_common.c index 95adb051..0e818d0d 100644 --- a/plat/common/plat_psci_common.c +++ b/plat/common/plat_psci_common.c @@ -18,6 +18,13 @@ /* Ticks elapsed in one second by a signal of 1 MHz */ #define MHZ_TICKS_PER_SEC 1000000 +/* Maximum time-stamp value read from architectural counters */ +#ifdef AARCH32 +#define MAX_TS UINT32_MAX +#else +#define MAX_TS UINT64_MAX +#endif + /* Following are used as ID's to capture time-stamp */ #define PSCI_STAT_ID_ENTER_LOW_PWR 0 #define PSCI_STAT_ID_EXIT_LOW_PWR 1 @@ -45,7 +52,7 @@ static u_register_t calc_stat_residency(unsigned long long pwrupts, assert(residency_div); if (pwrupts < pwrdnts) - res = UINT64_MAX - pwrdnts + pwrupts; + res = MAX_TS - pwrdnts + pwrupts; else res = pwrupts - pwrdnts; diff --git a/services/spd/opteed/opteed_common.c b/services/spd/opteed/opteed_common.c index 2693e7d1..e5e2be77 100644 --- a/services/spd/opteed/opteed_common.c +++ b/services/spd/opteed/opteed_common.c @@ -78,7 +78,7 @@ uint64_t opteed_synchronous_sp_entry(optee_context_t *optee_ctx) cm_set_next_eret_context(SECURE); rc = opteed_enter_sp(&optee_ctx->c_rt_ctx); -#if DEBUG +#if ENABLE_ASSERTIONS optee_ctx->c_rt_ctx = 0; #endif diff --git a/services/spd/tlkd/tlkd_common.c b/services/spd/tlkd/tlkd_common.c index 599d7a30..483d45b6 100644 --- a/services/spd/tlkd/tlkd_common.c +++ b/services/spd/tlkd/tlkd_common.c @@ -131,7 +131,7 @@ uint64_t tlkd_synchronous_sp_entry(tlk_context_t *tlk_ctx) cm_set_next_eret_context(SECURE); rc = tlkd_enter_sp(&tlk_ctx->c_rt_ctx); -#if DEBUG +#if ENABLE_ASSERTIONS tlk_ctx->c_rt_ctx = 0; #endif diff --git a/services/spd/tspd/tspd_common.c b/services/spd/tspd/tspd_common.c index 1538232f..de54dbe9 100644 --- a/services/spd/tspd/tspd_common.c +++ b/services/spd/tspd/tspd_common.c @@ -79,7 +79,7 @@ uint64_t tspd_synchronous_sp_entry(tsp_context_t *tsp_ctx) cm_set_next_eret_context(SECURE); rc = tspd_enter_sp(&tsp_ctx->c_rt_ctx); -#if DEBUG +#if ENABLE_ASSERTIONS tsp_ctx->c_rt_ctx = 0; #endif diff --git a/services/std_svc/sdei/sdei_event.c b/services/std_svc/sdei/sdei_event.c new file mode 100644 index 00000000..bf0e779d --- /dev/null +++ b/services/std_svc/sdei/sdei_event.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <utils.h> +#include "sdei_private.h" + +#define MAP_OFF(_map, _mapping) ((_map) - (_mapping)->map) + +/* + * Get SDEI entry with the given mapping: on success, returns pointer to SDEI + * entry. On error, returns NULL. + * + * Both shared and private maps are stored in single-dimensional array. Private + * event entries are kept for each PE forming a 2D array. + */ +sdei_entry_t *get_event_entry(sdei_ev_map_t *map) +{ + const sdei_mapping_t *mapping; + sdei_entry_t *cpu_priv_base; + unsigned int idx, base_idx; + + if (is_event_private(map)) { + /* + * For a private map, find the index of the mapping in the + * array. + */ + mapping = SDEI_PRIVATE_MAPPING(); + idx = MAP_OFF(map, mapping); + + /* Base of private mappings for this CPU */ + base_idx = plat_my_core_pos() * mapping->num_maps; + cpu_priv_base = &sdei_private_event_table[base_idx]; + + /* + * Return the address of the entry at the same index in the + * per-CPU event entry. + */ + return &cpu_priv_base[idx]; + } else { + mapping = SDEI_SHARED_MAPPING(); + idx = MAP_OFF(map, mapping); + + return &sdei_shared_event_table[idx]; + } +} + +/* + * Find event mapping for a given interrupt number: On success, returns pointer + * to the event mapping. On error, returns NULL. + */ +sdei_ev_map_t *find_event_map_by_intr(int intr_num, int shared) +{ + const sdei_mapping_t *mapping; + sdei_ev_map_t *map; + unsigned int i; + + /* + * Look for a match in private and shared mappings, as requested. This + * is a linear search. However, if the mappings are required to be + * sorted, for large maps, we could consider binary search. + */ + mapping = shared ? SDEI_SHARED_MAPPING() : SDEI_PRIVATE_MAPPING(); + iterate_mapping(mapping, i, map) { + if (map->intr == intr_num) + return map; + } + + return NULL; +} + +/* + * Find event mapping for a given event number: On success returns pointer to + * the event mapping. On error, returns NULL. + */ +sdei_ev_map_t *find_event_map(int ev_num) +{ + const sdei_mapping_t *mapping; + sdei_ev_map_t *map; + unsigned int i, j; + + /* + * Iterate through mappings to find a match. This is a linear search. + * However, if the mappings are required to be sorted, for large maps, + * we could consider binary search. + */ + for_each_mapping_type(i, mapping) { + iterate_mapping(mapping, j, map) { + if (map->ev_num == ev_num) + return map; + } + } + + return NULL; +} diff --git a/services/std_svc/sdei/sdei_intr_mgmt.c b/services/std_svc/sdei/sdei_intr_mgmt.c new file mode 100644 index 00000000..42bf46d0 --- /dev/null +++ b/services/std_svc/sdei/sdei_intr_mgmt.c @@ -0,0 +1,678 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> +#include <assert.h> +#include <bl_common.h> +#include <cassert.h> +#include <context_mgmt.h> +#include <debug.h> +#include <ehf.h> +#include <interrupt_mgmt.h> +#include <runtime_svc.h> +#include <sdei.h> +#include <string.h> +#include "sdei_private.h" + +#define PE_MASKED 1 +#define PE_NOT_MASKED 0 + +/* x0-x17 GPREGS context */ +#define SDEI_SAVED_GPREGS 18 + +/* Maximum preemption nesting levels: Critical priority and Normal priority */ +#define MAX_EVENT_NESTING 2 + +/* Per-CPU SDEI state access macro */ +#define sdei_get_this_pe_state() (&sdei_cpu_state[plat_my_core_pos()]) + +/* 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]; + + /* Exception state registers */ + uint64_t elr_el3; + uint64_t spsr_el3; +} sdei_dispatch_context_t; + +/* Per-CPU SDEI state data */ +typedef struct sdei_cpu_state { + sdei_dispatch_context_t dispatch_stack[MAX_EVENT_NESTING]; + unsigned short stack_top; /* Empty ascending */ + unsigned int pe_masked:1; + unsigned int pending_enables:1; +} sdei_cpu_state_t; + +/* SDEI states for all cores in the system */ +static sdei_cpu_state_t sdei_cpu_state[PLATFORM_CORE_COUNT]; + +unsigned int sdei_pe_mask(void) +{ + unsigned int ret; + sdei_cpu_state_t *state = sdei_get_this_pe_state(); + + /* + * Return value indicates whether this call had any effect in the mask + * status of this PE. + */ + ret = (state->pe_masked ^ PE_MASKED); + state->pe_masked = PE_MASKED; + + return ret; +} + +void sdei_pe_unmask(void) +{ + int i; + sdei_ev_map_t *map; + sdei_entry_t *se; + sdei_cpu_state_t *state = sdei_get_this_pe_state(); + uint64_t my_mpidr = read_mpidr_el1() & MPIDR_AFFINITY_MASK; + + /* + * If there are pending enables, iterate through the private mappings + * and enable those bound maps that are in enabled state. Also, iterate + * through shared mappings and enable interrupts of events that are + * targeted to this PE. + */ + if (state->pending_enables) { + for_each_private_map(i, map) { + se = get_event_entry(map); + if (is_map_bound(map) && GET_EV_STATE(se, ENABLED)) + plat_ic_enable_interrupt(map->intr); + } + + for_each_shared_map(i, map) { + se = get_event_entry(map); + + sdei_map_lock(map); + if (is_map_bound(map) && + GET_EV_STATE(se, ENABLED) && + (se->reg_flags == SDEI_REGF_RM_PE) && + (se->affinity == my_mpidr)) { + plat_ic_enable_interrupt(map->intr); + } + sdei_map_unlock(map); + } + } + + state->pending_enables = 0; + state->pe_masked = PE_NOT_MASKED; +} + +/* Push a dispatch context to the dispatch stack */ +static sdei_dispatch_context_t *push_dispatch(void) +{ + sdei_cpu_state_t *state = sdei_get_this_pe_state(); + sdei_dispatch_context_t *disp_ctx; + + /* Cannot have more than max events */ + assert(state->stack_top < MAX_EVENT_NESTING); + + disp_ctx = &state->dispatch_stack[state->stack_top]; + state->stack_top++; + + return disp_ctx; +} + +/* Pop a dispatch context to the dispatch stack */ +static sdei_dispatch_context_t *pop_dispatch(void) +{ + sdei_cpu_state_t *state = sdei_get_this_pe_state(); + + if (state->stack_top == 0) + return NULL; + + assert(state->stack_top <= MAX_EVENT_NESTING); + + state->stack_top--; + + return &state->dispatch_stack[state->stack_top]; +} + +/* Retrieve the context at the top of dispatch stack */ +static sdei_dispatch_context_t *get_outstanding_dispatch(void) +{ + sdei_cpu_state_t *state = sdei_get_this_pe_state(); + + if (state->stack_top == 0) + return NULL; + + assert(state->stack_top <= MAX_EVENT_NESTING); + + 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) +{ + sdei_dispatch_context_t *disp_ctx; + gp_regs_t *tgt_gpregs; + el3_state_t *tgt_el3; + + assert(tgt_ctx); + tgt_gpregs = get_gpregs_ctx(tgt_ctx); + tgt_el3 = get_el3state_ctx(tgt_ctx); + + 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); +} + +static void restore_event_ctx(sdei_dispatch_context_t *disp_ctx, void *tgt_ctx) +{ + gp_regs_t *tgt_gpregs; + el3_state_t *tgt_el3; + + assert(tgt_ctx); + tgt_gpregs = get_gpregs_ctx(tgt_ctx); + tgt_el3 = get_el3state_ctx(tgt_ctx); + + CASSERT(sizeof(disp_ctx->x) == (SDEI_SAVED_GPREGS * sizeof(uint64_t)), + foo); + + /* Restore general purpose and exception registers */ + memcpy(tgt_gpregs, disp_ctx->x, sizeof(disp_ctx->x)); + write_ctx_reg(tgt_el3, CTX_SPSR_EL3, disp_ctx->spsr_el3); + write_ctx_reg(tgt_el3, CTX_ELR_EL3, disp_ctx->elr_el3); +} + +static void save_secure_context(void) +{ + cm_el1_sysregs_context_save(SECURE); +} + +/* Restore Secure context and arrange to resume it at the next ERET */ +static void restore_and_resume_secure_context(void) +{ + cm_el1_sysregs_context_restore(SECURE); + cm_set_next_eret_context(SECURE); +} + +/* + * Restore Non-secure context and arrange to resume it at the next ERET. Return + * pointer to the Non-secure context. + */ +static cpu_context_t *restore_and_resume_ns_context(void) +{ + cpu_context_t *ns_ctx; + + cm_el1_sysregs_context_restore(NON_SECURE); + cm_set_next_eret_context(NON_SECURE); + + ns_ctx = cm_get_context(NON_SECURE); + assert(ns_ctx); + + return ns_ctx; +} + +/* + * Populate the Non-secure context so that the next ERET will dispatch to the + * 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) +{ + el3_state_t *el3_ctx = get_el3state_ctx(ctx); + + /* Push the event and context */ + save_event_ctx(map, ctx, sec_state_to_resume, intr_raw); + + /* + * Setup handler arguments: + * + * - x0: Event number + * - x1: Handler argument supplied at the time of event registration + * - x2: Interrupted PC + * - x3: Interrupted SPSR + */ + 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)); + + /* + * Prepare for ERET: + * + * - Set PC to the registered handler address + * - Set SPSR to jump to client EL with exceptions masked + */ + cm_set_elr_spsr_el3(NON_SECURE, (uintptr_t) se->ep, + SPSR_64(sdei_client_el(), MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS)); +} + +/* Handle a triggered SDEI interrupt while events were masked on this PE */ +static void handle_masked_trigger(sdei_ev_map_t *map, sdei_entry_t *se, + sdei_cpu_state_t *state, unsigned int intr_raw) +{ + uint64_t my_mpidr __unused = (read_mpidr_el1() & MPIDR_AFFINITY_MASK); + int disable = 0; + + /* Nothing to do for event 0 */ + if (map->ev_num == SDEI_EVENT_0) + return; + + /* + * For a private event, or for a shared event specifically routed to + * this CPU, we disable interrupt, leave the interrupt pending, and do + * EOI. + */ + if (is_event_private(map)) { + disable = 1; + } else if (se->reg_flags == SDEI_REGF_RM_PE) { + assert(se->affinity == my_mpidr); + disable = 1; + } + + if (disable) { + plat_ic_disable_interrupt(map->intr); + plat_ic_set_interrupt_pending(map->intr); + plat_ic_end_of_interrupt(intr_raw); + state->pending_enables = 1; + + return; + } + + /* + * We just received a shared event with routing set to ANY PE. The + * interrupt can't be delegated on this PE as SDEI events are masked. + * However, because its routing mode is ANY, it is possible that the + * event can be delegated on any other PE that hasn't masked events. + * Therefore, we set the interrupt back pending so as to give other + * suitable PEs a chance of handling it. + */ + assert(plat_ic_is_spi(map->intr)); + plat_ic_set_interrupt_pending(map->intr); + + /* + * Leaving the same interrupt pending also means that the same interrupt + * can target this PE again as soon as this PE leaves EL3. Whether and + * how often that happens depends on the implementation of GIC. + * + * We therefore call a platform handler to resolve this situation. + */ + plat_sdei_handle_masked_trigger(my_mpidr, map->intr); + + /* This PE is masked. We EOI the interrupt, as it can't be delegated */ + plat_ic_end_of_interrupt(intr_raw); +} + +/* SDEI main interrupt handler */ +int sdei_intr_handler(uint32_t intr_raw, uint32_t flags, void *handle, + void *cookie) +{ + sdei_entry_t *se; + cpu_context_t *ctx; + sdei_ev_map_t *map; + sdei_dispatch_context_t *disp_ctx; + unsigned int sec_state; + sdei_cpu_state_t *state; + uint32_t intr; + + /* + * To handle an event, the following conditions must be true: + * + * 1. Event must be signalled + * 2. Event must be enabled + * 3. This PE must be a target PE for the event + * 4. PE must be unmasked for SDEI + * 5. If this is a normal event, no event must be running + * 6. If this is a critical event, no critical event must be running + * + * (1) and (2) are true when this function is running + * (3) is enforced in GIC by selecting the appropriate routing option + * (4) is satisfied by client calling PE_UNMASK + * (5) and (6) is enforced using interrupt priority, the RPR, in GIC: + * - Normal SDEI events belong to Normal SDE priority class + * - Critical SDEI events belong to Critical CSDE priority class + * + * The interrupt has already been acknowledged, and therefore is active, + * so no other PE can handle this event while we are at it. + * + * Find if this is an SDEI interrupt. There must be an event mapped to + * this interrupt + */ + intr = plat_ic_get_interrupt_id(intr_raw); + map = find_event_map_by_intr(intr, plat_ic_is_spi(intr)); + if (!map) { + ERROR("No SDEI map for interrupt %u\n", intr); + panic(); + } + + /* + * Received interrupt number must either correspond to event 0, or must + * be bound interrupt. + */ + assert((map->ev_num == SDEI_EVENT_0) || is_map_bound(map)); + + se = get_event_entry(map); + state = sdei_get_this_pe_state(); + + if (state->pe_masked == PE_MASKED) { + /* + * Interrupts received while this PE was masked can't be + * dispatched. + */ + SDEI_LOG("interrupt %u on %lx while PE masked\n", map->intr, + read_mpidr_el1()); + if (is_event_shared(map)) + sdei_map_lock(map); + + handle_masked_trigger(map, se, state, intr_raw); + + if (is_event_shared(map)) + sdei_map_unlock(map); + + return 0; + } + + /* Insert load barrier for signalled SDEI event */ + if (map->ev_num == SDEI_EVENT_0) + dmbld(); + + if (is_event_shared(map)) + sdei_map_lock(map); + + /* Assert shared event routed to this PE had been configured so */ + if (is_event_shared(map) && (se->reg_flags == SDEI_REGF_RM_PE)) { + assert(se->affinity == + (read_mpidr_el1() & MPIDR_AFFINITY_MASK)); + } + + if (!can_sdei_state_trans(se, DO_DISPATCH)) { + SDEI_LOG("SDEI event 0x%x can't be dispatched; state=0x%x\n", + map->ev_num, se->state); + + /* + * If the event is registered, leave the interrupt pending so + * that it's delivered when the event is enabled. + */ + if (GET_EV_STATE(se, REGISTERED)) + plat_ic_set_interrupt_pending(map->intr); + + /* + * The interrupt was disabled or unregistered after the handler + * started to execute, which means now the interrupt is already + * disabled and we just need to EOI the interrupt. + */ + plat_ic_end_of_interrupt(intr_raw); + + if (is_event_shared(map)) + sdei_map_unlock(map); + + return 0; + } + + disp_ctx = get_outstanding_dispatch(); + if (is_event_critical(map)) { + /* + * If this event is Critical, and if there's an outstanding + * dispatch, assert the latter is a Normal dispatch. Critical + * events can preempt an outstanding Normal event dispatch. + */ + if (disp_ctx) + assert(is_event_normal(disp_ctx->map)); + } else { + /* + * If this event is Normal, assert that there are no outstanding + * dispatches. Normal events can't preempt any outstanding event + * dispatches. + */ + assert(disp_ctx == NULL); + } + + sec_state = get_interrupt_src_ss(flags); + + if (is_event_shared(map)) + sdei_map_unlock(map); + + SDEI_LOG("ACK %lx, ev:%d ss:%d spsr:%lx ELR:%lx\n", read_mpidr_el1(), + map->ev_num, sec_state, read_spsr_el3(), + read_elr_el3()); + + ctx = handle; + + /* + * Check if we interrupted secure state. Perform a context switch so + * that we can delegate to NS. + */ + if (sec_state == SECURE) { + save_secure_context(); + ctx = restore_and_resume_ns_context(); + } + + setup_ns_dispatch(map, se, ctx, sec_state, intr_raw); + + /* + * End of interrupt is done in sdei_event_complete, when the client + * signals completion. + */ + return 0; +} + +/* Explicitly dispatch the given SDEI event */ +int sdei_dispatch_event(int ev_num, unsigned int preempted_sec_state) +{ + sdei_entry_t *se; + sdei_ev_map_t *map; + cpu_context_t *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; + } + + /* Can't dispatch if events are masked on this PE */ + state = sdei_get_this_pe_state(); + if (state->pe_masked == PE_MASKED) + return -1; + + /* Event 0 can't be dispatched */ + if (ev_num == SDEI_EVENT_0) + return -1; + + /* Locate mapping corresponding to this event */ + map = find_event_map(ev_num); + 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)) + return -1; + + /* Examine state of dispatch stack */ + disp_ctx = get_outstanding_dispatch(); + if (disp_ctx) { + /* + * There's an outstanding dispatch. If the outstanding dispatch + * is critical, no more dispatches are possible. + */ + if (is_event_critical(disp_ctx->map)) + return -1; + + /* + * If the outstanding dispatch is Normal, only critical events + * can be dispatched. + */ + if (is_event_normal(map)) + return -1; + } + + se = get_event_entry(map); + if (!can_sdei_state_trans(se, DO_DISPATCH)) + return -1; + + /* Activate the priority corresponding to the event being dispatched */ + ehf_activate_priority(sdei_event_priority(map)); + + /* + * We assume the current context is SECURE, and that it's already been + * saved. + */ + ctx = restore_and_resume_ns_context(); + + /* + * The caller has effectively terminated execution. Record to resume the + * preempted context later when the event completes or + * complete-and-resumes. + */ + setup_ns_dispatch(map, se, ctx, preempted_sec_state, 0); + + return 0; +} + +int sdei_event_complete(int resume, uint64_t pc) +{ + sdei_dispatch_context_t *disp_ctx; + sdei_entry_t *se; + sdei_ev_map_t *map; + cpu_context_t *ctx; + sdei_action_t act; + unsigned int client_el = sdei_client_el(); + + /* Return error if called without an active event */ + disp_ctx = pop_dispatch(); + if (!disp_ctx) + return SDEI_EDENY; + + /* Validate resumption point */ + if (resume && (plat_sdei_validate_entry_point(pc, client_el) != 0)) + return SDEI_EDENY; + + map = disp_ctx->map; + assert(map); + + se = get_event_entry(map); + + SDEI_LOG("EOI:%lx, %d spsr:%lx elr:%lx\n", read_mpidr_el1(), + map->ev_num, read_spsr_el3(), read_elr_el3()); + + if (is_event_shared(map)) + sdei_map_lock(map); + + act = resume ? DO_COMPLETE_RESUME : DO_COMPLETE; + if (!can_sdei_state_trans(se, act)) { + if (is_event_shared(map)) + sdei_map_unlock(map); + return SDEI_EDENY; + } + + /* + * Restore Non-secure to how it was originally interrupted. Once done, + * it's up-to-date with the saved copy. + */ + ctx = cm_get_context(NON_SECURE); + restore_event_ctx(disp_ctx, ctx); + + if (resume) { + /* + * Complete-and-resume call. Prepare the Non-secure context + * (currently active) for complete and resume. + */ + cm_set_elr_spsr_el3(NON_SECURE, pc, SPSR_64(client_el, + MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS)); + + /* + * Make it look as if a synchronous exception were taken at the + * supplied Non-secure resumption point. Populate SPSR and + * ELR_ELx so that an ERET from there works as expected. + * + * The assumption is that the client, if necessary, would have + * saved any live content in these registers before making this + * call. + */ + if (client_el == MODE_EL2) { + write_elr_el2(disp_ctx->elr_el3); + write_spsr_el2(disp_ctx->spsr_el3); + } else { + /* EL1 */ + write_elr_el1(disp_ctx->elr_el3); + write_spsr_el1(disp_ctx->spsr_el3); + } + } + + /* + * 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); + + return 0; +} + +int sdei_event_context(void *handle, unsigned int param) +{ + sdei_dispatch_context_t *disp_ctx; + + if (param >= SDEI_SAVED_GPREGS) + return SDEI_EINVAL; + + /* Get outstanding dispatch on this CPU */ + disp_ctx = get_outstanding_dispatch(); + if (!disp_ctx) + return SDEI_EDENY; + + assert(disp_ctx->map); + + if (!can_sdei_state_trans(get_event_entry(disp_ctx->map), DO_CONTEXT)) + return SDEI_EDENY; + + /* + * No locking is required for the Running status as this is the only CPU + * which can complete the event + */ + + return disp_ctx->x[param]; +} diff --git a/services/std_svc/sdei/sdei_main.c b/services/std_svc/sdei/sdei_main.c new file mode 100644 index 00000000..2f08c8ba --- /dev/null +++ b/services/std_svc/sdei/sdei_main.c @@ -0,0 +1,1066 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> +#include <assert.h> +#include <bl31.h> +#include <bl_common.h> +#include <cassert.h> +#include <context.h> +#include <context_mgmt.h> +#include <debug.h> +#include <ehf.h> +#include <interrupt_mgmt.h> +#include <platform.h> +#include <pubsub.h> +#include <runtime_svc.h> +#include <sdei.h> +#include <stddef.h> +#include <string.h> +#include <utils.h> +#include "sdei_private.h" + +#define MAJOR_VERSION 1 +#define MINOR_VERSION 0 +#define VENDOR_VERSION 0 + +#define MAKE_SDEI_VERSION(_major, _minor, _vendor) \ + ((((unsigned long long)(_major)) << 48) | \ + (((unsigned long long)(_minor)) << 32) | \ + (_vendor)) + +#define LOWEST_INTR_PRIORITY 0xff + +#define is_valid_affinity(_mpidr) (plat_core_pos_by_mpidr(_mpidr) >= 0) + +CASSERT(PLAT_SDEI_CRITICAL_PRI < PLAT_SDEI_NORMAL_PRI, + sdei_critical_must_have_higher_priority); + +static unsigned int num_dyn_priv_slots, num_dyn_shrd_slots; + +/* Initialise SDEI map entries */ +static void init_map(sdei_ev_map_t *map) +{ + map->reg_count = 0; +} + +/* Convert mapping to SDEI class */ +sdei_class_t map_to_class(sdei_ev_map_t *map) +{ + return is_event_critical(map) ? SDEI_CRITICAL : SDEI_NORMAL; +} + +/* Clear SDEI event entries except state */ +static void clear_event_entries(sdei_entry_t *se) +{ + se->ep = 0; + se->arg = 0; + se->affinity = 0; + se->reg_flags = 0; +} + +/* Perform CPU-specific state initialisation */ +static void *sdei_cpu_on_init(const void *arg) +{ + int i; + sdei_ev_map_t *map; + sdei_entry_t *se; + + /* Initialize private mappings on this CPU */ + for_each_private_map(i, map) { + se = get_event_entry(map); + clear_event_entries(se); + se->state = 0; + } + + SDEI_LOG("Private events initialized on %lx\n", read_mpidr_el1()); + + /* All PEs start with SDEI events masked */ + sdei_pe_mask(); + + return 0; +} + +/* Initialise an SDEI class */ +void sdei_class_init(sdei_class_t class) +{ + unsigned int i, zero_found __unused = 0; + int ev_num_so_far __unused; + sdei_ev_map_t *map; + + /* Sanity check and configuration of shared events */ + ev_num_so_far = -1; + for_each_shared_map(i, map) { +#if ENABLE_ASSERTIONS + /* Ensure mappings are sorted */ + assert((ev_num_so_far < 0) || (map->ev_num > ev_num_so_far)); + + ev_num_so_far = map->ev_num; + + /* Event 0 must not be shared */ + assert(map->ev_num != SDEI_EVENT_0); + + /* Check for valid event */ + assert(map->ev_num >= 0); + + /* Make sure it's a shared event */ + assert(is_event_shared(map)); + + /* No shared mapping should have signalable property */ + assert(!is_event_signalable(map)); +#endif + + /* Skip initializing the wrong priority */ + if (map_to_class(map) != class) + continue; + + /* Platform events are always bound, so set the bound flag */ + if (is_map_dynamic(map)) { + assert(map->intr == SDEI_DYN_IRQ); + assert(is_event_normal(map)); + num_dyn_shrd_slots++; + } else { + /* Shared mappings must be bound to shared interrupt */ + assert(plat_ic_is_spi(map->intr)); + set_map_bound(map); + } + + init_map(map); + } + + /* Sanity check and configuration of private events for this CPU */ + ev_num_so_far = -1; + for_each_private_map(i, map) { +#if ENABLE_ASSERTIONS + /* Ensure mappings are sorted */ + assert((ev_num_so_far < 0) || (map->ev_num > ev_num_so_far)); + + ev_num_so_far = map->ev_num; + + if (map->ev_num == SDEI_EVENT_0) { + zero_found = 1; + + /* Event 0 must be a Secure SGI */ + assert(is_secure_sgi(map->intr)); + + /* + * Event 0 can have only have signalable flag (apart + * from being private + */ + assert(map->map_flags == (SDEI_MAPF_SIGNALABLE | + SDEI_MAPF_PRIVATE)); + } else { + /* No other mapping should have signalable property */ + assert(!is_event_signalable(map)); + } + + /* Check for valid event */ + assert(map->ev_num >= 0); + + /* Make sure it's a private event */ + assert(is_event_private(map)); +#endif + + /* Skip initializing the wrong priority */ + if (map_to_class(map) != class) + continue; + + /* Platform events are always bound, so set the bound flag */ + if (map->ev_num != SDEI_EVENT_0) { + if (is_map_dynamic(map)) { + assert(map->intr == SDEI_DYN_IRQ); + assert(is_event_normal(map)); + num_dyn_priv_slots++; + } else { + /* + * Private mappings must be bound to private + * interrupt. + */ + assert(plat_ic_is_ppi(map->intr)); + set_map_bound(map); + } + } + + init_map(map); + } + + /* Ensure event 0 is in the mapping */ + assert(zero_found); + + sdei_cpu_on_init(NULL); +} + +/* SDEI dispatcher initialisation */ +void sdei_init(void) +{ + sdei_class_init(SDEI_CRITICAL); + sdei_class_init(SDEI_NORMAL); + + /* Register priority level handlers */ + ehf_register_priority_handler(PLAT_SDEI_CRITICAL_PRI, + sdei_intr_handler); + ehf_register_priority_handler(PLAT_SDEI_NORMAL_PRI, + sdei_intr_handler); +} + +/* Populate SDEI event entry */ +static void set_sdei_entry(sdei_entry_t *se, uint64_t ep, uint64_t arg, + unsigned int flags, uint64_t affinity) +{ + assert(se != NULL); + + se->ep = ep; + se->arg = arg; + se->affinity = (affinity & MPIDR_AFFINITY_MASK); + se->reg_flags = flags; +} + +static unsigned long long sdei_version(void) +{ + return MAKE_SDEI_VERSION(MAJOR_VERSION, MINOR_VERSION, VENDOR_VERSION); +} + +/* Validate flags and MPIDR values for REGISTER and ROUTING_SET calls */ +static int validate_flags(uint64_t flags, uint64_t mpidr) +{ + /* Validate flags */ + switch (flags) { + case SDEI_REGF_RM_PE: + if (!is_valid_affinity(mpidr)) + return SDEI_EINVAL; + break; + case SDEI_REGF_RM_ANY: + break; + default: + /* Unknown flags */ + return SDEI_EINVAL; + } + + return 0; +} + +/* Set routing of an SDEI event */ +static int sdei_event_routing_set(int ev_num, uint64_t flags, uint64_t mpidr) +{ + int ret, routing; + sdei_ev_map_t *map; + sdei_entry_t *se; + + ret = validate_flags(flags, mpidr); + if (ret) + return ret; + + /* Check if valid event number */ + map = find_event_map(ev_num); + if (!map) + return SDEI_EINVAL; + + /* The event must not be private */ + if (is_event_private(map)) + return SDEI_EINVAL; + + se = get_event_entry(map); + + sdei_map_lock(map); + + if (!is_map_bound(map) || is_event_private(map)) { + ret = SDEI_EINVAL; + goto finish; + } + + if (!can_sdei_state_trans(se, DO_ROUTING)) { + ret = SDEI_EDENY; + goto finish; + } + + /* Choose appropriate routing */ + routing = (flags == SDEI_REGF_RM_ANY) ? INTR_ROUTING_MODE_ANY : + INTR_ROUTING_MODE_PE; + + /* Update event registration flag */ + se->reg_flags = flags; + + /* + * ROUTING_SET is permissible only when event composite state is + * 'registered, disabled, and not running'. This means that the + * interrupt is currently disabled, and not active. + */ + plat_ic_set_spi_routing(map->intr, routing, (u_register_t) mpidr); + +finish: + sdei_map_unlock(map); + + return ret; +} + +/* Register handler and argument for an SDEI event */ +static int sdei_event_register(int ev_num, uint64_t ep, uint64_t arg, + uint64_t flags, uint64_t mpidr) +{ + int ret; + sdei_entry_t *se; + sdei_ev_map_t *map; + sdei_state_t backup_state; + + if (!ep || (plat_sdei_validate_entry_point(ep, sdei_client_el()) != 0)) + return SDEI_EINVAL; + + ret = validate_flags(flags, mpidr); + if (ret) + return ret; + + /* Check if valid event number */ + map = find_event_map(ev_num); + if (!map) + return SDEI_EINVAL; + + /* Private events always target the PE */ + if (is_event_private(map)) + flags = SDEI_REGF_RM_PE; + + se = get_event_entry(map); + + /* + * Even though register operation is per-event (additionally for private + * events, registration is required individually), it has to be + * serialised with respect to bind/release, which are global operations. + * So we hold the lock throughout, unconditionally. + */ + sdei_map_lock(map); + + backup_state = se->state; + if (!can_sdei_state_trans(se, DO_REGISTER)) + goto fallback; + + /* + * When registering for dynamic events, make sure it's been bound + * already. This has to be the case as, without binding, the client + * can't know about the event number to register for. + */ + if (is_map_dynamic(map) && !is_map_bound(map)) + goto fallback; + + if (is_event_private(map)) { + /* Multiple calls to register are possible for private events */ + assert(map->reg_count >= 0); + } else { + /* Only single call to register is possible for shared events */ + assert(map->reg_count == 0); + } + + if (is_map_bound(map)) { + /* Meanwhile, did any PE ACK the interrupt? */ + if (plat_ic_get_interrupt_active(map->intr)) + goto fallback; + + /* The interrupt must currently owned by Non-secure */ + if (plat_ic_get_interrupt_type(map->intr) != INTR_TYPE_NS) + goto fallback; + + /* + * Disable forwarding of new interrupt triggers to CPU + * interface. + */ + plat_ic_disable_interrupt(map->intr); + + /* + * Any events that are triggered after register and before + * enable should remain pending. Clear any previous interrupt + * triggers which are pending (except for SGIs). This has no + * affect on level-triggered interrupts. + */ + if (ev_num != SDEI_EVENT_0) + plat_ic_clear_interrupt_pending(map->intr); + + /* Map interrupt to EL3 and program the correct priority */ + plat_ic_set_interrupt_type(map->intr, INTR_TYPE_EL3); + + /* Program the appropriate interrupt priority */ + plat_ic_set_interrupt_priority(map->intr, sdei_event_priority(map)); + + /* + * Set the routing mode for shared event as requested. We + * already ensure that shared events get bound to SPIs. + */ + if (is_event_shared(map)) { + plat_ic_set_spi_routing(map->intr, + ((flags == SDEI_REGF_RM_ANY) ? + INTR_ROUTING_MODE_ANY : + INTR_ROUTING_MODE_PE), + (u_register_t) mpidr); + } + } + + /* Populate event entries */ + set_sdei_entry(se, ep, arg, flags, mpidr); + + /* Increment register count */ + map->reg_count++; + + sdei_map_unlock(map); + + return 0; + +fallback: + /* Reinstate previous state */ + se->state = backup_state; + + sdei_map_unlock(map); + + return SDEI_EDENY; +} + +/* Enable SDEI event */ +static int sdei_event_enable(int ev_num) +{ + sdei_ev_map_t *map; + sdei_entry_t *se; + int ret, before, after; + + /* Check if valid event number */ + map = find_event_map(ev_num); + if (!map) + return SDEI_EINVAL; + + se = get_event_entry(map); + ret = SDEI_EDENY; + + if (is_event_shared(map)) + sdei_map_lock(map); + + before = GET_EV_STATE(se, ENABLED); + if (!can_sdei_state_trans(se, DO_ENABLE)) + goto finish; + after = GET_EV_STATE(se, ENABLED); + + /* + * Enable interrupt for bound events only if there's a change in enabled + * state. + */ + if (is_map_bound(map) && (!before && after)) + plat_ic_enable_interrupt(map->intr); + + ret = 0; + +finish: + if (is_event_shared(map)) + sdei_map_unlock(map); + + return ret; +} + +/* Disable SDEI event */ +static int sdei_event_disable(int ev_num) +{ + sdei_ev_map_t *map; + sdei_entry_t *se; + int ret, before, after; + + /* Check if valid event number */ + map = find_event_map(ev_num); + if (!map) + return SDEI_EINVAL; + + se = get_event_entry(map); + ret = SDEI_EDENY; + + if (is_event_shared(map)) + sdei_map_lock(map); + + before = GET_EV_STATE(se, ENABLED); + if (!can_sdei_state_trans(se, DO_DISABLE)) + goto finish; + after = GET_EV_STATE(se, ENABLED); + + /* + * Disable interrupt for bound events only if there's a change in + * enabled state. + */ + if (is_map_bound(map) && (before && !after)) + plat_ic_disable_interrupt(map->intr); + + ret = 0; + +finish: + if (is_event_shared(map)) + sdei_map_unlock(map); + + return ret; +} + +/* Query SDEI event information */ +static uint64_t sdei_event_get_info(int ev_num, int info) +{ + sdei_entry_t *se; + sdei_ev_map_t *map; + + unsigned int flags, registered; + uint64_t affinity; + + /* Check if valid event number */ + map = find_event_map(ev_num); + if (!map) + return SDEI_EINVAL; + + se = get_event_entry(map); + + if (is_event_shared(map)) + sdei_map_lock(map); + + /* Sample state under lock */ + registered = GET_EV_STATE(se, REGISTERED); + flags = se->reg_flags; + affinity = se->affinity; + + if (is_event_shared(map)) + sdei_map_unlock(map); + + switch (info) { + case SDEI_INFO_EV_TYPE: + return is_event_shared(map); + + case SDEI_INFO_EV_NOT_SIGNALED: + return !is_event_signalable(map); + + case SDEI_INFO_EV_PRIORITY: + return is_event_critical(map); + + case SDEI_INFO_EV_ROUTING_MODE: + if (!is_event_shared(map)) + return SDEI_EINVAL; + if (!registered) + return SDEI_EDENY; + return (flags == SDEI_REGF_RM_PE); + + case SDEI_INFO_EV_ROUTING_AFF: + if (!is_event_shared(map)) + return SDEI_EINVAL; + if (!registered) + return SDEI_EDENY; + if (flags != SDEI_REGF_RM_PE) + return SDEI_EINVAL; + return affinity; + + default: + return SDEI_EINVAL; + } +} + +/* Unregister an SDEI event */ +static int sdei_event_unregister(int ev_num) +{ + int ret = 0; + sdei_entry_t *se; + sdei_ev_map_t *map; + + /* Check if valid event number */ + map = find_event_map(ev_num); + if (!map) + return SDEI_EINVAL; + + se = get_event_entry(map); + + /* + * Even though unregister operation is per-event (additionally for + * private events, unregistration is required individually), it has to + * be serialised with respect to bind/release, which are global + * operations. So we hold the lock throughout, unconditionally. + */ + sdei_map_lock(map); + + if (!can_sdei_state_trans(se, DO_UNREGISTER)) { + /* + * Even if the call is invalid, and the handler is running (for + * example, having unregistered from a running handler earlier), + * return pending error code; otherwise, return deny. + */ + ret = GET_EV_STATE(se, RUNNING) ? SDEI_EPEND : SDEI_EDENY; + + goto finish; + } + + map->reg_count--; + if (is_event_private(map)) { + /* Multiple calls to register are possible for private events */ + assert(map->reg_count >= 0); + } else { + /* Only single call to register is possible for shared events */ + assert(map->reg_count == 0); + } + + if (is_map_bound(map)) { + plat_ic_disable_interrupt(map->intr); + + /* + * Clear pending interrupt. Skip for SGIs as they may not be + * cleared on interrupt controllers. + */ + if (ev_num != SDEI_EVENT_0) + plat_ic_clear_interrupt_pending(map->intr); + + assert(plat_ic_get_interrupt_type(map->intr) == INTR_TYPE_EL3); + plat_ic_set_interrupt_type(map->intr, INTR_TYPE_NS); + plat_ic_set_interrupt_priority(map->intr, LOWEST_INTR_PRIORITY); + } + + clear_event_entries(se); + + /* + * If the handler is running at the time of unregister, return the + * pending error code. + */ + if (GET_EV_STATE(se, RUNNING)) + ret = SDEI_EPEND; + +finish: + sdei_map_unlock(map); + + return ret; +} + +/* Query status of an SDEI event */ +static int sdei_event_status(int ev_num) +{ + sdei_ev_map_t *map; + sdei_entry_t *se; + sdei_state_t state; + + /* Check if valid event number */ + map = find_event_map(ev_num); + if (!map) + return SDEI_EINVAL; + + se = get_event_entry(map); + + if (is_event_shared(map)) + sdei_map_lock(map); + + /* State value directly maps to the expected return format */ + state = se->state; + + if (is_event_shared(map)) + sdei_map_unlock(map); + + return state; +} + +/* Bind an SDEI event to an interrupt */ +static int sdei_interrupt_bind(int intr_num) +{ + sdei_ev_map_t *map; + int retry = 1, shared_mapping; + + /* SGIs are not allowed to be bound */ + if (plat_ic_is_sgi(intr_num)) + return SDEI_EINVAL; + + shared_mapping = plat_ic_is_spi(intr_num); + do { + /* + * Bail out if there is already an event for this interrupt, + * either platform-defined or dynamic. + */ + map = find_event_map_by_intr(intr_num, shared_mapping); + if (map) { + if (is_map_dynamic(map)) { + if (is_map_bound(map)) { + /* + * Dynamic event, already bound. Return + * event number. + */ + return map->ev_num; + } + } else { + /* Binding non-dynamic event */ + return SDEI_EINVAL; + } + } + + /* + * The interrupt is not bound yet. Try to find a free slot to + * bind it. Free dynamic mappings have their interrupt set as + * SDEI_DYN_IRQ. + */ + map = find_event_map_by_intr(SDEI_DYN_IRQ, shared_mapping); + if (!map) + return SDEI_ENOMEM; + + /* The returned mapping must be dynamic */ + assert(is_map_dynamic(map)); + + /* + * We cannot assert for bound maps here, as we might be racing + * with another bind. + */ + + /* The requested interrupt must already belong to NS */ + if (plat_ic_get_interrupt_type(intr_num) != INTR_TYPE_NS) + return SDEI_EDENY; + + /* + * Interrupt programming and ownership transfer are deferred + * until register. + */ + + sdei_map_lock(map); + if (!is_map_bound(map)) { + map->intr = intr_num; + set_map_bound(map); + retry = 0; + } + sdei_map_unlock(map); + } while (retry); + + return map->ev_num; +} + +/* Release a bound SDEI event previously to an interrupt */ +static int sdei_interrupt_release(int ev_num) +{ + int ret = 0; + sdei_ev_map_t *map; + sdei_entry_t *se; + + /* Check if valid event number */ + map = find_event_map(ev_num); + if (!map) + return SDEI_EINVAL; + + if (!is_map_dynamic(map)) + return SDEI_EINVAL; + + se = get_event_entry(map); + + sdei_map_lock(map); + + /* Event must have been unregistered before release */ + if (map->reg_count != 0) { + ret = SDEI_EDENY; + goto finish; + } + + /* + * Interrupt release never causes the state to change. We only check + * whether it's permissible or not. + */ + if (!can_sdei_state_trans(se, DO_RELEASE)) { + ret = SDEI_EDENY; + goto finish; + } + + if (is_map_bound(map)) { + /* + * Deny release if the interrupt is active, which means it's + * probably being acknowledged and handled elsewhere. + */ + if (plat_ic_get_interrupt_active(map->intr)) { + ret = SDEI_EDENY; + goto finish; + } + + /* + * Interrupt programming and ownership transfer are already done + * during unregister. + */ + + map->intr = SDEI_DYN_IRQ; + clr_map_bound(map); + } else { + SDEI_LOG("Error release bound:%d cnt:%d\n", is_map_bound(map), + map->reg_count); + ret = SDEI_EINVAL; + } + +finish: + sdei_map_unlock(map); + + return ret; +} + +/* Perform reset of private SDEI events */ +static int sdei_private_reset(void) +{ + sdei_ev_map_t *map; + int ret = 0, final_ret = 0, i; + + /* Unregister all private events */ + for_each_private_map(i, map) { + /* + * The unregister can fail if the event is not registered, which + * is allowed, and a deny will be returned. But if the event is + * running or unregister pending, the call fails. + */ + ret = sdei_event_unregister(map->ev_num); + if ((ret == SDEI_EPEND) && (final_ret == 0)) + final_ret = ret; + } + + return final_ret; +} + +/* Perform reset of shared SDEI events */ +static int sdei_shared_reset(void) +{ + const sdei_mapping_t *mapping; + sdei_ev_map_t *map; + int ret = 0, final_ret = 0, i, j; + + /* Unregister all shared events */ + for_each_shared_map(i, map) { + /* + * The unregister can fail if the event is not registered, which + * is allowed, and a deny will be returned. But if the event is + * running or unregister pending, the call fails. + */ + ret = sdei_event_unregister(map->ev_num); + if ((ret == SDEI_EPEND) && (final_ret == 0)) + final_ret = ret; + } + + if (final_ret != 0) + return final_ret; + + /* + * Loop through both private and shared mappings, and release all + * bindings. + */ + for_each_mapping_type(i, mapping) { + iterate_mapping(mapping, j, map) { + /* + * Release bindings for mappings that are dynamic and + * bound. + */ + if (is_map_dynamic(map) && is_map_bound(map)) { + /* + * Any failure to release would mean there is at + * least a PE registered for the event. + */ + ret = sdei_interrupt_release(map->ev_num); + if ((ret != 0) && (final_ret == 0)) + final_ret = ret; + } + } + } + + return final_ret; +} + +/* Send a signal to another SDEI client PE */ +int sdei_signal(int event, uint64_t target_pe) +{ + sdei_ev_map_t *map; + + /* Only event 0 can be signalled */ + if (event != SDEI_EVENT_0) + return SDEI_EINVAL; + + /* Find mapping for event 0 */ + map = find_event_map(SDEI_EVENT_0); + if (!map) + return SDEI_EINVAL; + + /* The event must be signalable */ + if (!is_event_signalable(map)) + return SDEI_EINVAL; + + /* Validate target */ + if (plat_core_pos_by_mpidr(target_pe) < 0) + return SDEI_EINVAL; + + /* Raise SGI. Platform will validate target_pe */ + plat_ic_raise_el3_sgi(map->intr, (u_register_t) target_pe); + + return 0; +} + +/* Query SDEI dispatcher features */ +uint64_t sdei_features(unsigned int feature) +{ + if (feature == SDEI_FEATURE_BIND_SLOTS) { + return FEATURE_BIND_SLOTS(num_dyn_priv_slots, + num_dyn_shrd_slots); + } + + return SDEI_EINVAL; +} + +/* SDEI top level handler for servicing SMCs */ +uint64_t sdei_smc_handler(uint32_t smc_fid, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + + uint64_t x5; + int ss = get_interrupt_src_ss(flags); + int64_t ret; + unsigned int resume = 0; + + if (ss != NON_SECURE) + SMC_RET1(handle, SMC_UNK); + + /* Verify the caller EL */ + if (GET_EL(read_spsr_el3()) != sdei_client_el()) + SMC_RET1(handle, SMC_UNK); + + switch (smc_fid) { + case SDEI_VERSION: + SDEI_LOG("> VER\n"); + ret = sdei_version(); + SDEI_LOG("< VER:%lx\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_EVENT_REGISTER: + x5 = SMC_GET_GP(handle, CTX_GPREG_X5); + SDEI_LOG("> REG(n:%d e:%lx a:%lx f:%x m:%lx)\n", (int) x1, + x2, x3, (int) x4, x5); + ret = sdei_event_register(x1, x2, x3, x4, x5); + SDEI_LOG("< REG:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_EVENT_ENABLE: + SDEI_LOG("> ENABLE(n:%d)\n", (int) x1); + ret = sdei_event_enable(x1); + SDEI_LOG("< ENABLE:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_EVENT_DISABLE: + SDEI_LOG("> DISABLE(n:%d)\n", (int) x1); + ret = sdei_event_disable(x1); + SDEI_LOG("< DISABLE:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_EVENT_CONTEXT: + SDEI_LOG("> CTX(p:%d):%lx\n", (int) x1, read_mpidr_el1()); + ret = sdei_event_context(handle, x1); + SDEI_LOG("< CTX:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_EVENT_COMPLETE_AND_RESUME: + resume = 1; + /* Fall through */ + + case SDEI_EVENT_COMPLETE: + SDEI_LOG("> COMPLETE(r:%d sta/ep:%lx):%lx\n", resume, x1, + read_mpidr_el1()); + ret = sdei_event_complete(resume, x1); + SDEI_LOG("< COMPLETE:%lx\n", ret); + + /* + * Set error code only if the call failed. If the call + * succeeded, we discard the dispatched context, and restore the + * interrupted context to a pristine condition, and therefore + * shouldn't be modified. We don't return to the caller in this + * case anyway. + */ + if (ret) + SMC_RET1(handle, ret); + + SMC_RET0(handle); + break; + + case SDEI_EVENT_STATUS: + SDEI_LOG("> STAT(n:%d)\n", (int) x1); + ret = sdei_event_status(x1); + SDEI_LOG("< STAT:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_EVENT_GET_INFO: + SDEI_LOG("> INFO(n:%d, %d)\n", (int) x1, (int) x2); + ret = sdei_event_get_info(x1, x2); + SDEI_LOG("< INFO:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_EVENT_UNREGISTER: + SDEI_LOG("> UNREG(n:%d)\n", (int) x1); + ret = sdei_event_unregister(x1); + SDEI_LOG("< UNREG:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_PE_UNMASK: + SDEI_LOG("> UNMASK:%lx\n", read_mpidr_el1()); + sdei_pe_unmask(); + SDEI_LOG("< UNMASK:%d\n", 0); + SMC_RET1(handle, 0); + break; + + case SDEI_PE_MASK: + SDEI_LOG("> MASK:%lx\n", read_mpidr_el1()); + ret = sdei_pe_mask(); + SDEI_LOG("< MASK:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_INTERRUPT_BIND: + SDEI_LOG("> BIND(%d)\n", (int) x1); + ret = sdei_interrupt_bind(x1); + SDEI_LOG("< BIND:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_INTERRUPT_RELEASE: + SDEI_LOG("> REL(%d)\n", (int) x1); + ret = sdei_interrupt_release(x1); + SDEI_LOG("< REL:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_SHARED_RESET: + SDEI_LOG("> S_RESET():%lx\n", read_mpidr_el1()); + ret = sdei_shared_reset(); + SDEI_LOG("< S_RESET:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_PRIVATE_RESET: + SDEI_LOG("> P_RESET():%lx\n", read_mpidr_el1()); + ret = sdei_private_reset(); + SDEI_LOG("< P_RESET:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_EVENT_ROUTING_SET: + SDEI_LOG("> ROUTE_SET(n:%d f:%lx aff:%lx)\n", (int) x1, x2, x3); + ret = sdei_event_routing_set(x1, x2, x3); + SDEI_LOG("< ROUTE_SET:%ld\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_FEATURES: + SDEI_LOG("> FTRS(f:%lx)\n", x1); + ret = sdei_features(x1); + SDEI_LOG("< FTRS:%lx\n", ret); + SMC_RET1(handle, ret); + break; + + case SDEI_EVENT_SIGNAL: + SDEI_LOG("> SIGNAL(e:%lx t:%lx)\n", x1, x2); + ret = sdei_signal(x1, x2); + SDEI_LOG("< SIGNAL:%ld\n", ret); + SMC_RET1(handle, ret); + break; + default: + break; + } + + WARN("Unimplemented SDEI Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); +} + +/* Subscribe to PSCI CPU on to initialize per-CPU SDEI configuration */ +SUBSCRIBE_TO_EVENT(psci_cpu_on_finish, sdei_cpu_on_init); diff --git a/services/std_svc/sdei/sdei_private.h b/services/std_svc/sdei/sdei_private.h new file mode 100644 index 00000000..44db4193 --- /dev/null +++ b/services/std_svc/sdei/sdei_private.h @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __SDEI_PRIVATE_H__ +#define __SDEI_PRIVATE_H__ + +#include <arch_helpers.h> +#include <debug.h> +#include <errno.h> +#include <interrupt_mgmt.h> +#include <platform.h> +#include <sdei.h> +#include <spinlock.h> +#include <stdbool.h> +#include <types.h> +#include <utils_def.h> + +#ifdef AARCH32 +# error SDEI is implemented only for AArch64 systems +#endif + +#ifndef PLAT_SDEI_CRITICAL_PRI +# error Platform must define SDEI critical priority value +#endif + +#ifndef PLAT_SDEI_NORMAL_PRI +# error Platform must define SDEI normal priority value +#endif + +/* Output SDEI logs as verbose */ +#define SDEI_LOG(...) VERBOSE("SDEI: " __VA_ARGS__) + +/* SDEI handler unregistered state. This is the default state. */ +#define SDEI_STATE_UNREGISTERED 0 + +/* SDE event status values in bit position */ +#define SDEI_STATF_REGISTERED 0 +#define SDEI_STATF_ENABLED 1 +#define SDEI_STATF_RUNNING 2 + +/* SDEI SMC error codes */ +#define SDEI_EINVAL (-2) +#define SDEI_EDENY (-3) +#define SDEI_EPEND (-5) +#define SDEI_ENOMEM (-10) + +/* + * 'info' parameter to SDEI_EVENT_GET_INFO SMC. + * + * Note that the SDEI v1.0 speification mistakenly enumerates the + * SDEI_INFO_EV_SIGNALED as SDEI_INFO_SIGNALED. This will be corrected in a + * future version. + */ +#define SDEI_INFO_EV_TYPE 0 +#define SDEI_INFO_EV_NOT_SIGNALED 1 +#define SDEI_INFO_EV_PRIORITY 2 +#define SDEI_INFO_EV_ROUTING_MODE 3 +#define SDEI_INFO_EV_ROUTING_AFF 4 + +#define SDEI_PRIVATE_MAPPING() (&sdei_global_mappings[_SDEI_MAP_IDX_PRIV]) +#define SDEI_SHARED_MAPPING() (&sdei_global_mappings[_SDEI_MAP_IDX_SHRD]) + +#define for_each_mapping_type(_i, _mapping) \ + for (_i = 0, _mapping = &sdei_global_mappings[i]; \ + _i < _SDEI_MAP_IDX_MAX; \ + _i++, _mapping = &sdei_global_mappings[i]) + +#define iterate_mapping(_mapping, _i, _map) \ + for (_map = (_mapping)->map, _i = 0; \ + _i < (_mapping)->num_maps; \ + _i++, _map++) + +#define for_each_private_map(_i, _map) \ + iterate_mapping(SDEI_PRIVATE_MAPPING(), _i, _map) + +#define for_each_shared_map(_i, _map) \ + iterate_mapping(SDEI_SHARED_MAPPING(), _i, _map) + +/* SDEI_FEATURES */ +#define SDEI_FEATURE_BIND_SLOTS 0 +#define BIND_SLOTS_MASK 0xffff +#define FEATURES_SHARED_SLOTS_SHIFT 16 +#define FEATURES_PRIVATE_SLOTS_SHIFT 0 +#define FEATURE_BIND_SLOTS(_priv, _shrd) \ + ((((_priv) & BIND_SLOTS_MASK) << FEATURES_PRIVATE_SLOTS_SHIFT) | \ + (((_shrd) & BIND_SLOTS_MASK) << FEATURES_SHARED_SLOTS_SHIFT)) + +#define GET_EV_STATE(_e, _s) get_ev_state_bit(_e, SDEI_STATF_##_s) +#define SET_EV_STATE(_e, _s) clr_ev_state_bit(_e->state, SDEI_STATF_##_s) + +static inline int is_event_private(sdei_ev_map_t *map) +{ + return ((map->map_flags & BIT(_SDEI_MAPF_PRIVATE_SHIFT)) != 0); +} + +static inline int is_event_shared(sdei_ev_map_t *map) +{ + return !is_event_private(map); +} + +static inline int is_event_critical(sdei_ev_map_t *map) +{ + return ((map->map_flags & BIT(_SDEI_MAPF_CRITICAL_SHIFT)) != 0); +} + +static inline int is_event_normal(sdei_ev_map_t *map) +{ + return !is_event_critical(map); +} + +static inline int is_event_signalable(sdei_ev_map_t *map) +{ + return ((map->map_flags & BIT(_SDEI_MAPF_SIGNALABLE_SHIFT)) != 0); +} + +static inline int is_map_dynamic(sdei_ev_map_t *map) +{ + return ((map->map_flags & BIT(_SDEI_MAPF_DYNAMIC_SHIFT)) != 0); +} + +/* + * Checks whether an event is associated with an interrupt. Static events always + * return true, and dynamic events return whether SDEI_INTERRUPT_BIND had been + * called on them. This can be used on both static or dynamic events to check + * for an associated interrupt. + */ +static inline int is_map_bound(sdei_ev_map_t *map) +{ + return ((map->map_flags & BIT(_SDEI_MAPF_BOUND_SHIFT)) != 0); +} + +static inline void set_map_bound(sdei_ev_map_t *map) +{ + map->map_flags |= BIT(_SDEI_MAPF_BOUND_SHIFT); +} + +static inline void clr_map_bound(sdei_ev_map_t *map) +{ + map->map_flags &= ~(BIT(_SDEI_MAPF_BOUND_SHIFT)); +} + +static inline int is_secure_sgi(unsigned int intr) +{ + return (plat_ic_is_sgi(intr) && + (plat_ic_get_interrupt_type(intr) == INTR_TYPE_EL3)); +} + +/* + * Determine EL of the client. If EL2 is implemented (hence the enabled HCE + * bit), deem EL2; otherwise, deem EL1. + */ +static inline unsigned int sdei_client_el(void) +{ + return read_scr_el3() & SCR_HCE_BIT ? MODE_EL2 : MODE_EL1; +} + +static inline unsigned int sdei_event_priority(sdei_ev_map_t *map) +{ + return is_event_critical(map) ? PLAT_SDEI_CRITICAL_PRI : + PLAT_SDEI_NORMAL_PRI; +} + +static inline int get_ev_state_bit(sdei_entry_t *se, unsigned int bit_no) +{ + return ((se->state & BIT(bit_no)) != 0); +} + +static inline void clr_ev_state_bit(sdei_entry_t *se, unsigned int bit_no) +{ + se->state &= ~BIT(bit_no); +} + +/* SDEI actions for state transition */ +typedef enum { + /* + * Actions resulting from client requests. These directly map to SMC + * calls. Note that the state table columns are listed in this order + * too. + */ + DO_REGISTER = 0, + DO_RELEASE = 1, + DO_ENABLE = 2, + DO_DISABLE = 3, + DO_UNREGISTER = 4, + DO_ROUTING = 5, + DO_CONTEXT = 6, + DO_COMPLETE = 7, + DO_COMPLETE_RESUME = 8, + + /* Action for event dispatch */ + DO_DISPATCH = 9, + + DO_MAX, +} sdei_action_t; + +typedef enum { + SDEI_NORMAL, + SDEI_CRITICAL +} sdei_class_t; + +static inline void sdei_map_lock(sdei_ev_map_t *map) +{ + spin_lock(&map->lock); +} + +static inline void sdei_map_unlock(sdei_ev_map_t *map) +{ + spin_unlock(&map->lock); +} + +extern const sdei_mapping_t sdei_global_mappings[]; +extern sdei_entry_t sdei_private_event_table[]; +extern sdei_entry_t sdei_shared_event_table[]; + +void init_sdei_state(void); + +sdei_ev_map_t *find_event_map_by_intr(int intr_num, int shared); +sdei_ev_map_t *find_event_map(int ev_num); +sdei_entry_t *get_event_entry(sdei_ev_map_t *map); + +int sdei_event_context(void *handle, unsigned int param); +int sdei_event_complete(int resume, uint64_t arg); + +void sdei_pe_unmask(void); +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); + +#endif /* __SDEI_PRIVATE_H__ */ diff --git a/services/std_svc/sdei/sdei_state.c b/services/std_svc/sdei/sdei_state.c new file mode 100644 index 00000000..3f60dfd8 --- /dev/null +++ b/services/std_svc/sdei/sdei_state.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <cassert.h> +#include <stdbool.h> +#include "sdei_private.h" + +/* Aliases for SDEI handler states: 'R'unning, 'E'nabled, and re'G'istered */ +#define r_ 0 +#define R_ (1u << SDEI_STATF_RUNNING) + +#define e_ 0 +#define E_ (1u << SDEI_STATF_ENABLED) + +#define g_ 0 +#define G_ (1u << SDEI_STATF_REGISTERED) + +/* All possible composite handler states */ +#define reg_ (r_ | e_ | g_) +#define reG_ (r_ | e_ | G_) +#define rEg_ (r_ | E_ | g_) +#define rEG_ (r_ | E_ | G_) +#define Reg_ (R_ | e_ | g_) +#define ReG_ (R_ | e_ | G_) +#define REg_ (R_ | E_ | g_) +#define REG_ (R_ | E_ | G_) + +#define MAX_STATES (REG_ + 1) + +/* Invalid state */ +#define SDEI_STATE_INVALID ((sdei_state_t) (-1)) + +/* No change in state */ +#define SDEI_STATE_NOP ((sdei_state_t) (-2)) + +#define X___ SDEI_STATE_INVALID +#define NOP_ SDEI_STATE_NOP + +/* Ensure special states don't overlap with valid ones */ +CASSERT(X___ > REG_, sdei_state_overlap_invalid); +CASSERT(NOP_ > REG_, sdei_state_overlap_nop); + +/* + * SDEI handler state machine: refer to sections 6.1 and 6.1.2 of the SDEI v1.0 + * specification: + * + * http://infocenter.arm.com/help/topic/com.arm.doc.den0054a/ARM_DEN0054A_Software_Delegated_Exception_Interface.pdf + * + * Not all calls contribute to handler state transition. This table is also used + * to validate whether a call is permissible at a given handler state: + * + * - X___ denotes a forbidden transition; + * - NOP_ denotes a permitted transition, but there's no change in state; + * - Otherwise, XXX_ gives the new state. + * + * DISP[atch] is a transition added for the implementation, but is not mentioned + * in the spec. + * + * Those calls that the spec mentions as can be made any time don't picture in + * this table. + */ + +static const sdei_state_t sdei_state_table[MAX_STATES][DO_MAX] = { +/* + * Action: REG REL ENA DISA UREG ROUT CTX COMP COMPR DISP + * Notes: [3] [1] [3] [3][4] [2] + */ + /* Handler unregistered, disabled, and not running. This is the default state. */ +/* 0 */ [reg_] = { reG_, NOP_, X___, X___, X___, X___, X___, X___, X___, X___, }, + + /* Handler unregistered and running */ +/* 4 */ [Reg_] = { X___, X___, X___, X___, X___, X___, NOP_, reg_, reg_, X___, }, + + /* Handler registered */ +/* 1 */ [reG_] = { X___, X___, rEG_, NOP_, reg_, NOP_, X___, X___, X___, X___, }, + + /* Handler registered and running */ +/* 5 */ [ReG_] = { X___, X___, REG_, NOP_, Reg_, X___, NOP_, reG_, reG_, X___, }, + + /* Handler registered and enabled */ +/* 3 */ [rEG_] = { X___, X___, NOP_, reG_, reg_, X___, X___, X___, X___, REG_, }, + + /* Handler registered, enabled, and running */ +/* 7 */ [REG_] = { X___, X___, NOP_, ReG_, Reg_, X___, NOP_, rEG_, rEG_, X___, }, + + /* + * Invalid states: no valid transition would leave the handler in these + * states; and no transition from these states is possible either. + */ + + /* + * Handler can't be enabled without being registered. I.e., XEg is + * impossible. + */ +/* 2 */ [rEg_] = { X___, X___, X___, X___, X___, X___, X___, X___, X___, X___, }, +/* 6 */ [REg_] = { X___, X___, X___, X___, X___, X___, X___, X___, X___, X___, }, +}; + +/* + * [1] Unregister will always also disable the event, so the new state will have + * Xeg. + * [2] Event is considered for dispatch only when it's both registered and + * enabled. + * [3] Never causes change in state. + * [4] Only allowed when running. + */ + +/* + * Given an action, transition the state of an event by looking up the state + * table above: + * + * - Return false for invalid transition; + * - Return true for valid transition that causes no change in state; + * - Otherwise, update state and return true. + * + * This function assumes that the caller holds necessary locks. If the + * transition has constrains other than the state table describes, the caller is + * expected to restore the previous state. See sdei_event_register() for + * example. + */ +bool can_sdei_state_trans(sdei_entry_t *se, sdei_action_t act) +{ + sdei_state_t next; + + assert(act < DO_MAX); + if (se->state >= MAX_STATES) { + WARN(" event state invalid: %x\n", se->state); + return false; + } + + next = sdei_state_table[se->state][act]; + switch (next) { + case SDEI_STATE_INVALID: + return false; + + case SDEI_STATE_NOP: + return true; + + default: + /* Valid transition. Update state. */ + SDEI_LOG(" event state 0x%x => 0x%x\n", se->state, next); + se->state = next; + + return true; + } +} diff --git a/services/std_svc/spm/aarch64/spm_helpers.S b/services/std_svc/spm/aarch64/spm_helpers.S new file mode 100644 index 00000000..aa35811f --- /dev/null +++ b/services/std_svc/spm/aarch64/spm_helpers.S @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <asm_macros.S> +#include "../spm_private.h" + + .global spm_secure_partition_enter + .global spm_secure_partition_exit + + /* --------------------------------------------------------------------- + * This function is called with SP_EL0 as stack. Here we stash our EL3 + * callee-saved registers on to the stack as a part of saving the C + * runtime and enter the secure payload. + * 'x0' contains a pointer to the memory where the address of the C + * runtime context is to be saved. + * --------------------------------------------------------------------- + */ +func spm_secure_partition_enter + /* Make space for the registers that we're going to save */ + mov x3, sp + str x3, [x0, #0] + sub sp, sp, #SP_C_RT_CTX_SIZE + + /* Save callee-saved registers on to the stack */ + stp x19, x20, [sp, #SP_C_RT_CTX_X19] + stp x21, x22, [sp, #SP_C_RT_CTX_X21] + stp x23, x24, [sp, #SP_C_RT_CTX_X23] + stp x25, x26, [sp, #SP_C_RT_CTX_X25] + stp x27, x28, [sp, #SP_C_RT_CTX_X27] + stp x29, x30, [sp, #SP_C_RT_CTX_X29] + + /* --------------------------------------------------------------------- + * Everything is setup now. el3_exit() will use the secure context to + * restore to the general purpose and EL3 system registers to ERET + * into the secure payload. + * --------------------------------------------------------------------- + */ + b el3_exit +endfunc spm_secure_partition_enter + + /* --------------------------------------------------------------------- + * This function is called with 'x0' pointing to a C runtime context + * saved in spm_secure_partition_enter(). + * It restores the saved registers and jumps to that runtime with 'x0' + * as the new SP register. This destroys the C runtime context that had + * been built on the stack below the saved context by the caller. Later + * the second parameter 'x1' is passed as a return value to the caller. + * --------------------------------------------------------------------- + */ +func spm_secure_partition_exit + /* Restore the previous stack */ + mov sp, x0 + + /* Restore callee-saved registers on to the stack */ + ldp x19, x20, [x0, #(SP_C_RT_CTX_X19 - SP_C_RT_CTX_SIZE)] + ldp x21, x22, [x0, #(SP_C_RT_CTX_X21 - SP_C_RT_CTX_SIZE)] + ldp x23, x24, [x0, #(SP_C_RT_CTX_X23 - SP_C_RT_CTX_SIZE)] + ldp x25, x26, [x0, #(SP_C_RT_CTX_X25 - SP_C_RT_CTX_SIZE)] + ldp x27, x28, [x0, #(SP_C_RT_CTX_X27 - SP_C_RT_CTX_SIZE)] + ldp x29, x30, [x0, #(SP_C_RT_CTX_X29 - SP_C_RT_CTX_SIZE)] + + /* --------------------------------------------------------------------- + * This should take us back to the instruction after the call to the + * last spm_secure_partition_enter().* Place the second parameter to x0 + * so that the caller will see it as a return value from the original + * entry call. + * --------------------------------------------------------------------- + */ + mov x0, x1 + ret +endfunc spm_secure_partition_exit diff --git a/services/std_svc/spm/aarch64/spm_shim_exceptions.S b/services/std_svc/spm/aarch64/spm_shim_exceptions.S new file mode 100644 index 00000000..218245d8 --- /dev/null +++ b/services/std_svc/spm/aarch64/spm_shim_exceptions.S @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <bl_common.h> +#include <context.h> + +/* ----------------------------------------------------------------------------- + * Very simple stackless exception handlers used by the spm shim layer. + * ----------------------------------------------------------------------------- + */ + .globl spm_shim_exceptions_ptr + +vector_base spm_shim_exceptions_ptr, .spm_shim_exceptions + + /* ----------------------------------------------------- + * Current EL with SP0 : 0x0 - 0x200 + * ----------------------------------------------------- + */ +vector_entry SynchronousExceptionSP0, .spm_shim_exceptions + b . + check_vector_size SynchronousExceptionSP0 + +vector_entry IrqSP0, .spm_shim_exceptions + b . + check_vector_size IrqSP0 + +vector_entry FiqSP0, .spm_shim_exceptions + b . + check_vector_size FiqSP0 + +vector_entry SErrorSP0, .spm_shim_exceptions + b . + check_vector_size SErrorSP0 + + /* ----------------------------------------------------- + * Current EL with SPx: 0x200 - 0x400 + * ----------------------------------------------------- + */ +vector_entry SynchronousExceptionSPx, .spm_shim_exceptions + b . + check_vector_size SynchronousExceptionSPx + +vector_entry IrqSPx, .spm_shim_exceptions + b . + check_vector_size IrqSPx + +vector_entry FiqSPx, .spm_shim_exceptions + b . + check_vector_size FiqSPx + +vector_entry SErrorSPx, .spm_shim_exceptions + b . + check_vector_size SErrorSPx + + /* ----------------------------------------------------- + * Lower EL using AArch64 : 0x400 - 0x600. No exceptions + * are handled since secure_partition does not implement + * a lower EL + * ----------------------------------------------------- + */ +vector_entry SynchronousExceptionA64, .spm_shim_exceptions + msr tpidr_el1, x30 + mrs x30, esr_el1 + ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH + + cmp x30, #EC_AARCH64_SVC + b.eq do_smc + + cmp x30, #EC_AARCH32_SVC + b.eq do_smc + + cmp x30, #EC_AARCH64_SYS + b.eq handle_sys_trap + + /* Fail in all the other cases */ + b panic + + /* --------------------------------------------- + * Tell SPM that we are done initialising + * --------------------------------------------- + */ +do_smc: + mrs x30, tpidr_el1 + smc #0 + eret + + /* AArch64 system instructions trap are handled as a panic for now */ +handle_sys_trap: +panic: + b panic + check_vector_size SynchronousExceptionA64 + +vector_entry IrqA64, .spm_shim_exceptions + b . + check_vector_size IrqA64 + +vector_entry FiqA64, .spm_shim_exceptions + b . + check_vector_size FiqA64 + +vector_entry SErrorA64, .spm_shim_exceptions + b . + check_vector_size SErrorA64 + + /* ----------------------------------------------------- + * Lower EL using AArch32 : 0x600 - 0x800 + * ----------------------------------------------------- + */ +vector_entry SynchronousExceptionA32, .spm_shim_exceptions + b . + check_vector_size SynchronousExceptionA32 + +vector_entry IrqA32, .spm_shim_exceptions + b . + check_vector_size IrqA32 + +vector_entry FiqA32, .spm_shim_exceptions + b . + check_vector_size FiqA32 + +vector_entry SErrorA32, .spm_shim_exceptions + b . + check_vector_size SErrorA32 diff --git a/services/std_svc/spm/secure_partition_setup.c b/services/std_svc/spm/secure_partition_setup.c new file mode 100644 index 00000000..67301608 --- /dev/null +++ b/services/std_svc/spm/secure_partition_setup.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <arch_helpers.h> +#include <arm_spm_def.h> +#include <assert.h> +#include <common_def.h> +#include <context.h> +#include <context_mgmt.h> +#include <debug.h> +#include <platform_def.h> +#include <platform.h> +#include <secure_partition.h> +#include <string.h> +#include <types.h> +#include <xlat_tables_v2.h> + +#include "spm_private.h" +#include "spm_shim_private.h" + +/* Allocate and initialise the translation context for the secure partition. */ +REGISTER_XLAT_CONTEXT2(secure_partition, + PLAT_SP_IMAGE_MMAP_REGIONS, + PLAT_SP_IMAGE_MAX_XLAT_TABLES, + PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE, + EL1_EL0_REGIME); + +/* Export a handle on the secure partition translation context */ +xlat_ctx_t *secure_partition_xlat_ctx_handle = &secure_partition_xlat_ctx; + +/* Setup context of the Secure Partition */ +void secure_partition_setup(void) +{ + VERBOSE("S-EL1/S-EL0 context setup start...\n"); + + cpu_context_t *ctx = cm_get_context(SECURE); + + /* Make sure that we got a Secure context. */ + assert(ctx != NULL); + + /* Assert we are in Secure state. */ + assert((read_scr_el3() & SCR_NS_BIT) == 0); + + /* Disable MMU at EL1. */ + disable_mmu_icache_el1(); + + /* Invalidate TLBs at EL1. */ + tlbivmalle1(); + + /* + * General-Purpose registers + * ------------------------- + */ + + /* + * X0: Virtual address of a buffer shared between EL3 and Secure EL0. + * The buffer will be mapped in the Secure EL1 translation regime + * with Normal IS WBWA attributes and RO data and Execute Never + * instruction access permissions. + * + * X1: Size of the buffer in bytes + * + * X2: cookie value (Implementation Defined) + * + * X3: cookie value (Implementation Defined) + * + * X4 to X30 = 0 (already done by cm_init_my_context()) + */ + write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X0, PLAT_SPM_BUF_BASE); + write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X1, PLAT_SPM_BUF_SIZE); + write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X2, PLAT_SPM_COOKIE_0); + write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X3, PLAT_SPM_COOKIE_1); + + /* + * SP_EL0: A non-zero value will indicate to the SP that the SPM has + * initialized the stack pointer for the current CPU through + * implementation defined means. The value will be 0 otherwise. + */ + write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_SP_EL0, + PLAT_SP_IMAGE_STACK_BASE + PLAT_SP_IMAGE_STACK_PCPU_SIZE); + + /* + * Setup translation tables + * ------------------------ + */ + +#if ENABLE_ASSERTIONS + + /* Get max granularity supported by the platform. */ + + u_register_t id_aa64prf0_el1 = read_id_aa64pfr0_el1(); + + int tgran64_supported = + ((id_aa64prf0_el1 >> ID_AA64MMFR0_EL1_TGRAN64_SHIFT) & + ID_AA64MMFR0_EL1_TGRAN64_MASK) == + ID_AA64MMFR0_EL1_TGRAN64_SUPPORTED; + + int tgran16_supported = + ((id_aa64prf0_el1 >> ID_AA64MMFR0_EL1_TGRAN16_SHIFT) & + ID_AA64MMFR0_EL1_TGRAN16_MASK) == + ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED; + + int tgran4_supported = + ((id_aa64prf0_el1 >> ID_AA64MMFR0_EL1_TGRAN4_SHIFT) & + ID_AA64MMFR0_EL1_TGRAN4_MASK) == + ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED; + + uintptr_t max_granule_size; + + if (tgran64_supported) { + max_granule_size = 64 * 1024; + } else if (tgran16_supported) { + max_granule_size = 16 * 1024; + } else { + assert(tgran4_supported); + max_granule_size = 4 * 1024; + } + + VERBOSE("Max translation granule supported: %lu KiB\n", + max_granule_size); + + uintptr_t max_granule_size_mask = max_granule_size - 1; + + /* Base must be aligned to the max granularity */ + assert((ARM_SP_IMAGE_NS_BUF_BASE & max_granule_size_mask) == 0); + + /* Size must be a multiple of the max granularity */ + assert((ARM_SP_IMAGE_NS_BUF_SIZE & max_granule_size_mask) == 0); + +#endif /* ENABLE_ASSERTIONS */ + + /* This region contains the exception vectors used at S-EL1. */ + const mmap_region_t sel1_exception_vectors = + MAP_REGION_FLAT(SPM_SHIM_EXCEPTIONS_START, + SPM_SHIM_EXCEPTIONS_SIZE, + MT_CODE | MT_SECURE | MT_PRIVILEGED); + mmap_add_region_ctx(&secure_partition_xlat_ctx, + &sel1_exception_vectors); + + mmap_add_ctx(&secure_partition_xlat_ctx, + plat_get_secure_partition_mmap(NULL)); + + init_xlat_tables_ctx(&secure_partition_xlat_ctx); + + /* + * MMU-related registers + * --------------------- + */ + + /* Set attributes in the right indices of the MAIR */ + u_register_t mair_el1 = + MAIR_ATTR_SET(ATTR_DEVICE, ATTR_DEVICE_INDEX) | + MAIR_ATTR_SET(ATTR_IWBWA_OWBWA_NTR, ATTR_IWBWA_OWBWA_NTR_INDEX) | + MAIR_ATTR_SET(ATTR_NON_CACHEABLE, ATTR_NON_CACHEABLE_INDEX); + + write_ctx_reg(get_sysregs_ctx(ctx), CTX_MAIR_EL1, mair_el1); + + /* Setup TCR_EL1. */ + u_register_t tcr_ps_bits = tcr_physical_addr_size_bits(PLAT_PHY_ADDR_SPACE_SIZE); + + u_register_t tcr_el1 = + /* Size of region addressed by TTBR0_EL1 = 2^(64-T0SZ) bytes. */ + (64 - __builtin_ctzl(PLAT_VIRT_ADDR_SPACE_SIZE)) | + /* Inner and outer WBWA, shareable. */ + TCR_SH_INNER_SHAREABLE | TCR_RGN_OUTER_WBA | TCR_RGN_INNER_WBA | + /* Set the granularity to 4KB. */ + TCR_TG0_4K | + /* Limit Intermediate Physical Address Size. */ + tcr_ps_bits << TCR_EL1_IPS_SHIFT | + /* Disable translations using TBBR1_EL1. */ + TCR_EPD1_BIT + /* The remaining fields related to TBBR1_EL1 are left as zero. */ + ; + + tcr_el1 &= ~( + /* Enable translations using TBBR0_EL1 */ + TCR_EPD0_BIT + ); + + write_ctx_reg(get_sysregs_ctx(ctx), CTX_TCR_EL1, tcr_el1); + + /* Setup SCTLR_EL1 */ + u_register_t sctlr_el1 = read_ctx_reg(get_sysregs_ctx(ctx), CTX_SCTLR_EL1); + + sctlr_el1 |= + /*SCTLR_EL1_RES1 |*/ + /* Don't trap DC CVAU, DC CIVAC, DC CVAC, DC CVAP, or IC IVAU */ + SCTLR_UCI_BIT | + /* RW regions at xlat regime EL1&0 are forced to be XN. */ + SCTLR_WXN_BIT | + /* Don't trap to EL1 execution of WFI or WFE at EL0. */ + SCTLR_NTWI_BIT | SCTLR_NTWE_BIT | + /* Don't trap to EL1 accesses to CTR_EL0 from EL0. */ + SCTLR_UCT_BIT | + /* Don't trap to EL1 execution of DZ ZVA at EL0. */ + SCTLR_DZE_BIT | + /* Enable SP Alignment check for EL0 */ + SCTLR_SA0_BIT | + /* Allow cacheable data and instr. accesses to normal memory. */ + SCTLR_C_BIT | SCTLR_I_BIT | + /* Alignment fault checking enabled when at EL1 and EL0. */ + SCTLR_A_BIT | + /* Enable MMU. */ + SCTLR_M_BIT + ; + + sctlr_el1 &= ~( + /* Explicit data accesses at EL0 are little-endian. */ + SCTLR_E0E_BIT | + /* Accesses to DAIF from EL0 are trapped to EL1. */ + SCTLR_UMA_BIT + ); + + write_ctx_reg(get_sysregs_ctx(ctx), CTX_SCTLR_EL1, sctlr_el1); + + /* Point TTBR0_EL1 at the tables of the context created for the SP. */ + write_ctx_reg(get_sysregs_ctx(ctx), CTX_TTBR0_EL1, + (u_register_t)secure_partition_base_xlat_table); + + /* + * Setup other system registers + * ---------------------------- + */ + + /* Shim Exception Vector Base Address */ + write_ctx_reg(get_sysregs_ctx(ctx), CTX_VBAR_EL1, + SPM_SHIM_EXCEPTIONS_PTR); + + /* + * FPEN: Forbid the Secure Partition to access FP/SIMD registers. + * TTA: Enable access to trace registers. + * ZEN (v8.2): Trap SVE instructions and access to SVE registers. + */ + write_ctx_reg(get_sysregs_ctx(ctx), CTX_CPACR_EL1, + CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_ALL)); + + /* + * Prepare information in buffer shared between EL3 and S-EL0 + * ---------------------------------------------------------- + */ + + void *shared_buf_ptr = (void *) PLAT_SPM_BUF_BASE; + + /* Copy the boot information into the shared buffer with the SP. */ + assert((uintptr_t)shared_buf_ptr + sizeof(secure_partition_boot_info_t) + <= (PLAT_SPM_BUF_BASE + PLAT_SPM_BUF_SIZE)); + + assert(PLAT_SPM_BUF_BASE <= (UINTPTR_MAX - PLAT_SPM_BUF_SIZE + 1)); + + const secure_partition_boot_info_t *sp_boot_info = + plat_get_secure_partition_boot_info(NULL); + + assert(sp_boot_info != NULL); + + memcpy((void *) shared_buf_ptr, (const void *) sp_boot_info, + sizeof(secure_partition_boot_info_t)); + + /* Pointer to the MP information from the platform port. */ + secure_partition_mp_info_t *sp_mp_info = + ((secure_partition_boot_info_t *) shared_buf_ptr)->mp_info; + + assert(sp_mp_info != NULL); + + /* + * Point the shared buffer MP information pointer to where the info will + * be populated, just after the boot info. + */ + ((secure_partition_boot_info_t *) shared_buf_ptr)->mp_info = + (secure_partition_mp_info_t *) ((uintptr_t)shared_buf_ptr + + sizeof(secure_partition_boot_info_t)); + + /* + * Update the shared buffer pointer to where the MP information for the + * payload will be populated + */ + shared_buf_ptr = ((secure_partition_boot_info_t *) shared_buf_ptr)->mp_info; + + /* + * Copy the cpu information into the shared buffer area after the boot + * information. + */ + assert(sp_boot_info->num_cpus <= PLATFORM_CORE_COUNT); + + assert((uintptr_t)shared_buf_ptr + <= (PLAT_SPM_BUF_BASE + PLAT_SPM_BUF_SIZE - + (sp_boot_info->num_cpus * sizeof(*sp_mp_info)))); + + memcpy(shared_buf_ptr, (const void *) sp_mp_info, + sp_boot_info->num_cpus * sizeof(*sp_mp_info)); + + /* + * Calculate the linear indices of cores in boot information for the + * secure partition and flag the primary CPU + */ + sp_mp_info = (secure_partition_mp_info_t *) shared_buf_ptr; + + for (unsigned int index = 0; index < sp_boot_info->num_cpus; index++) { + u_register_t mpidr = sp_mp_info[index].mpidr; + + sp_mp_info[index].linear_id = plat_core_pos_by_mpidr(mpidr); + if (plat_my_core_pos() == sp_mp_info[index].linear_id) + sp_mp_info[index].flags |= MP_INFO_FLAG_PRIMARY_CPU; + } + + VERBOSE("S-EL1/S-EL0 context setup end.\n"); +} diff --git a/services/std_svc/spm/spm.mk b/services/std_svc/spm/spm.mk new file mode 100644 index 00000000..562eaee4 --- /dev/null +++ b/services/std_svc/spm/spm.mk @@ -0,0 +1,25 @@ +# +# Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +ifneq (${SPD},none) + $(error "Error: SPD and SPM are incompatible build options.") +endif +ifneq (${ARCH},aarch64) + $(error "Error: SPM is only supported on aarch64.") +endif + +# SPM sources + + +SPM_SOURCES := $(addprefix services/std_svc/spm/, \ + spm_main.c \ + ${ARCH}/spm_helpers.S \ + secure_partition_setup.c \ + ${ARCH}/spm_shim_exceptions.S) + + +# Let the top-level Makefile know that we intend to include a BL32 image +NEED_BL32 := yes diff --git a/services/std_svc/spm/spm_main.c b/services/std_svc/spm/spm_main.c new file mode 100644 index 00000000..1b40d81d --- /dev/null +++ b/services/std_svc/spm/spm_main.c @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> +#include <assert.h> +#include <bl31.h> +#include <context_mgmt.h> +#include <debug.h> +#include <errno.h> +#include <platform.h> +#include <runtime_svc.h> +#include <secure_partition.h> +#include <smcc.h> +#include <smcc_helpers.h> +#include <spinlock.h> +#include <spm_svc.h> +#include <utils.h> +#include <xlat_tables_v2.h> + +#include "spm_private.h" + +/* Lock used for SP_MEMORY_ATTRIBUTES_GET and SP_MEMORY_ATTRIBUTES_SET */ +static spinlock_t mem_attr_smc_lock; + +/******************************************************************************* + * Secure Partition context information. + ******************************************************************************/ +static secure_partition_context_t sp_ctx; +unsigned int sp_init_in_progress; + +/******************************************************************************* + * Replace the S-EL1 re-entry information with S-EL0 re-entry + * information + ******************************************************************************/ +void spm_setup_next_eret_into_sel0(cpu_context_t *secure_context) +{ + assert(secure_context == cm_get_context(SECURE)); + + cm_set_elr_spsr_el3(SECURE, read_elr_el1(), read_spsr_el1()); +} + +/******************************************************************************* + * This function takes an SP context pointer and: + * 1. Applies the S-EL1 system register context from sp_ctx->cpu_ctx. + * 2. Saves the current C runtime state (callee-saved registers) on the stack + * frame and saves a reference to this state. + * 3. Calls el3_exit() so that the EL3 system and general purpose registers + * from the sp_ctx->cpu_ctx are used to enter the secure payload image. + ******************************************************************************/ +static uint64_t spm_synchronous_sp_entry(secure_partition_context_t *sp_ctx_ptr) +{ + uint64_t rc; + + assert(sp_ctx_ptr != NULL); + assert(sp_ctx_ptr->c_rt_ctx == 0); + assert(cm_get_context(SECURE) == &sp_ctx_ptr->cpu_ctx); + + /* Apply the Secure EL1 system register context and switch to it */ + cm_el1_sysregs_context_restore(SECURE); + cm_set_next_eret_context(SECURE); + + VERBOSE("%s: We're about to enter the Secure partition...\n", __func__); + + rc = spm_secure_partition_enter(&sp_ctx_ptr->c_rt_ctx); +#if ENABLE_ASSERTIONS + sp_ctx_ptr->c_rt_ctx = 0; +#endif + + return rc; +} + + +/******************************************************************************* + * This function takes a Secure partition context pointer and: + * 1. Saves the S-EL1 system register context tp sp_ctx->cpu_ctx. + * 2. Restores the current C runtime state (callee saved registers) from the + * stack frame using the reference to this state saved in + * spm_secure_partition_enter(). + * 3. It does not need to save any general purpose or EL3 system register state + * as the generic smc entry routine should have saved those. + ******************************************************************************/ +static void __dead2 spm_synchronous_sp_exit( + secure_partition_context_t *sp_ctx_ptr, uint64_t ret) +{ + assert(sp_ctx_ptr != NULL); + /* Save the Secure EL1 system register context */ + assert(cm_get_context(SECURE) == &sp_ctx_ptr->cpu_ctx); + cm_el1_sysregs_context_save(SECURE); + + assert(sp_ctx_ptr->c_rt_ctx != 0); + spm_secure_partition_exit(sp_ctx_ptr->c_rt_ctx, ret); + + /* Should never reach here */ + assert(0); +} + +/******************************************************************************* + * This function passes control to the Secure Partition image (BL32) for the + * first time on the primary cpu after a cold boot. It assumes that a valid + * secure context has already been created by spm_setup() which can be directly + * used. This function performs a synchronous entry into the Secure payload. + * The SP passes control back to this routine through a SMC. + ******************************************************************************/ +int32_t spm_init(void) +{ + entry_point_info_t *secure_partition_ep_info; + uint64_t rc; + + VERBOSE("%s entry\n", __func__); + + /* + * Get information about the Secure Partition (BL32) image. Its + * absence is a critical failure. + */ + secure_partition_ep_info = bl31_plat_get_next_image_ep_info(SECURE); + assert(secure_partition_ep_info); + + /* + * Initialise the common context and then overlay the S-EL0 specific + * context on top of it. + */ + cm_init_my_context(secure_partition_ep_info); + secure_partition_setup(); + + /* + * Arrange for an entry into the secure payload. + */ + sp_init_in_progress = 1; + rc = spm_synchronous_sp_entry(&sp_ctx); + assert(rc == 0); + sp_init_in_progress = 0; + VERBOSE("SP_MEM_ATTRIBUTES_SET_AARCH64 availability has been revoked\n"); + + return rc; +} + +/******************************************************************************* + * Given a secure payload entrypoint info pointer, entry point PC & pointer to + * a context data structure, this function will initialize the SPM context and + * entry point info for the secure payload + ******************************************************************************/ +void spm_init_sp_ep_state(struct entry_point_info *sp_ep_info, + uint64_t pc, + secure_partition_context_t *sp_ctx_ptr) +{ + uint32_t ep_attr; + + assert(sp_ep_info); + assert(pc); + assert(sp_ctx_ptr); + + cm_set_context(&sp_ctx_ptr->cpu_ctx, SECURE); + + /* initialise an entrypoint to set up the CPU context */ + ep_attr = SECURE | EP_ST_ENABLE; + if (read_sctlr_el3() & SCTLR_EE_BIT) + ep_attr |= EP_EE_BIG; + SET_PARAM_HEAD(sp_ep_info, PARAM_EP, VERSION_1, ep_attr); + + sp_ep_info->pc = pc; + /* The SPM payload runs in S-EL0 */ + sp_ep_info->spsr = SPSR_64(MODE_EL0, + MODE_SP_EL0, + DISABLE_ALL_EXCEPTIONS); + + zeromem(&sp_ep_info->args, sizeof(sp_ep_info->args)); +} + +/******************************************************************************* + * Secure Partition Manager setup. The SPM finds out the SP entrypoint if not + * already known and initialises the context for entry into the SP for its + * initialisation. + ******************************************************************************/ +int32_t spm_setup(void) +{ + entry_point_info_t *secure_partition_ep_info; + + VERBOSE("%s entry\n", __func__); + + /* + * Get information about the Secure Partition (BL32) image. Its + * absence is a critical failure. + */ + secure_partition_ep_info = bl31_plat_get_next_image_ep_info(SECURE); + if (!secure_partition_ep_info) { + WARN("No SPM provided by BL2 boot loader, Booting device" + " without SPM initialization. SMCs destined for SPM" + " will return SMC_UNK\n"); + return 1; + } + + /* + * If there's no valid entry point for SP, we return a non-zero value + * signalling failure initializing the service. We bail out without + * registering any handlers + */ + if (!secure_partition_ep_info->pc) { + return 1; + } + + spm_init_sp_ep_state(secure_partition_ep_info, + secure_partition_ep_info->pc, + &sp_ctx); + + /* + * All SPM initialization done. Now register our init function with + * BL31 for deferred invocation + */ + bl31_register_bl32_init(&spm_init); + + VERBOSE("%s exit\n", __func__); + + return 0; +} + +/* + * Attributes are encoded using a different format in the SMC interface than in + * the Trusted Firmware, where the mmap_attr_t enum type is used. This function + * converts an attributes value from the SMC format to the mmap_attr_t format by + * setting MT_RW/MT_RO, MT_USER/MT_PRIVILEGED and MT_EXECUTE/MT_EXECUTE_NEVER. + * The other fields are left as 0 because they are ignored by the function + * change_mem_attributes(). + */ +static mmap_attr_t smc_attr_to_mmap_attr(unsigned int attributes) +{ + mmap_attr_t tf_attr = 0; + + unsigned int access = (attributes & SP_MEM_ATTR_ACCESS_MASK) + >> SP_MEM_ATTR_ACCESS_SHIFT; + + if (access == SP_MEM_ATTR_ACCESS_RW) { + tf_attr |= MT_RW | MT_USER; + } else if (access == SP_MEM_ATTR_ACCESS_RO) { + tf_attr |= MT_RO | MT_USER; + } else { + /* Other values are reserved. */ + assert(access == SP_MEM_ATTR_ACCESS_NOACCESS); + /* The only requirement is that there's no access from EL0 */ + tf_attr |= MT_RO | MT_PRIVILEGED; + } + + if ((attributes & SP_MEM_ATTR_NON_EXEC) == 0) { + tf_attr |= MT_EXECUTE; + } else { + tf_attr |= MT_EXECUTE_NEVER; + } + + return tf_attr; +} + +/* + * This function converts attributes from the Trusted Firmware format into the + * SMC interface format. + */ +static int smc_mmap_to_smc_attr(mmap_attr_t attr) +{ + int smc_attr = 0; + + int data_access; + + if ((attr & MT_USER) == 0) { + /* No access from EL0. */ + data_access = SP_MEM_ATTR_ACCESS_NOACCESS; + } else { + if ((attr & MT_RW) != 0) { + assert(MT_TYPE(attr) != MT_DEVICE); + data_access = SP_MEM_ATTR_ACCESS_RW; + } else { + data_access = SP_MEM_ATTR_ACCESS_RO; + } + } + + smc_attr |= (data_access & SP_MEM_ATTR_ACCESS_MASK) << SP_MEM_ATTR_ACCESS_SHIFT; + + if (attr & MT_EXECUTE_NEVER) { + smc_attr |= SP_MEM_ATTR_NON_EXEC; + } + + return smc_attr; +} + +static int spm_memory_attributes_get_smc_handler(uintptr_t base_va) +{ + spin_lock(&mem_attr_smc_lock); + + mmap_attr_t attributes; + int rc = get_mem_attributes(secure_partition_xlat_ctx_handle, + base_va, &attributes); + + spin_unlock(&mem_attr_smc_lock); + + /* Convert error codes of get_mem_attributes() into SPM ones. */ + assert(rc == 0 || rc == -EINVAL); + + if (rc == 0) { + return smc_mmap_to_smc_attr(attributes); + } else { + return SPM_INVALID_PARAMETER; + } +} + +static int spm_memory_attributes_set_smc_handler(u_register_t page_address, + u_register_t pages_count, + u_register_t smc_attributes) +{ + uintptr_t base_va = (uintptr_t) page_address; + size_t size = (size_t) (pages_count * PAGE_SIZE); + unsigned int attributes = (unsigned int) smc_attributes; + + INFO(" Start address : 0x%lx\n", base_va); + INFO(" Number of pages: %i (%zi bytes)\n", (int) pages_count, size); + INFO(" Attributes : 0x%x\n", attributes); + + spin_lock(&mem_attr_smc_lock); + + int ret = change_mem_attributes(secure_partition_xlat_ctx_handle, + base_va, size, smc_attr_to_mmap_attr(attributes)); + + spin_unlock(&mem_attr_smc_lock); + + /* Convert error codes of change_mem_attributes() into SPM ones. */ + assert(ret == 0 || ret == -EINVAL); + + return (ret == 0) ? SPM_SUCCESS : SPM_INVALID_PARAMETER; +} + + +uint64_t spm_smc_handler(uint32_t smc_fid, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + cpu_context_t *ns_cpu_context; + unsigned int ns; + + /* Determine which security state this SMC originated from */ + ns = is_caller_non_secure(flags); + + if (ns == SMC_FROM_SECURE) { + + /* Handle SMCs from Secure world. */ + + switch (smc_fid) { + + case SPM_VERSION_AARCH32: + SMC_RET1(handle, SPM_VERSION_COMPILED); + + case SP_EVENT_COMPLETE_AARCH64: + assert(handle == cm_get_context(SECURE)); + cm_el1_sysregs_context_save(SECURE); + spm_setup_next_eret_into_sel0(handle); + + if (sp_init_in_progress) { + /* + * SPM reports completion. The SPM must have + * initiated the original request through a + * synchronous entry into the secure + * partition. Jump back to the original C + * runtime context. + */ + spm_synchronous_sp_exit(&sp_ctx, x1); + assert(0); + } + + /* + * This is the result from the Secure partition of an + * earlier request. Copy the result into the non-secure + * context, save the secure state and return to the + * non-secure state. + */ + + /* Get a reference to the non-secure context */ + ns_cpu_context = cm_get_context(NON_SECURE); + assert(ns_cpu_context); + + /* Restore non-secure state */ + cm_el1_sysregs_context_restore(NON_SECURE); + cm_set_next_eret_context(NON_SECURE); + + /* Return to normal world */ + SMC_RET1(ns_cpu_context, x1); + + case SP_MEM_ATTRIBUTES_GET_AARCH64: + INFO("Received SP_MEM_ATTRIBUTES_GET_AARCH64 SMC\n"); + + if (!sp_init_in_progress) { + WARN("SP_MEM_ATTRIBUTES_GET_AARCH64 is available at boot time only\n"); + SMC_RET1(handle, SPM_NOT_SUPPORTED); + } + SMC_RET1(handle, spm_memory_attributes_get_smc_handler(x1)); + + case SP_MEM_ATTRIBUTES_SET_AARCH64: + INFO("Received SP_MEM_ATTRIBUTES_SET_AARCH64 SMC\n"); + + if (!sp_init_in_progress) { + WARN("SP_MEM_ATTRIBUTES_SET_AARCH64 is available at boot time only\n"); + SMC_RET1(handle, SPM_NOT_SUPPORTED); + } + SMC_RET1(handle, spm_memory_attributes_set_smc_handler(x1, x2, x3)); + default: + break; + } + } else { + + /* Handle SMCs from Non-secure world. */ + + switch (smc_fid) { + + case SP_VERSION_AARCH64: + case SP_VERSION_AARCH32: + SMC_RET1(handle, SP_VERSION_COMPILED); + + case SP_COMMUNICATE_AARCH32: + case SP_COMMUNICATE_AARCH64: + + /* Save the Normal world context */ + cm_el1_sysregs_context_save(NON_SECURE); + + /* + * Restore the secure world context and prepare for + * entry in S-EL0 + */ + assert(&sp_ctx.cpu_ctx == cm_get_context(SECURE)); + cm_el1_sysregs_context_restore(SECURE); + cm_set_next_eret_context(SECURE); + + if (x2 != 0) { + VERBOSE("SP_COMMUNICATE_AARCH32/64: X2 is not 0 as recommended."); + } + + SMC_RET4(&sp_ctx.cpu_ctx, + smc_fid, x2, x3, plat_my_core_pos()); + + case SP_MEM_ATTRIBUTES_GET_AARCH64: + case SP_MEM_ATTRIBUTES_SET_AARCH64: + /* SMC interfaces reserved for secure callers. */ + SMC_RET1(handle, SPM_NOT_SUPPORTED); + + default: + break; + } + } + + SMC_RET1(handle, SMC_UNK); +} diff --git a/services/std_svc/spm/spm_private.h b/services/std_svc/spm/spm_private.h new file mode 100644 index 00000000..16993e8c --- /dev/null +++ b/services/std_svc/spm/spm_private.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __SPM_PRIVATE_H__ +#define __SPM_PRIVATE_H__ + +#include <context.h> + +/******************************************************************************* + * Constants that allow assembler code to preserve callee-saved registers of the + * C runtime context while performing a security state switch. + ******************************************************************************/ +#define SP_C_RT_CTX_X19 0x0 +#define SP_C_RT_CTX_X20 0x8 +#define SP_C_RT_CTX_X21 0x10 +#define SP_C_RT_CTX_X22 0x18 +#define SP_C_RT_CTX_X23 0x20 +#define SP_C_RT_CTX_X24 0x28 +#define SP_C_RT_CTX_X25 0x30 +#define SP_C_RT_CTX_X26 0x38 +#define SP_C_RT_CTX_X27 0x40 +#define SP_C_RT_CTX_X28 0x48 +#define SP_C_RT_CTX_X29 0x50 +#define SP_C_RT_CTX_X30 0x58 + +#define SP_C_RT_CTX_SIZE 0x60 +#define SP_C_RT_CTX_ENTRIES (SP_C_RT_CTX_SIZE >> DWORD_SHIFT) + + +#ifndef __ASSEMBLY__ + +#include <stdint.h> +#include <xlat_tables_v2.h> + +/* Handle on the Secure partition translation context */ +extern xlat_ctx_t *secure_partition_xlat_ctx_handle; + +struct entry_point_info; + +typedef struct secure_partition_context { + uint64_t c_rt_ctx; + cpu_context_t cpu_ctx; +} secure_partition_context_t; + +uint64_t spm_secure_partition_enter(uint64_t *c_rt_ctx); +void __dead2 spm_secure_partition_exit(uint64_t c_rt_ctx, uint64_t ret); +void spm_init_sp_ep_state(struct entry_point_info *sp_ep_info, + uint64_t pc, + secure_partition_context_t *sp_ctx_ptr); +#endif /* __ASSEMBLY__ */ + +#endif /* __SPM_PRIVATE_H__ */ diff --git a/services/std_svc/spm/spm_shim_private.h b/services/std_svc/spm/spm_shim_private.h new file mode 100644 index 00000000..ad953cde --- /dev/null +++ b/services/std_svc/spm/spm_shim_private.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __SPM_SHIM_PRIVATE__ +#define __SPM_SHIM_PRIVATE__ + +#include <types.h> + +/* Assembly source */ +extern uintptr_t spm_shim_exceptions_ptr; + +/* Linker symbols */ +extern uintptr_t __SPM_SHIM_EXCEPTIONS_START__; +extern uintptr_t __SPM_SHIM_EXCEPTIONS_END__; + +/* Definitions */ +#define SPM_SHIM_EXCEPTIONS_PTR (uintptr_t)(&spm_shim_exceptions_ptr) + +#define SPM_SHIM_EXCEPTIONS_START \ + (uintptr_t)(&__SPM_SHIM_EXCEPTIONS_START__) +#define SPM_SHIM_EXCEPTIONS_END \ + (uintptr_t)(&__SPM_SHIM_EXCEPTIONS_END__) +#define SPM_SHIM_EXCEPTIONS_SIZE \ + (SPM_SHIM_EXCEPTIONS_END - SPM_SHIM_EXCEPTIONS_START) + +#endif /* __SPM_SHIM_PRIVATE__ */ diff --git a/services/std_svc/std_svc_setup.c b/services/std_svc/std_svc_setup.c index 8e690467..ffc34716 100644 --- a/services/std_svc/std_svc_setup.c +++ b/services/std_svc/std_svc_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,7 +11,9 @@ #include <psci.h> #include <runtime_instr.h> #include <runtime_svc.h> +#include <sdei.h> #include <smcc_helpers.h> +#include <spm_svc.h> #include <std_svc.h> #include <stdint.h> #include <uuid.h> @@ -25,15 +27,31 @@ DEFINE_SVC_UUID(arm_svc_uid, static int32_t std_svc_setup(void) { uintptr_t svc_arg; + int ret = 0; svc_arg = get_arm_std_svc_args(PSCI_FID_MASK); assert(svc_arg); /* - * PSCI is the only specification implemented as a Standard Service. + * PSCI is one of the specifications implemented as a Standard Service. * The `psci_setup()` also does EL3 architectural setup. */ - return psci_setup((const psci_lib_args_t *)svc_arg); + if (psci_setup((const psci_lib_args_t *)svc_arg) != PSCI_E_SUCCESS) { + ret = 1; + } + +#if ENABLE_SPM + if (spm_setup() != 0) { + ret = 1; + } +#endif + +#if SDEI_SUPPORT + /* SDEI initialisation */ + sdei_init(); +#endif + + return ret; } /* @@ -80,6 +98,24 @@ uintptr_t std_svc_smc_handler(uint32_t smc_fid, SMC_RET1(handle, ret); } +#if ENABLE_SPM + /* + * Dispatch SPM calls to SPM SMC handler and return its return + * value + */ + if (is_spm_fid(smc_fid)) { + return spm_smc_handler(smc_fid, x1, x2, x3, x4, cookie, + handle, flags); + } +#endif + +#if SDEI_SUPPORT + if (is_sdei_fid(smc_fid)) { + return sdei_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, + flags); + } +#endif + switch (smc_fid) { case ARM_STD_SVC_CALL_COUNT: /* diff --git a/tools/cert_create/include/cert.h b/tools/cert_create/include/cert.h index 256e7afd..9b4ef5af 100644 --- a/tools/cert_create/include/cert.h +++ b/tools/cert_create/include/cert.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -48,7 +48,13 @@ struct cert_s { int cert_init(void); cert_t *cert_get_by_opt(const char *opt); int cert_add_ext(X509 *issuer, X509 *subject, int nid, char *value); -int cert_new(int key_alg, cert_t *cert, int days, int ca, STACK_OF(X509_EXTENSION) * sk); +int cert_new( + int key_alg, + int md_alg, + cert_t *cert, + int days, + int ca, + STACK_OF(X509_EXTENSION) * sk); /* Macro to register the certificates used in the CoT */ #define REGISTER_COT(_certs) \ diff --git a/tools/cert_create/include/key.h b/tools/cert_create/include/key.h index 304fa615..1a253cc7 100644 --- a/tools/cert_create/include/key.h +++ b/tools/cert_create/include/key.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -30,6 +30,13 @@ enum { KEY_ALG_MAX_NUM }; +/* Supported hash algorithms */ +enum{ + HASH_ALG_SHA256, + HASH_ALG_SHA384, + HASH_ALG_SHA512, +}; + /* * This structure contains the relevant information to create the keys * required to sign the certificates. diff --git a/tools/cert_create/include/sha.h b/tools/cert_create/include/sha.h index 6907fa19..4d07a1e7 100644 --- a/tools/cert_create/include/sha.h +++ b/tools/cert_create/include/sha.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,6 @@ #ifndef SHA_H_ #define SHA_H_ -int sha_file(const char *filename, unsigned char *md); +int sha_file(int md_alg, const char *filename, unsigned char *md); #endif /* SHA_H_ */ diff --git a/tools/cert_create/src/cert.c b/tools/cert_create/src/cert.c index 3f0b4d36..8e8aee69 100644 --- a/tools/cert_create/src/cert.c +++ b/tools/cert_create/src/cert.c @@ -56,6 +56,19 @@ error: return ret; } +const EVP_MD *get_digest(int alg) +{ + switch (alg) { + case HASH_ALG_SHA256: + return EVP_sha256(); + case HASH_ALG_SHA384: + return EVP_sha384(); + case HASH_ALG_SHA512: + return EVP_sha512(); + default: + return NULL; + } +} int cert_add_ext(X509 *issuer, X509 *subject, int nid, char *value) { @@ -79,7 +92,13 @@ int cert_add_ext(X509 *issuer, X509 *subject, int nid, char *value) return 1; } -int cert_new(int key_alg, cert_t *cert, int days, int ca, STACK_OF(X509_EXTENSION) * sk) +int cert_new( + int key_alg, + int md_alg, + cert_t *cert, + int days, + int ca, + STACK_OF(X509_EXTENSION) * sk) { EVP_PKEY *pkey = keys[cert->key].key; cert_t *issuer_cert = &certs[cert->issuer]; @@ -118,7 +137,7 @@ int cert_new(int key_alg, cert_t *cert, int days, int ca, STACK_OF(X509_EXTENSIO } /* Sign the certificate with the issuer key */ - if (!EVP_DigestSignInit(mdCtx, &pKeyCtx, EVP_sha256(), NULL, ikey)) { + if (!EVP_DigestSignInit(mdCtx, &pKeyCtx, get_digest(md_alg), NULL, ikey)) { ERR_print_errors_fp(stdout); goto END; } @@ -138,7 +157,7 @@ int cert_new(int key_alg, cert_t *cert, int days, int ca, STACK_OF(X509_EXTENSIO goto END; } - if (!EVP_PKEY_CTX_set_rsa_mgf1_md(pKeyCtx, EVP_sha256())) { + if (!EVP_PKEY_CTX_set_rsa_mgf1_md(pKeyCtx, get_digest(md_alg))) { ERR_print_errors_fp(stdout); goto END; } diff --git a/tools/cert_create/src/main.c b/tools/cert_create/src/main.c index 741242f5..4abfe6dd 100644 --- a/tools/cert_create/src/main.c +++ b/tools/cert_create/src/main.c @@ -68,6 +68,7 @@ /* Global options */ static int key_alg; +static int hash_alg; static int new_keys; static int save_keys; static int print_cert; @@ -95,6 +96,12 @@ static const char *key_algs_str[] = { #endif /* OPENSSL_NO_EC */ }; +static const char *hash_algs_str[] = { + [HASH_ALG_SHA256] = "sha256", + [HASH_ALG_SHA384] = "sha384", + [HASH_ALG_SHA512] = "sha512", +}; + static void print_help(const char *cmd, const struct option *long_opt) { int rem, i = 0; @@ -150,6 +157,19 @@ static int get_key_alg(const char *key_alg_str) return -1; } +static int get_hash_alg(const char *hash_alg_str) +{ + int i; + + for (i = 0 ; i < NUM_ELEM(hash_algs_str) ; i++) { + if (0 == strcmp(hash_alg_str, hash_algs_str[i])) { + return i; + } + } + + return -1; +} + static void check_cmd_params(void) { cert_t *cert; @@ -228,6 +248,10 @@ static const cmd_opt_t common_cmd_opt[] = { PKCS#1 v2.1, 'rsa_1_5' - RSA PKCS#1 v1.5, 'ecdsa'" }, { + { "hash-alg", required_argument, NULL, 's' }, + "Hash algorithm : 'sha256' (default), 'sha384', 'sha512'" + }, + { { "save-keys", no_argument, NULL, 'k' }, "Save key pairs into files. Filenames must be provided" }, @@ -254,7 +278,8 @@ int main(int argc, char *argv[]) const struct option *cmd_opt; const char *cur_opt; unsigned int err_code; - unsigned char md[SHA256_DIGEST_LENGTH]; + unsigned char md[SHA512_DIGEST_LENGTH]; + unsigned int md_len; const EVP_MD *md_info; NOTICE("CoT Generation Tool: %s\n", build_msg); @@ -262,6 +287,7 @@ int main(int argc, char *argv[]) /* Set default options */ key_alg = KEY_ALG_RSA; + hash_alg = HASH_ALG_SHA256; /* Add common command line options */ for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) { @@ -291,7 +317,7 @@ int main(int argc, char *argv[]) while (1) { /* getopt_long stores the option index here. */ - c = getopt_long(argc, argv, "a:hknp", cmd_opt, &opt_idx); + c = getopt_long(argc, argv, "a:hknps:", cmd_opt, &opt_idx); /* Detect the end of the options. */ if (c == -1) { @@ -318,6 +344,13 @@ int main(int argc, char *argv[]) case 'p': print_cert = 1; break; + case 's': + hash_alg = get_hash_alg(optarg); + if (hash_alg < 0) { + ERROR("Invalid hash algorithm '%s'\n", optarg); + exit(1); + } + break; case CMD_OPT_EXT: cur_opt = cmd_opt_get_name(opt_idx); ext = ext_get_by_opt(cur_opt); @@ -343,9 +376,18 @@ int main(int argc, char *argv[]) /* Check command line arguments */ check_cmd_params(); - /* Indicate SHA256 as image hash algorithm in the certificate + /* Indicate SHA as image hash algorithm in the certificate * extension */ - md_info = EVP_sha256(); + if (hash_alg == HASH_ALG_SHA384) { + md_info = EVP_sha384(); + md_len = SHA384_DIGEST_LENGTH; + } else if (hash_alg == HASH_ALG_SHA512) { + md_info = EVP_sha512(); + md_len = SHA512_DIGEST_LENGTH; + } else { + md_info = EVP_sha256(); + md_len = SHA256_DIGEST_LENGTH; + } /* Load private keys from files (or generate new ones) */ for (i = 0 ; i < num_keys ; i++) { @@ -421,14 +463,14 @@ int main(int argc, char *argv[]) if (ext->arg == NULL) { if (ext->optional) { /* Include a hash filled with zeros */ - memset(md, 0x0, SHA256_DIGEST_LENGTH); + memset(md, 0x0, SHA512_DIGEST_LENGTH); } else { /* Do not include this hash in the certificate */ break; } } else { /* Calculate the hash of the file */ - if (!sha_file(ext->arg, md)) { + if (!sha_file(hash_alg, ext->arg, md)) { ERROR("Cannot calculate hash of %s\n", ext->arg); exit(1); @@ -436,7 +478,7 @@ int main(int argc, char *argv[]) } CHECK_NULL(cert_ext, ext_new_hash(ext_nid, EXT_CRIT, md_info, md, - SHA256_DIGEST_LENGTH)); + md_len)); break; case EXT_TYPE_PKEY: CHECK_NULL(cert_ext, ext_new_key(ext_nid, @@ -453,7 +495,7 @@ int main(int argc, char *argv[]) } /* Create certificate. Signed with corresponding key */ - if (cert->fn && !cert_new(key_alg, cert, VAL_DAYS, 0, sk)) { + if (cert->fn && !cert_new(key_alg, hash_alg, cert, VAL_DAYS, 0, sk)) { ERROR("Cannot create %s\n", cert->cn); exit(1); } diff --git a/tools/cert_create/src/sha.c b/tools/cert_create/src/sha.c index 29715930..3d977fbf 100644 --- a/tools/cert_create/src/sha.c +++ b/tools/cert_create/src/sha.c @@ -1,20 +1,21 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <openssl/sha.h> #include <stdio.h> - #include "debug.h" +#include "key.h" #define BUFFER_SIZE 256 -int sha_file(const char *filename, unsigned char *md) +int sha_file(int md_alg, const char *filename, unsigned char *md) { FILE *inFile; SHA256_CTX shaContext; + SHA512_CTX sha512Context; int bytes; unsigned char data[BUFFER_SIZE]; @@ -29,11 +30,25 @@ int sha_file(const char *filename, unsigned char *md) return 0; } - SHA256_Init(&shaContext); - while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) { - SHA256_Update(&shaContext, data, bytes); + if (md_alg == HASH_ALG_SHA384) { + SHA384_Init(&sha512Context); + while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) { + SHA384_Update(&sha512Context, data, bytes); + } + SHA384_Final(md, &sha512Context); + } else if (md_alg == HASH_ALG_SHA512) { + SHA512_Init(&sha512Context); + while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) { + SHA512_Update(&sha512Context, data, bytes); + } + SHA512_Final(md, &sha512Context); + } else { + SHA256_Init(&shaContext); + while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) { + SHA256_Update(&shaContext, data, bytes); + } + SHA256_Final(md, &shaContext); } - SHA256_Final(md, &shaContext); fclose(inFile); return 1; |