Merge pull request #101 from sandrine-bailleux:sb/tf-issue-81-v2
diff --git a/bl2/bl2_main.c b/bl2/bl2_main.c
index 33c63ee..81fecd6 100644
--- a/bl2/bl2_main.c
+++ b/bl2/bl2_main.c
@@ -134,32 +134,34 @@
bl2_plat_set_bl33_ep_info(bl2_to_bl31_params->bl33_image_info,
bl2_to_bl31_params->bl33_ep_info);
+
+#ifdef BL32_BASE
/*
* Load the BL32 image if there's one. It is upto to platform
* to specify where BL32 should be loaded if it exists. It
* could create space in the secure sram or point to a
- * completely different memory. A zero size indicates that the
- * platform does not want to load a BL32 image.
+ * completely different memory.
+ *
+ * If a platform does not want to attempt to load BL3-2 image
+ * it must leave BL32_BASE undefined
*/
bl2_plat_get_bl32_meminfo(&bl32_mem_info);
- if (bl32_mem_info.total_size) {
- e = load_image(&bl32_mem_info,
- BL32_IMAGE_NAME,
- bl32_mem_info.attr &
- LOAD_MASK,
- BL32_BASE,
- bl2_to_bl31_params->bl32_image_info,
- bl2_to_bl31_params->bl32_ep_info);
+ e = load_image(&bl32_mem_info,
+ BL32_IMAGE_NAME,
+ bl32_mem_info.attr & LOAD_MASK,
+ BL32_BASE,
+ bl2_to_bl31_params->bl32_image_info,
+ bl2_to_bl31_params->bl32_ep_info);
- /* Halt if failed to load normal world firmware. */
- if (e) {
- WARN("Failed to load BL3-2.\n");
- } else {
- bl2_plat_set_bl32_ep_info(
- bl2_to_bl31_params->bl32_image_info,
- bl2_to_bl31_params->bl32_ep_info);
- }
+ /* Issue a diagnostic if no Secure Payload could be loaded */
+ if (e) {
+ WARN("Failed to load BL3-2.\n");
+ } else {
+ bl2_plat_set_bl32_ep_info(
+ bl2_to_bl31_params->bl32_image_info,
+ bl2_to_bl31_params->bl32_ep_info);
}
+#endif /* BL32_BASE */
/*
* Run BL31 via an SMC to BL1. Information on how to pass control to
diff --git a/bl31/aarch64/bl31_entrypoint.S b/bl31/aarch64/bl31_entrypoint.S
index f582e76..3c9042b 100644
--- a/bl31/aarch64/bl31_entrypoint.S
+++ b/bl31/aarch64/bl31_entrypoint.S
@@ -31,8 +31,6 @@
#include <arch.h>
#include <asm_macros.S>
#include <bl_common.h>
-#include <cm_macros.S>
-
.globl bl31_entrypoint
diff --git a/bl31/aarch64/runtime_exceptions.S b/bl31/aarch64/runtime_exceptions.S
index b6dcccb..4789b33 100644
--- a/bl31/aarch64/runtime_exceptions.S
+++ b/bl31/aarch64/runtime_exceptions.S
@@ -30,14 +30,118 @@
#include <arch.h>
#include <asm_macros.S>
-#include <cm_macros.S>
#include <context.h>
+#include <interrupt_mgmt.h>
#include <platform.h>
#include <runtime_svc.h>
.globl runtime_exceptions
.globl el3_exit
+ /* -----------------------------------------------------
+ * Handle SMC exceptions seperately from other sync.
+ * exceptions.
+ * -----------------------------------------------------
+ */
+ .macro handle_sync_exception
+ str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
+ mrs x30, esr_el3
+ ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
+
+ cmp x30, #EC_AARCH32_SMC
+ b.eq smc_handler32
+
+ cmp x30, #EC_AARCH64_SMC
+ b.eq smc_handler64
+
+ /* -----------------------------------------------------
+ * The following code handles any synchronous exception
+ * that is not an SMC.
+ * -----------------------------------------------------
+ */
+
+ bl dump_state_and_die
+ .endm
+
+
+ /* -----------------------------------------------------
+ * This macro handles FIQ or IRQ interrupts i.e. EL3,
+ * S-EL1 and NS interrupts.
+ * -----------------------------------------------------
+ */
+ .macro handle_interrupt_exception label
+ str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
+ bl save_gp_registers
+
+ /* Switch to the runtime stack i.e. SP_EL0 */
+ ldr x2, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
+ mov x20, sp
+ msr spsel, #0
+ mov sp, x2
+
+ /*
+ * Find out whether this is a valid interrupt type. If the
+ * interrupt controller reports a spurious interrupt then
+ * return to where we came from.
+ */
+ bl ic_get_pending_interrupt_type
+ cmp x0, #INTR_TYPE_INVAL
+ b.eq interrupt_exit_\label
+
+ /*
+ * Get the registered handler for this interrupt type. A
+ * NULL return value implies that an interrupt was generated
+ * for which there is no handler registered or the interrupt
+ * was routed incorrectly. This is a problem of the framework
+ * so report it as an error.
+ */
+ bl get_interrupt_type_handler
+ cbz x0, interrupt_error_\label
+ mov x21, x0
+
+ mov x0, #INTR_ID_UNAVAILABLE
+#if IMF_READ_INTERRUPT_ID
+ /*
+ * Read the id of the highest priority pending interrupt. If
+ * no interrupt is asserted then return to where we came from.
+ */
+ bl ic_get_pending_interrupt_id
+ cmp x0, #INTR_ID_UNAVAILABLE
+ b.eq interrupt_exit_\label
+#endif
+
+ /*
+ * Save the EL3 system registers needed to return from
+ * this exception.
+ */
+ mrs x3, spsr_el3
+ mrs x4, elr_el3
+ stp x3, x4, [x20, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
+
+ /* Set the current security state in the 'flags' parameter */
+ mrs x2, scr_el3
+ ubfx x1, x2, #0, #1
+
+ /* Restore the reference to the 'handle' i.e. SP_EL3 */
+ mov x2, x20
+
+ /* Call the interrupt type handler */
+ blr x21
+
+interrupt_exit_\label:
+ /* Return from exception, possibly in a different security state */
+ b el3_exit
+
+ /*
+ * This label signifies a problem with the interrupt management
+ * framework where it is not safe to go back to the instruction
+ * where the interrupt was generated.
+ */
+interrupt_error_\label:
+ bl dump_intr_state_and_die
+ .endm
+
+
.macro save_x18_to_x29_sp_el0
stp x18, x19, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X18]
stp x20, x21, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X20]
@@ -140,12 +244,12 @@
* -----------------------------------------------------
*/
irq_aarch64:
- bl dump_intr_state_and_die
+ handle_interrupt_exception irq_aarch64
check_vector_size irq_aarch64
.align 7
fiq_aarch64:
- bl dump_intr_state_and_die
+ handle_interrupt_exception fiq_aarch64
check_vector_size fiq_aarch64
.align 7
@@ -177,12 +281,12 @@
* -----------------------------------------------------
*/
irq_aarch32:
- bl dump_intr_state_and_die
+ handle_interrupt_exception irq_aarch32
check_vector_size irq_aarch32
.align 7
fiq_aarch32:
- bl dump_intr_state_and_die
+ handle_interrupt_exception fiq_aarch32
check_vector_size fiq_aarch32
.align 7
diff --git a/bl31/bl31.mk b/bl31/bl31.mk
index 6c9650f..8155f3d 100644
--- a/bl31/bl31.mk
+++ b/bl31/bl31.mk
@@ -31,6 +31,7 @@
BL31_SOURCES += bl31/bl31_main.c \
bl31/context_mgmt.c \
bl31/runtime_svc.c \
+ bl31/interrupt_mgmt.c \
bl31/aarch64/bl31_arch_setup.c \
bl31/aarch64/bl31_entrypoint.S \
bl31/aarch64/context.S \
@@ -50,3 +51,11 @@
services/std_svc/psci/psci_setup.c
BL31_LINKERFILE := bl31/bl31.ld.S
+
+# Flag used by the generic interrupt management framework to determine if
+# upon the assertion of an interrupt, it should pass the interrupt id or not
+IMF_READ_INTERRUPT_ID := 0
+
+$(eval $(call assert_boolean,IMF_READ_INTERRUPT_ID))
+$(eval $(call add_define,IMF_READ_INTERRUPT_ID))
+
diff --git a/bl31/bl31_main.c b/bl31/bl31_main.c
index 1dc0a7a..ff7caf1 100644
--- a/bl31/bl31_main.c
+++ b/bl31/bl31_main.c
@@ -168,9 +168,15 @@
assert(next_image_info);
scr = read_scr();
+ scr &= ~SCR_NS_BIT;
if (image_type == NON_SECURE)
scr |= SCR_NS_BIT;
+ scr &= ~SCR_RW_BIT;
+ if ((next_image_info->spsr & (1 << MODE_RW_SHIFT)) ==
+ (MODE_RW_64 << MODE_RW_SHIFT))
+ scr |= SCR_RW_BIT;
+
/*
* Tell the context mgmt. library to ensure that SP_EL3 points to
* the right context to exit from EL3 correctly.
diff --git a/bl31/context_mgmt.c b/bl31/context_mgmt.c
index eae608c..2e7e62d 100644
--- a/bl31/context_mgmt.c
+++ b/bl31/context_mgmt.c
@@ -28,12 +28,14 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <bl_common.h>
#include <bl31.h>
#include <context.h>
#include <context_mgmt.h>
+#include <interrupt_mgmt.h>
#include <platform.h>
#include <runtime_svc.h>
@@ -145,10 +147,10 @@
}
/*******************************************************************************
- * This function function populates 'cpu_context' pertaining to the given
- * security state with the entrypoint, SPSR and SCR values so that an ERET from
- * this securit state correctly restores corresponding values to drop the CPU to
- * the next exception level
+ * This function populates 'cpu_context' pertaining to the given security state
+ * with the entrypoint, SPSR and SCR values so that an ERET from this security
+ * state correctly restores corresponding values to drop the CPU to the next
+ * exception level
******************************************************************************/
void cm_set_el3_eret_context(uint32_t security_state, uint64_t entrypoint,
uint32_t spsr, uint32_t scr)
@@ -159,6 +161,11 @@
ctx = cm_get_context(read_mpidr(), security_state);
assert(ctx);
+ /* Program the interrupt routing model for this security state */
+ scr &= ~SCR_FIQ_BIT;
+ scr &= ~SCR_IRQ_BIT;
+ scr |= get_scr_el3_from_routing_model(security_state);
+
/* Populate EL3 state so that we've the right context before doing ERET */
state = get_el3state_ctx(ctx);
write_ctx_reg(state, CTX_SPSR_EL3, spsr);
@@ -167,10 +174,10 @@
}
/*******************************************************************************
- * This function function populates ELR_EL3 member of 'cpu_context' pertaining
- * to the given security state with the given entrypoint
+ * This function populates ELR_EL3 member of 'cpu_context' pertaining to the
+ * given security state with the given entrypoint
******************************************************************************/
-void cm_set_el3_elr(uint32_t security_state, uint64_t entrypoint)
+void cm_set_elr_el3(uint32_t security_state, uint64_t entrypoint)
{
cpu_context_t *ctx;
el3_state_t *state;
@@ -184,6 +191,56 @@
}
/*******************************************************************************
+ * This function updates a single bit in the SCR_EL3 member of the 'cpu_context'
+ * pertaining to the given security state using the value and bit position
+ * specified in the parameters. It preserves all other bits.
+ ******************************************************************************/
+void cm_write_scr_el3_bit(uint32_t security_state,
+ uint32_t bit_pos,
+ uint32_t value)
+{
+ cpu_context_t *ctx;
+ el3_state_t *state;
+ uint32_t scr_el3;
+
+ ctx = cm_get_context(read_mpidr(), security_state);
+ assert(ctx);
+
+ /* Ensure that the bit position is a valid one */
+ assert((1 << bit_pos) & SCR_VALID_BIT_MASK);
+
+ /* Ensure that the 'value' is only a bit wide */
+ assert(value <= 1);
+
+ /*
+ * Get the SCR_EL3 value from the cpu context, clear the desired bit
+ * and set it to its new value.
+ */
+ state = get_el3state_ctx(ctx);
+ scr_el3 = read_ctx_reg(state, CTX_SCR_EL3);
+ scr_el3 &= ~(1 << bit_pos);
+ scr_el3 |= value << bit_pos;
+ write_ctx_reg(state, CTX_SCR_EL3, scr_el3);
+}
+
+/*******************************************************************************
+ * This function retrieves SCR_EL3 member of 'cpu_context' pertaining to the
+ * given security state.
+ ******************************************************************************/
+uint32_t cm_get_scr_el3(uint32_t security_state)
+{
+ cpu_context_t *ctx;
+ el3_state_t *state;
+
+ ctx = cm_get_context(read_mpidr(), security_state);
+ assert(ctx);
+
+ /* Populate EL3 state so that ERET jumps to the correct entry */
+ state = get_el3state_ctx(ctx);
+ return read_ctx_reg(state, CTX_SCR_EL3);
+}
+
+/*******************************************************************************
* This function is used to program the context that's used for exception
* return. This initializes the SP_EL3 to a pointer to a 'cpu_context' set for
* the required security state
diff --git a/bl31/interrupt_mgmt.c b/bl31/interrupt_mgmt.c
new file mode 100644
index 0000000..2b0c797
--- /dev/null
+++ b/bl31/interrupt_mgmt.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <bl_common.h>
+#include <context_mgmt.h>
+#include <errno.h>
+#include <interrupt_mgmt.h>
+#include <platform.h>
+#include <stdio.h>
+
+/*******************************************************************************
+ * Local structure and corresponding array to keep track of the state of the
+ * registered interrupt handlers for each interrupt type.
+ * The field descriptions are:
+ *
+ * 'flags' : Bit[0], Routing model for this interrupt type when execution is
+ * not in EL3 in the secure state. '1' implies that this
+ * interrupt will be routed to EL3. '0' implies that this
+ * interrupt will be routed to the current exception level.
+ *
+ * Bit[1], Routing model for this interrupt type when execution is
+ * not in EL3 in the non-secure state. '1' implies that this
+ * interrupt will be routed to EL3. '0' implies that this
+ * interrupt will be routed to the current exception level.
+ *
+ * All other bits are reserved and SBZ.
+ *
+ * 'scr_el3[2]' : Mapping of the routing model in the 'flags' field to the
+ * value of the SCR_EL3.IRQ or FIQ bit for each security state.
+ * There are two instances of this field corresponding to the
+ * two security states.
+ ******************************************************************************/
+typedef struct intr_type_desc {
+ interrupt_type_handler_t handler;
+ uint32_t flags;
+ uint32_t scr_el3[2];
+} intr_type_desc_t;
+
+static intr_type_desc_t intr_type_descs[MAX_INTR_TYPES];
+
+/*******************************************************************************
+ * This function validates the interrupt type. EL3 interrupts are currently not
+ * supported.
+ ******************************************************************************/
+static int32_t validate_interrupt_type(uint32_t type)
+{
+ if (type == INTR_TYPE_EL3)
+ return -ENOTSUP;
+
+ if (type != INTR_TYPE_S_EL1 && type != INTR_TYPE_NS)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*******************************************************************************
+* This function validates the routing model for this type of interrupt
+ ******************************************************************************/
+static int32_t validate_routing_model(uint32_t type, uint32_t flags)
+{
+ flags >>= INTR_RM_FLAGS_SHIFT;
+ flags &= INTR_RM_FLAGS_MASK;
+
+ if (type == INTR_TYPE_S_EL1)
+ return validate_sel1_interrupt_rm(flags);
+
+ if (type == INTR_TYPE_NS)
+ return validate_ns_interrupt_rm(flags);
+
+ return -EINVAL;
+}
+
+/*******************************************************************************
+ * This function returns the cached copy of the SCR_EL3 which contains the
+ * routing model (expressed through the IRQ and FIQ bits) for a security state
+ * which was stored through a call to 'set_routing_model()' earlier.
+ ******************************************************************************/
+uint32_t get_scr_el3_from_routing_model(uint32_t security_state)
+{
+ uint32_t scr_el3;
+
+ assert(security_state <= NON_SECURE);
+ scr_el3 = intr_type_descs[INTR_TYPE_NS].scr_el3[security_state];
+ scr_el3 |= intr_type_descs[INTR_TYPE_S_EL1].scr_el3[security_state];
+ scr_el3 |= intr_type_descs[INTR_TYPE_EL3].scr_el3[security_state];
+ return scr_el3;
+}
+
+/*******************************************************************************
+ * This function uses the 'interrupt_type_flags' parameter to obtain the value
+ * of the trap bit (IRQ/FIQ) in the SCR_EL3 for a security state for this
+ * interrupt type. It uses it to update the SCR_EL3 in the cpu context and the
+ * 'intr_type_desc' for that security state.
+ ******************************************************************************/
+static void set_scr_el3_from_rm(uint32_t type,
+ uint32_t interrupt_type_flags,
+ uint32_t security_state)
+{
+ uint32_t flag, bit_pos;
+
+ flag = get_interrupt_rm_flag(interrupt_type_flags, security_state);
+ bit_pos = plat_interrupt_type_to_line(type, security_state);
+ intr_type_descs[type].scr_el3[security_state] = flag << bit_pos;
+ cm_write_scr_el3_bit(security_state, bit_pos, flag);
+}
+
+/*******************************************************************************
+ * This function validates the routing model specified in the 'flags' and
+ * updates internal data structures to reflect the new routing model. It also
+ * updates the copy of SCR_EL3 for each security state with the new routing
+ * model in the 'cpu_context' structure for this cpu.
+ ******************************************************************************/
+int32_t set_routing_model(uint32_t type, uint32_t flags)
+{
+ int32_t rc;
+
+ rc = validate_interrupt_type(type);
+ if (rc)
+ return rc;
+
+ rc = validate_routing_model(type, flags);
+ if (rc)
+ return rc;
+
+ /* Update the routing model in internal data structures */
+ intr_type_descs[type].flags = flags;
+ set_scr_el3_from_rm(type, flags, SECURE);
+ set_scr_el3_from_rm(type, flags, NON_SECURE);
+
+ return 0;
+}
+
+/*******************************************************************************
+ * This function registers a handler for the 'type' of interrupt specified. It
+ * also validates the routing model specified in the 'flags' for this type of
+ * interrupt.
+ ******************************************************************************/
+int32_t register_interrupt_type_handler(uint32_t type,
+ interrupt_type_handler_t handler,
+ uint32_t flags)
+{
+ int32_t rc;
+
+ /* Validate the 'handler' parameter */
+ if (!handler)
+ return -EINVAL;
+
+ /* Validate the 'flags' parameter */
+ if (flags & INTR_TYPE_FLAGS_MASK)
+ return -EINVAL;
+
+ /* Check if a handler has already been registered */
+ if (intr_type_descs[type].handler)
+ return -EALREADY;
+
+ rc = set_routing_model(type, flags);
+ if (rc)
+ return rc;
+
+ /* Save the handler */
+ intr_type_descs[type].handler = handler;
+
+ return 0;
+}
+
+/*******************************************************************************
+ * This function is called when an interrupt is generated and returns the
+ * handler for the interrupt type (if registered). It returns NULL if the
+ * interrupt type is not supported or its handler has not been registered.
+ ******************************************************************************/
+interrupt_type_handler_t get_interrupt_type_handler(uint32_t type)
+{
+ if (validate_interrupt_type(type))
+ return NULL;
+
+ return intr_type_descs[type].handler;
+}
+
diff --git a/bl31/runtime_svc.c b/bl31/runtime_svc.c
index b2ba685..08cd2d8 100644
--- a/bl31/runtime_svc.c
+++ b/bl31/runtime_svc.c
@@ -109,26 +109,35 @@
goto error;
}
- /* Call the initialisation routine for this runtime service */
- rc = rt_svc_descs[index].init();
- if (rc) {
- ERROR("Error initializing runtime service %s\n",
- rt_svc_descs[index].name);
- } else {
- /*
- * Fill the indices corresponding to the start and end
- * owning entity numbers with the index of the
- * descriptor which will handle the SMCs for this owning
- * entity range.
- */
- start_idx = get_unique_oen(rt_svc_descs[index].start_oen,
- rt_svc_descs[index].call_type);
- end_idx = get_unique_oen(rt_svc_descs[index].end_oen,
- rt_svc_descs[index].call_type);
-
- for (; start_idx <= end_idx; start_idx++)
- rt_svc_descs_indices[start_idx] = index;
+ /*
+ * The runtime service may have seperate rt_svc_desc_t
+ * for its fast smc and standard smc. Since the service itself
+ * need to be initialized only once, only one of them will have
+ * an initialisation routine defined. Call the initialisation
+ * routine for this runtime service, if it is defined.
+ */
+ if (rt_svc_descs[index].init) {
+ rc = rt_svc_descs[index].init();
+ if (rc) {
+ ERROR("Error initializing runtime service %s\n",
+ rt_svc_descs[index].name);
+ continue;
+ }
}
+
+ /*
+ * Fill the indices corresponding to the start and end
+ * owning entity numbers with the index of the
+ * descriptor which will handle the SMCs for this owning
+ * entity range.
+ */
+ start_idx = get_unique_oen(rt_svc_descs[index].start_oen,
+ rt_svc_descs[index].call_type);
+ end_idx = get_unique_oen(rt_svc_descs[index].end_oen,
+ rt_svc_descs[index].call_type);
+
+ for (; start_idx <= end_idx; start_idx++)
+ rt_svc_descs_indices[start_idx] = index;
}
return;
diff --git a/bl32/tsp/aarch64/tsp_entrypoint.S b/bl32/tsp/aarch64/tsp_entrypoint.S
index fab64cf..8fdfbc3 100644
--- a/bl32/tsp/aarch64/tsp_entrypoint.S
+++ b/bl32/tsp/aarch64/tsp_entrypoint.S
@@ -34,11 +34,9 @@
.globl tsp_entrypoint
- .globl tsp_cpu_on_entry
- .globl tsp_cpu_off_entry
- .globl tsp_cpu_suspend_entry
- .globl tsp_cpu_resume_entry
- .globl tsp_fast_smc_entry
+ .globl tsp_vector_table
+
+
/* ---------------------------------------------
* Populate the params in x0-x7 from the pointer
@@ -53,6 +51,22 @@
smc #0
.endm
+ .macro save_eret_context reg1 reg2
+ mrs \reg1, elr_el1
+ mrs \reg2, spsr_el1
+ stp \reg1, \reg2, [sp, #-0x10]!
+ stp x30, x18, [sp, #-0x10]!
+ .endm
+
+ .macro restore_eret_context reg1 reg2
+ ldp x30, x18, [sp], #0x10
+ ldp \reg1, \reg2, [sp], #0x10
+ msr elr_el1, \reg1
+ msr spsr_el1, \reg2
+ .endm
+
+ .section .text, "ax"
+ .align 3
func tsp_entrypoint
@@ -70,7 +84,7 @@
* Set the exception vector to something sane.
* ---------------------------------------------
*/
- adr x0, early_exceptions
+ adr x0, tsp_exceptions
msr vbar_el1, x0
/* ---------------------------------------------
@@ -137,6 +151,21 @@
tsp_entrypoint_panic:
b tsp_entrypoint_panic
+
+ /* -------------------------------------------
+ * Table of entrypoint vectors provided to the
+ * TSPD for the various entrypoints
+ * -------------------------------------------
+ */
+func tsp_vector_table
+ b tsp_std_smc_entry
+ b tsp_fast_smc_entry
+ b tsp_cpu_on_entry
+ b tsp_cpu_off_entry
+ b tsp_cpu_resume_entry
+ b tsp_cpu_suspend_entry
+ b tsp_fiq_entry
+
/*---------------------------------------------
* This entrypoint is used by the TSPD when this
* cpu is to be turned off through a CPU_OFF
@@ -167,7 +196,7 @@
* Set the exception vector to something sane.
* ---------------------------------------------
*/
- adr x0, early_exceptions
+ adr x0, tsp_exceptions
msr vbar_el1, x0
/* ---------------------------------------------
@@ -227,6 +256,58 @@
restore_args_call_smc
/*---------------------------------------------
+ * This entrypoint is used by the TSPD to pass
+ * control for handling a pending S-EL1 FIQ.
+ * 'x0' contains a magic number which indicates
+ * this. TSPD expects control to be handed back
+ * at the end of FIQ processing. This is done
+ * through an SMC. The handover agreement is:
+ *
+ * 1. PSTATE.DAIF are set upon entry. 'x1' has
+ * the ELR_EL3 from the non-secure state.
+ * 2. TSP has to preserve the callee saved
+ * general purpose registers, SP_EL1/EL0 and
+ * LR.
+ * 3. TSP has to preserve the system and vfp
+ * registers (if applicable).
+ * 4. TSP can use 'x0-x18' to enable its C
+ * runtime.
+ * 5. TSP returns to TSPD using an SMC with
+ * 'x0' = TSP_HANDLED_S_EL1_FIQ
+ * ---------------------------------------------
+ */
+func tsp_fiq_entry
+#if DEBUG
+ mov x2, #(TSP_HANDLE_FIQ_AND_RETURN & ~0xffff)
+ movk x2, #(TSP_HANDLE_FIQ_AND_RETURN & 0xffff)
+ cmp x0, x2
+ b.ne tsp_fiq_entry_panic
+#endif
+ /*---------------------------------------------
+ * Save any previous context needed to perform
+ * an exception return from S-EL1 e.g. context
+ * from a previous IRQ. Update statistics and
+ * handle the FIQ before returning to the TSPD.
+ * IRQ/FIQs are not enabled since that will
+ * complicate the implementation. Execution
+ * will be transferred back to the normal world
+ * in any case. A non-zero return value from the
+ * fiq handler is an error.
+ * ---------------------------------------------
+ */
+ save_eret_context x2 x3
+ bl tsp_update_sync_fiq_stats
+ bl tsp_fiq_handler
+ cbnz x0, tsp_fiq_entry_panic
+ restore_eret_context x2 x3
+ mov x0, #(TSP_HANDLED_S_EL1_FIQ & ~0xffff)
+ movk x0, #(TSP_HANDLED_S_EL1_FIQ & 0xffff)
+ smc #0
+
+tsp_fiq_entry_panic:
+ b tsp_fiq_entry_panic
+
+ /*---------------------------------------------
* This entrypoint is used by the TSPD when this
* cpu resumes execution after an earlier
* CPU_SUSPEND psci call to ask the TSP to
@@ -248,8 +329,22 @@
* ---------------------------------------------
*/
func tsp_fast_smc_entry
- bl tsp_fast_smc_handler
+ bl tsp_smc_handler
restore_args_call_smc
tsp_fast_smc_entry_panic:
b tsp_fast_smc_entry_panic
+ /*---------------------------------------------
+ * This entrypoint is used by the TSPD to ask
+ * the TSP to service a std smc request.
+ * We will enable preemption during execution
+ * of tsp_smc_handler.
+ * ---------------------------------------------
+ */
+func tsp_std_smc_entry
+ msr daifclr, #DAIF_FIQ_BIT | DAIF_IRQ_BIT
+ bl tsp_smc_handler
+ msr daifset, #DAIF_FIQ_BIT | DAIF_IRQ_BIT
+ restore_args_call_smc
+tsp_std_smc_entry_panic:
+ b tsp_std_smc_entry_panic
diff --git a/bl32/tsp/aarch64/tsp_exceptions.S b/bl32/tsp/aarch64/tsp_exceptions.S
new file mode 100644
index 0000000..f84b5e0
--- /dev/null
+++ b/bl32/tsp/aarch64/tsp_exceptions.S
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <bl_common.h>
+#include <arch.h>
+#include <tsp.h>
+#include <asm_macros.S>
+
+
+ /* ----------------------------------------------------
+ * The caller-saved registers x0-x18 and LR are saved
+ * here.
+ * ----------------------------------------------------
+ */
+
+#define SCRATCH_REG_SIZE #(20 * 8)
+
+ .macro save_caller_regs_and_lr
+ sub sp, sp, SCRATCH_REG_SIZE
+ stp x0, x1, [sp]
+ stp x2, x3, [sp, #0x10]
+ stp x4, x5, [sp, #0x20]
+ stp x6, x7, [sp, #0x30]
+ stp x8, x9, [sp, #0x40]
+ stp x10, x11, [sp, #0x50]
+ stp x12, x13, [sp, #0x60]
+ stp x14, x15, [sp, #0x70]
+ stp x16, x17, [sp, #0x80]
+ stp x18, x30, [sp, #0x90]
+ .endm
+
+ .macro restore_caller_regs_and_lr
+ ldp x0, x1, [sp]
+ ldp x2, x3, [sp, #0x10]
+ ldp x4, x5, [sp, #0x20]
+ ldp x6, x7, [sp, #0x30]
+ ldp x8, x9, [sp, #0x40]
+ ldp x10, x11, [sp, #0x50]
+ ldp x12, x13, [sp, #0x60]
+ ldp x14, x15, [sp, #0x70]
+ ldp x16, x17, [sp, #0x80]
+ ldp x18, x30, [sp, #0x90]
+ add sp, sp, SCRATCH_REG_SIZE
+ .endm
+
+ .globl tsp_exceptions
+
+ /* -----------------------------------------------------
+ * TSP exception handlers.
+ * -----------------------------------------------------
+ */
+ .section .vectors, "ax"; .align 11
+
+ .align 7
+tsp_exceptions:
+ /* -----------------------------------------------------
+ * Current EL with _sp_el0 : 0x0 - 0x180. No exceptions
+ * are expected and treated as irrecoverable errors.
+ * -----------------------------------------------------
+ */
+sync_exception_sp_el0:
+ wfi
+ b sync_exception_sp_el0
+ check_vector_size sync_exception_sp_el0
+
+ .align 7
+
+irq_sp_el0:
+ b irq_sp_el0
+ check_vector_size irq_sp_el0
+
+ .align 7
+fiq_sp_el0:
+ b fiq_sp_el0
+ check_vector_size fiq_sp_el0
+
+ .align 7
+serror_sp_el0:
+ b serror_sp_el0
+ check_vector_size serror_sp_el0
+
+
+ /* -----------------------------------------------------
+ * Current EL with SPx: 0x200 - 0x380. Only IRQs/FIQs
+ * are expected and handled
+ * -----------------------------------------------------
+ */
+ .align 7
+sync_exception_sp_elx:
+ wfi
+ b sync_exception_sp_elx
+ check_vector_size sync_exception_sp_elx
+
+ .align 7
+irq_sp_elx:
+ save_caller_regs_and_lr
+ /* We just update some statistics in the handler */
+ bl tsp_irq_received
+ /* Hand over control to the normal world to handle the IRQ */
+ smc #0
+ /* The resume std smc starts from here */
+ restore_caller_regs_and_lr
+ eret
+ check_vector_size irq_sp_elx
+
+ .align 7
+fiq_sp_elx:
+ save_caller_regs_and_lr
+ bl tsp_fiq_handler
+ cbz x0, fiq_sp_elx_done
+
+ /*
+ * This FIQ was not targetted to S-EL1 so send it to
+ * the monitor and wait for execution to resume.
+ */
+ smc #0
+fiq_sp_elx_done:
+ restore_caller_regs_and_lr
+ eret
+ check_vector_size fiq_sp_elx
+
+ .align 7
+serror_sp_elx:
+ b serror_sp_elx
+ check_vector_size serror_sp_elx
+
+
+ /* -----------------------------------------------------
+ * Lower EL using AArch64 : 0x400 - 0x580. No exceptions
+ * are handled since TSP does not implement a lower EL
+ * -----------------------------------------------------
+ */
+ .align 7
+sync_exception_aarch64:
+ wfi
+ b sync_exception_aarch64
+ check_vector_size sync_exception_aarch64
+
+ .align 7
+irq_aarch64:
+ b irq_aarch64
+ check_vector_size irq_aarch64
+
+ .align 7
+fiq_aarch64:
+ b fiq_aarch64
+ check_vector_size fiq_aarch64
+
+ .align 7
+serror_aarch64:
+ b serror_aarch64
+ check_vector_size serror_aarch64
+
+
+ /* -----------------------------------------------------
+ * Lower EL using AArch32 : 0x600 - 0x780. No exceptions
+ * handled since the TSP does not implement a lower EL.
+ * -----------------------------------------------------
+ */
+ .align 7
+sync_exception_aarch32:
+ wfi
+ b sync_exception_aarch32
+ check_vector_size sync_exception_aarch32
+
+ .align 7
+irq_aarch32:
+ b irq_aarch32
+ check_vector_size irq_aarch32
+
+ .align 7
+fiq_aarch32:
+ b fiq_aarch32
+ check_vector_size fiq_aarch32
+
+ .align 7
+serror_aarch32:
+ b serror_aarch32
+ check_vector_size serror_aarch32
+ .align 7
diff --git a/bl32/tsp/tsp-fvp.mk b/bl32/tsp/tsp-fvp.mk
index 5d8a0e3..b1d0afe 100644
--- a/bl32/tsp/tsp-fvp.mk
+++ b/bl32/tsp/tsp-fvp.mk
@@ -29,7 +29,9 @@
#
# TSP source files specific to FVP platform
-BL32_SOURCES += plat/common/aarch64/platform_mp_stack.S \
- plat/fvp/bl32_plat_setup.c \
+BL32_SOURCES += drivers/arm/gic/gic_v2.c \
+ plat/common/aarch64/platform_mp_stack.S \
plat/fvp/aarch64/plat_common.c \
- plat/fvp/aarch64/plat_helpers.S
+ plat/fvp/aarch64/plat_helpers.S \
+ plat/fvp/bl32_plat_setup.c \
+ plat/fvp/plat_gic.c
diff --git a/bl32/tsp/tsp.mk b/bl32/tsp/tsp.mk
index c478b43..b9084d5 100644
--- a/bl32/tsp/tsp.mk
+++ b/bl32/tsp/tsp.mk
@@ -30,7 +30,10 @@
BL32_SOURCES += bl32/tsp/tsp_main.c \
bl32/tsp/aarch64/tsp_entrypoint.S \
+ bl32/tsp/aarch64/tsp_exceptions.S \
bl32/tsp/aarch64/tsp_request.S \
+ bl32/tsp/tsp_interrupt.c \
+ bl32/tsp/tsp_timer.c \
common/aarch64/early_exceptions.S \
lib/locks/exclusive/spinlock.S
diff --git a/bl32/tsp/tsp_interrupt.c b/bl32/tsp/tsp_interrupt.c
new file mode 100644
index 0000000..5719c06
--- /dev/null
+++ b/bl32/tsp/tsp_interrupt.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <gic_v2.h>
+#include <tsp.h>
+#include <platform.h>
+
+/*******************************************************************************
+ * This function updates the TSP statistics for FIQs handled synchronously i.e
+ * the ones that have been handed over by the TSPD. It also keeps count of the
+ * number of times control was passed back to the TSPD after handling an FIQ.
+ * In the future it will be possible that the TSPD hands over an FIQ to the TSP
+ * but does not expect it to return execution. This statistic will be useful to
+ * distinguish between these two models of synchronous FIQ handling.
+ * The 'elr_el3' parameter contains the address of the instruction in normal
+ * world where this FIQ was generated.
+ ******************************************************************************/
+void tsp_update_sync_fiq_stats(uint32_t type, uint64_t elr_el3)
+{
+ uint64_t mpidr = read_mpidr();
+ uint32_t linear_id = platform_get_core_pos(mpidr);
+
+ tsp_stats[linear_id].sync_fiq_count++;
+ if (type == TSP_HANDLE_FIQ_AND_RETURN)
+ tsp_stats[linear_id].sync_fiq_ret_count++;
+
+ spin_lock(&console_lock);
+ printf("TSP: cpu 0x%x sync fiq request from 0x%llx \n\r",
+ mpidr, elr_el3);
+ INFO("cpu 0x%x: %d sync fiq requests, %d sync fiq returns\n",
+ mpidr,
+ tsp_stats[linear_id].sync_fiq_count,
+ tsp_stats[linear_id].sync_fiq_ret_count);
+ spin_unlock(&console_lock);
+}
+
+/*******************************************************************************
+ * TSP FIQ handler called as a part of both synchronous and asynchronous
+ * handling of FIQ interrupts. It returns 0 upon successfully handling a S-EL1
+ * FIQ and treats all other FIQs as EL3 interrupts. It assumes that the GIC
+ * architecture version in v2.0 and the secure physical timer interrupt is the
+ * only S-EL1 interrupt that it needs to handle.
+ ******************************************************************************/
+int32_t tsp_fiq_handler()
+{
+ uint64_t mpidr = read_mpidr();
+ uint32_t linear_id = platform_get_core_pos(mpidr), id;
+
+ /*
+ * Get the highest priority pending interrupt id and see if it is the
+ * secure physical generic timer interrupt in which case, handle it.
+ * Otherwise throw this interrupt at the EL3 firmware.
+ */
+ id = ic_get_pending_interrupt_id();
+
+ /* TSP can only handle the secure physical timer interrupt */
+ if (id != IRQ_SEC_PHY_TIMER)
+ return TSP_EL3_FIQ;
+
+ /*
+ * Handle the interrupt. Also sanity check if it has been preempted by
+ * another secure interrupt through an assertion.
+ */
+ id = ic_acknowledge_interrupt();
+ assert(id == IRQ_SEC_PHY_TIMER);
+ tsp_generic_timer_handler();
+ ic_end_of_interrupt(id);
+
+ /* Update the statistics and print some messages */
+ tsp_stats[linear_id].fiq_count++;
+ spin_lock(&console_lock);
+ printf("TSP: cpu 0x%x handled fiq %d \n\r",
+ mpidr, id);
+ INFO("cpu 0x%x: %d fiq requests \n",
+ mpidr, tsp_stats[linear_id].fiq_count);
+ spin_unlock(&console_lock);
+
+ return 0;
+}
+
+int32_t tsp_irq_received()
+{
+ uint64_t mpidr = read_mpidr();
+ uint32_t linear_id = platform_get_core_pos(mpidr);
+
+ tsp_stats[linear_id].irq_count++;
+ spin_lock(&console_lock);
+ printf("TSP: cpu 0x%x received irq\n\r", mpidr);
+ INFO("cpu 0x%x: %d irq requests \n",
+ mpidr, tsp_stats[linear_id].irq_count);
+ spin_unlock(&console_lock);
+
+ return TSP_PREEMPTED;
+}
diff --git a/bl32/tsp/tsp_main.c b/bl32/tsp/tsp_main.c
index a7c7386..ad7ee0a 100644
--- a/bl32/tsp/tsp_main.c
+++ b/bl32/tsp/tsp_main.c
@@ -58,23 +58,9 @@
/*******************************************************************************
* Per cpu data structure to keep track of TSP activity
******************************************************************************/
-static work_statistics_t tsp_stats[PLATFORM_CORE_COUNT];
+work_statistics_t tsp_stats[PLATFORM_CORE_COUNT];
/*******************************************************************************
- * Single reference to the various entry points exported by the test secure
- * payload. A single copy should suffice for all cpus as they are not expected
- * to change.
- ******************************************************************************/
-static const entry_info_t tsp_entry_info = {
- tsp_fast_smc_entry,
- tsp_cpu_on_entry,
- tsp_cpu_off_entry,
- tsp_cpu_resume_entry,
- tsp_cpu_suspend_entry,
-};
-
-
-/*******************************************************************************
* The BL32 memory footprint starts with an RO sections and ends
* with a section for coherent RAM. Use it to find the memory size
******************************************************************************/
@@ -116,7 +102,7 @@
/*******************************************************************************
* TSP main entry point where it gets the opportunity to initialize its secure
* state/applications. Once the state is initialized, it must return to the
- * SPD with a pointer to the 'tsp_entry_info' structure.
+ * SPD with a pointer to the 'tsp_vector_table' jump table.
******************************************************************************/
uint64_t tsp_main(void)
{
@@ -127,6 +113,7 @@
bl32_platform_setup();
/* Initialize secure/applications state here */
+ tsp_generic_timer_start();
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
@@ -144,12 +131,7 @@
tsp_stats[linear_id].cpu_on_count);
spin_unlock(&console_lock);
- /*
- * TODO: There is a massive assumption that the SPD and SP can see each
- * other's memory without issues so it is safe to pass pointers to
- * internal memory. Replace this with a shared communication buffer.
- */
- return (uint64_t) &tsp_entry_info;
+ return (uint64_t) &tsp_vector_table;
}
/*******************************************************************************
@@ -162,6 +144,9 @@
uint64_t mpidr = read_mpidr();
uint32_t linear_id = platform_get_core_pos(mpidr);
+ /* Initialize secure/applications state here */
+ tsp_generic_timer_start();
+
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
@@ -195,6 +180,13 @@
uint64_t mpidr = read_mpidr();
uint32_t linear_id = platform_get_core_pos(mpidr);
+ /*
+ * This cpu is being turned off, so disable the timer to prevent the
+ * secure timer interrupt from interfering with power down. A pending
+ * interrupt will be lost but we do not care as we are turning off.
+ */
+ tsp_generic_timer_stop();
+
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
@@ -230,6 +222,13 @@
uint64_t mpidr = read_mpidr();
uint32_t linear_id = platform_get_core_pos(mpidr);
+ /*
+ * Save the time context and disable it to prevent the secure timer
+ * interrupt from interfering with wakeup from the suspend state.
+ */
+ tsp_generic_timer_save();
+ tsp_generic_timer_stop();
+
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
@@ -265,6 +264,9 @@
uint64_t mpidr = read_mpidr();
uint32_t linear_id = platform_get_core_pos(mpidr);
+ /* Restore the generic timer context */
+ tsp_generic_timer_restore();
+
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
@@ -287,9 +289,9 @@
* TSP fast smc handler. The secure monitor jumps to this function by
* doing the ERET after populating X0-X7 registers. The arguments are received
* in the function arguments in order. Once the service is rendered, this
- * function returns to Secure Monitor by raising SMC
+ * function returns to Secure Monitor by raising SMC.
******************************************************************************/
-tsp_args_t *tsp_fast_smc_handler(uint64_t func,
+tsp_args_t *tsp_smc_handler(uint64_t func,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
@@ -302,18 +304,20 @@
uint64_t service_args[2];
uint64_t mpidr = read_mpidr();
uint32_t linear_id = platform_get_core_pos(mpidr);
+ const char *smc_type;
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
- printf("SP: cpu 0x%x received fast smc 0x%x\n", read_mpidr(), func);
+ smc_type = ((func >> 31) & 1) == 1 ? "fast" : "standard";
+
+ printf("SP: cpu 0x%x received %s smc 0x%x\n", read_mpidr(), smc_type, func);
INFO("cpu 0x%x: %d smcs, %d erets\n", mpidr,
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count);
/* Render secure services and obtain results here */
-
results[0] = arg1;
results[1] = arg2;
@@ -324,20 +328,20 @@
tsp_get_magic(service_args);
/* Determine the function to perform based on the function ID */
- switch (func) {
- case TSP_FID_ADD:
+ switch (TSP_BARE_FID(func)) {
+ case TSP_ADD:
results[0] += service_args[0];
results[1] += service_args[1];
break;
- case TSP_FID_SUB:
+ case TSP_SUB:
results[0] -= service_args[0];
results[1] -= service_args[1];
break;
- case TSP_FID_MUL:
+ case TSP_MUL:
results[0] *= service_args[0];
results[1] *= service_args[1];
break;
- case TSP_FID_DIV:
+ case TSP_DIV:
results[0] /= service_args[0] ? service_args[0] : 1;
results[1] /= service_args[1] ? service_args[1] : 1;
break;
@@ -345,9 +349,9 @@
break;
}
- return set_smc_args(func,
+ return set_smc_args(func, 0,
results[0],
results[1],
- 0, 0, 0, 0, 0);
+ 0, 0, 0, 0);
}
diff --git a/bl32/tsp/tsp_timer.c b/bl32/tsp/tsp_timer.c
new file mode 100644
index 0000000..f66ff9f
--- /dev/null
+++ b/bl32/tsp/tsp_timer.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <arch_helpers.h>
+#include <assert.h>
+#include <tsp.h>
+
+/*******************************************************************************
+ * Data structure to keep track of per-cpu secure generic timer context across
+ * power management operations.
+ ******************************************************************************/
+typedef struct timer_context {
+ uint64_t cval;
+ uint32_t ctl;
+} timer_context_t;
+
+static timer_context_t pcpu_timer_context[PLATFORM_CORE_COUNT];
+
+/*******************************************************************************
+ * This function initializes the generic timer to fire every 0.5 second
+ ******************************************************************************/
+void tsp_generic_timer_start()
+{
+ uint64_t cval;
+ uint32_t ctl = 0;
+
+ /* The timer will fire every 0.5 second */
+ cval = read_cntpct_el0() + (read_cntfrq_el0() >> 1);
+ write_cntps_cval_el1(cval);
+
+ /* Enable the secure physical timer */
+ set_cntp_ctl_enable(ctl);
+ write_cntps_ctl_el1(ctl);
+}
+
+/*******************************************************************************
+ * This function deasserts the timer interrupt and sets it up again
+ ******************************************************************************/
+void tsp_generic_timer_handler()
+{
+ /* Ensure that the timer did assert the interrupt */
+ assert(get_cntp_ctl_istatus(read_cntps_ctl_el1()));
+
+ /* Disable the timer and reprogram it */
+ write_cntps_ctl_el1(0);
+ tsp_generic_timer_start();
+}
+
+/*******************************************************************************
+ * This function deasserts the timer interrupt prior to cpu power down
+ ******************************************************************************/
+void tsp_generic_timer_stop()
+{
+ /* Disable the timer */
+ write_cntps_ctl_el1(0);
+}
+
+/*******************************************************************************
+ * This function saves the timer context prior to cpu suspension
+ ******************************************************************************/
+void tsp_generic_timer_save()
+{
+ uint32_t linear_id = platform_get_core_pos(read_mpidr());
+
+ pcpu_timer_context[linear_id].cval = read_cntps_cval_el1();
+ pcpu_timer_context[linear_id].ctl = read_cntps_ctl_el1();
+ flush_dcache_range((uint64_t) &pcpu_timer_context[linear_id],
+ sizeof(pcpu_timer_context[linear_id]));
+}
+
+/*******************************************************************************
+ * This function restores the timer context post cpu resummption
+ ******************************************************************************/
+void tsp_generic_timer_restore()
+{
+ uint32_t linear_id = platform_get_core_pos(read_mpidr());
+
+ write_cntps_cval_el1(pcpu_timer_context[linear_id].cval);
+ write_cntps_ctl_el1(pcpu_timer_context[linear_id].ctl);
+}
diff --git a/docs/firmware-design.md b/docs/firmware-design.md
index 959e16c..ab575ed 100644
--- a/docs/firmware-design.md
+++ b/docs/firmware-design.md
@@ -219,6 +219,15 @@
abstraction layer is initialized which is used to load further bootloader
images.
+#### BL3-0 (System Control Processor Firmware) image load
+
+Some systems have a separate System Control Processor (SCP) for power, clock,
+reset and system control. BL2 loads the optional BL3-0 image from platform
+storage into a platform-specific region of secure memory. The subsequent
+handling of BL3-0 is platform specific. Typically the image is transferred into
+SCP memory using a platform-specific protocol. The SCP executes BL3-0 and
+signals to the Application Processor (AP) for BL2 execution to continue.
+
#### BL3-1 (EL3 Runtime Firmware) image load
BL2 loads the BL3-1 image from platform storage into a platform-specific address
diff --git a/docs/porting-guide.md b/docs/porting-guide.md
index 37c2bf9..b4e0a58 100644
--- a/docs/porting-guide.md
+++ b/docs/porting-guide.md
@@ -610,23 +610,24 @@
if the platform wants to restrict the amount of memory visible to BL3-1.
Details of this function are given below.
-4. Loading the BL3-2 binary image (if present) in platform provided memory
- using semi-hosting. To load the BL3-2 image, BL2 makes use of the
- `bl32_meminfo` field in the `bl31_args` structure to which a pointer is
+4. (Optional) Loading the BL3-2 binary image (if present) from platform
+ provided non-volatile storage. To load the BL3-2 image, BL2 makes use of
+ the `bl32_meminfo` field in the `bl31_args` structure to which a pointer is
returned by the `bl2_get_bl31_args_ptr()` function. The platform also
- defines the address in memory where BL3-2 is loaded through the constant
- `BL32_BASE`. BL2 uses this information to determine if there is enough
- memory to load the BL3-2 image.
+ defines the address in memory where BL3-2 is loaded through the optional
+ constant `BL32_BASE`. BL2 uses this information to determine if there is
+ enough memory to load the BL3-2 image. If `BL32_BASE` is not defined then
+ this and the following two steps are not performed.
-5. Arranging to pass control to the BL3-2 image (if present) that has been
- pre-loaded at `BL32_BASE`. BL2 populates an `el_change_info` structure
- in memory provided by the platform with information about how BL3-1 should
- pass control to the BL3-2 image. This structure follows the
+5. (Optional) Arranging to pass control to the BL3-2 image (if present) that
+ has been pre-loaded at `BL32_BASE`. BL2 populates an `el_change_info`
+ structure in memory provided by the platform with information about how
+ BL3-1 should pass control to the BL3-2 image. This structure follows the
`el_change_info` structure populated for the normal world BL image in 2.
above.
-6. Populating a `meminfo` structure with the following information in
- memory that is accessible by BL3-1 immediately upon entry.
+6. (Optional) Populating a `meminfo` structure with the following information
+ in memory that is accessible by BL3-1 immediately upon entry.
meminfo.total_base = Base address of memory visible to BL3-2
meminfo.total_size = Size of memory visible to BL3-2
@@ -636,7 +637,7 @@
BL3-2
BL2 populates this information in the `bl32_meminfo` field of the pointer
- returned by the `bl2_get_bl31_args_ptr() function.
+ returned by the `bl2_get_bl31_args_ptr()` function.
The following functions must be implemented by the platform port to enable BL2
to perform the above tasks.
diff --git a/docs/user-guide.md b/docs/user-guide.md
index a6256ef..484ffeb 100644
--- a/docs/user-guide.md
+++ b/docs/user-guide.md
@@ -158,6 +158,15 @@
* `V`: Verbose build. If assigned anything other than 0, the build commands
are printed. Default is 0
+* `FVP_GIC_ARCH`: Choice of ARM GIC architecture version used by the FVP port
+ for implementing the platform GIC API. This API is used by the interrupt
+ management framework. Default is 2 i.e. version 2.0
+
+* `IMF_READ_INTERRUPT_ID`: Boolean flag used by the interrupt management
+ framework to enable passing of the interrupt id to its handler. The id is
+ read using a platform GIC API. `INTR_ID_UNAVAILABLE` is passed instead if
+ this option set to 0. Default is 0.
+
### Creating a Firmware Image Package
FIPs are automatically created as part of the build instructions described in
@@ -323,7 +332,7 @@
git clone -n https://github.com/tianocore/edk2.git
cd edk2
- git checkout c1cdcab9526506673b882017845a043cead8bc69
+ git checkout 129ff94661bd3a6c759b1e154c143d0136bedc7d
To build the software to be compatible with Foundation and Base FVPs, follow
diff --git a/drivers/arm/gic/gic_v2.c b/drivers/arm/gic/gic_v2.c
index 00464cb..27a39b9 100644
--- a/drivers/arm/gic/gic_v2.c
+++ b/drivers/arm/gic/gic_v2.c
@@ -28,8 +28,10 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#include <arch.h>
#include <assert.h>
#include <gic_v2.h>
+#include <interrupt_mgmt.h>
#include <mmio.h>
/*******************************************************************************
@@ -290,3 +292,27 @@
(1 << iface) << (byte_off << 3));
}
+/*******************************************************************************
+ * This function allows the interrupt management framework to determine (through
+ * the platform) which interrupt line (IRQ/FIQ) to use for an interrupt type to
+ * route it to EL3. The interrupt line is represented as the bit position of the
+ * IRQ or FIQ bit in the SCR_EL3.
+ ******************************************************************************/
+uint32_t gicv2_interrupt_type_to_line(uint32_t cpuif_base, uint32_t type)
+{
+ uint32_t gicc_ctlr;
+
+ /* Non-secure interrupts are signalled on the IRQ line always */
+ if (type == INTR_TYPE_NS)
+ return __builtin_ctz(SCR_IRQ_BIT);
+
+ /*
+ * Secure interrupts are signalled using the IRQ line if the FIQ_EN
+ * bit is not set else they are signalled using the FIQ line.
+ */
+ gicc_ctlr = gicc_read_ctlr(cpuif_base);
+ if (gicc_ctlr & FIQ_EN)
+ return __builtin_ctz(SCR_FIQ_BIT);
+ else
+ return __builtin_ctz(SCR_IRQ_BIT);
+}
diff --git a/drivers/io/io_fip.c b/drivers/io/io_fip.c
index 4848a70..8ed7f47 100644
--- a/drivers/io/io_fip.c
+++ b/drivers/io/io_fip.c
@@ -65,7 +65,10 @@
static const plat_fip_name_uuid_t name_uuid[] = {
{BL2_IMAGE_NAME, UUID_TRUSTED_BOOT_FIRMWARE_BL2},
{BL31_IMAGE_NAME, UUID_EL3_RUNTIME_FIRMWARE_BL31},
+#ifdef BL32_IMAGE_NAME
+ /* BL3-2 is optional in the platform */
{BL32_IMAGE_NAME, UUID_SECURE_PAYLOAD_BL32},
+#endif /* BL32_IMAGE_NAME */
{BL33_IMAGE_NAME, UUID_NON_TRUSTED_FIRMWARE_BL33},
};
diff --git a/fdts/fvp-base-gicv2-psci.dtb b/fdts/fvp-base-gicv2-psci.dtb
index abdb9a0..efe83be 100644
--- a/fdts/fvp-base-gicv2-psci.dtb
+++ b/fdts/fvp-base-gicv2-psci.dtb
Binary files differ
diff --git a/fdts/fvp-base-gicv2-psci.dts b/fdts/fvp-base-gicv2-psci.dts
index 7d08922..2b2c2b0 100644
--- a/fdts/fvp-base-gicv2-psci.dts
+++ b/fdts/fvp-base-gicv2-psci.dts
@@ -115,7 +115,7 @@
memory@80000000 {
device_type = "memory";
- reg = <0x00000000 0x80000000 0 0x80000000>,
+ reg = <0x00000000 0x80000000 0 0x7F000000>,
<0x00000008 0x80000000 0 0x80000000>;
};
diff --git a/fdts/fvp-base-gicv2legacy-psci.dtb b/fdts/fvp-base-gicv2legacy-psci.dtb
index 3fc6b3e..7243c06 100644
--- a/fdts/fvp-base-gicv2legacy-psci.dtb
+++ b/fdts/fvp-base-gicv2legacy-psci.dtb
Binary files differ
diff --git a/fdts/fvp-base-gicv2legacy-psci.dts b/fdts/fvp-base-gicv2legacy-psci.dts
index f095231..620bc05 100644
--- a/fdts/fvp-base-gicv2legacy-psci.dts
+++ b/fdts/fvp-base-gicv2legacy-psci.dts
@@ -115,7 +115,7 @@
memory@80000000 {
device_type = "memory";
- reg = <0x00000000 0x80000000 0 0x80000000>,
+ reg = <0x00000000 0x80000000 0 0x7F000000>,
<0x00000008 0x80000000 0 0x80000000>;
};
diff --git a/fdts/fvp-base-gicv3-psci.dtb b/fdts/fvp-base-gicv3-psci.dtb
index 1efa136..b9fe1cf 100644
--- a/fdts/fvp-base-gicv3-psci.dtb
+++ b/fdts/fvp-base-gicv3-psci.dtb
Binary files differ
diff --git a/fdts/fvp-base-gicv3-psci.dts b/fdts/fvp-base-gicv3-psci.dts
index 96d264e..d111a99 100644
--- a/fdts/fvp-base-gicv3-psci.dts
+++ b/fdts/fvp-base-gicv3-psci.dts
@@ -115,7 +115,7 @@
memory@80000000 {
device_type = "memory";
- reg = <0x00000000 0x80000000 0 0x80000000>,
+ reg = <0x00000000 0x80000000 0 0x7F000000>,
<0x00000008 0x80000000 0 0x80000000>;
};
diff --git a/fdts/fvp-foundation-gicv2-psci.dtb b/fdts/fvp-foundation-gicv2-psci.dtb
index ca10088..70175e8 100644
--- a/fdts/fvp-foundation-gicv2-psci.dtb
+++ b/fdts/fvp-foundation-gicv2-psci.dtb
Binary files differ
diff --git a/fdts/fvp-foundation-gicv2-psci.dts b/fdts/fvp-foundation-gicv2-psci.dts
index bf368a0..8f3de9d 100644
--- a/fdts/fvp-foundation-gicv2-psci.dts
+++ b/fdts/fvp-foundation-gicv2-psci.dts
@@ -91,7 +91,7 @@
memory@80000000 {
device_type = "memory";
- reg = <0x00000000 0x80000000 0 0x80000000>,
+ reg = <0x00000000 0x80000000 0 0x7F000000>,
<0x00000008 0x80000000 0 0x80000000>;
};
diff --git a/fdts/fvp-foundation-gicv2legacy-psci.dtb b/fdts/fvp-foundation-gicv2legacy-psci.dtb
index a602ff5..564d223 100644
--- a/fdts/fvp-foundation-gicv2legacy-psci.dtb
+++ b/fdts/fvp-foundation-gicv2legacy-psci.dtb
Binary files differ
diff --git a/fdts/fvp-foundation-gicv2legacy-psci.dts b/fdts/fvp-foundation-gicv2legacy-psci.dts
index 63cef80..951da06 100644
--- a/fdts/fvp-foundation-gicv2legacy-psci.dts
+++ b/fdts/fvp-foundation-gicv2legacy-psci.dts
@@ -91,7 +91,7 @@
memory@80000000 {
device_type = "memory";
- reg = <0x00000000 0x80000000 0 0x80000000>,
+ reg = <0x00000000 0x80000000 0 0x7F000000>,
<0x00000008 0x80000000 0 0x80000000>;
};
diff --git a/fdts/fvp-foundation-gicv3-psci.dtb b/fdts/fvp-foundation-gicv3-psci.dtb
index f64e421..26800ba 100644
--- a/fdts/fvp-foundation-gicv3-psci.dtb
+++ b/fdts/fvp-foundation-gicv3-psci.dtb
Binary files differ
diff --git a/fdts/fvp-foundation-gicv3-psci.dts b/fdts/fvp-foundation-gicv3-psci.dts
index f9f1ff3..7692c61 100644
--- a/fdts/fvp-foundation-gicv3-psci.dts
+++ b/fdts/fvp-foundation-gicv3-psci.dts
@@ -91,7 +91,7 @@
memory@80000000 {
device_type = "memory";
- reg = <0x00000000 0x80000000 0 0x80000000>,
+ reg = <0x00000000 0x80000000 0 0x7F000000>,
<0x00000008 0x80000000 0 0x80000000>;
};
diff --git a/include/bl31/cm_macros.S b/include/bl31/cm_macros.S
deleted file mode 100644
index f12f8c3..0000000
--- a/include/bl31/cm_macros.S
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of ARM nor the names of its contributors may be used
- * to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#include <arch.h>
-#include <context.h>
-
- /* -----------------------------------------------------
- * Handle SMC exceptions seperately from other sync.
- * exceptions.
- * -----------------------------------------------------
- */
- .macro handle_sync_exception
- str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
- mrs x30, esr_el3
- ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
-
- cmp x30, #EC_AARCH32_SMC
- b.eq smc_handler32
-
- cmp x30, #EC_AARCH64_SMC
- b.eq smc_handler64
-
- /* -----------------------------------------------------
- * The following code handles any synchronous exception
- * that is not an SMC.
- * -----------------------------------------------------
- */
-
- bl dump_state_and_die
- .endm
-
diff --git a/include/bl31/context_mgmt.h b/include/bl31/context_mgmt.h
index d2598ee..ad9d785 100644
--- a/include/bl31/context_mgmt.h
+++ b/include/bl31/context_mgmt.h
@@ -47,10 +47,13 @@
extern void cm_el1_sysregs_context_restore(uint32_t security_state);
extern void cm_set_el3_eret_context(uint32_t security_state, uint64_t entrypoint,
uint32_t spsr, uint32_t scr);
-extern void cm_set_el3_elr(uint32_t security_state, uint64_t entrypoint);
+extern void cm_set_elr_el3(uint32_t security_state, uint64_t entrypoint);
+extern void cm_write_scr_el3_bit(uint32_t security_state,
+ uint32_t bit_pos,
+ uint32_t value);
extern void cm_set_next_eret_context(uint32_t security_state);
extern void cm_init_pcpu_ptr_cache();
extern void cm_set_pcpu_ptr_cache(const void *pcpu_ptr);
extern void *cm_get_pcpu_ptr_cache(void);
-
+extern uint32_t cm_get_scr_el3(uint32_t security_state);
#endif /* __CM_H__ */
diff --git a/include/bl31/interrupt_mgmt.h b/include/bl31/interrupt_mgmt.h
new file mode 100644
index 0000000..0b24f39
--- /dev/null
+++ b/include/bl31/interrupt_mgmt.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INTERRUPT_MGMT_H__
+#define __INTERRUPT_MGMT_H__
+
+#include <arch.h>
+
+/*******************************************************************************
+ * Constants for the types of interrupts recognised by the IM framework
+ ******************************************************************************/
+#define INTR_TYPE_S_EL1 0
+#define INTR_TYPE_EL3 1
+#define INTR_TYPE_NS 2
+#define MAX_INTR_TYPES 3
+#define INTR_TYPE_INVAL MAX_INTR_TYPES
+/*
+ * Constant passed to the interrupt handler in the 'id' field when the
+ * framework does not read the gic registers to determine the interrupt id.
+ */
+#define INTR_ID_UNAVAILABLE 0xFFFFFFFF
+
+
+/*******************************************************************************
+ * Mask for _both_ the routing model bits in the 'flags' parameter and
+ * constants to define the valid routing models for each supported interrupt
+ * type
+ ******************************************************************************/
+#define INTR_RM_FLAGS_SHIFT 0x0
+#define INTR_RM_FLAGS_MASK 0x3
+/* Routed to EL3 from NS. Taken to S-EL1 from Secure */
+#define INTR_SEL1_VALID_RM0 0x2
+/* Routed to EL3 from NS and Secure */
+#define INTR_SEL1_VALID_RM1 0x3
+/* Routed to EL1/EL2 from NS and to S-EL1 from Secure */
+#define INTR_NS_VALID_RM0 0x0
+/* Routed to EL1/EL2 from NS and to EL3 from Secure */
+#define INTR_NS_VALID_RM1 0x1
+
+
+/*******************************************************************************
+ * Constants for the _individual_ routing model bits in the 'flags' field for
+ * each interrupt type and mask to validate the 'flags' parameter while
+ * registering an interrupt handler
+ ******************************************************************************/
+#define INTR_TYPE_FLAGS_MASK 0xFFFFFFFC
+
+#define INTR_RM_FROM_SEC_SHIFT SECURE /* BIT[0] */
+#define INTR_RM_FROM_NS_SHIFT NON_SECURE /* BIT[1] */
+#define INTR_RM_FROM_FLAG_MASK 1
+#define get_interrupt_rm_flag(flag, ss) (((flag >> INTR_RM_FLAGS_SHIFT) >> ss) \
+ & INTR_RM_FROM_FLAG_MASK)
+#define set_interrupt_rm_flag(flag, ss) (flag |= 1 << ss)
+#define clr_interrupt_rm_flag(flag, ss) (flag &= ~(1 << ss))
+
+
+/*******************************************************************************
+ * Macros to validate the routing model bits in the 'flags' for a type
+ * of interrupt. If the model does not match one of the valid masks
+ * -EINVAL is returned.
+ ******************************************************************************/
+#define validate_sel1_interrupt_rm(x) (x == INTR_SEL1_VALID_RM0 ? 0 : \
+ (x == INTR_SEL1_VALID_RM1 ? 0 :\
+ -EINVAL))
+
+#define validate_ns_interrupt_rm(x) (x == INTR_NS_VALID_RM0 ? 0 : \
+ (x == INTR_NS_VALID_RM1 ? 0 :\
+ -EINVAL))
+
+/*******************************************************************************
+ * Macros to set the 'flags' parameter passed to an interrupt type handler. Only
+ * the flag to indicate the security state when the exception was generated is
+ * supported.
+ ******************************************************************************/
+#define INTR_SRC_SS_FLAG_SHIFT 0 /* BIT[0] */
+#define INTR_SRC_SS_FLAG_MASK 1
+#define set_interrupt_src_ss(flag, val) (flag |= val << INTR_SRC_SS_FLAG_SHIFT)
+#define clr_interrupt_src_ss(flag) (flag &= ~(1 << INTR_SRC_SS_FLAG_SHIFT))
+#define get_interrupt_src_ss(flag) ((flag >> INTR_SRC_SS_FLAG_SHIFT) & \
+ INTR_SRC_SS_FLAG_MASK)
+
+#ifndef __ASSEMBLY__
+
+/* Prototype for defining a handler for an interrupt type */
+typedef uint64_t (*interrupt_type_handler_t)(uint32_t id,
+ uint32_t flags,
+ void *handle,
+ void *cookie);
+
+/*******************************************************************************
+ * Function & variable prototypes
+ ******************************************************************************/
+extern uint32_t get_scr_el3_from_routing_model(uint32_t security_state);
+extern int32_t set_routing_model(uint32_t type, uint32_t flags);
+extern int32_t register_interrupt_type_handler(uint32_t type,
+ interrupt_type_handler_t handler,
+ uint32_t flags);
+extern interrupt_type_handler_t get_interrupt_type_handler(uint32_t interrupt_type);
+
+#endif /*__ASSEMBLY__*/
+#endif /* __INTERRUPT_MGMT_H__ */
diff --git a/include/bl31/runtime_svc.h b/include/bl31/runtime_svc.h
index 6d70896..66562e1 100644
--- a/include/bl31/runtime_svc.h
+++ b/include/bl31/runtime_svc.h
@@ -51,13 +51,15 @@
#define GET_SMC_CC(id) ((id >> FUNCID_CC_SHIFT) & \
FUNCID_CC_MASK)
+#define GET_SMC_TYPE(id) ((id >> FUNCID_TYPE_SHIFT) & \
+ FUNCID_TYPE_MASK)
#define SMC_64 1
#define SMC_32 0
#define SMC_UNK 0xffffffff
#define SMC_TYPE_FAST 1
#define SMC_TYPE_STD 0
-
+#define SMC_PREEMPTED 0xfffffffe
/*******************************************************************************
* Owning entity number definitions inside the function id as per the SMC
* calling convention
@@ -135,9 +137,12 @@
typedef int32_t (*rt_svc_init_t)(void);
/* Convenience macros to return from SMC handler */
+#define SMC_RET0(_h) { \
+ return (uint64_t) (_h); \
+}
#define SMC_RET1(_h, _x0) { \
write_ctx_reg(get_gpregs_ctx(_h), CTX_GPREG_X0, (_x0)); \
- return _x0; \
+ SMC_RET0(_h); \
}
#define SMC_RET2(_h, _x0, _x1) { \
write_ctx_reg(get_gpregs_ctx(_h), CTX_GPREG_X1, (_x1)); \
diff --git a/include/bl31/services/psci.h b/include/bl31/services/psci.h
index 570fe5b..b6e272c 100644
--- a/include/bl31/services/psci.h
+++ b/include/bl31/services/psci.h
@@ -190,6 +190,7 @@
extern int psci_cpu_on(unsigned long,
unsigned long,
unsigned long);
+extern void __dead2 psci_power_down_wfi(void);
extern void psci_aff_on_finish_entry(void);
extern void psci_aff_suspend_finish_entry(void);
extern void psci_register_spd_pm_hook(const spd_pm_ops_t *);
diff --git a/include/bl32/payloads/tsp.h b/include/bl32/payloads/tsp.h
index 1f542d5..9239ba4 100644
--- a/include/bl32/payloads/tsp.h
+++ b/include/bl32/payloads/tsp.h
@@ -40,16 +40,44 @@
#define TSP_OFF_DONE 0xf2000002
#define TSP_SUSPEND_DONE 0xf2000003
#define TSP_RESUME_DONE 0xf2000004
-#define TSP_WORK_DONE 0xf2000005
+#define TSP_PREEMPTED 0xf2000005
-/* SMC function ID that TSP uses to request service from secure montior */
+/*
+ * Function identifiers to handle FIQs through the synchronous handling model.
+ * If the TSP was previously interrupted then control has to be returned to
+ * the TSPD after handling the interrupt else execution can remain in the TSP.
+ */
+#define TSP_HANDLED_S_EL1_FIQ 0xf2000006
+#define TSP_EL3_FIQ 0xf2000007
+
+/* SMC function ID that TSP uses to request service from secure monitor */
#define TSP_GET_ARGS 0xf2001000
+/*
+ * Identifiers for various TSP services. Corresponding function IDs (whether
+ * fast or standard) are generated by macros defined below
+ */
+#define TSP_ADD 0x2000
+#define TSP_SUB 0x2001
+#define TSP_MUL 0x2002
+#define TSP_DIV 0x2003
+#define TSP_HANDLE_FIQ_AND_RETURN 0x2004
+
-/* Function IDs for various TSP services */
-#define TSP_FID_ADD 0xf2002000
-#define TSP_FID_SUB 0xf2002001
-#define TSP_FID_MUL 0xf2002002
-#define TSP_FID_DIV 0xf2002003
+/*
+ * Generate function IDs for TSP services to be used in SMC calls, by
+ * appropriately setting bit 31 to differentiate standard and fast SMC calls
+ */
+#define TSP_STD_FID(fid) ((fid) | 0x72000000 | (0 << 31))
+#define TSP_FAST_FID(fid) ((fid) | 0x72000000 | (1 << 31))
+
+/* SMC function ID to request a previously preempted std smc */
+#define TSP_FID_RESUME TSP_STD_FID(0x3000)
+
+/*
+ * Identify a TSP service from function ID filtering the last 16 bits from the
+ * SMC function ID
+ */
+#define TSP_BARE_FID(fid) ((fid) & 0xffff)
/*
* Total number of function IDs implemented for services offered to NS clients.
@@ -86,26 +114,26 @@
#include <cassert.h>
#include <platform.h> /* For CACHE_WRITEBACK_GRANULE */
+#include <spinlock.h>
#include <stdint.h>
-typedef void (*tsp_generic_fptr_t)(uint64_t arg0,
- uint64_t arg1,
- uint64_t arg2,
- uint64_t arg3,
- uint64_t arg4,
- uint64_t arg5,
- uint64_t arg6,
- uint64_t arg7);
+typedef uint32_t tsp_vector_isn_t;
-typedef struct entry_info {
- tsp_generic_fptr_t fast_smc_entry;
- tsp_generic_fptr_t cpu_on_entry;
- tsp_generic_fptr_t cpu_off_entry;
- tsp_generic_fptr_t cpu_resume_entry;
- tsp_generic_fptr_t cpu_suspend_entry;
-} entry_info_t;
+typedef struct tsp_vectors {
+ tsp_vector_isn_t std_smc_entry;
+ tsp_vector_isn_t fast_smc_entry;
+ tsp_vector_isn_t cpu_on_entry;
+ tsp_vector_isn_t cpu_off_entry;
+ tsp_vector_isn_t cpu_resume_entry;
+ tsp_vector_isn_t cpu_suspend_entry;
+ tsp_vector_isn_t fiq_entry;
+} tsp_vectors_t;
typedef struct work_statistics {
+ uint32_t fiq_count; /* Number of FIQs on this cpu */
+ uint32_t irq_count; /* Number of IRQs on this cpu */
+ uint32_t sync_fiq_count; /* Number of sync. fiqs on this cpu */
+ uint32_t sync_fiq_ret_count; /* Number of fiq returns on this cpu */
uint32_t smc_count; /* Number of returns on this cpu */
uint32_t eret_count; /* Number of entries on this cpu */
uint32_t cpu_on_count; /* Number of cpu on requests */
@@ -120,7 +148,7 @@
/* Macros to access members of the above structure using their offsets */
#define read_sp_arg(args, offset) ((args)->_regs[offset >> 3])
-#define write_sp_arg(args, offset, val)(((args)->_regs[offset >> 3]) \
+#define write_sp_arg(args, offset, val) (((args)->_regs[offset >> 3]) \
= val)
/*
@@ -131,22 +159,6 @@
extern void tsp_get_magic(uint64_t args[4]);
-extern void tsp_fast_smc_entry(uint64_t arg0,
- uint64_t arg1,
- uint64_t arg2,
- uint64_t arg3,
- uint64_t arg4,
- uint64_t arg5,
- uint64_t arg6,
- uint64_t arg7);
-extern void tsp_cpu_resume_entry(uint64_t arg0,
- uint64_t arg1,
- uint64_t arg2,
- uint64_t arg3,
- uint64_t arg4,
- uint64_t arg5,
- uint64_t arg6,
- uint64_t arg7);
extern tsp_args_t *tsp_cpu_resume_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
@@ -155,14 +167,6 @@
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
-extern void tsp_cpu_suspend_entry(uint64_t arg0,
- uint64_t arg1,
- uint64_t arg2,
- uint64_t arg3,
- uint64_t arg4,
- uint64_t arg5,
- uint64_t arg6,
- uint64_t arg7);
extern tsp_args_t *tsp_cpu_suspend_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
@@ -171,23 +175,7 @@
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
-extern void tsp_cpu_on_entry(uint64_t arg0,
- uint64_t arg1,
- uint64_t arg2,
- uint64_t arg3,
- uint64_t arg4,
- uint64_t arg5,
- uint64_t arg6,
- uint64_t arg7);
extern tsp_args_t *tsp_cpu_on_main(void);
-extern void tsp_cpu_off_entry(uint64_t arg0,
- uint64_t arg1,
- uint64_t arg2,
- uint64_t arg3,
- uint64_t arg4,
- uint64_t arg5,
- uint64_t arg6,
- uint64_t arg7);
extern tsp_args_t *tsp_cpu_off_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
@@ -196,6 +184,24 @@
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
+
+/* Generic Timer functions */
+extern void tsp_generic_timer_start(void);
+extern void tsp_generic_timer_handler(void);
+extern void tsp_generic_timer_stop(void);
+extern void tsp_generic_timer_save(void);
+extern void tsp_generic_timer_restore(void);
+
+/* FIQ management functions */
+extern void tsp_update_sync_fiq_stats(uint32_t type, uint64_t elr_el3);
+
+/* Data structure to keep track of TSP statistics */
+extern spinlock_t console_lock;
+extern work_statistics_t tsp_stats[PLATFORM_CORE_COUNT];
+
+/* Vector table of jumps */
+extern tsp_vectors_t tsp_vector_table;
+
#endif /* __ASSEMBLY__ */
#endif /* __BL2_H__ */
diff --git a/include/drivers/arm/gic_v2.h b/include/drivers/arm/gic_v2.h
index ccf3d32..e819676 100644
--- a/include/drivers/arm/gic_v2.h
+++ b/include/drivers/arm/gic_v2.h
@@ -43,6 +43,7 @@
#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_SPURIOUS_INTERRUPT 1023
#define ENABLE_GRP0 (1 << 0)
#define ENABLE_GRP1 (1 << 1)
@@ -88,6 +89,7 @@
#define GICC_EOIR 0x10
#define GICC_RPR 0x14
#define GICC_HPPIR 0x18
+#define GICC_AHPPIR 0x28
#define GICC_IIDR 0xFC
#define GICC_DIR 0x1000
#define GICC_PRIODROP GICC_EOIR
@@ -247,6 +249,11 @@
return mmio_read_32(base + GICC_HPPIR);
}
+static inline unsigned int gicc_read_ahppir(unsigned int base)
+{
+ return mmio_read_32(base + GICC_AHPPIR);
+}
+
static inline unsigned int gicc_read_dir(unsigned int base)
{
return mmio_read_32(base + GICC_DIR);
@@ -298,6 +305,12 @@
mmio_write_32(base + GICC_DIR, val);
}
+/*******************************************************************************
+ * Prototype of function to map an interrupt type to the interrupt line used to
+ * signal it.
+ ******************************************************************************/
+uint32_t gicv2_interrupt_type_to_line(uint32_t cpuif_base, uint32_t type);
+
#endif /*__ASSEMBLY__*/
#endif /* __GIC_V2_H__ */
diff --git a/include/drivers/arm/pl011.h b/include/drivers/arm/pl011.h
index 1254920..281330e 100644
--- a/include/drivers/arm/pl011.h
+++ b/include/drivers/arm/pl011.h
@@ -107,32 +107,32 @@
* Pl011 CPU interface accessors for writing registers
******************************************************************************/
-static inline void pl011_write_ibrd(unsigned int base, unsigned int val)
+static inline void pl011_write_ibrd(unsigned long base, unsigned int val)
{
mmio_write_32(base + UARTIBRD, val);
}
-static inline void pl011_write_fbrd(unsigned int base, unsigned int val)
+static inline void pl011_write_fbrd(unsigned long base, unsigned int val)
{
mmio_write_32(base + UARTFBRD, val);
}
-static inline void pl011_write_lcr_h(unsigned int base, unsigned int val)
+static inline void pl011_write_lcr_h(unsigned long base, unsigned int val)
{
mmio_write_32(base + UARTLCR_H, val);
}
-static inline void pl011_write_ecr(unsigned int base, unsigned int val)
+static inline void pl011_write_ecr(unsigned long base, unsigned int val)
{
mmio_write_32(base + UARTECR, val);
}
-static inline void pl011_write_cr(unsigned int base, unsigned int val)
+static inline void pl011_write_cr(unsigned long base, unsigned int val)
{
mmio_write_32(base + UARTCR, val);
}
-static inline void pl011_write_dr(unsigned int base, unsigned int val)
+static inline void pl011_write_dr(unsigned long base, unsigned int val)
{
mmio_write_32(base + UARTDR, val);
}
@@ -141,12 +141,12 @@
* Pl011 CPU interface accessors for reading registers
******************************************************************************/
-static inline unsigned int pl011_read_fr(unsigned int base)
+static inline unsigned int pl011_read_fr(unsigned long base)
{
return mmio_read_32(base + UARTFR);
}
-static inline unsigned int pl011_read_dr(unsigned int base)
+static inline unsigned int pl011_read_dr(unsigned long base)
{
return mmio_read_32(base + UARTDR);
}
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index 1c11af3..920dfc9 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -148,6 +148,7 @@
#define SCR_FIQ_BIT (1 << 2)
#define SCR_IRQ_BIT (1 << 1)
#define SCR_NS_BIT (1 << 0)
+#define SCR_VALID_BIT_MASK 0x2f8f
/* HCR definitions */
#define HCR_RW_BIT (1ull << 31)
@@ -264,6 +265,28 @@
((aif) & SPSR_AIF_MASK) << SPSR_AIF_SHIFT)
+/* Physical timer control register bit fields shifts and masks */
+#define CNTP_CTL_ENABLE_SHIFT 0
+#define CNTP_CTL_IMASK_SHIFT 1
+#define CNTP_CTL_ISTATUS_SHIFT 2
+
+#define CNTP_CTL_ENABLE_MASK 1
+#define CNTP_CTL_IMASK_MASK 1
+#define CNTP_CTL_ISTATUS_MASK 1
+
+#define get_cntp_ctl_enable(x) ((x >> CNTP_CTL_ENABLE_SHIFT) & \
+ CNTP_CTL_ENABLE_MASK)
+#define get_cntp_ctl_imask(x) ((x >> CNTP_CTL_IMASK_SHIFT) & \
+ CNTP_CTL_IMASK_MASK)
+#define get_cntp_ctl_istatus(x) ((x >> CNTP_CTL_ISTATUS_SHIFT) & \
+ CNTP_CTL_ISTATUS_MASK)
+
+#define set_cntp_ctl_enable(x) (x |= 1 << CNTP_CTL_ENABLE_SHIFT)
+#define set_cntp_ctl_imask(x) (x |= 1 << CNTP_CTL_IMASK_SHIFT)
+
+#define clr_cntp_ctl_enable(x) (x &= ~(1 << CNTP_CTL_ENABLE_SHIFT))
+#define clr_cntp_ctl_imask(x) (x &= ~(1 << CNTP_CTL_IMASK_SHIFT))
+
/* Miscellaneous MMU related constants */
#define NUM_2MB_IN_GB (1 << 9)
#define NUM_4K_IN_2MB (1 << 9)
diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h
index 0a398d0..f30301d 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -202,6 +202,10 @@
extern unsigned long read_cpacr(void);
extern unsigned long read_cpuectlr(void);
extern unsigned int read_cntfrq_el0(void);
+extern unsigned int read_cntps_ctl_el1(void);
+extern unsigned int read_cntps_tval_el1(void);
+extern unsigned long read_cntps_cval_el1(void);
+extern unsigned long read_cntpct_el0(void);
extern unsigned long read_cnthctl_el2(void);
extern unsigned long read_tpidr_el3(void);
@@ -210,6 +214,9 @@
extern void write_hcr(unsigned long);
extern void write_cpacr(unsigned long);
extern void write_cntfrq_el0(unsigned int);
+extern void write_cntps_ctl_el1(unsigned int);
+extern void write_cntps_tval_el1(unsigned int);
+extern void write_cntps_cval_el1(unsigned long);
extern void write_cnthctl_el2(unsigned long);
extern void write_vbar_el1(unsigned long);
diff --git a/lib/aarch64/sysreg_helpers.S b/lib/aarch64/sysreg_helpers.S
index c86fdba..925e93e 100644
--- a/lib/aarch64/sysreg_helpers.S
+++ b/lib/aarch64/sysreg_helpers.S
@@ -142,6 +142,15 @@
.globl read_cntfrq_el0
.globl write_cntfrq_el0
+ .globl read_cntps_ctl_el1
+ .globl write_cntps_ctl_el1
+
+ .globl read_cntps_cval_el1
+ .globl write_cntps_cval_el1
+
+ .globl read_cntps_tval_el1
+ .globl write_cntps_tval_el1
+
.globl read_scr
.globl write_scr
@@ -151,6 +160,7 @@
.globl read_midr
.globl read_mpidr
+ .globl read_cntpct_el0
.globl read_current_el
.globl read_id_pfr1_el1
.globl read_id_aa64pfr0_el1
@@ -672,6 +682,33 @@
msr cntfrq_el0, x0
ret
+func read_cntps_ctl_el1
+ mrs x0, cntps_ctl_el1
+ ret
+
+func write_cntps_ctl_el1
+ msr cntps_ctl_el1, x0
+ ret
+
+func read_cntps_cval_el1
+ mrs x0, cntps_cval_el1
+ ret
+
+func write_cntps_cval_el1
+ msr cntps_cval_el1, x0
+ ret
+
+func read_cntps_tval_el1
+ mrs x0, cntps_tval_el1
+ ret
+
+func write_cntps_tval_el1
+ msr cntps_tval_el1, x0
+ ret
+
+func read_cntpct_el0
+ mrs x0, cntpct_el0
+ ret
func read_cpuectlr
mrs x0, CPUECTLR_EL1
diff --git a/lib/aarch64/xlat_tables.c b/lib/aarch64/xlat_tables.c
index 6e04f65..48b0714 100644
--- a/lib/aarch64/xlat_tables.c
+++ b/lib/aarch64/xlat_tables.c
@@ -173,7 +173,7 @@
unsigned level_size_shift = L1_XLAT_ADDRESS_SHIFT - (level - 1) *
XLAT_TABLE_ENTRIES_SHIFT;
unsigned level_size = 1 << level_size_shift;
- unsigned level_index_mask = XLAT_TABLE_ENTRIES_MASK << level_size_shift;
+ unsigned long level_index_mask = XLAT_TABLE_ENTRIES_MASK << level_size_shift;
assert(level <= 3);
diff --git a/plat/fvp/aarch64/plat_common.c b/plat/fvp/aarch64/plat_common.c
index 29bf602..2845f3e 100644
--- a/plat/fvp/aarch64/plat_common.c
+++ b/plat/fvp/aarch64/plat_common.c
@@ -122,7 +122,7 @@
{ DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW | MT_SECURE },
/* 2nd GB as device for now...*/
{ 0x40000000, 0x40000000, MT_DEVICE | MT_RW | MT_SECURE },
- { DRAM_BASE, DRAM_SIZE, MT_MEMORY | MT_RW | MT_NS },
+ { DRAM1_BASE, DRAM1_SIZE, MT_MEMORY | MT_RW | MT_NS },
{0}
};
diff --git a/plat/fvp/bl2_plat_setup.c b/plat/fvp/bl2_plat_setup.c
index b945db9..cf987ff 100644
--- a/plat/fvp/bl2_plat_setup.c
+++ b/plat/fvp/bl2_plat_setup.c
@@ -285,9 +285,9 @@
void bl2_plat_get_bl33_meminfo(meminfo_t *bl33_meminfo)
{
bl33_meminfo->total_base = DRAM_BASE;
- bl33_meminfo->total_size = DRAM_SIZE;
+ bl33_meminfo->total_size = DRAM_SIZE - DRAM1_SEC_SIZE;
bl33_meminfo->free_base = DRAM_BASE;
- bl33_meminfo->free_size = DRAM_SIZE;
+ bl33_meminfo->free_size = DRAM_SIZE - DRAM1_SEC_SIZE;
bl33_meminfo->attr = 0;
bl33_meminfo->attr = 0;
}
diff --git a/plat/fvp/bl31_plat_setup.c b/plat/fvp/bl31_plat_setup.c
index 033a8fa..7b60a78 100644
--- a/plat/fvp/bl31_plat_setup.c
+++ b/plat/fvp/bl31_plat_setup.c
@@ -210,8 +210,8 @@
fvp_cci_setup();
#endif
- configure_mmu_el3(TZRAM_BASE,
- TZRAM_SIZE,
+ configure_mmu_el3(BL31_RO_BASE,
+ (BL31_COHERENT_RAM_LIMIT - BL31_RO_BASE),
BL31_RO_BASE,
BL31_RO_LIMIT,
BL31_COHERENT_RAM_BASE,
diff --git a/plat/fvp/bl32_plat_setup.c b/plat/fvp/bl32_plat_setup.c
index 8406d31..772e972 100644
--- a/plat/fvp/bl32_plat_setup.c
+++ b/plat/fvp/bl32_plat_setup.c
@@ -73,6 +73,9 @@
* messages from TSP
*/
console_init(PL011_UART1_BASE);
+
+ /* Initialize the platform config for future decision making */
+ platform_config_setup();
}
/*******************************************************************************
diff --git a/plat/fvp/plat_gic.c b/plat/fvp/plat_gic.c
index db3c9cf..7dec404 100644
--- a/plat/fvp/plat_gic.c
+++ b/plat/fvp/plat_gic.c
@@ -29,18 +29,15 @@
*/
#include <arch_helpers.h>
+#include <assert.h>
+#include <bl_common.h>
#include <debug.h>
#include <gic_v2.h>
#include <gic_v3.h>
+#include <interrupt_mgmt.h>
#include <platform.h>
#include <stdint.h>
-
-/*******************************************************************************
- * TODO: Revisit if priorities are being set such that no non-secure interrupt
- * can have a higher priority than a secure one as recommended in the GICv2 spec
- ******************************************************************************/
-
/*******************************************************************************
* This function does some minimal GICv3 configuration. The Firmware itself does
* not fully support GICv3 at this time and relies on GICv2 emulation as
@@ -284,3 +281,126 @@
gic_cpuif_setup(gicc_base);
gic_distif_setup(gicd_base);
}
+
+/*******************************************************************************
+ * An ARM processor signals interrupt exceptions through the IRQ and FIQ pins.
+ * The interrupt controller knows which pin/line it uses to signal a type of
+ * interrupt. The platform knows which interrupt controller type is being used
+ * in a particular security state e.g. with an ARM GIC, normal world could use
+ * the GICv2 features while the secure world could use GICv3 features and vice
+ * versa.
+ * This function is exported by the platform to let the interrupt management
+ * framework determine for a type of interrupt and security state, which line
+ * should be used in the SCR_EL3 to control its routing to EL3. The interrupt
+ * line is represented as the bit position of the IRQ or FIQ bit in the SCR_EL3.
+ ******************************************************************************/
+uint32_t plat_interrupt_type_to_line(uint32_t type, uint32_t security_state)
+{
+ uint32_t gicc_base = platform_get_cfgvar(CONFIG_GICC_ADDR);
+
+ assert(type == INTR_TYPE_S_EL1 ||
+ type == INTR_TYPE_EL3 ||
+ type == INTR_TYPE_NS);
+
+ assert(security_state == NON_SECURE || security_state == SECURE);
+
+ /*
+ * We ignore the security state parameter under the assumption that
+ * both normal and secure worlds are using ARM GICv2. This parameter
+ * will be used when the secure world starts using GICv3.
+ */
+#if FVP_GIC_ARCH == 2
+ return gicv2_interrupt_type_to_line(gicc_base, type);
+#else
+#error "Invalid GIC architecture version specified for FVP port"
+#endif
+}
+
+#if FVP_GIC_ARCH == 2
+/*******************************************************************************
+ * This function returns the type of the highest priority pending interrupt at
+ * the GIC cpu interface. INTR_TYPE_INVAL is returned when there is no
+ * interrupt pending.
+ ******************************************************************************/
+uint32_t ic_get_pending_interrupt_type()
+{
+ uint32_t id, gicc_base;
+
+ gicc_base = platform_get_cfgvar(CONFIG_GICC_ADDR);
+ id = gicc_read_hppir(gicc_base);
+
+ /* Assume that all secure interrupts are S-EL1 interrupts */
+ if (id < 1022)
+ return INTR_TYPE_S_EL1;
+
+ if (id == GIC_SPURIOUS_INTERRUPT)
+ return INTR_TYPE_INVAL;
+
+ return INTR_TYPE_NS;
+}
+
+/*******************************************************************************
+ * This function returns the id of the highest priority pending interrupt at
+ * the GIC cpu interface. INTR_ID_UNAVAILABLE is returned when there is no
+ * interrupt pending.
+ ******************************************************************************/
+uint32_t ic_get_pending_interrupt_id()
+{
+ uint32_t id, gicc_base;
+
+ gicc_base = platform_get_cfgvar(CONFIG_GICC_ADDR);
+ id = gicc_read_hppir(gicc_base);
+
+ if (id < 1022)
+ return id;
+
+ if (id == 1023)
+ return INTR_ID_UNAVAILABLE;
+
+ /*
+ * Find out which non-secure interrupt it is under the assumption that
+ * the GICC_CTLR.AckCtl bit is 0.
+ */
+ return gicc_read_ahppir(gicc_base);
+}
+
+/*******************************************************************************
+ * This functions reads the GIC cpu interface Interrupt Acknowledge register
+ * to start handling the pending interrupt. It returns the contents of the IAR.
+ ******************************************************************************/
+uint32_t ic_acknowledge_interrupt()
+{
+ return gicc_read_IAR(platform_get_cfgvar(CONFIG_GICC_ADDR));
+}
+
+/*******************************************************************************
+ * This functions writes the GIC cpu interface End Of Interrupt register with
+ * the passed value to finish handling the active interrupt
+ ******************************************************************************/
+void ic_end_of_interrupt(uint32_t id)
+{
+ gicc_write_EOIR(platform_get_cfgvar(CONFIG_GICC_ADDR), id);
+ return;
+}
+
+/*******************************************************************************
+ * This function returns the type of the interrupt id depending upon the group
+ * this interrupt has been configured under by the interrupt controller i.e.
+ * group0 or group1.
+ ******************************************************************************/
+uint32_t ic_get_interrupt_type(uint32_t id)
+{
+ uint32_t group;
+
+ group = gicd_get_igroupr(platform_get_cfgvar(CONFIG_GICD_ADDR), id);
+
+ /* Assume that all secure interrupts are S-EL1 interrupts */
+ if (group == GRP0)
+ return INTR_TYPE_S_EL1;
+ else
+ return INTR_TYPE_NS;
+}
+
+#else
+#error "Invalid GIC architecture version specified for FVP port"
+#endif
diff --git a/plat/fvp/plat_security.c b/plat/fvp/plat_security.c
index c39907a..9da5612 100644
--- a/plat/fvp/plat_security.c
+++ b/plat/fvp/plat_security.c
@@ -100,16 +100,23 @@
/* Set to cover the first block of DRAM */
tzc_configure_region(&controller, FILTER_SHIFT(0), 1,
- DRAM_BASE, 0xFFFFFFFF, TZC_REGION_S_NONE,
+ DRAM1_BASE, DRAM1_END - DRAM1_SEC_SIZE,
+ TZC_REGION_S_NONE,
TZC_REGION_ACCESS_RDWR(FVP_NSAID_DEFAULT) |
TZC_REGION_ACCESS_RDWR(FVP_NSAID_PCI) |
TZC_REGION_ACCESS_RDWR(FVP_NSAID_AP) |
TZC_REGION_ACCESS_RDWR(FVP_NSAID_VIRTIO) |
TZC_REGION_ACCESS_RDWR(FVP_NSAID_VIRTIO_OLD));
+ /* Set to cover the secure reserved region */
+ tzc_configure_region(&controller, FILTER_SHIFT(0), 3,
+ (DRAM1_END - DRAM1_SEC_SIZE) + 1 , DRAM1_END,
+ TZC_REGION_S_RDWR,
+ 0x0);
+
/* Set to cover the second block of DRAM */
tzc_configure_region(&controller, FILTER_SHIFT(0), 2,
- 0x880000000, 0xFFFFFFFFF, TZC_REGION_S_NONE,
+ DRAM2_BASE, DRAM2_END, TZC_REGION_S_NONE,
TZC_REGION_ACCESS_RDWR(FVP_NSAID_DEFAULT) |
TZC_REGION_ACCESS_RDWR(FVP_NSAID_PCI) |
TZC_REGION_ACCESS_RDWR(FVP_NSAID_AP) |
diff --git a/plat/fvp/platform.h b/plat/fvp/platform.h
index 7fdbf81..786988c 100644
--- a/plat/fvp/platform.h
+++ b/plat/fvp/platform.h
@@ -68,7 +68,7 @@
/* Non-Trusted Firmware BL33 and its load address */
#define BL33_IMAGE_NAME "bl33.bin" /* e.g. UEFI */
-#define NS_IMAGE_OFFSET (DRAM_BASE + 0x8000000) /* DRAM + 128MB */
+#define NS_IMAGE_OFFSET (DRAM1_BASE + 0x8000000) /* DRAM + 128MB */
/* Firmware Image Package */
#define FIP_IMAGE_NAME "fip.bin"
@@ -139,8 +139,17 @@
#define PARAMS_BASE TZDRAM_BASE
-#define DRAM_BASE 0x80000000ull
-#define DRAM_SIZE 0x80000000ull
+#define DRAM1_BASE 0x80000000ull
+#define DRAM1_SIZE 0x80000000ull
+#define DRAM1_END (DRAM1_BASE + DRAM1_SIZE - 1)
+#define DRAM1_SEC_SIZE 0x01000000ull
+
+#define DRAM_BASE DRAM1_BASE
+#define DRAM_SIZE DRAM1_SIZE
+
+#define DRAM2_BASE 0x880000000ull
+#define DRAM2_SIZE 0x780000000ull
+#define DRAM2_END (DRAM2_BASE + DRAM2_SIZE - 1)
#define PCIE_EXP_BASE 0x40000000
#define TZRNG_BASE 0x7fe60000
@@ -459,13 +468,20 @@
extern void fvp_cci_setup(void);
-/* Declarations for fvp_gic.c */
+/* Declarations for plat_gic.c */
+extern uint32_t ic_get_pending_interrupt_id(void);
+extern uint32_t ic_get_pending_interrupt_type(void);
+extern uint32_t ic_acknowledge_interrupt(void);
+extern uint32_t ic_get_interrupt_type(uint32_t id);
+extern void ic_end_of_interrupt(uint32_t id);
extern void gic_cpuif_deactivate(unsigned int);
extern void gic_cpuif_setup(unsigned int);
extern void gic_pcpu_distif_setup(unsigned int);
extern void gic_setup(void);
+extern uint32_t plat_interrupt_type_to_line(uint32_t type,
+ uint32_t security_state);
-/* Declarations for fvp_topology.c */
+/* Declarations for plat_topology.c */
extern int plat_setup_topology(void);
extern int plat_get_max_afflvl(void);
extern unsigned int plat_get_aff_count(unsigned int, unsigned long);
diff --git a/plat/fvp/platform.mk b/plat/fvp/platform.mk
index 82bafed..f1d6f87 100644
--- a/plat/fvp/platform.mk
+++ b/plat/fvp/platform.mk
@@ -86,3 +86,8 @@
BL31_SOURCES += drivers/arm/tzc400/tzc400.c \
plat/fvp/plat_security.c
endif
+
+# Flag used by the FVP port to determine the version of ARM GIC architecture
+# to use for interrupt management in EL3.
+FVP_GIC_ARCH := 2
+$(eval $(call add_define,FVP_GIC_ARCH))
diff --git a/services/spd/tspd/tspd_common.c b/services/spd/tspd/tspd_common.c
index d3fe5dd..2ca6a56 100644
--- a/services/spd/tspd/tspd_common.c
+++ b/services/spd/tspd/tspd_common.c
@@ -42,9 +42,9 @@
* programming an entry into the secure payload.
******************************************************************************/
int32_t tspd_init_secure_context(uint64_t entrypoint,
- uint32_t rw,
- uint64_t mpidr,
- tsp_context_t *tsp_ctx)
+ uint32_t rw,
+ uint64_t mpidr,
+ tsp_context_t *tsp_ctx)
{
uint32_t scr, sctlr;
el1_sys_regs_t *el1_state;
@@ -65,10 +65,14 @@
*/
memset(tsp_ctx, 0, sizeof(*tsp_ctx));
- /* Set the right security state and register width for the SP */
+ /*
+ * Set the right security state, register width and enable access to
+ * the secure physical timer for the SP.
+ */
scr = read_scr();
scr &= ~SCR_NS_BIT;
scr &= ~SCR_RW_BIT;
+ scr |= SCR_ST_BIT;
if (rw == TSP_AARCH64)
scr |= SCR_RW_BIT;
@@ -85,7 +89,14 @@
write_ctx_reg(el1_state, CTX_SCTLR_EL1, sctlr);
/* Set this context as ready to be initialised i.e OFF */
- tsp_ctx->state = TSP_STATE_OFF;
+ set_tsp_pstate(tsp_ctx->state, TSP_PSTATE_OFF);
+
+ /*
+ * This context has not been used yet. It will become valid
+ * when the TSP is interrupted and wants the TSPD to preserve
+ * the context.
+ */
+ clr_std_smc_active_flag(tsp_ctx->state);
/* Associate this context with the cpu specified */
tsp_ctx->mpidr = mpidr;
diff --git a/services/spd/tspd/tspd_main.c b/services/spd/tspd/tspd_main.c
index 21ff7ff..1dbe6ba 100644
--- a/services/spd/tspd/tspd_main.c
+++ b/services/spd/tspd/tspd_main.c
@@ -43,6 +43,9 @@
#include <bl_common.h>
#include <bl31.h>
#include <context_mgmt.h>
+#include <debug.h>
+#include <errno.h>
+#include <platform.h>
#include <runtime_svc.h>
#include <stddef.h>
#include <tsp.h>
@@ -50,10 +53,10 @@
#include "tspd_private.h"
/*******************************************************************************
- * Single structure to hold information about the various entry points into the
- * Secure Payload. It is initialised once on the primary core after a cold boot.
+ * Address of the entrypoint vector table in the Secure Payload. It is
+ * initialised once on the primary core after a cold boot.
******************************************************************************/
-entry_info_t *tsp_entry_info;
+tsp_vectors_t *tsp_vectors;
/*******************************************************************************
* Array to keep track of per-cpu Secure Payload state
@@ -68,6 +71,75 @@
int32_t tspd_init(void);
+/*******************************************************************************
+ * This function is the handler registered for S-EL1 interrupts by the TSPD. It
+ * validates the interrupt and upon success arranges entry into the TSP at
+ * 'tsp_fiq_entry()' for handling the interrupt.
+ ******************************************************************************/
+static uint64_t tspd_sel1_interrupt_handler(uint32_t id,
+ uint32_t flags,
+ void *handle,
+ void *cookie)
+{
+ uint32_t linear_id;
+ uint64_t mpidr;
+ tsp_context_t *tsp_ctx;
+
+ /* Check the security state when the exception was generated */
+ assert(get_interrupt_src_ss(flags) == NON_SECURE);
+
+#if IMF_READ_INTERRUPT_ID
+ /* Check the security status of the interrupt */
+ assert(ic_get_interrupt_group(id) == SECURE);
+#endif
+
+ /* Sanity check the pointer to this cpu's context */
+ mpidr = read_mpidr();
+ assert(handle == cm_get_context(mpidr, NON_SECURE));
+
+ /* Save the non-secure context before entering the TSP */
+ cm_el1_sysregs_context_save(NON_SECURE);
+
+ /* Get a reference to this cpu's TSP context */
+ linear_id = platform_get_core_pos(mpidr);
+ tsp_ctx = &tspd_sp_context[linear_id];
+ assert(&tsp_ctx->cpu_ctx == cm_get_context(mpidr, SECURE));
+
+ /*
+ * Determine if the TSP was previously preempted. Its last known
+ * context has to be preserved in this case.
+ * The TSP should return control to the TSPD after handling this
+ * FIQ. Preserve essential EL3 context to allow entry into the
+ * TSP at the FIQ entry point using the 'cpu_context' structure.
+ * There is no need to save the secure system register context
+ * since the TSP is supposed to preserve it during S-EL1 interrupt
+ * handling.
+ */
+ if (get_std_smc_active_flag(tsp_ctx->state)) {
+ tsp_ctx->saved_spsr_el3 = SMC_GET_EL3(&tsp_ctx->cpu_ctx,
+ CTX_SPSR_EL3);
+ tsp_ctx->saved_elr_el3 = SMC_GET_EL3(&tsp_ctx->cpu_ctx,
+ CTX_ELR_EL3);
+ }
+
+ SMC_SET_EL3(&tsp_ctx->cpu_ctx,
+ CTX_SPSR_EL3,
+ SPSR_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS));
+ SMC_SET_EL3(&tsp_ctx->cpu_ctx,
+ CTX_ELR_EL3,
+ (uint64_t) &tsp_vectors->fiq_entry);
+ cm_el1_sysregs_context_restore(SECURE);
+ cm_set_next_eret_context(SECURE);
+
+ /*
+ * Tell the TSP that it has to handle an FIQ synchronously. Also the
+ * instruction in normal world where the interrupt was generated is
+ * passed for debugging purposes. It is safe to retrieve this address
+ * from ELR_EL3 as the secure context will not take effect until
+ * el3_exit().
+ */
+ SMC_RET2(&tsp_ctx->cpu_ctx, TSP_HANDLE_FIQ_AND_RETURN, read_elr_el3());
+}
/*******************************************************************************
* Secure Payload Dispatcher setup. The SPD finds out the SP entrypoint and type
@@ -131,7 +203,7 @@
int32_t tspd_init(void)
{
uint64_t mpidr = read_mpidr();
- uint32_t linear_id = platform_get_core_pos(mpidr);
+ uint32_t linear_id = platform_get_core_pos(mpidr), flags;
uint64_t rc;
tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id];
@@ -142,7 +214,7 @@
rc = tspd_synchronous_sp_entry(tsp_ctx);
assert(rc != 0);
if (rc) {
- tsp_ctx->state = TSP_STATE_ON;
+ set_tsp_pstate(tsp_ctx->state, TSP_PSTATE_ON);
/*
* TSP has been successfully initialized. Register power
@@ -151,6 +223,18 @@
psci_register_spd_pm_hook(&tspd_pm);
}
+ /*
+ * Register an interrupt handler for S-EL1 interrupts when generated
+ * during code executing in the non-secure state.
+ */
+ flags = 0;
+ set_interrupt_rm_flag(flags, NON_SECURE);
+ rc = register_interrupt_type_handler(INTR_TYPE_S_EL1,
+ tspd_sel1_interrupt_handler,
+ flags);
+ if (rc)
+ panic();
+
return rc;
}
@@ -173,7 +257,6 @@
uint64_t flags)
{
cpu_context_t *ns_cpu_context;
- gp_regs_t *ns_gp_regs;
unsigned long mpidr = read_mpidr();
uint32_t linear_id = platform_get_core_pos(mpidr), ns;
tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id];
@@ -184,6 +267,98 @@
switch (smc_fid) {
/*
+ * This function ID is used by TSP to indicate that it was
+ * preempted by a normal world IRQ.
+ *
+ */
+ case TSP_PREEMPTED:
+ if (ns)
+ SMC_RET1(handle, SMC_UNK);
+
+ assert(handle == cm_get_context(mpidr, SECURE));
+ cm_el1_sysregs_context_save(SECURE);
+ /* Get a reference to the non-secure context */
+ ns_cpu_context = cm_get_context(mpidr, NON_SECURE);
+ assert(ns_cpu_context);
+
+ /*
+ * Restore non-secure state. There is no need to save the
+ * secure system register context since the TSP was supposed
+ * to preserve it during S-EL1 interrupt handling.
+ */
+ cm_el1_sysregs_context_restore(NON_SECURE);
+ cm_set_next_eret_context(NON_SECURE);
+
+ SMC_RET1(ns_cpu_context, SMC_PREEMPTED);
+
+ /*
+ * This function ID is used only by the TSP to indicate that it has
+ * finished handling a S-EL1 FIQ interrupt. Execution should resume
+ * in the normal world.
+ */
+ case TSP_HANDLED_S_EL1_FIQ:
+ if (ns)
+ SMC_RET1(handle, SMC_UNK);
+
+ assert(handle == cm_get_context(mpidr, SECURE));
+
+ /*
+ * Restore the relevant EL3 state which saved to service
+ * this SMC.
+ */
+ if (get_std_smc_active_flag(tsp_ctx->state)) {
+ SMC_SET_EL3(&tsp_ctx->cpu_ctx,
+ CTX_SPSR_EL3,
+ tsp_ctx->saved_spsr_el3);
+ SMC_SET_EL3(&tsp_ctx->cpu_ctx,
+ CTX_ELR_EL3,
+ tsp_ctx->saved_elr_el3);
+ }
+
+ /* Get a reference to the non-secure context */
+ ns_cpu_context = cm_get_context(mpidr, NON_SECURE);
+ assert(ns_cpu_context);
+
+ /*
+ * Restore non-secure state. There is no need to save the
+ * secure system register context since the TSP was supposed
+ * to preserve it during S-EL1 interrupt handling.
+ */
+ cm_el1_sysregs_context_restore(NON_SECURE);
+ cm_set_next_eret_context(NON_SECURE);
+
+ SMC_RET0((uint64_t) ns_cpu_context);
+
+
+ /*
+ * This function ID is used only by the TSP to indicate that it was
+ * interrupted due to a EL3 FIQ interrupt. Execution should resume
+ * in the normal world.
+ */
+ case TSP_EL3_FIQ:
+ if (ns)
+ SMC_RET1(handle, SMC_UNK);
+
+ assert(handle == cm_get_context(mpidr, SECURE));
+
+ /* Assert that standard SMC execution has been preempted */
+ assert(get_std_smc_active_flag(tsp_ctx->state));
+
+ /* Save the secure system register state */
+ cm_el1_sysregs_context_save(SECURE);
+
+ /* Get a reference to the non-secure context */
+ ns_cpu_context = cm_get_context(mpidr, NON_SECURE);
+ assert(ns_cpu_context);
+
+ /* Restore non-secure state */
+ cm_el1_sysregs_context_restore(NON_SECURE);
+ cm_set_next_eret_context(NON_SECURE);
+
+ SMC_RET1(ns_cpu_context, TSP_EL3_FIQ);
+
+
+ /*
* This function ID is used only by the SP to indicate it has
* finished initialising itself after a cold boot
*/
@@ -195,8 +370,8 @@
* Stash the SP entry points information. This is done
* only once on the primary cpu
*/
- assert(tsp_entry_info == NULL);
- tsp_entry_info = (entry_info_t *) x1;
+ assert(tsp_vectors == NULL);
+ tsp_vectors = (tsp_vectors_t *) x1;
/*
* SP reports completion. The SPD must have initiated
@@ -206,9 +381,6 @@
*/
tspd_synchronous_sp_exit(tsp_ctx, x1);
- /* Should never reach here */
- assert(0);
-
/*
* These function IDs is used only by the SP to indicate it has
* finished:
@@ -241,18 +413,20 @@
*/
tspd_synchronous_sp_exit(tsp_ctx, x1);
- /* Should never reach here */
- assert(0);
-
/*
* Request from non-secure client to perform an
* arithmetic operation or response from secure
* payload to an earlier request.
*/
- case TSP_FID_ADD:
- case TSP_FID_SUB:
- case TSP_FID_MUL:
- case TSP_FID_DIV:
+ case TSP_FAST_FID(TSP_ADD):
+ case TSP_FAST_FID(TSP_SUB):
+ case TSP_FAST_FID(TSP_MUL):
+ case TSP_FAST_FID(TSP_DIV):
+
+ case TSP_STD_FID(TSP_ADD):
+ case TSP_STD_FID(TSP_SUB):
+ case TSP_STD_FID(TSP_MUL):
+ case TSP_STD_FID(TSP_DIV):
if (ns) {
/*
* This is a fresh request from the non-secure client.
@@ -261,11 +435,15 @@
* state and send the request to the secure payload.
*/
assert(handle == cm_get_context(mpidr, NON_SECURE));
+
+ /* Check if we are already preempted */
+ if (get_std_smc_active_flag(tsp_ctx->state))
+ SMC_RET1(handle, SMC_UNK);
+
cm_el1_sysregs_context_save(NON_SECURE);
/* Save x1 and x2 for use by TSP_GET_ARGS call below */
- SMC_SET_GP(handle, CTX_GPREG_X1, x1);
- SMC_SET_GP(handle, CTX_GPREG_X2, x2);
+ store_tsp_args(tsp_ctx, x1, x2);
/*
* We are done stashing the non-secure context. Ask the
@@ -280,17 +458,27 @@
* from this function.
*/
assert(&tsp_ctx->cpu_ctx == cm_get_context(mpidr, SECURE));
- set_aapcs_args7(&tsp_ctx->cpu_ctx, smc_fid, x1, x2, 0, 0,
- 0, 0, 0);
- cm_set_el3_elr(SECURE, (uint64_t) tsp_entry_info->fast_smc_entry);
+
+ /* Set appropriate entry for SMC.
+ * We expect the TSP to manage the PSTATE.I and PSTATE.F
+ * flags as appropriate.
+ */
+ if (GET_SMC_TYPE(smc_fid) == SMC_TYPE_FAST) {
+ cm_set_elr_el3(SECURE, (uint64_t)
+ &tsp_vectors->fast_smc_entry);
+ } else {
+ set_std_smc_active_flag(tsp_ctx->state);
+ cm_set_elr_el3(SECURE, (uint64_t)
+ &tsp_vectors->std_smc_entry);
+ }
+
cm_el1_sysregs_context_restore(SECURE);
cm_set_next_eret_context(SECURE);
-
- return smc_fid;
+ SMC_RET3(&tsp_ctx->cpu_ctx, smc_fid, x1, x2);
} else {
/*
* This is the result from the secure client of an
- * earlier request. The results are in x1-x2. Copy it
+ * earlier request. The results are in x1-x3. Copy it
* into the non-secure context, save the secure state
* and return to the non-secure state.
*/
@@ -300,18 +488,53 @@
/* Get a reference to the non-secure context */
ns_cpu_context = cm_get_context(mpidr, NON_SECURE);
assert(ns_cpu_context);
- ns_gp_regs = get_gpregs_ctx(ns_cpu_context);
/* Restore non-secure state */
cm_el1_sysregs_context_restore(NON_SECURE);
cm_set_next_eret_context(NON_SECURE);
-
- SMC_RET2(ns_gp_regs, x1, x2);
+ if (GET_SMC_TYPE(smc_fid) == SMC_TYPE_STD)
+ clr_std_smc_active_flag(tsp_ctx->state);
+ SMC_RET3(ns_cpu_context, x1, x2, x3);
}
break;
/*
+ * Request from non secure world to resume the preempted
+ * Standard SMC call.
+ */
+ case TSP_FID_RESUME:
+ /* RESUME should be invoked only by normal world */
+ if (!ns) {
+ assert(0);
+ break;
+ }
+
+ /*
+ * This is a resume request from the non-secure client.
+ * save the non-secure state and send the request to
+ * the secure payload.
+ */
+ assert(handle == cm_get_context(mpidr, NON_SECURE));
+
+ /* Check if we are already preempted before resume */
+ if (!get_std_smc_active_flag(tsp_ctx->state))
+ SMC_RET1(handle, SMC_UNK);
+
+ cm_el1_sysregs_context_save(NON_SECURE);
+
+ /*
+ * We are done stashing the non-secure context. Ask the
+ * secure payload to do the work now.
+ */
+
+ /* We just need to return to the preempted point in
+ * TSP and the execution will resume as normal.
+ */
+ cm_el1_sysregs_context_restore(SECURE);
+ cm_set_next_eret_context(SECURE);
+
+ /*
* This is a request from the secure payload for more arguments
* for an ongoing arithmetic operation requested by the
* non-secure world. Simply return the arguments from the non-
@@ -324,10 +547,9 @@
/* Get a reference to the non-secure context */
ns_cpu_context = cm_get_context(mpidr, NON_SECURE);
assert(ns_cpu_context);
- ns_gp_regs = get_gpregs_ctx(ns_cpu_context);
- SMC_RET2(handle, read_ctx_reg(ns_gp_regs, CTX_GPREG_X1),
- read_ctx_reg(ns_gp_regs, CTX_GPREG_X2));
+ get_tsp_args(tsp_ctx, x1, x2);
+ SMC_RET2(handle, x1, x2);
case TOS_CALL_COUNT:
/*
@@ -351,9 +573,9 @@
SMC_RET1(handle, SMC_UNK);
}
-/* Define a SPD runtime service descriptor */
+/* Define a SPD runtime service descriptor for fast SMC calls */
DECLARE_RT_SVC(
- spd,
+ tspd_fast,
OEN_TOS_START,
OEN_TOS_END,
@@ -361,3 +583,14 @@
tspd_setup,
tspd_smc_handler
);
+
+/* Define a SPD runtime service descriptor for standard SMC calls */
+DECLARE_RT_SVC(
+ tspd_std,
+
+ OEN_TOS_START,
+ OEN_TOS_END,
+ SMC_TYPE_STD,
+ NULL,
+ tspd_smc_handler
+);
diff --git a/services/spd/tspd/tspd_pm.c b/services/spd/tspd/tspd_pm.c
index 2447d9e..2f20449 100644
--- a/services/spd/tspd/tspd_pm.c
+++ b/services/spd/tspd/tspd_pm.c
@@ -55,11 +55,11 @@
uint32_t linear_id = platform_get_core_pos(mpidr);
tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id];
- assert(tsp_entry_info);
- assert(tsp_ctx->state == TSP_STATE_ON);
+ assert(tsp_vectors);
+ assert(get_tsp_pstate(tsp_ctx->state) == TSP_PSTATE_ON);
/* Program the entry point and enter the TSP */
- cm_set_el3_elr(SECURE, (uint64_t) tsp_entry_info->cpu_off_entry);
+ cm_set_elr_el3(SECURE, (uint64_t) &tsp_vectors->cpu_off_entry);
rc = tspd_synchronous_sp_entry(tsp_ctx);
/*
@@ -73,7 +73,7 @@
* Reset TSP's context for a fresh start when this cpu is turned on
* subsequently.
*/
- tsp_ctx->state = TSP_STATE_OFF;
+ set_tsp_pstate(tsp_ctx->state, TSP_PSTATE_OFF);
return 0;
}
@@ -89,14 +89,14 @@
uint32_t linear_id = platform_get_core_pos(mpidr);
tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id];
- assert(tsp_entry_info);
- assert(tsp_ctx->state == TSP_STATE_ON);
+ assert(tsp_vectors);
+ assert(get_tsp_pstate(tsp_ctx->state) == TSP_PSTATE_ON);
/* Program the entry point, power_state parameter and enter the TSP */
write_ctx_reg(get_gpregs_ctx(&tsp_ctx->cpu_ctx),
CTX_GPREG_X0,
power_state);
- cm_set_el3_elr(SECURE, (uint64_t) tsp_entry_info->cpu_suspend_entry);
+ cm_set_elr_el3(SECURE, (uint64_t) &tsp_vectors->cpu_suspend_entry);
rc = tspd_synchronous_sp_entry(tsp_ctx);
/*
@@ -107,7 +107,7 @@
panic();
/* Update its context to reflect the state the TSP is in */
- tsp_ctx->state = TSP_STATE_SUSPEND;
+ set_tsp_pstate(tsp_ctx->state, TSP_PSTATE_SUSPEND);
}
/*******************************************************************************
@@ -123,11 +123,11 @@
uint32_t linear_id = platform_get_core_pos(mpidr);
tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id];
- assert(tsp_entry_info);
- assert(tsp_ctx->state == TSP_STATE_OFF);
+ assert(tsp_vectors);
+ assert(get_tsp_pstate(tsp_ctx->state) == TSP_PSTATE_OFF);
/* Initialise this cpu's secure context */
- tspd_init_secure_context((uint64_t) tsp_entry_info->cpu_on_entry,
+ tspd_init_secure_context((uint64_t) &tsp_vectors->cpu_on_entry,
TSP_AARCH64,
mpidr,
tsp_ctx);
@@ -143,7 +143,7 @@
panic();
/* Update its context to reflect the state the SP is in */
- tsp_ctx->state = TSP_STATE_ON;
+ set_tsp_pstate(tsp_ctx->state, TSP_PSTATE_ON);
}
/*******************************************************************************
@@ -158,14 +158,14 @@
uint32_t linear_id = platform_get_core_pos(mpidr);
tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id];
- assert(tsp_entry_info);
- assert(tsp_ctx->state == TSP_STATE_SUSPEND);
+ assert(tsp_vectors);
+ assert(get_tsp_pstate(tsp_ctx->state) == TSP_PSTATE_SUSPEND);
/* Program the entry point, suspend_level and enter the SP */
write_ctx_reg(get_gpregs_ctx(&tsp_ctx->cpu_ctx),
CTX_GPREG_X0,
suspend_level);
- cm_set_el3_elr(SECURE, (uint64_t) tsp_entry_info->cpu_resume_entry);
+ cm_set_elr_el3(SECURE, (uint64_t) &tsp_vectors->cpu_resume_entry);
rc = tspd_synchronous_sp_entry(tsp_ctx);
/*
@@ -176,7 +176,7 @@
panic();
/* Update its context to reflect the state the SP is in */
- tsp_ctx->state = TSP_STATE_ON;
+ set_tsp_pstate(tsp_ctx->state, TSP_PSTATE_ON);
}
/*******************************************************************************
diff --git a/services/spd/tspd/tspd_private.h b/services/spd/tspd/tspd_private.h
index 81484e1..fbb0388 100644
--- a/services/spd/tspd/tspd_private.h
+++ b/services/spd/tspd/tspd_private.h
@@ -33,15 +33,47 @@
#include <arch.h>
#include <context.h>
+#include <interrupt_mgmt.h>
#include <platform.h>
#include <psci.h>
/*******************************************************************************
* Secure Payload PM state information e.g. SP is suspended, uninitialised etc
+ * and macros to access the state information in the per-cpu 'state' flags
******************************************************************************/
-#define TSP_STATE_OFF 0
-#define TSP_STATE_ON 1
-#define TSP_STATE_SUSPEND 2
+#define TSP_PSTATE_OFF 0
+#define TSP_PSTATE_ON 1
+#define TSP_PSTATE_SUSPEND 2
+#define TSP_PSTATE_SHIFT 0
+#define TSP_PSTATE_MASK 0x3
+#define get_tsp_pstate(state) ((state >> TSP_PSTATE_SHIFT) & TSP_PSTATE_MASK)
+#define clr_tsp_pstate(state) (state &= ~(TSP_PSTATE_MASK \
+ << TSP_PSTATE_SHIFT))
+#define set_tsp_pstate(st, pst) do { \
+ clr_tsp_pstate(st); \
+ st |= (pst & TSP_PSTATE_MASK) << \
+ TSP_PSTATE_SHIFT; \
+ } while (0);
+
+
+/*
+ * This flag is used by the TSPD to determine if the TSP is servicing a standard
+ * SMC request prior to programming the next entry into the TSP e.g. if TSP
+ * execution is preempted by a non-secure interrupt and handed control to the
+ * normal world. If another request which is distinct from what the TSP was
+ * previously doing arrives, then this flag will be help the TSPD to either
+ * reject the new request or service it while ensuring that the previous context
+ * is not corrupted.
+ */
+#define STD_SMC_ACTIVE_FLAG_SHIFT 2
+#define STD_SMC_ACTIVE_FLAG_MASK 1
+#define get_std_smc_active_flag(state) ((state >> STD_SMC_ACTIVE_FLAG_SHIFT) \
+ & STD_SMC_ACTIVE_FLAG_MASK)
+#define set_std_smc_active_flag(state) (state |= \
+ 1 << STD_SMC_ACTIVE_FLAG_SHIFT)
+#define clr_std_smc_active_flag(state) (state &= \
+ ~(STD_SMC_ACTIVE_FLAG_MASK \
+ << STD_SMC_ACTIVE_FLAG_SHIFT))
/*******************************************************************************
* Secure Payload execution state information i.e. aarch32 or aarch64
@@ -93,6 +125,12 @@
#include <cassert.h>
#include <stdint.h>
+/*
+ * The number of arguments to save during a SMC call for TSP.
+ * Currently only x1 and x2 are used by TSP.
+ */
+#define TSP_NUM_ARGS 0x2
+
/* AArch64 callee saved general purpose register context structure. */
DEFINE_REG_STRUCT(c_rt_regs, TSPD_C_RT_CTX_ENTRIES);
@@ -106,26 +144,46 @@
/*******************************************************************************
* Structure which helps the SPD to maintain the per-cpu state of the SP.
- * 'state' - collection of flags to track SP state e.g. on/off
- * 'mpidr' - mpidr to associate a context with a cpu
- * 'c_rt_ctx' - stack address to restore C runtime context from after returning
- * from a synchronous entry into the SP.
- * 'cpu_ctx' - space to maintain SP architectural state
+ * 'saved_spsr_el3' - temporary copy to allow FIQ handling when the TSP has been
+ * preempted.
+ * 'saved_elr_el3' - temporary copy to allow FIQ handling when the TSP has been
+ * preempted.
+ * 'state' - collection of flags to track SP state e.g. on/off
+ * 'mpidr' - mpidr to associate a context with a cpu
+ * 'c_rt_ctx' - stack address to restore C runtime context from after
+ * returning from a synchronous entry into the SP.
+ * 'cpu_ctx' - space to maintain SP architectural state
+ * 'saved_tsp_args' - space to store arguments for TSP arithmetic operations
+ * which will queried using the TSP_GET_ARGS SMC by TSP.
******************************************************************************/
typedef struct tsp_context {
+ uint64_t saved_elr_el3;
+ uint32_t saved_spsr_el3;
uint32_t state;
uint64_t mpidr;
uint64_t c_rt_ctx;
cpu_context_t cpu_ctx;
+ uint64_t saved_tsp_args[TSP_NUM_ARGS];
} tsp_context_t;
+/* Helper macros to store and retrieve tsp args from tsp_context */
+#define store_tsp_args(tsp_ctx, x1, x2) do {\
+ tsp_ctx->saved_tsp_args[0] = x1;\
+ tsp_ctx->saved_tsp_args[1] = x2;\
+ } while (0)
+
+#define get_tsp_args(tsp_ctx, x1, x2) do {\
+ x1 = tsp_ctx->saved_tsp_args[0];\
+ x2 = tsp_ctx->saved_tsp_args[1];\
+ } while (0)
+
/* TSPD power management handlers */
extern const spd_pm_ops_t tspd_pm;
/*******************************************************************************
* Forward declarations
******************************************************************************/
-struct entry_info;
+struct tsp_vectors;
/*******************************************************************************
* Function & Data prototypes
@@ -139,7 +197,7 @@
uint64_t mpidr,
tsp_context_t *tsp_ctx);
extern tsp_context_t tspd_sp_context[TSPD_CORE_COUNT];
-extern struct entry_info *tsp_entry_info;
+extern struct tsp_vectors *tsp_vectors;
#endif /*__ASSEMBLY__*/
#endif /* __TSPD_PRIVATE_H__ */
diff --git a/services/std_svc/psci/psci_entry.S b/services/std_svc/psci/psci_entry.S
index 256c538..bc8d900 100644
--- a/services/std_svc/psci/psci_entry.S
+++ b/services/std_svc/psci/psci_entry.S
@@ -30,13 +30,13 @@
#include <arch.h>
#include <asm_macros.S>
-#include <cm_macros.S>
#include <psci.h>
.globl psci_aff_on_finish_entry
.globl psci_aff_suspend_finish_entry
.globl __psci_cpu_off
.globl __psci_cpu_suspend
+ .globl psci_power_down_wfi
/* -----------------------------------------------------
* This cpu has been physically powered up. Depending
@@ -120,9 +120,6 @@
mrs x0, mpidr_el1
bl platform_set_coherent_stack
bl psci_cpu_off
- mov x1, #PSCI_E_SUCCESS
- cmp x0, x1
- b.eq final_wfi
mov sp, x19
ldp x19, x20, [sp,#0]
add sp, sp, #0x10
@@ -144,9 +141,6 @@
mov x1, x21
mov x2, x22
bl psci_cpu_suspend
- mov x1, #PSCI_E_SUCCESS
- cmp x0, x1
- b.eq final_wfi
mov sp, x19
ldp x21, x22, [sp,#0x10]
ldp x19, x20, [sp,#0]
@@ -154,7 +148,16 @@
func_epilogue
ret
-func final_wfi
+ /* --------------------------------------------
+ * This function is called to indicate to the
+ * power controller that it is safe to power
+ * down this cpu. It should not exit the wfi
+ * and will be released from reset upon power
+ * up. 'wfi_spill' is used to catch erroneous
+ * exits from wfi.
+ * --------------------------------------------
+ */
+func psci_power_down_wfi
dsb sy // ensure write buffer empty
wfi
wfi_spill:
diff --git a/services/std_svc/psci/psci_main.c b/services/std_svc/psci/psci_main.c
index 1bcf216..c0866fb 100644
--- a/services/std_svc/psci/psci_main.c
+++ b/services/std_svc/psci/psci_main.c
@@ -90,23 +90,37 @@
if (target_afflvl > MPIDR_MAX_AFFLVL)
return PSCI_E_INVALID_PARAMS;
+ /* Determine the 'state type' in the 'power_state' parameter */
pstate_type = psci_get_pstate_type(power_state);
+
+ /*
+ * Ensure that we have a platform specific handler for entering
+ * a standby state.
+ */
if (pstate_type == PSTATE_TYPE_STANDBY) {
- if (psci_plat_pm_ops->affinst_standby)
- rc = psci_plat_pm_ops->affinst_standby(power_state);
- else
+ if (!psci_plat_pm_ops->affinst_standby)
return PSCI_E_INVALID_PARAMS;
- } else {
- mpidr = read_mpidr();
- rc = psci_afflvl_suspend(mpidr,
- entrypoint,
- context_id,
- power_state,
- MPIDR_AFFLVL0,
- target_afflvl);
+
+ rc = psci_plat_pm_ops->affinst_standby(power_state);
+ assert(rc == PSCI_E_INVALID_PARAMS || rc == PSCI_E_SUCCESS);
+ return rc;
}
- assert(rc == PSCI_E_INVALID_PARAMS || rc == PSCI_E_SUCCESS);
+ /*
+ * Do what is needed to enter the power down state. Upon success,
+ * enter the final wfi which will power down this cpu else return
+ * an error.
+ */
+ mpidr = read_mpidr();
+ rc = psci_afflvl_suspend(mpidr,
+ entrypoint,
+ context_id,
+ power_state,
+ MPIDR_AFFLVL0,
+ target_afflvl);
+ if (rc == PSCI_E_SUCCESS)
+ psci_power_down_wfi();
+ assert(rc == PSCI_E_INVALID_PARAMS);
return rc;
}
@@ -127,10 +141,18 @@
rc = psci_afflvl_off(mpidr, MPIDR_AFFLVL0, target_afflvl);
/*
+ * Check if all actions needed to safely power down this cpu have
+ * successfully completed. Enter a wfi loop which will allow the
+ * power controller to physically power down this cpu.
+ */
+ if (rc == PSCI_E_SUCCESS)
+ psci_power_down_wfi();
+
+ /*
* The only error cpu_off can return is E_DENIED. So check if that's
* indeed the case.
*/
- assert (rc == PSCI_E_SUCCESS || rc == PSCI_E_DENIED);
+ assert (rc == PSCI_E_DENIED);
return rc;
}
diff --git a/tools/fip_create/fip_create.c b/tools/fip_create/fip_create.c
index c97204a..d1802b7 100644
--- a/tools/fip_create/fip_create.c
+++ b/tools/fip_create/fip_create.c
@@ -53,7 +53,7 @@
* const char* format_type_str[] = { "RAW", "ELF", "PIC" };
*/
-/* Currently only BL2 and BL31 images are supported. */
+/* The images used depends on the platform. */
static entry_lookup_list_t toc_entry_lookup_list[] = {
{ "Trusted Boot Firmware BL2", UUID_TRUSTED_BOOT_FIRMWARE_BL2,
"bl2", NULL, FLAG_FILENAME },