文章目录
- ATF SMC上下文结构体
- SMC handler实现
ATF SMC上下文结构体
ATF在处理SMC的时候会把CPU的寄存器保存起来,退出SMC的时候恢复现场。使用qemu_v8.mk编译出来的ATF没有定义CTX_INCLUDE_EL2_REGS,CTX_INCLUDE_FPREGS和CTX_INCLUDE_PAUTH_REGS。
lib/context.h
typedef struct cpu_context {
gp_regs_t gpregs_ctx;
el3_state_t el3state_ctx;
el1_sysregs_t el1_sysregs_ctx;
#if CTX_INCLUDE_EL2_REGS
el2_sysregs_t el2_sysregs_ctx;
#endif
#if CTX_INCLUDE_FPREGS
fp_regs_t fpregs_ctx;
#endif
cve_2018_3639_t cve_2018_3639_ctx;
#if CTX_INCLUDE_PAUTH_REGS
pauth_t pauth_ctx;
#endif
} cpu_context_t;
以gp_regs_t 为例,它就是定义了一个uint64_t数组,总共包含64个元素。
DEFINE_REG_STRUCT(gp_regs, CTX_GPREG_ALL);
#define DEFINE_REG_STRUCT(name, num_regs) \
typedef struct name { \
uint64_t ctx_regs[num_regs]; \
} __aligned(16) name##_t
#define CTX_GPREG_ALL (CTX_GPREGS_END >> DWORD_SHIFT)
#define CTX_GPREGS_OFFSET U(0x0)
#define CTX_GPREG_X0 U(0x0)
...
#define CTX_GPREG_X29 U(0xe8)
#define CTX_GPREG_LR U(0xf0)
#define CTX_GPREG_SP_EL0 U(0xf8)
#define CTX_GPREGS_END U(0x100)
#define DWORD_SHIFT U(3)
0x00 - 0xFF 保存了通用寄存器。
0x100 - 0x13F 保存了EL3的状态寄存器
#define CTX_SCR_EL3 U(0x0)
#define CTX_ESR_EL3 U(0x8)
#define CTX_RUNTIME_SP U(0x10)
#define CTX_SPSR_EL3 U(0x18)
#define CTX_ELR_EL3 U(0x20)
#define CTX_PMCR_EL0 U(0x28)
#define CTX_IS_IN_EL3 U(0x30)
#define CTX_EL3STATE_END U(0x40) /* Align to the next 16 byte boundary */
0x140 - 0x1AF保存了EL1的系统寄存器
#define CTX_SPSR_EL1 U(0x0)
#define CTX_ELR_EL1 U(0x8)
#define CTX_SCTLR_EL1 U(0x10)
#define CTX_TCR_EL1 U(0x18)
#define CTX_CPACR_EL1 U(0x20)
#define CTX_CSSELR_EL1 U(0x28)
#define CTX_SP_EL1 U(0x30)
#define CTX_ESR_EL1 U(0x38)
#define CTX_TTBR0_EL1 U(0x40)
#define CTX_TTBR1_EL1 U(0x48)
#define CTX_MAIR_EL1 U(0x50)
#define CTX_AMAIR_EL1 U(0x58)
#define CTX_ACTLR_EL1 U(0x60)
#define CTX_TPIDR_EL1 U(0x68)
#define CTX_TPIDR_EL0 U(0x70)
#define CTX_TPIDRRO_EL0 U(0x78)
#define CTX_PAR_EL1 U(0x80)
#define CTX_FAR_EL1 U(0x88)
#define CTX_AFSR0_EL1 U(0x90)
#define CTX_AFSR1_EL1 U(0x98)
#define CTX_CONTEXTIDR_EL1 U(0xa0)
#define CTX_VBAR_EL1 U(0xa8)
#define CTX_AARCH32_END U(0xb0) /* Align to the next 16 byte boundary */
SMC handler实现
REE从EL1调用SMC后就会进入到ATF的sync_exception_aarch64中。从反汇编来看,apply_at_speculative_wa和check_and_unmask_ea是空实现,所以直接进到了handle_sync_exception中。
bl31/aarch64/runtime_exception.S
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.
*/
apply_at_speculative_wa
check_and_unmask_ea
handle_sync_exception
end_vector_entry sync_exception_aarch64
这段代码很简单,
- 把当前的时间戳存到当前线程上下文中。
- 读出ESR_EL3的EC字段
- 根据EC字段的值处理,如果是触发的aarch32的SMC, 就进入smc_handler32;如果触发的aarch64的SMC,就进入smc_handler64;否则就进入默认处理函数enter_lower_el_sync_ea
.macro handle_sync_exception
#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 //读出CNTPCT的值
str x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29] //把X29存到上下文中
mrs x29, tpidr_el3 //读出当前线程上下文
str x30, [x29, #CPU_DATA_PMF_TS0_OFFSET] //把TIMESTAMP存到线程上下文中
ldr x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29] //恢复X29
#endif
mrs x30, esr_el3 //读出ESR_EL3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH //根据ESR_EL3中的EC值来选择SMC
/* Handle SMC exceptions separately from other synchronous exceptions */
cmp x30, #EC_AARCH32_SMC
b.eq smc_handler32
cmp x30, #EC_AARCH64_SMC //AARCH64走的这条路
b.eq smc_handler64
/* Synchronous exceptions other than the above are assumed to be EA */
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
b enter_lower_el_sync_ea
.endm
smc_handler64实现如下:
- 保存gp,pmcr,patuh寄存器到上下文中。
bl31/aarch64/runtime_exception.S
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.
*/
bl save_gp_pmcr_pauth_regs
lib/el3_runtime/context.S
func save_gp_pmcr_pauth_regs
stp x0, x1, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X0]
stp x2, x3, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X2]
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]
/* ----------------------------------------------------------
* Check if earlier initialization MDCR_EL3.SCCD to 1 failed,
* meaning that ARMv8-PMU is not implemented and PMCR_EL0
* should be saved in non-secure context.
* ----------------------------------------------------------
*/
mrs x9, mdcr_el3
tst x9, #MDCR_SCCD_BIT
bne 1f
/* Secure Cycle Counter is not disabled */
mrs x9, pmcr_el0
/* Check caller's security state */
mrs x10, scr_el3
tst x10, #SCR_NS_BIT
beq 2f
/* Save PMCR_EL0 if called from Non-secure state */
str x9, [sp, #CTX_EL3STATE_OFFSET + CTX_PMCR_EL0]
/* Disable cycle counter when event counting is prohibited */
2: orr x9, x9, #PMCR_EL0_DP_BIT
msr pmcr_el0, x9
isb
1:
#if CTX_INCLUDE_PAUTH_REGS
/* ----------------------------------------------------------
* Save the ARMv8.3-PAuth keys as they are not banked
* by exception level
* ----------------------------------------------------------
*/
add x19, sp, #CTX_PAUTH_REGS_OFFSET
mrs x20, APIAKeyLo_EL1 /* x21:x20 = APIAKey */
mrs x21, APIAKeyHi_EL1
mrs x22, APIBKeyLo_EL1 /* x23:x22 = APIBKey */
mrs x23, APIBKeyHi_EL1
mrs x24, APDAKeyLo_EL1 /* x25:x24 = APDAKey */
mrs x25, APDAKeyHi_EL1
mrs x26, APDBKeyLo_EL1 /* x27:x26 = APDBKey */
mrs x27, APDBKeyHi_EL1
mrs x28, APGAKeyLo_EL1 /* x29:x28 = APGAKey */
mrs x29, APGAKeyHi_EL1
stp x20, x21, [x19, #CTX_PACIAKEY_LO]
stp x22, x23, [x19, #CTX_PACIBKEY_LO]
stp x24, x25, [x19, #CTX_PACDAKEY_LO]
stp x26, x27, [x19, #CTX_PACDBKEY_LO]
stp x28, x29, [x19, #CTX_PACGAKEY_LO]
#endif /* CTX_INCLUDE_PAUTH_REGS */
ret
endfunc save_gp_pmcr_pauth_regs
- 切换SMC runtime的栈指针为SP_EL0, 并保存EL3相关寄存器。
/*
* 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] //从当前栈SP_EL3中拿出smc runtime栈SP_EL0指针
/* Switch to SP_EL0 */
msr spsel, #MODE_SP_EL0 //切换栈指针到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 //保存SPSR_EL3, ELR_EL3 & SCR_EL3到ATF SMC上下文中
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
- 计算SMC handler索引,并判断其不得大于127。
/* Get the unique owning entity number */ //获取SMC Handler的索引
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
- 根据SMC handler索引从__RT_SVC_DESCS_HANDLE中获取具体handler的函数指针。这个函数指针是在runtime_svc_init中初始化的。
/*
* 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 //跳转到具体的smc handler
- smc处理完之后调用el3_exit退出SMC。el3_exit上来先把SP设为SP_EL3,然后从栈中恢复SCR_EL3, SPSR_EL3,这个函数正好跟之前的保存上下文的操作反过来。
func el3_exit
#if ENABLE_ASSERTIONS
/* el3_exit assumes SP_EL0 on entry */
mrs x17, spsel
cmp x17, #MODE_SP_EL0
ASM_ASSERT(eq)
#endif
/* ----------------------------------------------------------
* Save the current SP_EL0 i.e. the EL3 runtime stack which
* will be used for handling the next SMC.
* Then switch to SP_EL3.
* ----------------------------------------------------------
*/
mov x17, sp
msr spsel, #MODE_SP_ELX
str x17, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
/* ----------------------------------------------------------
* Restore SPSR_EL3, ELR_EL3 and SCR_EL3 prior to ERET
* ----------------------------------------------------------
*/
ldr x18, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
ldp x16, x17, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
msr scr_el3, x18
msr spsr_el3, x16
msr elr_el3, x17
接下来恢复el1的系统寄存器,通用寄存器,PMCR和PATUH寄存器。
restore_ptw_el1_sys_regs
/* ----------------------------------------------------------
* Restore general purpose (including x30), PMCR_EL0 and
* ARMv8.3-PAuth registers.
* Exit EL3 via ERET to a lower exception level.
* ----------------------------------------------------------
*/
bl restore_gp_pmcr_pauth_regs
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
最后调用exception_return退出EL3的模式。exception_return就是一句eret,然后程序会回到el1,从elr_el1所指向的地方跑。