/******************************************************************************* * Given a secure payload entrypoint, register width, cpu id & pointer to a * context data structure, this function will create a secure context ready for * programming an entry into the secure payload. ******************************************************************************/ void tlkd_init_tlk_ep_state(struct entry_point_info *tlk_entry_point, uint32_t rw, uint64_t pc, tlk_context_t *tlk_ctx) { uint32_t ep_attr, spsr; /* Passing a NULL context is a critical programming error */ assert(tlk_ctx); assert(tlk_entry_point); assert(pc); /* Associate this context with the cpu specified */ tlk_ctx->mpidr = read_mpidr_el1(); clr_std_smc_active_flag(tlk_ctx->state); cm_set_context(&tlk_ctx->cpu_ctx, SECURE); if (rw == SP_AARCH64) spsr = SPSR_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); else spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM, read_sctlr_el3() & SCTLR_EE_BIT, DISABLE_ALL_EXCEPTIONS); /* initialise an entrypoint to set up the CPU context */ ep_attr = SECURE | EP_ST_ENABLE; if (read_sctlr_el3() & SCTLR_EE_BIT) ep_attr |= EP_EE_BIG; SET_PARAM_HEAD(tlk_entry_point, PARAM_EP, VERSION_1, ep_attr); tlk_entry_point->pc = pc; tlk_entry_point->spsr = spsr; }
/******************************************************************************* * This function determines the full entrypoint information for the requested * PSCI entrypoint on power on/resume and returns it. ******************************************************************************/ static int psci_get_ns_ep_info(entry_point_info_t *ep, uintptr_t entrypoint, u_register_t context_id) { unsigned long ep_attr, sctlr; unsigned int daif, ee, mode; unsigned long ns_scr_el3 = read_scr_el3(); unsigned long ns_sctlr_el1 = read_sctlr_el1(); sctlr = ns_scr_el3 & SCR_HCE_BIT ? read_sctlr_el2() : ns_sctlr_el1; ee = 0; ep_attr = NON_SECURE | EP_ST_DISABLE; if (sctlr & SCTLR_EE_BIT) { ep_attr |= EP_EE_BIG; ee = 1; } SET_PARAM_HEAD(ep, PARAM_EP, VERSION_1, ep_attr); ep->pc = entrypoint; memset(&ep->args, 0, sizeof(ep->args)); ep->args.arg0 = context_id; /* * Figure out whether the cpu enters the non-secure address space * in aarch32 or aarch64 */ if (ns_scr_el3 & SCR_RW_BIT) { /* * Check whether a Thumb entry point has been provided for an * aarch64 EL */ if (entrypoint & 0x1) return PSCI_E_INVALID_ADDRESS; mode = ns_scr_el3 & SCR_HCE_BIT ? MODE_EL2 : MODE_EL1; ep->spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); } else { mode = ns_scr_el3 & SCR_HCE_BIT ? MODE32_hyp : MODE32_svc; /* * TODO: Choose async. exception bits if HYP mode is not * implemented according to the values of SCR.{AW, FW} bits */ daif = DAIF_ABT_BIT | DAIF_IRQ_BIT | DAIF_FIQ_BIT; ep->spsr = SPSR_MODE32(mode, entrypoint & 0x1, ee, daif); } return PSCI_E_SUCCESS; }
uint32_t hikey_get_spsr_for_bl33_entry(void) { unsigned int hyp_status, mode, spsr; hyp_status = GET_VIRT_EXT(read_id_pfr1()); mode = (hyp_status) ? MODE32_hyp : MODE32_svc; /* * TODO: Consider the possibility of specifying the SPSR in * the FIP ToC and allowing the platform to have a say as * well. */ spsr = SPSR_MODE32(mode, plat_get_ns_image_entrypoint() & 0x1, SPSR_E_LITTLE, DISABLE_ALL_EXCEPTIONS); return spsr; }
/******************************************************************************* * This function prepare boot argument for 32 bit kernel entry ******************************************************************************/ static entry_point_info_t *bl31_plat_get_next_kernel32_ep_info(void) { entry_point_info_t *next_image_info; unsigned int mode; mode = 0; /* Kernel image is always non-secured */ next_image_info = &bl33_image_ep_info; /* Figure out what mode we enter the non-secure world in */ mode = MODE32_hyp; /* * TODO: Consider the possibility of specifying the SPSR in * the FIP ToC and allowing the platform to have a say as * well. */ INFO("Kernel is 32Bit\n"); next_image_info->spsr = SPSR_MODE32(mode, SPSR_T_ARM, SPSR_E_LITTLE, (DAIF_FIQ_BIT | DAIF_IRQ_BIT | DAIF_ABT_BIT)); next_image_info->pc = get_kernel_info_pc(); next_image_info->args.arg0 = get_kernel_info_r0(); next_image_info->args.arg1 = get_kernel_info_r1(); next_image_info->args.arg2 = get_kernel_info_r2(); INFO("pc=0x%lx, r0=0x%lx, r1=0x%lx, r2=0x%lx\n", next_image_info->pc, next_image_info->args.arg0, next_image_info->args.arg1, next_image_info->args.arg2); SET_SECURITY_STATE(next_image_info->h.attr, NON_SECURE); /* None of the images on this platform can have 0x0 as the entrypoint */ if (next_image_info->pc) return next_image_info; else return NULL; }
/* * Handle SMC from a lower exception level to switch its execution state * (either from AArch64 to AArch32, or vice versa). * * smc_fid: * SMC function ID - either ARM_SIP_SVC_STATE_SWITCH_64 or * ARM_SIP_SVC_STATE_SWITCH_32. * pc_hi, pc_lo: * PC upon re-entry to the calling exception level; width dependent on the * calling exception level. * cookie_hi, cookie_lo: * Opaque pointer pairs received from the caller to pass it back, upon * re-entry. * handle: * Handle to saved context. */ int 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) { /* Execution state can be switched only if EL3 is AArch64 */ #ifdef AARCH64 int caller_64, from_el2, el, endianness, thumb = 0; u_register_t spsr, pc, scr, sctlr; entry_point_info_t ep; cpu_context_t *ctx = (cpu_context_t *) handle; el3_state_t *el3_ctx = get_el3state_ctx(ctx); /* That the SMC originated from NS is already validated by the caller */ /* * Disallow state switch if any of the secondaries have been brought up. */ if (psci_secondaries_brought_up()) goto exec_denied; spsr = read_ctx_reg(el3_ctx, CTX_SPSR_EL3); caller_64 = (GET_RW(spsr) == MODE_RW_64); if (caller_64) { /* * If the call originated from AArch64, expect 32-bit pointers when * switching to AArch32. */ if ((pc_hi != 0) || (cookie_hi != 0)) goto invalid_param; pc = pc_lo; /* Instruction state when entering AArch32 */ thumb = pc & 1; } else { /* Construct AArch64 PC */ pc = (((u_register_t) pc_hi) << 32) | pc_lo; } /* Make sure PC is 4-byte aligned, except for Thumb */ if ((pc & 0x3) && !thumb) goto invalid_param; /* * EL3 controls register width of the immediate lower EL only. Expect * this request from EL2/Hyp unless: * * - EL2 is not implemented; * - EL2 is implemented, but was disabled. This can be inferred from * SCR_EL3.HCE. */ from_el2 = caller_64 ? (GET_EL(spsr) == MODE_EL2) : (GET_M32(spsr) == MODE32_hyp); scr = read_ctx_reg(el3_ctx, CTX_SCR_EL3); if (!from_el2) { /* The call is from NS privilege level other than HYP */ /* * Disallow switching state if there's a Hypervisor in place; * this request must be taken up with the Hypervisor instead. */ if (scr & SCR_HCE_BIT) goto exec_denied; } /* * Return to the caller using the same endianness. Extract * endianness bit from the respective system control register * directly. */ sctlr = from_el2 ? read_sctlr_el2() : read_sctlr_el1(); endianness = !!(sctlr & SCTLR_EE_BIT); /* Construct SPSR for the exception state we're about to switch to */ if (caller_64) { int impl; /* * Switching from AArch64 to AArch32. Ensure this CPU implements * the target EL in AArch32. */ impl = from_el2 ? EL_IMPLEMENTED(2) : EL_IMPLEMENTED(1); if (impl != EL_IMPL_A64_A32) goto exec_denied; /* Return to the equivalent AArch32 privilege level */ el = from_el2 ? MODE32_hyp : MODE32_svc; spsr = SPSR_MODE32(el, thumb ? SPSR_T_THUMB : SPSR_T_ARM, endianness, DISABLE_ALL_EXCEPTIONS); } else { /* * Switching from AArch32 to AArch64. Since it's not possible to * implement an EL as AArch32-only (from which this call was * raised), it's safe to assume AArch64 is also implemented. */ el = from_el2 ? MODE_EL2 : MODE_EL1; spsr = SPSR_64(el, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); } /* * Use the context management library to re-initialize the existing * context with the execution state flipped. Since the library takes * entry_point_info_t pointer as the argument, construct a dummy one * with PC, state width, endianness, security etc. appropriately set. * Other entries in the entry point structure are irrelevant for * purpose. */ zeromem(&ep, sizeof(ep)); ep.pc = pc; ep.spsr = spsr; SET_PARAM_HEAD(&ep, PARAM_EP, VERSION_1, ((endianness ? EP_EE_BIG : EP_EE_LITTLE) | NON_SECURE | EP_ST_DISABLE)); /* * Re-initialize the system register context, and exit EL3 as if for the * first time. State switch is effectively a soft reset of the * calling EL. */ cm_init_my_context(&ep); cm_prepare_el3_exit(NON_SECURE); /* * State switch success. The caller of SMC wouldn't see the SMC * returning. Instead, execution starts at the supplied entry point, * with context pointers populated in registers 0 and 1. */ SMC_RET2(handle, cookie_hi, cookie_lo); invalid_param: SMC_RET1(handle, STATE_SW_E_PARAM); exec_denied: #endif /* State switch denied */ SMC_RET1(handle, STATE_SW_E_DENIED); }
entry_point_info_t *bl31_plat_get_next_kernel_ep_info(uint32_t type) { entry_point_info_t *next_image_info; unsigned long el_status; unsigned int mode; #if RESET_TO_BL31 next_image_info = (type == NON_SECURE) ? &bl33_entrypoint_info : &bl32_entrypoint_info; mt_get_entry_point_info(type, next_image_info); #else next_image_info = (type == NON_SECURE) ? bl2_to_bl31_params->bl33_ep_info : bl2_to_bl31_params->bl32_ep_info; #endif /* Figure out what mode we enter the non-secure world in */ el_status = read_id_aa64pfr0_el1() >> ID_AA64PFR0_EL2_SHIFT; el_status &= ID_AA64PFR0_ELX_MASK; if (el_status) mode = MODE_EL2; else mode = MODE_EL1; #if 0 if (0 == rw) { printf("LK is AArch32\n"); printf("LK start_addr=x0x%x\n", bl33_ep_info->pc); mode = MODE32_svc; ee = 0; /* * TODO: Choose async. exception bits if HYP mode is not * implemented according to the values of SCR.{AW, FW} bits */ daif = DAIF_ABT_BIT | DAIF_IRQ_BIT | DAIF_FIQ_BIT; bl33_ep_info->spsr = SPSR_MODE32(mode, 0, ee, daif); /* * Pass boot argument to LK * ldr w4, =pl_boot_argument * ldr w5, =BOOT_ARGUMENT_SIZE */ bl33_ep_info->args.arg4=(unsigned long)(uintptr_t)&pl_boot_argument; bl33_ep_info->args.arg5=(unsigned long)(uintptr_t)BOOT_ARGUMENT_SIZE; } else #endif { printf("Kernel is 64Bit\n"); next_image_info->spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); next_image_info->pc = get_kernel_info_pc(); next_image_info->args.arg0=get_kernel_info_r0(); next_image_info->args.arg1=get_kernel_info_r1(); printf("pc=0x%llx, r0=0x%llx, r1=0x%llx\n", next_image_info->pc, next_image_info->args.arg0, next_image_info->args.arg1); } SET_SECURITY_STATE(next_image_info->h.attr, NON_SECURE); /* None of the images on this platform can have 0x0 as the entrypoint */ if (next_image_info->pc) return next_image_info; else return NULL; }