summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAchin Gupta <achin.gupta@arm.com>2017-05-20 22:06:34 +0100
committerAchin Gupta <achin.gupta@arm.com>2017-05-26 12:37:12 +0100
commit6b2b57649fffda8d3c003eda7cc9094fd418aec4 (patch)
treea919067eea84c2c900e466c7f5b054fc2262ac7c
parente68cc8e3e61fbb75bad20d5298a72bb2c0d455cf (diff)
downloadarm-trusted-firmware-prototypes/secure_partitions/rfc_v1.tar.gz
SPM: Initialise secure partitions on secondary cpusprototypes/secure_partitions/rfc_v1
This patch leverages the SPD power management operations structure to register notifiers for PSCI CPU_ON, CPU_OFF and CPU_SUSPEND operations. The notifier for the ON finish operation enables the SPM to initialise the secure partition on secondary CPUs. The notifiers for all other operations are placeholders for future expansion. Some private APIs have been made public to enable re-use in the notifier functions. An interface has been added to initialise the context for a secondary CPU during a warm boot that enables it to jump into the Secure partition to perform any initialisations. A data structure has been defined that is used to pass boot information to the secondary CPU. Change-Id: I0b4407e551eefe18f2bad3947e0262fee0568f01 Signed-off-by: Achin Gupta <achin.gupta@arm.com>
-rw-r--r--include/services/secure_partition.h11
-rw-r--r--services/std_svc/spm/secure_partition_setup.c130
-rw-r--r--services/std_svc/spm/spm.mk1
-rw-r--r--services/std_svc/spm/spm_main.c16
-rw-r--r--services/std_svc/spm/spm_private.h10
-rw-r--r--services/std_svc/spm/spm_pwr_mgmt.c125
6 files changed, 286 insertions, 7 deletions
diff --git a/include/services/secure_partition.h b/include/services/secure_partition.h
index ebf259919..554925fed 100644
--- a/include/services/secure_partition.h
+++ b/include/services/secure_partition.h
@@ -82,10 +82,19 @@ typedef struct secure_partition_boot_info {
secure_partition_mp_info_t *mp_info;
} secure_partition_boot_info_t;
+typedef struct secure_partition_warm_boot_info {
+ param_header_t h;
+ unsigned long sp_stack_base;
+ unsigned long sp_shared_buf_base;
+ unsigned int sp_pcpu_stack_size;
+ unsigned int sp_pcpu_shared_buf_size;
+ secure_partition_mp_info_t mp_info;
+} sp_warm_boot_info_t;
+
/* General setup functions for secure partitions context. */
void secure_partition_setup(void);
-
+void secure_partition_prepare_warm_boot_context(void);
void secure_partition_prepare_context(void);
#endif /* __SECURE_PARTITION_H__ */
diff --git a/services/std_svc/spm/secure_partition_setup.c b/services/std_svc/spm/secure_partition_setup.c
index 9ca52252b..13fcf2bd3 100644
--- a/services/std_svc/spm/secure_partition_setup.c
+++ b/services/std_svc/spm/secure_partition_setup.c
@@ -69,7 +69,7 @@ void secure_partition_setup(void)
VERBOSE("S-EL1/S-EL0 context setup end.\n");
}
-void secure_partition_prepare_context(void)
+void secure_partition_prepare_context()
{
VERBOSE("Updating S-EL1/S-EL0 context registers.\n");
@@ -206,3 +206,131 @@ void secure_partition_prepare_context(void)
write_daifset(DAIF_FIQ_BIT | DAIF_IRQ_BIT | DAIF_ABT_BIT | DAIF_DBG_BIT);
}
+
+
+void secure_partition_prepare_warm_boot_context(void)
+{
+ cpu_context_t *ctx = cm_get_context(SECURE);
+ unsigned int linear_id = plat_my_core_pos();
+ secure_partition_boot_info_t *cold_boot_info;
+ sp_warm_boot_info_t *warm_boot_info;
+
+ assert(ctx);
+
+ /* MMU-related registers */
+
+ uint64_t mair_el1, tcr_el1, ttbr_el1, sctlr_el1, sp_el0;
+
+ secure_partition_prepare_xlat_context(&mair_el1, &tcr_el1, &ttbr_el1,
+ &sctlr_el1);
+
+ sctlr_el1 |= SCTLR_UCI_BIT | SCTLR_NTWE_BIT | SCTLR_NTWI_BIT |
+ SCTLR_UCT_BIT | SCTLR_DZE_BIT | SCTLR_I_BIT |
+ SCTLR_UMA_BIT | SCTLR_SA0_BIT | SCTLR_A_BIT;
+ sctlr_el1 &= ~SCTLR_E0E_BIT;
+
+ write_ctx_reg(get_sysregs_ctx(ctx), CTX_SCTLR_EL1, sctlr_el1);
+ write_ctx_reg(get_sysregs_ctx(ctx), CTX_TTBR0_EL1, ttbr_el1);
+ write_ctx_reg(get_sysregs_ctx(ctx), CTX_MAIR_EL1, mair_el1);
+ write_ctx_reg(get_sysregs_ctx(ctx), CTX_TCR_EL1, tcr_el1);
+
+ /* Other system registers */
+
+ write_ctx_reg(get_sysregs_ctx(ctx),
+ CTX_VBAR_EL1,
+ SECURE_PARTITION_EXCEPTIONS_BASE_PTR);
+
+ uint64_t cpacr_el1 = read_ctx_reg(get_sysregs_ctx(ctx), CTX_CPACR_EL1);
+ cpacr_el1 |= CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_NONE);
+ write_ctx_reg(get_sysregs_ctx(ctx), CTX_CPACR_EL1, cpacr_el1);
+
+ /*
+ * Obtain a pointer to the information passed to the partition during a
+ * cold boot.
+ */
+ cold_boot_info = (secure_partition_boot_info_t *)
+ plat_arm_get_secure_partition_boot_info(NULL);
+
+ /*
+ * Get a pointer to the base address of the communication buffer between
+ * SPM and the secure partition for this CPU. Update the base address
+ * and size of this buffer.
+ */
+ warm_boot_info = (sp_warm_boot_info_t *) (
+ cold_boot_info->sp_shared_buf_base
+ + (linear_id * cold_boot_info->sp_pcpu_shared_buf_size));
+
+ warm_boot_info->sp_shared_buf_base = (unsigned long) warm_boot_info;
+ warm_boot_info->sp_pcpu_shared_buf_size =
+ cold_boot_info->sp_pcpu_shared_buf_size;
+
+ /*
+ * Populate the boot information for this secondary CPU. It is based
+ * upon the cold boot information where applicable
+ */
+ warm_boot_info->h.type = cold_boot_info->h.type;
+ warm_boot_info->h.version = cold_boot_info->h.version;
+ warm_boot_info->h.size = sizeof(sp_warm_boot_info_t);
+ warm_boot_info->h.attr = 0;
+
+ /* Find the base address of this CPU's descending stack */
+ warm_boot_info->sp_stack_base = cold_boot_info->sp_stack_base +
+ ((linear_id + 1) * cold_boot_info->sp_pcpu_stack_size);
+ warm_boot_info->sp_pcpu_stack_size = cold_boot_info->sp_pcpu_stack_size;
+
+ warm_boot_info->mp_info.mpidr = read_mpidr_el1();
+ warm_boot_info->mp_info.linear_id = linear_id;
+ warm_boot_info->mp_info.flags = 0;
+
+ /* General-Purpose registers */
+
+ /*
+ * X0: Virtual address of a buffer shared between EL3 and Secure EL0.
+ * The buffer will be mapped in the Secure EL1 translation regime
+ * with Normal IS WBWA attributes and RO data and Execute Never
+ * instruction access permissions.
+ *
+ * X1: Size of the buffer in bytes
+ *
+ * X2: cookie value (Implementation Defined)
+ *
+ * X3: cookie value (Implementation Defined)
+ */
+ write_ctx_reg(get_gpregs_ctx(ctx),
+ CTX_GPREG_X0,
+ (unsigned long) warm_boot_info);
+ write_ctx_reg(get_gpregs_ctx(ctx),
+ CTX_GPREG_X1,
+ (unsigned long) warm_boot_info->sp_pcpu_shared_buf_size);
+ write_ctx_reg(get_gpregs_ctx(ctx),
+ CTX_GPREG_X2,
+ 0);
+ write_ctx_reg(get_gpregs_ctx(ctx),
+ CTX_GPREG_X3,
+ 0);
+
+ /* X4 to X30 = 0, done by cm_init_my_context() */
+
+ /*
+ * SP_EL0: A non-zero value will indicate that the Dispatcher has
+ * initialized the stack pointer for the current CPU through
+ * implementation defined means. The value will be 0 otherwise.
+ */
+ sp_el0 = warm_boot_info->sp_stack_base;;
+ write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_SP_EL0, sp_el0);
+
+ /*
+ * PSTATE
+ * D,A,I,F=1
+ * SpSel = 0 ### XXX : TODO
+ * NRW = 0 ### XXX : TODO
+ */
+
+ /* XXX : TODO : Does this go here or in secure_partition_exceptions.S ?
+ * I'd say this goes here in case the payload wants to change it, but
+ * I'm not sure.
+ */
+ write_daifset(DAIF_FIQ_BIT | DAIF_IRQ_BIT | DAIF_ABT_BIT | DAIF_DBG_BIT);
+
+
+}
diff --git a/services/std_svc/spm/spm.mk b/services/std_svc/spm/spm.mk
index 43c70490d..dec4a0bdb 100644
--- a/services/std_svc/spm/spm.mk
+++ b/services/std_svc/spm/spm.mk
@@ -46,6 +46,7 @@ endif
SPM_SOURCES := $(addprefix services/std_svc/spm/, \
spm_main.c \
+ spm_pwr_mgmt.c \
${ARCH}/spm_helpers.S \
secure_partition_setup.c \
secure_partition_xlat_tables.c \
diff --git a/services/std_svc/spm/spm_main.c b/services/std_svc/spm/spm_main.c
index 0bc62ffeb..6107c3c86 100644
--- a/services/std_svc/spm/spm_main.c
+++ b/services/std_svc/spm/spm_main.c
@@ -47,7 +47,7 @@ uintptr_t warm_boot_entry_point;
/*******************************************************************************
* Secure Partition context information.
******************************************************************************/
-static secure_partition_context_t sp_ctx[PLATFORM_CORE_COUNT];
+secure_partition_context_t sp_ctx[PLATFORM_CORE_COUNT];
/*******************************************************************************
* Replace the S-EL1 re-entry information with S-EL0 re-entry
@@ -74,7 +74,7 @@ void spm_setup_next_eret_into_sel0(cpu_context_t *secure_context)
* 3. Calls el3_exit() so that the EL3 system and general purpose registers
* from the sp_ctx->cpu_ctx are used to enter the secure payload image.
******************************************************************************/
-static uint64_t spm_synchronous_sp_entry(secure_partition_context_t *sp_ctx_ptr)
+uint64_t spm_synchronous_sp_entry(secure_partition_context_t *sp_ctx_ptr)
{
uint64_t rc;
@@ -106,8 +106,8 @@ static uint64_t spm_synchronous_sp_entry(secure_partition_context_t *sp_ctx_ptr)
* 3. It does not need to save any general purpose or EL3 system register state
* as the generic smc entry routine should have saved those.
******************************************************************************/
-static void spm_synchronous_sp_exit(secure_partition_context_t *sp_ctx_ptr,
- uint64_t ret)
+void spm_synchronous_sp_exit(secure_partition_context_t *sp_ctx_ptr,
+ uint64_t ret)
{
assert(sp_ctx_ptr != NULL);
/* Save the Secure EL1 system register context */
@@ -159,7 +159,13 @@ int32_t spm_init(void)
set_sp_pstate(sp_ctx[linear_id].flags, SP_PSTATE_OFF);
rc = spm_synchronous_sp_entry(&sp_ctx[linear_id]);
assert(rc == 0);
-
+
+ /*
+ * The partition has been successfully initialized. Register power
+ * managemnt hooks with PSCI
+ */
+ psci_register_spd_pm_hook(&spm_pm);
+
/* Mark the partition as being ON on this CPU */
set_sp_pstate(sp_ctx[linear_id].flags, SP_PSTATE_ON);
return rc;
diff --git a/services/std_svc/spm/spm_private.h b/services/std_svc/spm/spm_private.h
index f16074e70..fa4aca8d6 100644
--- a/services/std_svc/spm/spm_private.h
+++ b/services/std_svc/spm/spm_private.h
@@ -84,12 +84,22 @@ typedef struct secure_partition_context {
} secure_partition_context_t;
extern uintptr_t warm_boot_entry_point;
+extern secure_partition_context_t sp_ctx[PLATFORM_CORE_COUNT];
+extern const spd_pm_ops_t spm_pm;
uint64_t spm_secure_partition_enter(uint64_t *c_rt_ctx);
void __dead2 spm_secure_partition_exit(uint64_t c_rt_ctx, uint64_t ret);
+uint64_t spm_synchronous_sp_entry(secure_partition_context_t *sp_ctx_ptr);
+void spm_synchronous_sp_exit(secure_partition_context_t *sp_ctx_ptr,
+ uint64_t ret);
void spm_init_sp_ep_state(struct entry_point_info *sp_ep_info,
uint64_t pc,
secure_partition_context_t *sp_ctx_ptr);
+
+void secure_partition_prepare_context(void);
+uint64_t spm_secure_partition_enter(uint64_t *c_rt_ctx);
+void __dead2 spm_secure_partition_exit(uint64_t c_rt_ctx, uint64_t ret);
+
#endif /* __ASSEMBLY__ */
#endif /* __SPM_PRIVATE_H__ */
diff --git a/services/std_svc/spm/spm_pwr_mgmt.c b/services/std_svc/spm/spm_pwr_mgmt.c
new file mode 100644
index 000000000..cf61e69c5
--- /dev/null
+++ b/services/std_svc/spm/spm_pwr_mgmt.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <bl_common.h>
+#include <context_mgmt.h>
+#include <debug.h>
+#include <platform.h>
+#include <secure_partition.h>
+#include "spm_private.h"
+
+/*******************************************************************************
+ * This cpu is being turned off. Allow the secure partition to perform any
+ * actions needed.
+ ******************************************************************************/
+static int spm_cpu_off_handler(unsigned long unused)
+{
+ unsigned int linear_id = plat_my_core_pos();
+ secure_partition_context_t *sp_ctx_ptr = &sp_ctx[linear_id];
+
+ assert(get_sp_pstate(sp_ctx_ptr->flags) == SP_PSTATE_ON);
+
+ /*
+ * Set the partition state to off for a fresh start when this cpu is
+ * turned on subsequently.
+ */
+ set_sp_pstate(sp_ctx_ptr->flags, SP_PSTATE_OFF);
+
+ return 0;
+}
+
+/*******************************************************************************
+ * This cpu has been turned on. Enter the partition to initialise it. Entry in
+ * S-EL0 is done after initialising minimal architectural state that guarantees
+ * safe execution.
+ ******************************************************************************/
+static void spm_cpu_on_finish_handler(unsigned long unused)
+{
+ int rc = 0;
+ unsigned int linear_id = plat_my_core_pos();
+ secure_partition_context_t *sp_ctx_ptr = &sp_ctx[linear_id];
+ entry_point_info_t sp_ep_info;
+
+ assert(get_sp_pstate(sp_ctx_ptr->flags) == SP_PSTATE_OFF);
+
+ /* Initialise the entry point information for this secondary CPU */
+ spm_init_sp_ep_state(&sp_ep_info,
+ warm_boot_entry_point,
+ &sp_ctx[linear_id]);
+
+ /*
+ * Initialise the common context and then overlay the S-EL0 specific
+ * context on top of it.
+ */
+ cm_init_my_context(&sp_ep_info);
+
+ secure_partition_prepare_warm_boot_context();
+
+ /* Enter the Secure partition */
+ rc = spm_synchronous_sp_entry(&sp_ctx[linear_id]);
+ if (rc)
+ panic();
+
+ /* Mark the partition as being ON on this CPU */
+ set_sp_pstate(sp_ctx[linear_id].flags, SP_PSTATE_ON);
+}
+
+/*******************************************************************************
+ * This cpu is being suspended. Save any secure partition state.
+ * - Memory state will be automatically preserved as the caches will be flushed.
+ * - System register state for a partition has been saved in its context
+ * information.
+ * - Device state will need to be saved but at the moment there are no devices
+ * local to this cpu that we care about.
+ ******************************************************************************/
+static void spm_cpu_suspend_handler(unsigned long max_off_pwrlvl)
+{
+ unsigned int linear_id = plat_my_core_pos();
+ secure_partition_context_t *sp_ctx_ptr = &sp_ctx[linear_id];
+
+ assert(get_sp_pstate(sp_ctx_ptr->flags) == SP_PSTATE_ON);
+
+ /* Update the context to reflect the state the partition is in */
+ set_sp_pstate(sp_ctx_ptr->flags, SP_PSTATE_SUSPEND);
+}
+
+
+/*******************************************************************************
+ * This cpu has been resumed from suspend. Restore any secure partition state.
+ * - Memory state has automatically been preserved as caches were flushed.
+ * - System register state for a partition was saved in its context information
+ * and will be restored upon the next ERET into the partition.
+ * - There is no device state to worry about right now.
+ ******************************************************************************/
+static void spm_cpu_suspend_finish_handler(unsigned long max_off_pwrlvl)
+{
+ unsigned int linear_id = plat_my_core_pos();
+ secure_partition_context_t *sp_ctx_ptr = &sp_ctx[linear_id];
+
+ assert(get_sp_pstate(sp_ctx_ptr->flags) == SP_PSTATE_SUSPEND);
+
+ /* Update the context to reflect the state the partition is in */
+ set_sp_pstate(sp_ctx_ptr->flags, SP_PSTATE_SUSPEND);
+}
+
+/*******************************************************************************
+ * Structure populated by the Secure Partition Manager to be given a chance to
+ * perform any partition specific bookkeeping before PSCI executes a power
+ * management operation.
+ ******************************************************************************/
+const spd_pm_ops_t spm_pm = {
+ .svc_on = NULL,
+ .svc_off = spm_cpu_off_handler,
+ .svc_suspend = spm_cpu_suspend_handler,
+ .svc_on_finish = spm_cpu_on_finish_handler,
+ .svc_suspend_finish = spm_cpu_suspend_finish_handler,
+ .svc_migrate = NULL,
+ .svc_migrate_info = NULL,
+ .svc_system_off = NULL,
+ .svc_system_reset = NULL
+};