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 */
arm_execution_state_switch(unsigned int smc_fid,uint32_t pc_hi,uint32_t pc_lo,uint32_t cookie_hi,uint32_t cookie_lo,void * handle)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