天天看點

[ATF]-ATF的代碼學習篇-一篇就夠了

★★★ 個人部落格導讀首頁—點選此處 ★★★

.

說明:

在預設情況下,本文講述的都是ARMV8-aarch64架構,linux kernel 64位

文章目錄

        • 1、ATF裡都有什麼?
        • 2、ATF的編譯
        • 3、ATF的啟動
        • 4、進入ATF的和退出ATF方式
          • (1)、進入ATF的方式
          • (2)、退出ATF的方式
        • 5、ATF中向量表的介紹
        • 6、ATF中棧的設定
        • 7、ATF中寄存器的儲存和恢複
        • 8、ATF的rt_svc介紹(runtime service)
          • (1)、SMC Calling convention文檔
          • (2)、DECLARE_RT_SVC的使用
          • (3)、DECLARE_RT_SVC的定義
          • (4)、在同步異常中smc_handler64,跳轉到響應的rt_svc
          • (5)、smc在驅動中的調用
          • 5、smc流程下的代碼分析

1、ATF裡都有什麼?

ATF的Current features Overview

  • 安全世界的初始化,例如異常向量表、一些控制寄存器和中斷寄
  • CPU reset和power down的時序。包括Arm DynamIQ cpu的支援。
  • 标準的system IP的驅動,例如Generic Interrupt Controller (GIC), Cache Coherent Interconnect (CCI), Cache Coherent Network (CCN), Network Interconnect (NIC) and TrustZone Controller (TZC).
  • 一種通用的SCMI驅動程式, 适用于電源控制接口,例如ARM SYSTEM Control Processor(SCP)
  • smc處理,using an EL3 runtime services framework
  • PSCI庫的支援,用于CPU/Cluster/system的電源管理,這個庫內建到了aarch64 el3的runtime中,也适用于aarch32 el3
  • secure monitor代碼,用于world切換、中斷routing
  • SPDs for the OP-TEE Secure OS, NVIDIA Trusted Little Kernel and Trusty Secure OS
  • SecureBoot實作
  • 預內建TBB與Arm CryptoCell産品,利用其硬體Root的信任和加密加速服務。

2、ATF的編譯

TODO

3、ATF的啟動

TODO

4、進入ATF的和退出ATF方式

透過事務看本質, 進入和退出ATF,就是就是EL等級切換的過程,那麼EL等級都是怎麼切換的呢?通過下面一張圖就可以說明這一切:

[ATF]-ATF的代碼學習篇-一篇就夠了
(1)、進入ATF的方式

進入ATF的方式觸發異常:同步異常SMC、異步異常(irq,fiq)

  • ➨ 如果是同步異常,那麼一定是在linux或tee中發生了smc調用,此時進入跳轉ATF中異常向量表中的同步異常程式smc_handler64或smc_handler32

    .

    在該程式中,解析smc id,來選擇跳轉到具體哪一個rt-svc(runtime service)

  • ➨ 如果是異步異常,那麼一定是觸發了irq或fiq或serror中斷等,此時進入跳轉ATF中異常向量表中的異步異常程式,進而跳轉到響應的中斷處理函數.

    .

    在ATF中僅實作irq_aarch64、fiq_aarch64、irq_aarch32、fiq_aarch32 四個異常中斷處理函數

vector_entry irq_aarch64
	check_and_unmask_ea
	handle_interrupt_exception irq_aarch64
end_vector_entry irq_aarch64

vector_entry fiq_aarch64
	check_and_unmask_ea
	handle_interrupt_exception fiq_aarch64
end_vector_entry fiq_aarch64

vector_entry irq_aarch32
	check_and_unmask_ea
	handle_interrupt_exception irq_aarch32
end_vector_entry irq_aarch32

vector_entry fiq_aarch32
	check_and_unmask_ea
	handle_interrupt_exception fiq_aarch32
end_vector_entry fiq_aarch32
           

在中斷函數中,先調用plat_ic_get_pending_interrupt_type擷取interrupt_type,其實就是通過讀取寄存器read_icc_hppir0_el1() ,判斷中斷是從那裡來的,然後傳回下面三種interrupt_type:

  • #define INTR_TYPE_S_EL1 U(0)
  • #define INTR_TYPE_EL3 U(1)
  • #define INTR_TYPE_NS U(2)

有了type,再get_interrupt_type_handler擷取handler程式,進而跳轉到相應的handler程式。

ATF中中斷的注冊(這三種類型的handler程式的注冊),以INTR_TYPE_S_EL1為例:

在開機bl32_main調用opteed_setup()時,将opteed_sel1_interrupt_handler()函數注冊成了INTR_TYPE_S_EL1類型中斷,同時也會将REE(Linux)使用的SCR_EL3.FIQ配置成1,

也意味着當CPU運作在TEE時,來了一個secure group1的中斷,此中斷在REE中被标記FIQ後将被target到EL3,進入EL3(ATF)的中斷處理函數,也就是剛才注冊的opteed_sel1_interrupt_handler()函數,在該函數中,會将cpu切換到TEE中,去處理這個中斷;

(2)、退出ATF的方式

進入EL3(ATF)的方式觸發異常:ERET指令、或是主動修改PSTATE寄存器

在ATF中執行smc_handler或中斷handler結束後,會調用el3_exit,el3_exit會調用ERET指令,恢複Secure或non-secure的PC指針和PSTATE,回到secure EL1或non-secure EL1.

下圖是一個smc進入ATF,處理完任務後再傳回EL1的過程:

[ATF]-ATF的代碼學習篇-一篇就夠了

5、ATF中向量表的介紹

TODO

6、ATF中棧的設定

TODO : 運作時棧SP_EL3,函數調用時切換SP_EL0使用的棧

7、ATF中寄存器的儲存和恢複

TODO

8、ATF的rt_svc介紹(runtime service)

(1)、SMC Calling convention文檔
[ATF]-ATF的代碼學習篇-一篇就夠了

我們重點看下這張表,對應smc id的定義

  • bit31決定是fast call,還是std call(yield對應的就是std call)
  • bit30表示是以32位傳參,還是以64位傳參, 注意我們看了optee在linux的driver,都是以32位方式
  • bit29:24 決定服務的類型
  • bit23:16 reserved
  • bit15:0 每種call類型下,表示range

bit31、bit30、bit23:16、bit15:0 都是很好了解,我們來講一下bit29:24

在ATF中定義rt_svc(runtime service)時,需按照該文檔的描述來定義

例如在opteed_main.c中,定義了一個service,它的call類型是OEN_TOS_START–OEN_TOS_END,對應的恰好是bit29:24 = 50–63

DECLARE_RT_SVC(
	opteed_fast,

	OEN_TOS_START,
	OEN_TOS_END,
	SMC_TYPE_FAST,
	opteed_setup,
	opteed_smc_handler
);
           

那麼我們在linux kernel中,調用smc時的smc id的bit29:24需要等于50,那麼此次的smc調用才會調用到這個runtime service的handler程式

例如在arm_arch_svc_setup.c中,定義了一個service,它的call類型是OEN_ARM_START–OEN_ARM_END,對應的恰好是bit29:24 = 0–0

/* Register Standard Service Calls as runtime service */
DECLARE_RT_SVC(
		arm_arch_svc,
		OEN_ARM_START,
		OEN_ARM_END,
		SMC_TYPE_FAST,
		NULL,
		arm_arch_svc_smc_handler
);
           

那麼我們在linux kernel中,調用smc時的smc id的bit29:24需要等于0,那麼此次的smc調用才會調用到這個runtime service的handler程式

例如mtk代碼中,mtk_sip_svc.c中,定義了一個service,它的call類型是OEN_SIP_START–OEN_SIP_END,對應的恰好是bit29:24 = 2–2

/* Define a runtime service descriptor for fast SMC calls */
DECLARE_RT_SVC(
	mediatek_sip_svc,
	OEN_SIP_START,
	OEN_SIP_END,
	SMC_TYPE_FAST,
	NULL,
	sip_smc_handler
);
           

那麼我們在linux kernel中,調用smc時的smc id的bit29:24需要等于2,那麼此次的smc調用才會調用到這個runtime service的handler程式

(2)、DECLARE_RT_SVC的使用

DECLARE_RT_SVC是一個宏,用于定義一組service,例如在opteed_main.c中的使用

/* Define an OPTEED runtime service descriptor for fast SMC calls */
DECLARE_RT_SVC(
	opteed_fast,

	OEN_TOS_START,
	OEN_TOS_END,
	SMC_TYPE_FAST,
	opteed_setup,
	opteed_smc_handler
);

/* Define an OPTEED runtime service descriptor for yielding SMC calls */
DECLARE_RT_SVC(
	opteed_std,

	OEN_TOS_START,
	OEN_TOS_END,
	SMC_TYPE_YIELD,
	NULL,
	opteed_smc_handler
);
           

其中OEN_TOS_START和OEN_TOS_END、SMC_TYPE_FAST和SMC_TYPE_YIELD都是按照SMC Calling convention文檔來定義的

#define OEN_ARM_START			U(0)
#define OEN_ARM_END			U(0)
#define OEN_CPU_START			U(1)
#define OEN_CPU_END			U(1)
#define OEN_SIP_START			U(2)
#define OEN_SIP_END			U(2)
#define OEN_OEM_START			U(3)
#define OEN_OEM_END			U(3)
#define OEN_STD_START			U(4)	/* Standard Service Calls */
#define OEN_STD_END			U(4)
#define OEN_STD_HYP_START		U(5)	/* Standard Hypervisor Service calls */
#define OEN_STD_HYP_END			U(5)
#define OEN_VEN_HYP_START		U(6)	/* Vendor Hypervisor Service calls */
#define OEN_VEN_HYP_END			U(6)
#define OEN_TAP_START			U(48)	/* Trusted Applications */
#define OEN_TAP_END			U(49)
#define OEN_TOS_START			U(50)	/* Trusted OS */
#define OEN_TOS_END			U(63)
#define OEN_LIMIT			U(64)

#define SMC_TYPE_FAST			ULL(1)
#define SMC_TYPE_YIELD			ULL(0)
           

SMC_TYPE_FAST和SMC_TYPE_YIELD也是根據SMC Calling convention文檔定義

(3)、DECLARE_RT_SVC的定義

在runtime_svc.h中,其實就是在section(“rt_svc_descs”)段中定義了一個全局變量.

/*
 * Convenience macros to declare a service descriptor
 */
#define DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _smch)	\
	static const rt_svc_desc_t __svc_desc_ ## _name			\
		__section("rt_svc_descs") __used = {			\
			.start_oen = (_start),				\
			.end_oen = (_end),				\
			.call_type = (_type),				\
			.name = #_name,					\
			.init = (_setup),				\
			.handle = (_smch)				\
		}
           

section “rt_svc_descs”在RT_SVC_DESCS宏中

#define RT_SVC_DESCS					\
	. = ALIGN(STRUCT_ALIGN);			\
	__RT_SVC_DESCS_START__ = .;			\
	KEEP(*(rt_svc_descs))				\
	__RT_SVC_DESCS_END__ = .;
           

而在rodata_common的宏中,定義了RT_SVC_DESCS

#define RODATA_COMMON					\
	RT_SVC_DESCS					\
	FCONF_POPULATOR					\
	PMF_SVC_DESCS					\
	PARSER_LIB_DESCS				\
	CPU_OPS						\
	GOT						\
	BASE_XLAT_TABLE_RO
           

在bl31.ld.S中,将RODATA_COMMON放入了rodata段

.rodata . : {
        __RODATA_START__ = .;
        *(SORT_BY_ALIGNMENT(.rodata*))

	RODATA_COMMON

        /* Place pubsub sections for events */
        . = ALIGN(8);
#include <lib/el3_runtime/pubsub_events.h>

        . = ALIGN(PAGE_SIZE);
        __RODATA_END__ = .;
    } >RAM
           
(4)、在同步異常中smc_handler64,跳轉到響應的rt_svc

附上完整代碼和注釋

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

#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]

	/* 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  ---------------- 擷取FUNCID_OEN_SHIFT,對應第一節中的OEN_TOS_START
	ubfx	x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH  ---------------- 擷取FUNCID_TYPE_SHIFT,對應第一節中的SMC_TYPE_FAST(fast還是yield,yield其實就是standard)
	orr	x16, x16, x15, lsl #FUNCID_OEN_WIDTH

	/* Load descriptor index from array of indices */
	adrp	x14, rt_svc_descs_indices  ----在runtime_svc_init()中會将所有的section rt_svc_descs段放入rt_svc_descs_indices數組,這裡擷取該數組位址
	add	x14, x14, :lo12:rt_svc_descs_indices
	ldrb	w15, [x14, x16]   ---找到rt_svc在rt_svc_descs_indices數組中的index

	/* 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]    ------------------------------這句話對應的就是上述注釋:handler = (base + off) + (index << log2(size))

	/*
	 * 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     -------------------------------------跳轉到handler

	b	el3_exit
           
(5)、smc在驅動中的調用

在optee_smc.h中,我們可以檢視linux kernel中給driver定義的smc的類型有:

首先是兩個宏,一個用于定義fast call,一個用于定義std call

#define OPTEE_SMC_STD_CALL_VAL(func_num) \
	ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \
			   ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))
#define OPTEE_SMC_FAST_CALL_VAL(func_num) \
	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
			   ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))
           

std call隻有兩個cmd,一個用于正向調用,一個用于rpc調用

#define OPTEE_SMC_FUNCID_RETURN_FROM_RPC	3
#define OPTEE_SMC_CALL_RETURN_FROM_RPC \
	OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_RETURN_FROM_RPC)

#define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEE_MSG_FUNCID_CALL_WITH_ARG
#define OPTEE_SMC_CALL_WITH_ARG \
	OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG)
           

fast call有5個分别用于: get_os_uuid、get_shm_config、exchange_capabilities、disable_shm_cache、enable_shm_cache

#define OPTEE_SMC_FUNCID_GET_OS_UUID OPTEE_MSG_FUNCID_GET_OS_UUID
#define OPTEE_SMC_CALL_GET_OS_UUID \
	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_UUID)

#define OPTEE_SMC_FUNCID_GET_SHM_CONFIG	7
#define OPTEE_SMC_GET_SHM_CONFIG \
	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_SHM_CONFIG)

#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES	9
#define OPTEE_SMC_EXCHANGE_CAPABILITIES \
	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES)

#define OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE	10
#define OPTEE_SMC_DISABLE_SHM_CACHE \
	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE)

#define OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE	11
#define OPTEE_SMC_ENABLE_SHM_CACHE \
	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE)
           
5、smc流程下的代碼分析
[ATF]-ATF的代碼學習篇-一篇就夠了
歡迎添加微信、微信群,多多交流
[ATF]-ATF的代碼學習篇-一篇就夠了

繼續閱讀