• ATF SMC处理


    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    以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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    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 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这段代码很简单,

    1. 把当前的时间戳存到当前线程上下文中。
    2. 读出ESR_EL3的EC字段在这里插入图片描述
    3. 根据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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    smc_handler64实现如下:

    1. 保存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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    1. 切换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															
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    1. 计算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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 根据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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    接下来恢复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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    最后调用exception_return退出EL3的模式。exception_return就是一句eret,然后程序会回到el1,从elr_el1所指向的地方跑。

  • 相关阅读:
    50从零开始用Rust编写nginx,原来TLS证书还可以这么申请
    C++常考面试题(第一篇)
    如何做好自动化测试?揭开测试项目团队的自动化实践过程……
    RSA加密原理与RSA公钥加密系统、数字签名
    哪款蓝牙运动耳机性价比最高、性价比最高的蓝牙运动耳机
    Spring注解开发
    2008年武汉高校630操作系统真题B卷
    Chrome扩展程序是如何进行消息传递的
    单个Nginx发布多个react静态页面
    Go 泛型之泛型约束
  • 原文地址:https://blog.csdn.net/u011280717/article/details/127902274