Linux kernel 运行在 Non-Secure EL1,如果要进入TEE,首先需要调用汇编指令 smc
进入 EL3,由 monitor(ATF)来完成 Non-Secure world到 Secure world的切换。在 mtk 平台上函数 mt_secure_call
是进入EL3 的入口函数,它调用 smc 指令并通过x0~x3
传入四个参数。其中x0
中是多个位域的一个编码,根据它可以找到哪个service以及service中的哪一项服务。
static noinline int mt_secure_call(u64 function_id, u64 arg0, u64 arg1, u64 arg2)
{
register u64 reg0 __asm__("x0") = function_id;
register u64 reg1 __asm__("x1") = arg0;
register u64 reg2 __asm__("x2") = arg1;
register u64 reg3 __asm__("x3") = arg2;
int ret = 0;
asm volatile ("smc #0\n" : "+r" (reg0) :"r"(reg1), "r"(reg2), "r"(reg3));
ret = (int)reg0;
return ret;
}
前面运行指令smc触发一个同步异常,进入EL3异常向量表对应同步异常入口,如下(bl31/aarch64/runtime_exceptions.S
)
/* ---------------------------------------------------------------------
* 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:
/* NOTE: The code below must preserve x0-x4 */
/*
* Save general purpose and ARMv8.3-PAuth registers (if enabled).
* If Secure Cycle Counter is not disabled in MDCR_EL3 when
* ARMv8.5-PMU is implemented, save PMCR_EL0 and disable Cycle Counter.
* Also set the PSTATE to a known state.
*/
bl prepare_el3_entry
#if ENABLE_PAUTH
/* Load and program APIAKey firmware key */
bl pauth_load_bl31_apiakey
#endif
/*
* 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.
*/
mov x5, xzr
mov x6, sp
/*
* 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]
/* Switch to SP_EL0 */
msr spsel, #MODE_SP_EL0
/*
* 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]
/* Clear flag register */
mov x7, xzr
#if ENABLE_RME
/* Copy SCR_EL3.NSE bit to the flag to indicate caller's security */
ubfx x7, x18, #SCR_NSE_SHIFT, 1
/*
* Shift copied SCR_EL3.NSE bit by 5 to create space for
* SCR_EL3.NS bit. Bit 5 of the flag correspondes to
* the SCR_EL3.NSE bit.
*/
lsl x7, x7, #5
#endif /* ENABLE_RME */
/* Copy SCR_EL3.NS bit to the flag to indicate caller's security */
bfi x7, x18, #0, #1
mov sp, x12
/* 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
/* Load descriptor index from array of indices */
adrp x14, rt_svc_descs_indices
add x14, x14, :lo12:rt_svc_descs_indices
ldrb w15, [x14, x16]
/* Any index greater than 127 is invalid. Check bit 7. */
tbnz w15, 7, smc_unknown
/*
* Get the descriptor using the index
* x11 = (base + off), w15 = index
*
* handler = (base + off) + (index << log2(size))
*/
adr x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)
lsl w10, w15, #RT_SVC_SIZE_LOG2
ldr x15, [x11, w10, uxtw]
/*
* 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:
/*
* Unknown SMC call. Populate return value with SMC_UNK and call
* el3_exit() which will restore the remaining architectural state
* i.e., SYS, GP and PAuth registers(if any) prior to issuing the ERET
* to the desired lower EL.
*/
mov x0, #SMC_UNK
str x0, [x6, #CTX_GPREGS_OFFSET + CTX_GPREG_X0]
b el3_exit
smc_prohibited:
restore_ptw_el1_sys_regs
ldp x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28]
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
mov x0, #SMC_UNK
exception_return
函数 smc_handler64
主要做了下面事情:
spsr_el3
、elr_el3
、scr_el3
到栈中。Index = (function_id >> 24 & 0x3f) | ((function_id >> 31) << 6)
;Handler = __RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE + rt_svc_descs_indices[Index] << 5
__RT_SVC_DESCS_START__
为 rt_svc_descs
的起始地址,RT_SVC_DESC_HANDLE
为服务处理函数 handle
在结构体rt_svc_desc
中的偏移,左移5
,是因为结构体 rt_svc_desc
大小为 32
字节。相关宏定义:
include/common/runtime_svc.h
/*
* Constants to allow the assembler access a runtime service
* descriptor
*/
#ifdef __aarch64__
#define RT_SVC_SIZE_LOG2 U(5)
#define RT_SVC_DESC_INIT U(16)
#define RT_SVC_DESC_HANDLE U(24)
#else
#define RT_SVC_SIZE_LOG2 U(4)
#define RT_SVC_DESC_INIT U(8)
#define RT_SVC_DESC_HANDLE U(12)
#endif /* __aarch64__ */
#define SIZEOF_RT_SVC_DESC (U(1) << RT_SVC_SIZE_LOG2)
include/lib/smccc.h
/*******************************************************************************
* Bit definitions inside the function id as per the SMC calling convention
******************************************************************************/
#define FUNCID_TYPE_SHIFT U(31)
#define FUNCID_TYPE_MASK U(0x1)
#define FUNCID_TYPE_WIDTH U(1)
#define FUNCID_CC_SHIFT U(30)
#define FUNCID_CC_MASK U(0x1)
#define FUNCID_CC_WIDTH U(1)
#define FUNCID_OEN_SHIFT U(24)
#define FUNCID_OEN_MASK U(0x3f)
#define FUNCID_OEN_WIDTH U(6)
#define FUNCID_NUM_SHIFT U(0)
#define FUNCID_NUM_MASK U(0xffff)
#define FUNCID_NUM_WIDTH U(16)
#define GET_SMC_NUM(id) (((id) >> FUNCID_NUM_SHIFT) & \
FUNCID_NUM_MASK)
#define GET_SMC_TYPE(id) (((id) >> FUNCID_TYPE_SHIFT) & \
FUNCID_TYPE_MASK)
#define GET_SMC_CC(id) (((id) >> FUNCID_CC_SHIFT) & \
FUNCID_CC_MASK)
#define GET_SMC_OEN(id) (((id) >> FUNCID_OEN_SHIFT) & \
FUNCID_OEN_MASK)