davidcunado-arm
8 years ago
committed by
GitHub
17 changed files with 383 additions and 35 deletions
@ -0,0 +1,91 @@ |
|||
|
|||
ARM SiP Service |
|||
=============== |
|||
|
|||
This document enumerates and describes the ARM SiP (Silicon Provider) services. |
|||
|
|||
SiP services are non-standard, platform-specific services offered by the silicon |
|||
implementer or platform provider. They are accessed via. `SMC` ("SMC calls") |
|||
instruction executed from Exception Levels below EL3. SMC calls for SiP |
|||
services: |
|||
|
|||
* Follow [SMC Calling Convention][SMCCC]; |
|||
* Use SMC function IDs that fall in the SiP range, which are `0xc2000000` - |
|||
`0xc200ffff` for 64-bit calls, and `0x82000000` - `0x8200ffff` for 32-bit |
|||
calls. |
|||
|
|||
The ARM SiP implementation offers the following services: |
|||
|
|||
* Performance Measurement Framework (PMF) |
|||
* Execution State Switching service |
|||
|
|||
Source definitions for ARM SiP service are located in the `arm_sip_svc.h` header |
|||
file. |
|||
|
|||
Performance Measurement Framework (PMF) |
|||
--------------------------------------- |
|||
|
|||
The [Performance Measurement Framework](./firmware-design.md#13--performance-measurement-framework) |
|||
allows callers to retrieve timestamps captured at various paths in ARM Trusted |
|||
Firmware execution. It's described in detail in [Firmware Design document][Firmware Design]. |
|||
|
|||
Execution State Switching service |
|||
--------------------------------- |
|||
|
|||
Execution State Switching service provides a mechanism for a non-secure lower |
|||
Exception Level (either EL2, or NS EL1 if EL2 isn't implemented) to request to |
|||
switch its execution state (a.k.a. Register Width), either from AArch64 to |
|||
AArch32, or from AArch32 to AArch64, for the calling CPU. This service is only |
|||
available when ARM Trusted Firmware is built for AArch64 (i.e. when build option |
|||
`ARCH` is set to `aarch64`). |
|||
|
|||
### `ARM_SIP_SVC_EXE_STATE_SWITCH` |
|||
|
|||
Arguments: |
|||
uint32_t Function ID |
|||
uint32_t PC hi |
|||
uint32_t PC lo |
|||
uint32_t Cookie hi |
|||
uint32_t Cookie lo |
|||
|
|||
Return: |
|||
uint32_t |
|||
|
|||
The function ID parameter must be `0x82000020`. It uniquely identifies the |
|||
Execution State Switching service being requested. |
|||
|
|||
The parameters _PC hi_ and _PC lo_ defines upper and lower words, respectively, |
|||
of the entry point (physical address) at which execution should start, after |
|||
Execution State has been switched. When calling from AArch64, _PC hi_ must be 0. |
|||
|
|||
When execution starts at the supplied entry point after Execution State has been |
|||
switched, the parameters _Cookie hi_ and _Cookie lo_ are passed in CPU registers |
|||
0 and 1, respectively. When calling from AArch64, _Cookie hi_ must be 0. |
|||
|
|||
This call can only be made on the primary CPU, before any secondaries were |
|||
brought up with `CPU_ON` PSCI call. Otherwise, the call will always fail. |
|||
|
|||
The effect of switching execution state is as if the Exception Level were |
|||
entered for the first time, following power on. This means CPU registers that |
|||
have a defined reset value by the Architecture will assume that value. Other |
|||
registers should not be expected to hold their values before the call was made. |
|||
CPU endianness, however, is preserved from the previous execution state. Note |
|||
that this switches the execution state of the calling CPU only. This is not a |
|||
substitute for PSCI `SYSTEM_RESET`. |
|||
|
|||
The service may return the following error codes: |
|||
|
|||
- `STATE_SW_E_PARAM`: If any of the parameters were deemed invalid for |
|||
a specific request. |
|||
- `STATE_SW_E_DENIED`: If the call is not successful, or when ARM Trusted |
|||
Firmware is built for AArch32. |
|||
|
|||
If the call is successful, the caller wouldn't observe the SMC returning. |
|||
Instead, execution starts at the supplied entry point, with the CPU registers 0 |
|||
and 1 populated with the supplied _Cookie hi_ and _Cookie lo_ values, |
|||
respectively. |
|||
|
|||
- - - - - - - - - - - - - - - - - - - - - - - - - - |
|||
|
|||
[Firmware Design]: ./firmware-design.md |
|||
[SMCCC]: http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html "SMC Calling Convention PDD (ARM DEN 0028A)" |
@ -0,0 +1,197 @@ |
|||
/*
|
|||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* Redistribution and use in source and binary forms, with or without |
|||
* modification, are permitted provided that the following conditions are met: |
|||
* |
|||
* Redistributions of source code must retain the above copyright notice, this |
|||
* list of conditions and the following disclaimer. |
|||
* |
|||
* Redistributions in binary form must reproduce the above copyright notice, |
|||
* this list of conditions and the following disclaimer in the documentation |
|||
* and/or other materials provided with the distribution. |
|||
* |
|||
* Neither the name of ARM nor the names of its contributors may be used |
|||
* to endorse or promote products derived from this software without specific |
|||
* prior written permission. |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|||
* POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#include <arch_helpers.h> |
|||
#include <arm_sip_svc.h> |
|||
#include <context.h> |
|||
#include <context_mgmt.h> |
|||
#include <plat_arm.h> |
|||
#include <psci.h> |
|||
#include <smcc_helpers.h> |
|||
#include <string.h> |
|||
#include <utils.h> |
|||
|
|||
/*
|
|||
* 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); |
|||
} |
Loading…
Reference in new issue