1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved. 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu 7*91f16700Schasinglulu #include <assert.h> 8*91f16700Schasinglulu #include <errno.h> 9*91f16700Schasinglulu #include <inttypes.h> 10*91f16700Schasinglulu #include <stdint.h> 11*91f16700Schasinglulu 12*91f16700Schasinglulu #include <lib/el3_runtime/context_mgmt.h> 13*91f16700Schasinglulu #include <lib/spinlock.h> 14*91f16700Schasinglulu #include "spmd_private.h" 15*91f16700Schasinglulu 16*91f16700Schasinglulu static struct { 17*91f16700Schasinglulu bool secondary_ep_locked; 18*91f16700Schasinglulu uintptr_t secondary_ep; 19*91f16700Schasinglulu spinlock_t lock; 20*91f16700Schasinglulu } g_spmd_pm; 21*91f16700Schasinglulu 22*91f16700Schasinglulu /******************************************************************************* 23*91f16700Schasinglulu * spmd_pm_secondary_ep_register 24*91f16700Schasinglulu ******************************************************************************/ 25*91f16700Schasinglulu int spmd_pm_secondary_ep_register(uintptr_t entry_point) 26*91f16700Schasinglulu { 27*91f16700Schasinglulu int ret = FFA_ERROR_INVALID_PARAMETER; 28*91f16700Schasinglulu 29*91f16700Schasinglulu spin_lock(&g_spmd_pm.lock); 30*91f16700Schasinglulu 31*91f16700Schasinglulu if (g_spmd_pm.secondary_ep_locked == true) { 32*91f16700Schasinglulu goto out; 33*91f16700Schasinglulu } 34*91f16700Schasinglulu 35*91f16700Schasinglulu /* 36*91f16700Schasinglulu * Check entry_point address is a PA within 37*91f16700Schasinglulu * load_address <= entry_point < load_address + binary_size 38*91f16700Schasinglulu */ 39*91f16700Schasinglulu if (!spmd_check_address_in_binary_image(entry_point)) { 40*91f16700Schasinglulu ERROR("%s entry point is not within image boundaries\n", 41*91f16700Schasinglulu __func__); 42*91f16700Schasinglulu goto out; 43*91f16700Schasinglulu } 44*91f16700Schasinglulu 45*91f16700Schasinglulu g_spmd_pm.secondary_ep = entry_point; 46*91f16700Schasinglulu g_spmd_pm.secondary_ep_locked = true; 47*91f16700Schasinglulu 48*91f16700Schasinglulu VERBOSE("%s %lx\n", __func__, entry_point); 49*91f16700Schasinglulu 50*91f16700Schasinglulu ret = 0; 51*91f16700Schasinglulu 52*91f16700Schasinglulu out: 53*91f16700Schasinglulu spin_unlock(&g_spmd_pm.lock); 54*91f16700Schasinglulu 55*91f16700Schasinglulu return ret; 56*91f16700Schasinglulu } 57*91f16700Schasinglulu 58*91f16700Schasinglulu /******************************************************************************* 59*91f16700Schasinglulu * This CPU has been turned on. Enter SPMC to initialise S-EL1 or S-EL2. As part 60*91f16700Schasinglulu * of the SPMC initialization path, they will initialize any SPs that they 61*91f16700Schasinglulu * manage. Entry into SPMC is done after initialising minimal architectural 62*91f16700Schasinglulu * state that guarantees safe execution. 63*91f16700Schasinglulu ******************************************************************************/ 64*91f16700Schasinglulu static void spmd_cpu_on_finish_handler(u_register_t unused) 65*91f16700Schasinglulu { 66*91f16700Schasinglulu spmd_spm_core_context_t *ctx = spmd_get_context(); 67*91f16700Schasinglulu unsigned int linear_id = plat_my_core_pos(); 68*91f16700Schasinglulu el3_state_t *el3_state; 69*91f16700Schasinglulu uintptr_t entry_point; 70*91f16700Schasinglulu uint64_t rc; 71*91f16700Schasinglulu 72*91f16700Schasinglulu assert(ctx != NULL); 73*91f16700Schasinglulu assert(ctx->state != SPMC_STATE_ON); 74*91f16700Schasinglulu 75*91f16700Schasinglulu spin_lock(&g_spmd_pm.lock); 76*91f16700Schasinglulu 77*91f16700Schasinglulu /* 78*91f16700Schasinglulu * Leave the possibility that the SPMC does not call 79*91f16700Schasinglulu * FFA_SECONDARY_EP_REGISTER in which case re-use the 80*91f16700Schasinglulu * primary core address for booting secondary cores. 81*91f16700Schasinglulu */ 82*91f16700Schasinglulu if (g_spmd_pm.secondary_ep_locked == true) { 83*91f16700Schasinglulu /* 84*91f16700Schasinglulu * The CPU context has already been initialized at boot time 85*91f16700Schasinglulu * (in spmd_spmc_init by a call to cm_setup_context). Adjust 86*91f16700Schasinglulu * below the target core entry point based on the address 87*91f16700Schasinglulu * passed to by FFA_SECONDARY_EP_REGISTER. 88*91f16700Schasinglulu */ 89*91f16700Schasinglulu entry_point = g_spmd_pm.secondary_ep; 90*91f16700Schasinglulu el3_state = get_el3state_ctx(&ctx->cpu_ctx); 91*91f16700Schasinglulu write_ctx_reg(el3_state, CTX_ELR_EL3, entry_point); 92*91f16700Schasinglulu } 93*91f16700Schasinglulu 94*91f16700Schasinglulu spin_unlock(&g_spmd_pm.lock); 95*91f16700Schasinglulu 96*91f16700Schasinglulu /* Mark CPU as initiating ON operation. */ 97*91f16700Schasinglulu ctx->state = SPMC_STATE_ON_PENDING; 98*91f16700Schasinglulu 99*91f16700Schasinglulu rc = spmd_spm_core_sync_entry(ctx); 100*91f16700Schasinglulu if (rc != 0ULL) { 101*91f16700Schasinglulu ERROR("%s failed (%" PRIu64 ") on CPU%u\n", __func__, rc, 102*91f16700Schasinglulu linear_id); 103*91f16700Schasinglulu ctx->state = SPMC_STATE_OFF; 104*91f16700Schasinglulu return; 105*91f16700Schasinglulu } 106*91f16700Schasinglulu 107*91f16700Schasinglulu ctx->state = SPMC_STATE_ON; 108*91f16700Schasinglulu 109*91f16700Schasinglulu VERBOSE("CPU %u on!\n", linear_id); 110*91f16700Schasinglulu } 111*91f16700Schasinglulu 112*91f16700Schasinglulu /******************************************************************************* 113*91f16700Schasinglulu * spmd_cpu_off_handler 114*91f16700Schasinglulu ******************************************************************************/ 115*91f16700Schasinglulu static int32_t spmd_cpu_off_handler(u_register_t unused) 116*91f16700Schasinglulu { 117*91f16700Schasinglulu spmd_spm_core_context_t *ctx = spmd_get_context(); 118*91f16700Schasinglulu unsigned int linear_id = plat_my_core_pos(); 119*91f16700Schasinglulu int64_t rc; 120*91f16700Schasinglulu 121*91f16700Schasinglulu assert(ctx != NULL); 122*91f16700Schasinglulu assert(ctx->state != SPMC_STATE_OFF); 123*91f16700Schasinglulu 124*91f16700Schasinglulu /* Build an SPMD to SPMC direct message request. */ 125*91f16700Schasinglulu gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx); 126*91f16700Schasinglulu spmd_build_spmc_message(gpregs, FFA_FWK_MSG_PSCI, PSCI_CPU_OFF); 127*91f16700Schasinglulu 128*91f16700Schasinglulu /* Clear remaining x8 - x17 at EL3/SEL2 or EL3/SEL1 boundary. */ 129*91f16700Schasinglulu write_ctx_reg(gpregs, CTX_GPREG_X8, 0); 130*91f16700Schasinglulu write_ctx_reg(gpregs, CTX_GPREG_X9, 0); 131*91f16700Schasinglulu write_ctx_reg(gpregs, CTX_GPREG_X10, 0); 132*91f16700Schasinglulu write_ctx_reg(gpregs, CTX_GPREG_X11, 0); 133*91f16700Schasinglulu write_ctx_reg(gpregs, CTX_GPREG_X12, 0); 134*91f16700Schasinglulu write_ctx_reg(gpregs, CTX_GPREG_X13, 0); 135*91f16700Schasinglulu write_ctx_reg(gpregs, CTX_GPREG_X14, 0); 136*91f16700Schasinglulu write_ctx_reg(gpregs, CTX_GPREG_X15, 0); 137*91f16700Schasinglulu write_ctx_reg(gpregs, CTX_GPREG_X16, 0); 138*91f16700Schasinglulu write_ctx_reg(gpregs, CTX_GPREG_X17, 0); 139*91f16700Schasinglulu 140*91f16700Schasinglulu rc = spmd_spm_core_sync_entry(ctx); 141*91f16700Schasinglulu if (rc != 0ULL) { 142*91f16700Schasinglulu ERROR("%s failed (%" PRIu64 ") on CPU%u\n", __func__, rc, linear_id); 143*91f16700Schasinglulu } 144*91f16700Schasinglulu 145*91f16700Schasinglulu /* Expect a direct message response from the SPMC. */ 146*91f16700Schasinglulu u_register_t ffa_resp_func = read_ctx_reg(get_gpregs_ctx(&ctx->cpu_ctx), 147*91f16700Schasinglulu CTX_GPREG_X0); 148*91f16700Schasinglulu if (ffa_resp_func != FFA_MSG_SEND_DIRECT_RESP_SMC32) { 149*91f16700Schasinglulu ERROR("%s invalid SPMC response (%lx).\n", 150*91f16700Schasinglulu __func__, ffa_resp_func); 151*91f16700Schasinglulu return -EINVAL; 152*91f16700Schasinglulu } 153*91f16700Schasinglulu 154*91f16700Schasinglulu ctx->state = SPMC_STATE_OFF; 155*91f16700Schasinglulu 156*91f16700Schasinglulu VERBOSE("CPU %u off!\n", linear_id); 157*91f16700Schasinglulu 158*91f16700Schasinglulu return 0; 159*91f16700Schasinglulu } 160*91f16700Schasinglulu 161*91f16700Schasinglulu /******************************************************************************* 162*91f16700Schasinglulu * Structure populated by the SPM Dispatcher to perform any bookkeeping before 163*91f16700Schasinglulu * PSCI executes a power mgmt. operation. 164*91f16700Schasinglulu ******************************************************************************/ 165*91f16700Schasinglulu const spd_pm_ops_t spmd_pm = { 166*91f16700Schasinglulu .svc_on_finish = spmd_cpu_on_finish_handler, 167*91f16700Schasinglulu .svc_off = spmd_cpu_off_handler 168*91f16700Schasinglulu }; 169