19d725191SBence Szépkúti /* 2*8b95e848SZelalem Aweke * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. 39d725191SBence Szépkúti * 49d725191SBence Szépkúti * SPDX-License-Identifier: BSD-3-Clause 59d725191SBence Szépkúti */ 69d725191SBence Szépkúti 79d725191SBence Szépkúti #include <stdbool.h> 89d725191SBence Szépkúti #include <string.h> 99d725191SBence Szépkúti 109d725191SBence Szépkúti #include <arch_helpers.h> 119d725191SBence Szépkúti #include <context.h> 129d725191SBence Szépkúti #include <lib/el3_runtime/context_mgmt.h> 139d725191SBence Szépkúti #include <lib/psci/psci.h> 149d725191SBence Szépkúti #include <lib/utils.h> 159d725191SBence Szépkúti #include <plat/arm/common/plat_arm.h> 169d725191SBence Szépkúti #include <smccc_helpers.h> 179d725191SBence Szépkúti 189d725191SBence Szépkúti /* 199d725191SBence Szépkúti * Handle SMC from a lower exception level to switch its execution state 209d725191SBence Szépkúti * (either from AArch64 to AArch32, or vice versa). 219d725191SBence Szépkúti * 229d725191SBence Szépkúti * smc_fid: 239d725191SBence Szépkúti * SMC function ID - either ARM_SIP_SVC_STATE_SWITCH_64 or 249d725191SBence Szépkúti * ARM_SIP_SVC_STATE_SWITCH_32. 259d725191SBence Szépkúti * pc_hi, pc_lo: 269d725191SBence Szépkúti * PC upon re-entry to the calling exception level; width dependent on the 279d725191SBence Szépkúti * calling exception level. 289d725191SBence Szépkúti * cookie_hi, cookie_lo: 299d725191SBence Szépkúti * Opaque pointer pairs received from the caller to pass it back, upon 309d725191SBence Szépkúti * re-entry. 319d725191SBence Szépkúti * handle: 329d725191SBence Szépkúti * Handle to saved context. 339d725191SBence Szépkúti */ 349d725191SBence Szépkúti int arm_execution_state_switch(unsigned int smc_fid, 359d725191SBence Szépkúti uint32_t pc_hi, 369d725191SBence Szépkúti uint32_t pc_lo, 379d725191SBence Szépkúti uint32_t cookie_hi, 389d725191SBence Szépkúti uint32_t cookie_lo, 399d725191SBence Szépkúti void *handle) 409d725191SBence Szépkúti { 419d725191SBence Szépkúti bool caller_64, thumb = false, from_el2; 429d725191SBence Szépkúti unsigned int el, endianness; 439d725191SBence Szépkúti u_register_t spsr, pc, scr, sctlr; 449d725191SBence Szépkúti entry_point_info_t ep; 459d725191SBence Szépkúti cpu_context_t *ctx = (cpu_context_t *) handle; 469d725191SBence Szépkúti el3_state_t *el3_ctx = get_el3state_ctx(ctx); 479d725191SBence Szépkúti 489d725191SBence Szépkúti /* Validate supplied entry point */ 499d725191SBence Szépkúti pc = (u_register_t) (((uint64_t) pc_hi << 32) | pc_lo); 509d725191SBence Szépkúti if (arm_validate_ns_entrypoint(pc) != 0) 519d725191SBence Szépkúti goto invalid_param; 529d725191SBence Szépkúti 539d725191SBence Szépkúti /* That the SMC originated from NS is already validated by the caller */ 549d725191SBence Szépkúti 559d725191SBence Szépkúti /* 569d725191SBence Szépkúti * Disallow state switch if any of the secondaries have been brought up. 579d725191SBence Szépkúti */ 589d725191SBence Szépkúti if (psci_secondaries_brought_up() != 0) 599d725191SBence Szépkúti goto exec_denied; 609d725191SBence Szépkúti 619d725191SBence Szépkúti spsr = read_ctx_reg(el3_ctx, CTX_SPSR_EL3); 629d725191SBence Szépkúti caller_64 = (GET_RW(spsr) == MODE_RW_64); 639d725191SBence Szépkúti 649d725191SBence Szépkúti if (caller_64) { 659d725191SBence Szépkúti /* 669d725191SBence Szépkúti * If the call originated from AArch64, expect 32-bit pointers when 679d725191SBence Szépkúti * switching to AArch32. 689d725191SBence Szépkúti */ 699d725191SBence Szépkúti if ((pc_hi != 0U) || (cookie_hi != 0U)) 709d725191SBence Szépkúti goto invalid_param; 719d725191SBence Szépkúti 729d725191SBence Szépkúti pc = pc_lo; 739d725191SBence Szépkúti 749d725191SBence Szépkúti /* Instruction state when entering AArch32 */ 759d725191SBence Szépkúti thumb = (pc & 1U) != 0U; 769d725191SBence Szépkúti } else { 779d725191SBence Szépkúti /* Construct AArch64 PC */ 789d725191SBence Szépkúti pc = (((u_register_t) pc_hi) << 32) | pc_lo; 799d725191SBence Szépkúti } 809d725191SBence Szépkúti 819d725191SBence Szépkúti /* Make sure PC is 4-byte aligned, except for Thumb */ 829d725191SBence Szépkúti if (((pc & 0x3U) != 0U) && !thumb) 839d725191SBence Szépkúti goto invalid_param; 849d725191SBence Szépkúti 859d725191SBence Szépkúti /* 869d725191SBence Szépkúti * EL3 controls register width of the immediate lower EL only. Expect 879d725191SBence Szépkúti * this request from EL2/Hyp unless: 889d725191SBence Szépkúti * 899d725191SBence Szépkúti * - EL2 is not implemented; 909d725191SBence Szépkúti * - EL2 is implemented, but was disabled. This can be inferred from 919d725191SBence Szépkúti * SCR_EL3.HCE. 929d725191SBence Szépkúti */ 939d725191SBence Szépkúti from_el2 = caller_64 ? (GET_EL(spsr) == MODE_EL2) : 949d725191SBence Szépkúti (GET_M32(spsr) == MODE32_hyp); 959d725191SBence Szépkúti scr = read_ctx_reg(el3_ctx, CTX_SCR_EL3); 969d725191SBence Szépkúti if (!from_el2) { 979d725191SBence Szépkúti /* The call is from NS privilege level other than HYP */ 989d725191SBence Szépkúti 999d725191SBence Szépkúti /* 1009d725191SBence Szépkúti * Disallow switching state if there's a Hypervisor in place; 1019d725191SBence Szépkúti * this request must be taken up with the Hypervisor instead. 1029d725191SBence Szépkúti */ 1039d725191SBence Szépkúti if ((scr & SCR_HCE_BIT) != 0U) 1049d725191SBence Szépkúti goto exec_denied; 1059d725191SBence Szépkúti } 1069d725191SBence Szépkúti 1079d725191SBence Szépkúti /* 1089d725191SBence Szépkúti * Return to the caller using the same endianness. Extract 1099d725191SBence Szépkúti * endianness bit from the respective system control register 1109d725191SBence Szépkúti * directly. 1119d725191SBence Szépkúti */ 1129d725191SBence Szépkúti sctlr = from_el2 ? read_sctlr_el2() : read_sctlr_el1(); 1139d725191SBence Szépkúti endianness = ((sctlr & SCTLR_EE_BIT) != 0U) ? 1U : 0U; 1149d725191SBence Szépkúti 1159d725191SBence Szépkúti /* Construct SPSR for the exception state we're about to switch to */ 1169d725191SBence Szépkúti if (caller_64) { 1179d725191SBence Szépkúti unsigned long long impl; 1189d725191SBence Szépkúti 1199d725191SBence Szépkúti /* 1209d725191SBence Szépkúti * Switching from AArch64 to AArch32. Ensure this CPU implements 1219d725191SBence Szépkúti * the target EL in AArch32. 1229d725191SBence Szépkúti */ 1239d725191SBence Szépkúti impl = from_el2 ? el_implemented(2) : el_implemented(1); 1249d725191SBence Szépkúti if (impl != EL_IMPL_A64_A32) 1259d725191SBence Szépkúti goto exec_denied; 1269d725191SBence Szépkúti 1279d725191SBence Szépkúti /* Return to the equivalent AArch32 privilege level */ 1289d725191SBence Szépkúti el = from_el2 ? MODE32_hyp : MODE32_svc; 1299d725191SBence Szépkúti spsr = SPSR_MODE32((u_register_t) el, 1309d725191SBence Szépkúti thumb ? SPSR_T_THUMB : SPSR_T_ARM, 1319d725191SBence Szépkúti endianness, DISABLE_ALL_EXCEPTIONS); 1329d725191SBence Szépkúti } else { 1339d725191SBence Szépkúti /* 1349d725191SBence Szépkúti * Switching from AArch32 to AArch64. Since it's not possible to 1359d725191SBence Szépkúti * implement an EL as AArch32-only (from which this call was 1369d725191SBence Szépkúti * raised), it's safe to assume AArch64 is also implemented. 1379d725191SBence Szépkúti */ 1389d725191SBence Szépkúti el = from_el2 ? MODE_EL2 : MODE_EL1; 1399d725191SBence Szépkúti spsr = SPSR_64((u_register_t) el, MODE_SP_ELX, 1409d725191SBence Szépkúti DISABLE_ALL_EXCEPTIONS); 1419d725191SBence Szépkúti } 1429d725191SBence Szépkúti 1439d725191SBence Szépkúti /* 1449d725191SBence Szépkúti * Use the context management library to re-initialize the existing 1459d725191SBence Szépkúti * context with the execution state flipped. Since the library takes 1469d725191SBence Szépkúti * entry_point_info_t pointer as the argument, construct a dummy one 1479d725191SBence Szépkúti * with PC, state width, endianness, security etc. appropriately set. 1489d725191SBence Szépkúti * Other entries in the entry point structure are irrelevant for 1499d725191SBence Szépkúti * purpose. 1509d725191SBence Szépkúti */ 1519d725191SBence Szépkúti zeromem(&ep, sizeof(ep)); 1529d725191SBence Szépkúti ep.pc = pc; 1539d725191SBence Szépkúti ep.spsr = (uint32_t) spsr; 1549d725191SBence Szépkúti SET_PARAM_HEAD(&ep, PARAM_EP, VERSION_1, 1559d725191SBence Szépkúti ((unsigned int) ((endianness != 0U) ? EP_EE_BIG : 1569d725191SBence Szépkúti EP_EE_LITTLE) 1579d725191SBence Szépkúti | NON_SECURE | EP_ST_DISABLE)); 1589d725191SBence Szépkúti 1599d725191SBence Szépkúti /* 1609d725191SBence Szépkúti * Re-initialize the system register context, and exit EL3 as if for the 1619d725191SBence Szépkúti * first time. State switch is effectively a soft reset of the 1629d725191SBence Szépkúti * calling EL. 1639d725191SBence Szépkúti */ 1649d725191SBence Szépkúti cm_init_my_context(&ep); 165*8b95e848SZelalem Aweke cm_prepare_el3_exit_ns(); 1669d725191SBence Szépkúti 1679d725191SBence Szépkúti /* 1689d725191SBence Szépkúti * State switch success. The caller of SMC wouldn't see the SMC 1699d725191SBence Szépkúti * returning. Instead, execution starts at the supplied entry point, 1709d725191SBence Szépkúti * with context pointers populated in registers 0 and 1. 1719d725191SBence Szépkúti */ 1729d725191SBence Szépkúti SMC_RET2(handle, cookie_hi, cookie_lo); 1739d725191SBence Szépkúti 1749d725191SBence Szépkúti invalid_param: 1759d725191SBence Szépkúti SMC_RET1(handle, STATE_SW_E_PARAM); 1769d725191SBence Szépkúti 1779d725191SBence Szépkúti exec_denied: 1789d725191SBence Szépkúti /* State switch denied */ 1799d725191SBence Szépkúti SMC_RET1(handle, STATE_SW_E_DENIED); 1809d725191SBence Szépkúti } 181