mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-15 17:14:21 +00:00

According to the SMC Calling Convention (ARM DEN0028B): The Unknown SMC Function Identifier is a sign-extended value of (-1) that is returned in R0, W0 or X0 register. The value wasn't sign-extended because it was defined as a 32-bit unsigned value (0xFFFFFFFF). SMC_PREEMPT has been redefined as -2 for the same reason. NOTE: This might be a compatibility break for some AArch64 platforms that don't follow the previous version of the SMCCC (ARM DEN0028A) correctly. That document specifies that only the bottom 32 bits of the returned value must be checked. If a platform relies on the top 32 bits of the result being 0 (so that SMC_UNK is 0x00000000FFFFFFFF), it will have to fix its code to comply with the SMCCC. Change-Id: I7f7b109f6b30c114fe570aa0ead3c335383cb54d Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
405 lines
12 KiB
ArmAsm
405 lines
12 KiB
ArmAsm
/*
|
|
* Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <arch.h>
|
|
#include <asm_macros.S>
|
|
#include <context.h>
|
|
#include <cpu_data.h>
|
|
#include <interrupt_mgmt.h>
|
|
#include <platform_def.h>
|
|
#include <runtime_svc.h>
|
|
|
|
.globl runtime_exceptions
|
|
|
|
.globl sync_exception_sp_el0
|
|
.globl irq_sp_el0
|
|
.globl fiq_sp_el0
|
|
.globl serror_sp_el0
|
|
|
|
.globl sync_exception_sp_elx
|
|
.globl irq_sp_elx
|
|
.globl fiq_sp_elx
|
|
.globl serror_sp_elx
|
|
|
|
.globl sync_exception_aarch64
|
|
.globl irq_aarch64
|
|
.globl fiq_aarch64
|
|
.globl serror_aarch64
|
|
|
|
.globl sync_exception_aarch32
|
|
.globl irq_aarch32
|
|
.globl fiq_aarch32
|
|
.globl serror_aarch32
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* This macro handles Synchronous exceptions.
|
|
* Only SMC exceptions are supported.
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
.macro handle_sync_exception
|
|
/* Enable the SError interrupt */
|
|
msr daifclr, #DAIF_ABT_BIT
|
|
|
|
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
|
|
|
|
#if ENABLE_RUNTIME_INSTRUMENTATION
|
|
/*
|
|
* Read the timestamp value and store it in per-cpu data. The value
|
|
* will be extracted from per-cpu data by the C level SMC handler and
|
|
* saved to the PMF timestamp region.
|
|
*/
|
|
mrs x30, cntpct_el0
|
|
str x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
|
|
mrs x29, tpidr_el3
|
|
str x30, [x29, #CPU_DATA_PMF_TS0_OFFSET]
|
|
ldr x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
|
|
#endif
|
|
|
|
mrs x30, esr_el3
|
|
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
|
|
|
|
/* Handle SMC exceptions separately from other synchronous exceptions */
|
|
cmp x30, #EC_AARCH32_SMC
|
|
b.eq smc_handler32
|
|
|
|
cmp x30, #EC_AARCH64_SMC
|
|
b.eq smc_handler64
|
|
|
|
/* Other kinds of synchronous exceptions are not handled */
|
|
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
|
|
b report_unhandled_exception
|
|
.endm
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* This macro handles FIQ or IRQ interrupts i.e. EL3, S-EL1 and NS
|
|
* interrupts.
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
.macro handle_interrupt_exception label
|
|
/* Enable the SError interrupt */
|
|
msr daifclr, #DAIF_ABT_BIT
|
|
|
|
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
|
|
bl save_gp_registers
|
|
|
|
/* Save the EL3 system registers needed to return from this exception */
|
|
mrs x0, spsr_el3
|
|
mrs x1, elr_el3
|
|
stp x0, x1, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
|
|
|
|
/* Switch to the runtime stack i.e. SP_EL0 */
|
|
ldr x2, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
|
|
mov x20, sp
|
|
msr spsel, #0
|
|
mov sp, x2
|
|
|
|
/*
|
|
* Find out whether this is a valid interrupt type.
|
|
* If the interrupt controller reports a spurious interrupt then return
|
|
* to where we came from.
|
|
*/
|
|
bl plat_ic_get_pending_interrupt_type
|
|
cmp x0, #INTR_TYPE_INVAL
|
|
b.eq interrupt_exit_\label
|
|
|
|
/*
|
|
* Get the registered handler for this interrupt type.
|
|
* A NULL return value could be 'cause of the following conditions:
|
|
*
|
|
* a. An interrupt of a type was routed correctly but a handler for its
|
|
* type was not registered.
|
|
*
|
|
* b. An interrupt of a type was not routed correctly so a handler for
|
|
* its type was not registered.
|
|
*
|
|
* c. An interrupt of a type was routed correctly to EL3, but was
|
|
* deasserted before its pending state could be read. Another
|
|
* interrupt of a different type pended at the same time and its
|
|
* type was reported as pending instead. However, a handler for this
|
|
* type was not registered.
|
|
*
|
|
* a. and b. can only happen due to a programming error. The
|
|
* occurrence of c. could be beyond the control of Trusted Firmware.
|
|
* It makes sense to return from this exception instead of reporting an
|
|
* error.
|
|
*/
|
|
bl get_interrupt_type_handler
|
|
cbz x0, interrupt_exit_\label
|
|
mov x21, x0
|
|
|
|
mov x0, #INTR_ID_UNAVAILABLE
|
|
|
|
/* Set the current security state in the 'flags' parameter */
|
|
mrs x2, scr_el3
|
|
ubfx x1, x2, #0, #1
|
|
|
|
/* Restore the reference to the 'handle' i.e. SP_EL3 */
|
|
mov x2, x20
|
|
|
|
/* x3 will point to a cookie (not used now) */
|
|
mov x3, xzr
|
|
|
|
/* Call the interrupt type handler */
|
|
blr x21
|
|
|
|
interrupt_exit_\label:
|
|
/* Return from exception, possibly in a different security state */
|
|
b el3_exit
|
|
|
|
.endm
|
|
|
|
|
|
.macro save_x4_to_x29_sp_el0
|
|
stp x4, x5, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4]
|
|
stp x6, x7, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X6]
|
|
stp x8, x9, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X8]
|
|
stp x10, x11, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X10]
|
|
stp x12, x13, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X12]
|
|
stp x14, x15, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X14]
|
|
stp x16, x17, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X16]
|
|
stp x18, x19, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X18]
|
|
stp x20, x21, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X20]
|
|
stp x22, x23, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X22]
|
|
stp x24, x25, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X24]
|
|
stp x26, x27, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X26]
|
|
stp x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28]
|
|
mrs x18, sp_el0
|
|
str x18, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_SP_EL0]
|
|
.endm
|
|
|
|
|
|
vector_base runtime_exceptions
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* Current EL with SP_EL0 : 0x0 - 0x200
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
vector_entry sync_exception_sp_el0
|
|
/* We don't expect any synchronous exceptions from EL3 */
|
|
b report_unhandled_exception
|
|
check_vector_size sync_exception_sp_el0
|
|
|
|
vector_entry irq_sp_el0
|
|
/*
|
|
* EL3 code is non-reentrant. Any asynchronous exception is a serious
|
|
* error. Loop infinitely.
|
|
*/
|
|
b report_unhandled_interrupt
|
|
check_vector_size irq_sp_el0
|
|
|
|
|
|
vector_entry fiq_sp_el0
|
|
b report_unhandled_interrupt
|
|
check_vector_size fiq_sp_el0
|
|
|
|
|
|
vector_entry serror_sp_el0
|
|
b report_unhandled_exception
|
|
check_vector_size serror_sp_el0
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* Current EL with SP_ELx: 0x200 - 0x400
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
vector_entry sync_exception_sp_elx
|
|
/*
|
|
* This exception will trigger if anything went wrong during a previous
|
|
* exception entry or exit or while handling an earlier unexpected
|
|
* synchronous exception. There is a high probability that SP_EL3 is
|
|
* corrupted.
|
|
*/
|
|
b report_unhandled_exception
|
|
check_vector_size sync_exception_sp_elx
|
|
|
|
vector_entry irq_sp_elx
|
|
b report_unhandled_interrupt
|
|
check_vector_size irq_sp_elx
|
|
|
|
vector_entry fiq_sp_elx
|
|
b report_unhandled_interrupt
|
|
check_vector_size fiq_sp_elx
|
|
|
|
vector_entry serror_sp_elx
|
|
b report_unhandled_exception
|
|
check_vector_size serror_sp_elx
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* Lower EL using AArch64 : 0x400 - 0x600
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
vector_entry sync_exception_aarch64
|
|
/*
|
|
* This exception vector will be the entry point for SMCs and traps
|
|
* that are unhandled at lower ELs most commonly. SP_EL3 should point
|
|
* to a valid cpu context where the general purpose and system register
|
|
* state can be saved.
|
|
*/
|
|
handle_sync_exception
|
|
check_vector_size sync_exception_aarch64
|
|
|
|
vector_entry irq_aarch64
|
|
handle_interrupt_exception irq_aarch64
|
|
check_vector_size irq_aarch64
|
|
|
|
vector_entry fiq_aarch64
|
|
handle_interrupt_exception fiq_aarch64
|
|
check_vector_size fiq_aarch64
|
|
|
|
vector_entry serror_aarch64
|
|
/*
|
|
* SError exceptions from lower ELs are not currently supported.
|
|
* Report their occurrence.
|
|
*/
|
|
b report_unhandled_exception
|
|
check_vector_size serror_aarch64
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* Lower EL using AArch32 : 0x600 - 0x800
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
vector_entry sync_exception_aarch32
|
|
/*
|
|
* This exception vector will be the entry point for SMCs and traps
|
|
* that are unhandled at lower ELs most commonly. SP_EL3 should point
|
|
* to a valid cpu context where the general purpose and system register
|
|
* state can be saved.
|
|
*/
|
|
handle_sync_exception
|
|
check_vector_size sync_exception_aarch32
|
|
|
|
vector_entry irq_aarch32
|
|
handle_interrupt_exception irq_aarch32
|
|
check_vector_size irq_aarch32
|
|
|
|
vector_entry fiq_aarch32
|
|
handle_interrupt_exception fiq_aarch32
|
|
check_vector_size fiq_aarch32
|
|
|
|
vector_entry serror_aarch32
|
|
/*
|
|
* SError exceptions from lower ELs are not currently supported.
|
|
* Report their occurrence.
|
|
*/
|
|
b report_unhandled_exception
|
|
check_vector_size serror_aarch32
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* The following code handles secure monitor calls.
|
|
* Depending upon the execution state from where the SMC has been
|
|
* invoked, it frees some general purpose registers to perform the
|
|
* remaining tasks. They involve finding the runtime service handler
|
|
* that is the target of the SMC & switching to runtime stacks (SP_EL0)
|
|
* before calling the handler.
|
|
*
|
|
* Note that x30 has been explicitly saved and can be used here
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
func smc_handler
|
|
smc_handler32:
|
|
/* Check whether aarch32 issued an SMC64 */
|
|
tbnz x0, #FUNCID_CC_SHIFT, smc_prohibited
|
|
|
|
smc_handler64:
|
|
/*
|
|
* Populate the parameters for the SMC handler.
|
|
* We already have x0-x4 in place. x5 will point to a cookie (not used
|
|
* now). x6 will point to the context structure (SP_EL3) and x7 will
|
|
* contain flags we need to pass to the handler.
|
|
*
|
|
* Save x4-x29 and sp_el0. Refer to SMCCC v1.1.
|
|
*/
|
|
save_x4_to_x29_sp_el0
|
|
|
|
mov x5, xzr
|
|
mov x6, sp
|
|
|
|
/* Get the unique owning entity number */
|
|
ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH
|
|
ubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH
|
|
orr x16, x16, x15, lsl #FUNCID_OEN_WIDTH
|
|
|
|
adr x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)
|
|
|
|
/* Load descriptor index from array of indices */
|
|
adr x14, rt_svc_descs_indices
|
|
ldrb w15, [x14, x16]
|
|
|
|
/*
|
|
* Restore the saved C runtime stack value which will become the new
|
|
* SP_EL0 i.e. EL3 runtime stack. It was saved in the 'cpu_context'
|
|
* structure prior to the last ERET from EL3.
|
|
*/
|
|
ldr x12, [x6, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
|
|
|
|
/*
|
|
* Any index greater than 127 is invalid. Check bit 7 for
|
|
* a valid index
|
|
*/
|
|
tbnz w15, 7, smc_unknown
|
|
|
|
/* Switch to SP_EL0 */
|
|
msr spsel, #0
|
|
|
|
/*
|
|
* Get the descriptor using the index
|
|
* x11 = (base + off), x15 = index
|
|
*
|
|
* handler = (base + off) + (index << log2(size))
|
|
*/
|
|
lsl w10, w15, #RT_SVC_SIZE_LOG2
|
|
ldr x15, [x11, w10, uxtw]
|
|
|
|
/*
|
|
* Save the SPSR_EL3, ELR_EL3, & SCR_EL3 in case there is a world
|
|
* switch during SMC handling.
|
|
* TODO: Revisit if all system registers can be saved later.
|
|
*/
|
|
mrs x16, spsr_el3
|
|
mrs x17, elr_el3
|
|
mrs x18, scr_el3
|
|
stp x16, x17, [x6, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
|
|
str x18, [x6, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
|
|
|
|
/* Copy SCR_EL3.NS bit to the flag to indicate caller's security */
|
|
bfi x7, x18, #0, #1
|
|
|
|
mov sp, x12
|
|
|
|
/*
|
|
* Call the Secure Monitor Call handler and then drop directly into
|
|
* el3_exit() which will program any remaining architectural state
|
|
* prior to issuing the ERET to the desired lower EL.
|
|
*/
|
|
#if DEBUG
|
|
cbz x15, rt_svc_fw_critical_error
|
|
#endif
|
|
blr x15
|
|
|
|
b el3_exit
|
|
|
|
smc_unknown:
|
|
/*
|
|
* Here we restore x4-x18 regardless of where we came from. AArch32
|
|
* callers will find the registers contents unchanged, but AArch64
|
|
* callers will find the registers modified (with stale earlier NS
|
|
* content). Either way, we aren't leaking any secure information
|
|
* through them.
|
|
*/
|
|
mov x0, #SMC_UNK
|
|
b restore_gp_registers_callee_eret
|
|
|
|
smc_prohibited:
|
|
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
|
|
mov x0, #SMC_UNK
|
|
eret
|
|
|
|
rt_svc_fw_critical_error:
|
|
/* Switch to SP_ELx */
|
|
msr spsel, #1
|
|
no_ret report_unhandled_exception
|
|
endfunc smc_handler
|