diff options
author | Jeenu Viswambharan <jeenu.viswambharan@arm.com> | 2014-02-06 10:36:15 +0000 |
---|---|---|
committer | Dan Handley <dan.handley@arm.com> | 2014-02-17 18:51:44 +0000 |
commit | caa84939a4d8b1189dea8619ccc57bdb3026b125 (patch) | |
tree | a7eb5e977cc9971c051ef469877fc3d00cba24ad /common | |
parent | 07f4e078b6871e5c74f6cb38f2726a2cfcb2b746 (diff) |
Add support for handling runtime service requests
This patch uses the reworked exception handling support to handle
runtime service requests through SMCs following the SMC calling
convention. This is a giant commit since all the changes are
inter-related. It does the following:
1. Replace the old exception handling mechanism with the new one
2. Enforce that SP_EL0 is used C runtime stacks.
3. Ensures that the cold and warm boot paths use the 'cpu_context'
structure to program an ERET into the next lower EL.
4. Ensures that SP_EL3 always points to the next 'cpu_context'
structure prior to an ERET into the next lower EL
5. Introduces a PSCI SMC handler which completes the use of PSCI as a
runtime service
Change-Id: I661797f834c0803d2c674d20f504df1b04c2b852
Co-authored-by: Achin Gupta <achin.gupta@arm.com>
Diffstat (limited to 'common')
-rw-r--r-- | common/psci/psci_common.c | 49 | ||||
-rw-r--r-- | common/psci/psci_entry.S | 34 | ||||
-rw-r--r-- | common/psci/psci_main.c | 70 | ||||
-rw-r--r-- | common/psci/psci_private.h | 10 | ||||
-rw-r--r-- | common/psci/psci_setup.c | 5 | ||||
-rw-r--r-- | common/runtime_svc.c | 3 |
6 files changed, 129 insertions, 42 deletions
diff --git a/common/psci/psci_common.c b/common/psci/psci_common.c index 193655d..214db78 100644 --- a/common/psci/psci_common.c +++ b/common/psci/psci_common.c @@ -36,6 +36,7 @@ #include <platform.h> #include <psci.h> #include <psci_private.h> +#include <context_mgmt.h> #include <runtime_svc.h> #include "debug.h" @@ -86,7 +87,8 @@ int get_power_on_target_afflvl(unsigned long mpidr) unsigned int state; /* Retrieve our node from the topology tree */ - node = psci_get_aff_map_node(mpidr & MPIDR_AFFINITY_MASK, MPIDR_AFFLVL0); + node = psci_get_aff_map_node(mpidr & MPIDR_AFFINITY_MASK, + MPIDR_AFFLVL0); assert(node); /* @@ -222,13 +224,12 @@ int psci_validate_mpidr(unsigned long mpidr, int level) void psci_get_ns_entry_info(unsigned int index) { unsigned long sctlr = 0, scr, el_status, id_aa64pfr0; - gp_regs *ns_gp_regs; + uint64_t mpidr = read_mpidr(); + cpu_context *ns_entry_context; + gp_regs *ns_entry_gpregs; scr = read_scr(); - /* Switch to the non-secure view of the registers */ - write_scr(scr | SCR_NS_BIT); - /* Find out which EL we are going to */ id_aa64pfr0 = read_id_aa64pfr0_el1(); el_status = (id_aa64pfr0 >> ID_AA64PFR0_EL2_SHIFT) & @@ -257,23 +258,29 @@ void psci_get_ns_entry_info(unsigned int index) write_sctlr_el1(sctlr); /* Fulfill the cpu_on entry reqs. as per the psci spec */ - write_scr(scr); - write_elr(psci_ns_entry_info[index].eret_info.entrypoint); + ns_entry_context = (cpu_context *) cm_get_context(mpidr, NON_SECURE); + assert(ns_entry_context); /* - * Set the general purpose registers to ~0 upon entry into the - * non-secure world except for x0 which should contain the - * context id & spsr. This is done directly on the "would be" - * stack pointer. Prior to entry into the non-secure world, an - * offset equivalent to the size of the 'gp_regs' structure is - * added to the sp. This general purpose register context is - * retrieved then. + * Setup general purpose registers to return the context id and + * prevent leakage of secure information into the normal world. */ - ns_gp_regs = (gp_regs *) platform_get_stack(read_mpidr()); - ns_gp_regs--; - memset(ns_gp_regs, ~0, sizeof(*ns_gp_regs)); - ns_gp_regs->x0 = psci_ns_entry_info[index].context_id; - ns_gp_regs->spsr = psci_ns_entry_info[index].eret_info.spsr; + ns_entry_gpregs = get_gpregs_ctx(ns_entry_context); + write_ctx_reg(ns_entry_gpregs, + CTX_GPREG_X0, + psci_ns_entry_info[index].context_id); + + /* + * Tell the context management library to setup EL3 system registers to + * be able to ERET into the ns state, and SP_EL3 points to the right + * context to exit from EL3 correctly. + */ + cm_set_el3_eret_context(NON_SECURE, + psci_ns_entry_info[index].eret_info.entrypoint, + psci_ns_entry_info[index].eret_info.spsr, + scr); + + cm_set_next_eret_context(NON_SECURE); } /******************************************************************************* @@ -344,7 +351,7 @@ int psci_set_ns_entry_info(unsigned int index, */ spsr |= DAIF_ABT_BIT | DAIF_IRQ_BIT | DAIF_FIQ_BIT; spsr <<= PSR_DAIF_SHIFT; - if(ee) + if (ee) spsr |= SPSR32_EE_BIT; spsr |= mode; @@ -500,7 +507,7 @@ void psci_afflvl_power_on_finish(unsigned long mpidr, mpidr_aff_map_nodes mpidr_nodes; int rc; - mpidr &= MPIDR_AFFINITY_MASK;; + mpidr &= MPIDR_AFFINITY_MASK; /* * Collect the pointers to the nodes in the topology tree for diff --git a/common/psci/psci_entry.S b/common/psci/psci_entry.S index 28a4143..15e074c 100644 --- a/common/psci/psci_entry.S +++ b/common/psci/psci_entry.S @@ -34,6 +34,7 @@ #include <psci_private.h> #include <runtime_svc.h> #include <asm_macros.S> +#include <cm_macros.S> .globl psci_aff_on_finish_entry .globl psci_aff_suspend_finish_entry @@ -74,6 +75,13 @@ psci_aff_common_finish_entry: msr vbar_el3, x0 isb + /* --------------------------------------------- + * Use SP_EL0 for the C runtime stack. + * --------------------------------------------- + */ + msr spsel, #0 + isb + bl read_mpidr mov x19, x0 bl platform_set_coherent_stack @@ -102,31 +110,19 @@ psci_aff_common_finish_entry: bl platform_set_stack /* --------------------------------------------- - * Now that the execution stack has been set + * Now that the context management has been set * up, enable full runtime exception handling. - * Since we're just about to leave this EL with - * ERET, we don't need an ISB here + * SP_EL3 is pointing to a 'cpu_context' + * structure which has an exception stack + * allocated. Since we're just about to leave + * this EL with ERET, we don't need an ISB here * --------------------------------------------- */ adr x0, runtime_exceptions msr vbar_el3, x0 - /* -------------------------------------------- - * Use the size of the general purpose register - * context to restore the register state - * stashed by earlier code - * -------------------------------------------- - */ - sub sp, sp, #SIZEOF_GPREGS - exception_exit restore_regs - - /* -------------------------------------------- - * Jump back to the non-secure world assuming - * that the elr and spsr setup has been done - * by the finishers - * -------------------------------------------- - */ - eret + zero_callee_saved_regs + b el3_exit _panic: b _panic diff --git a/common/psci/psci_main.c b/common/psci/psci_main.c index a70a21a..67f189d 100644 --- a/common/psci/psci_main.c +++ b/common/psci/psci_main.c @@ -35,6 +35,9 @@ #include <console.h> #include <platform.h> #include <psci_private.h> +#include <runtime_svc.h> +#include <debug.h> +#include <context_mgmt.h> /******************************************************************************* * PSCI frontend api for servicing SMCs. Described in the PSCI spec. @@ -199,3 +202,70 @@ void psci_system_reset(void) assert(0); } +/******************************************************************************* + * PSCI top level handler for servicing SMCs. + ******************************************************************************/ +uint64_t psci_smc_handler(uint32_t smc_fid, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + uint64_t rc; + + switch (smc_fid) { + case PSCI_VERSION: + rc = psci_version(); + break; + + case PSCI_CPU_OFF: + rc = __psci_cpu_off(); + break; + + case PSCI_CPU_SUSPEND_AARCH64: + case PSCI_CPU_SUSPEND_AARCH32: + rc = __psci_cpu_suspend(x1, x2, x3); + break; + + case PSCI_CPU_ON_AARCH64: + case PSCI_CPU_ON_AARCH32: + rc = psci_cpu_on(x1, x2, x3); + break; + + case PSCI_AFFINITY_INFO_AARCH32: + case PSCI_AFFINITY_INFO_AARCH64: + rc = psci_affinity_info(x1, x2); + break; + + case PSCI_MIG_AARCH32: + case PSCI_MIG_AARCH64: + rc = psci_migrate(x1); + break; + + case PSCI_MIG_INFO_TYPE: + rc = psci_migrate_info_type(); + break; + + case PSCI_MIG_INFO_UP_CPU_AARCH32: + case PSCI_MIG_INFO_UP_CPU_AARCH64: + rc = psci_migrate_info_up_cpu(); + break; + + case PSCI_SYSTEM_OFF: + psci_system_off(); + assert(0); + + case PSCI_SYSTEM_RESET: + psci_system_reset(); + assert(0); + + default: + rc = SMC_UNK; + WARN("Unimplemented psci call -> 0x%x \n", smc_fid); + } + + SMC_RET1(handle, rc); +} diff --git a/common/psci/psci_private.h b/common/psci/psci_private.h index de9c291..3d7ae74 100644 --- a/common/psci/psci_private.h +++ b/common/psci/psci_private.h @@ -151,6 +151,16 @@ extern int psci_afflvl_suspend(unsigned long, int, int); extern unsigned int psci_afflvl_suspend_finish(unsigned long, int, int); + +/* Private exported functions from psci_main.c */ +extern uint64_t psci_smc_handler(uint32_t smc_fid, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags); #endif /*__ASSEMBLY__*/ #endif /* __PSCI_PRIVATE_H__ */ diff --git a/common/psci/psci_setup.c b/common/psci/psci_setup.c index c471d1f..8d7903c 100644 --- a/common/psci/psci_setup.c +++ b/common/psci/psci_setup.c @@ -195,6 +195,9 @@ static void psci_init_aff_map_node(unsigned long mpidr, cm_set_context(mpidr, (void *) &psci_ns_context[linear_id], NON_SECURE); + + /* Initialize exception stack in the context */ + cm_init_exception_stack(mpidr, NON_SECURE); } return; @@ -348,5 +351,5 @@ DECLARE_RT_SVC( OEN_STD_END, SMC_TYPE_FAST, psci_setup, - NULL + psci_smc_handler ); diff --git a/common/runtime_svc.c b/common/runtime_svc.c index 0ea1bf5..6e8a1bb 100644 --- a/common/runtime_svc.c +++ b/common/runtime_svc.c @@ -41,6 +41,7 @@ #include <runtime_svc.h> #include <context.h> #include <debug.h> +#include <context_mgmt.h> /******************************************************************************* * The 'rt_svc_descs' array holds the runtime service descriptors exported by @@ -146,7 +147,7 @@ error: void fault_handler(void *handle) { - gp_regs_next *gpregs_ctx = get_gpregs_ctx(handle); + gp_regs *gpregs_ctx = get_gpregs_ctx(handle); ERROR("Unhandled synchronous fault. Register dump @ 0x%x \n", gpregs_ctx); panic(); |