[ATF]-ATF的代码学习篇-一篇就够了
快速鏈接:
.
👉👉👉 個人博客筆記導(dǎo)讀目錄(全部) 👈👈👈
文章目錄
- 1、ATF里都有什么?
- 2、ATF的編譯
- 3、ATF的啟動
- 4、進(jìn)入ATF的和退出ATF方式
- (1)、進(jìn)入ATF的方式
- (2)、退出ATF的方式
- 5、ATF中向量表的介紹
- 6、ATF中棧的設(shè)置
- 7、ATF中寄存器的保存和恢復(fù)
- 8、ATF的rt_svc介紹(runtime service)
- (1)、SMC Calling convention文檔
- (2)、DECLARE_RT_SVC的使用
- (3)、DECLARE_RT_SVC的定義
- (4)、在同步異常中smc_handler64,跳轉(zhuǎn)到響應(yīng)的rt_svc
- (5)、smc在驅(qū)動中的調(diào)用
- 5、smc流程下的代碼分析
1、ATF里都有什么?
最初的功能很簡單:
- cpu_context的保存和恢復(fù),即: 雙系統(tǒng)的切換
- 電源管理、PSCI等
但是隨著技術(shù)的發(fā)展,功能也越來越多,越來越復(fù)雜,以下列舉了當(dāng)前的部分功能:
- 安全世界的初始化,例如異常向量表、一些控制寄存器和中斷寄
- CPU reset和power down的時序。包括Arm DynamIQ cpu的支持。
- 標(biāo)準(zhǔn)的system IP的驅(qū)動,例如Generic Interrupt Controller (GIC), Cache Coherent Interconnect (CCI), Cache Coherent Network (CCN), Network Interconnect (NIC) and TrustZone Controller (TZC).
- 一種通用的SCMI驅(qū)動程序, 適用于電源控制接口,例如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實(shí)現(xiàn)
- 預(yù)集成TBB與Arm CryptoCell產(chǎn)品,利用其硬件Root的信任和加密加速服務(wù)。
如需更詳細(xì),請參考《ATF里面都有什么?》 一文
2、ATF的編譯
不同平臺之間的設(shè)計(jì)肯定都是不一樣的,但大多數(shù)類似如下,請注意RESET_TO_BL31=1,表示該ATF從BL31啟動。
make -C $DIRPATH RESET_TO_BL31=1 PLAT=xxx clean
make -C $DIRPATH RESET_TO_BL31=1 PLAT=xxx HIGHADDR_DEVICE=1 all
3、ATF的啟動
廢話不多說,直接上圖,請自行理解:BL1 BL2 BL31 BL32 BL33的概念、EL3 S-EL1 NS-EL1的概念。
4、進(jìn)入ATF的和退出ATF方式
透過事務(wù)看本質(zhì), 進(jìn)入和退出ATF,就是就是EL等級切換的過程,那么EL等級都是怎么切換的呢?通過下面一張圖就可以說明這一切:
(事實(shí)上除了以上同步異常指令,如果是觸發(fā)異步異常,也會trapped到ATF)
(1)、進(jìn)入ATF的方式
進(jìn)入ATF的方式觸發(fā)異常:同步異常SMC、異步異常(irq,fiq)
-
? 如果是同步異常,那么一定是在linux或tee中發(fā)生了smc調(diào)用,此時進(jìn)入跳轉(zhuǎn)ATF中異常向量表中的同步異常程序smc_handler64或smc_handler32
.
在該程序中,解析smc id,來選擇跳轉(zhuǎn)到具體哪一個rt-svc(runtime service) -
? 如果是異步異常,那么一定是觸發(fā)了irq或fiq或serror中斷等,此時進(jìn)入跳轉(zhuǎn)ATF中異常向量表中的異步異常程序,進(jìn)而跳轉(zhuǎn)到響應(yīng)的中斷處理函數(shù).
.
在ATF中僅實(shí)現(xiàn)irq_aarch64、fiq_aarch64、irq_aarch32、fiq_aarch32 四個異常中斷處理函數(shù)
在中斷函數(shù)中,先調(diào)用plat_ic_get_pending_interrupt_type獲取interrupt_type,其實(shí)就是通過讀取寄存器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程序,進(jìn)而跳轉(zhuǎn)到相應(yīng)的handler程序。
ATF中中斷的注冊(這三種類型的handler程序的注冊),以INTR_TYPE_S_EL1為例:
在開機(jī)bl32_main調(diào)用opteed_setup()時,將opteed_sel1_interrupt_handler()函數(shù)注冊成了INTR_TYPE_S_EL1類型中斷,同時也會將REE(Linux)使用的SCR_EL3.FIQ配置成1,
也意味著當(dāng)CPU運(yùn)行在TEE時,來了一個secure group1的中斷,此中斷在REE中被標(biāo)記FIQ后將被target到EL3,進(jìn)入EL3(ATF)的中斷處理函數(shù),也就是剛才注冊的opteed_sel1_interrupt_handler()函數(shù),在該函數(shù)中,會將cpu切換到TEE中,去處理這個中斷;
(2)、退出ATF的方式
進(jìn)入EL3(ATF)的方式觸發(fā)異常:ERET指令、或是主動修改PSTATE寄存器
在ATF中執(zhí)行smc_handler或中斷handler結(jié)束后,會調(diào)用el3_exit,el3_exit會調(diào)用ERET指令,恢復(fù)Secure或non-secure的PC指針和PSTATE,回到secure EL1或non-secure EL1.
下圖是一個smc進(jìn)入ATF,處理完任務(wù)后再返回EL1的過程:
5、ATF中向量表的介紹
請參考《Linux Kernel/optee/ATF等操作系統(tǒng)的異常向量表的速查》 一文
6、ATF中棧的設(shè)置
請參考《思想解讀:TF-A(ATF)中棧指針和棧內(nèi)存的設(shè)計(jì)思想解讀》 一文
7、ATF中寄存器的保存和恢復(fù)
請參考《TF-A代碼閱讀: 雙系統(tǒng)切換時是如何保存寄存器的(cpu_context介紹)》 一文
8、ATF的rt_svc介紹(runtime service)
(1)、SMC Calling convention文檔
我們重點(diǎn)看下這張表,對應(yīng)smc id的定義
- bit31決定是fast call,還是std call(yield對應(yīng)的就是std call)
- bit30表示是以32位傳參,還是以64位傳參, 注意我們看了optee在linux的driver,都是以32位方式
- bit29:24 決定服務(wù)的類型
- 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,對應(yīng)的恰好是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中,調(diào)用smc時的smc id的bit29:24需要等于50,那么此次的smc調(diào)用才會調(diào)用到這個runtime service的handler程序
例如在arm_arch_svc_setup.c中,定義了一個service,它的call類型是OEN_ARM_START–OEN_ARM_END,對應(yīng)的恰好是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中,調(diào)用smc時的smc id的bit29:24需要等于0,那么此次的smc調(diào)用才會調(diào)用到這個runtime service的handler程序
例如mtk代碼中,mtk_sip_svc.c中,定義了一個service,它的call類型是OEN_SIP_START–OEN_SIP_END,對應(yīng)的恰好是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中,調(diào)用smc時的smc id的bit29:24需要等于2,那么此次的smc調(diào)用才會調(diào)用到這個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也是根據(jù)SMC Calling convention文檔定義
(3)、DECLARE_RT_SVC的定義
在runtime_svc.h中,其實(shí)就是在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,跳轉(zhuǎn)到響應(yīng)的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, xzrmov 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_el3mrs x17, elr_el3mrs x18, scr_el3stp 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, #1mov sp, x12/* Get the unique owning entity number */ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH ---------------- 獲取FUNCID_OEN_SHIFT,對應(yīng)第一節(jié)中的OEN_TOS_STARTubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH ---------------- 獲取FUNCID_TYPE_SHIFT,對應(yīng)第一節(jié)中的SMC_TYPE_FAST(fast還是yield,yield其實(shí)就是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數(shù)組,這里獲取該數(shù)組地址add x14, x14, :lo12:rt_svc_descs_indicesldrb w15, [x14, x16] ---找到rt_svc在rt_svc_descs_indices數(shù)組中的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)) ------ 這句注釋特別重要,整段匯編看不懂沒關(guān)系,這句注釋看懂就行*/adr x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)lsl w10, w15, #RT_SVC_SIZE_LOG2ldr x15, [x11, w10, uxtw] ------------------------------這句話對應(yīng)的就是上述注釋: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 DEBUGcbz x15, rt_svc_fw_critical_error #endifblr x15 -------------------------------------跳轉(zhuǎn)到handlerb el3_exit(5)、smc在驅(qū)動中的調(diào)用
在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,一個用于正向調(diào)用,一個用于rpc調(diào)用
#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流程下的代碼分析
歡迎添加微信、微信群,多多交流
總結(jié)
以上是生活随笔為你收集整理的[ATF]-ATF的代码学习篇-一篇就够了的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [ARM-assembly]-ARM交叉
- 下一篇: ARMV8-aarch64的寄存器介绍(