安全引導功能及ATF的啟動過程(四)
ATF中bl31的啟動
在bl2中觸發安全監控模式調用后會跳轉到bl31中執行,bl31最主要的作用是建立EL3運行態的軟件配置,在該階段會完成各種類型的安全監控模式調用ID的注冊和對應的ARM核狀態的切換,bl31運行在EL3。bl31的執行流程如圖所示。
bl31_entrypoint函數
通過bl31.ld.S文件可知,bl31的入口函數是bl31_entrypoint。該函數的內容如下:
/root/optee/trusted-firmware-a/bl31/bl31.ld.S
OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
ENTRY(bl31_entrypoint)
/root/optee/trusted-firmware-a/bl31/aarch64/bl31_entrypoint.S
func bl31_entrypoint/* ---------------------------------------------------------------* Stash the previous bootloader arguments x0 - x3 for later use.* ---------------------------------------------------------------*///保存x0/x1/x2/x3寄存器中的值 mov x20, x0mov x21, x1mov x22, x2mov x23, x3#if !RESET_TO_BL31//根據是否啟用了RESET_TO_BL31參數為el3_entrypoint_common傳入不同參數// RESET_TO_BL31=y 系統復位后直接進入 BL31// RESET_TO_BL31=n BL31 是由前一級(如 BL2)加載并跳轉進來的/* ---------------------------------------------------------------------* For !RESET_TO_BL31 systems, only the primary CPU ever reaches* bl31_entrypoint() during the cold boot flow, so the cold/warm boot* and primary/secondary CPU logic should not be executed in this case.** Also, assume that the previous bootloader has already initialised the* SCTLR_EL3, including the endianness, and has initialised the memory.* ---------------------------------------------------------------------*/// 不進行初始化類操作el3_entrypoint_common \_init_sctlr=0 \_warm_boot_mailbox=0 \_secondary_cold_boot=0 \_init_memory=0 \_init_c_runtime=1 \//設置 EL3 異常向量 _exception_vectors=runtime_exceptions \_pie_fixup_size=BL31_LIMIT - BL31_BASE
#else/* ---------------------------------------------------------------------* For RESET_TO_BL31 systems which have a programmable reset address,* bl31_entrypoint() is executed only on the cold boot path so we can* skip the warm boot mailbox mechanism.* ---------------------------------------------------------------------*///進行初始化類操作el3_entrypoint_common \_init_sctlr=1 \_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \_init_memory=1 \_init_c_runtime=1 \_exception_vectors=runtime_exceptions \_pie_fixup_size=BL31_LIMIT - BL31_BASE
#endif /* RESET_TO_BL31 *//* --------------------------------------------------------------------* Perform BL31 setup* --------------------------------------------------------------------*///恢復x0/x1/x2/x3數據,配置bl31mov x0, x20mov x1, x21mov x2, x22mov x3, x23bl bl31_setup#if ENABLE_PAUTH/* --------------------------------------------------------------------* Program APIAKey_EL1 and enable pointer authentication* --------------------------------------------------------------------*///執行指針認證初始化 bl pauth_init_enable_el3
#endif /* ENABLE_PAUTH *//* --------------------------------------------------------------------* Jump to main function* --------------------------------------------------------------------*/// 執行bl31_mainbl bl31_main /* --------------------------------------------------------------------* Clean the .data & .bss sections to main memory. This ensures* that any global data which was initialised by the primary CPU* is visible to secondary CPUs before they enable their data* caches and participate in coherency.* --------------------------------------------------------------------*///清理數據段和 未初始化的全局變量和靜態變量段adrp x0, __DATA_START__add x0, x0, :lo12:__DATA_START__adrp x1, __DATA_END__add x1, x1, :lo12:__DATA_END__sub x1, x1, x0bl clean_dcache_rangeadrp x0, __BSS_START__add x0, x0, :lo12:__BSS_START__adrp x1, __BSS_END__add x1, x1, :lo12:__BSS_END__sub x1, x1, x0bl clean_dcache_rangeb el3_exit
endfunc bl31_entrypoint
bl31_main函數
該函數主要完成必要的初始化操作,注冊EL3中各種安全監控模式調用的處理函數,以便在啟動完成后響應在REE側和TEE側產生的安全監控模式調用。該函數的內容如下:
/root/optee/trusted-firmware-a/bl31/bl31_main.c
//BL31 負責為啟動 CPU 設置運行時服務,然后將控制權移交給引導加載程序或操作系統。
//該函數調用 runtime_svc_init(),該函數會初始化所有已注冊的運行時服務。
//這些運行時服務會為處理器核心切換到下一個異常級別建立足夠的上下文環境。
//當該函數返回后,核心將通過 ERET 指令切換到已設定的異常級別。
void bl31_main(void)
{/* Init registers that never change for the lifetime of TF-A *///初始化在 TF-A 生命周期內永不更改的寄存器。cm_manage_extensions_el3(plat_my_core_pos());/* Init per-world context registers for non-secure world */// 設置那些會在 安全世界(Secure)與非安全世界之間切換時需要恢復的寄存器manage_extensions_nonsecure_per_world();NOTICE("BL31: %s\n", build_version_string);NOTICE("BL31: %s\n", build_message);#if FEATURE_DETECTION/* Detect if features enabled during compilation are supported by PE. *///檢測在編譯期間啟用的功能是否被處理器單元(PE)所支持detect_arch_features();
#endif /* FEATURE_DETECTION */#if ENABLE_RUNTIME_INSTRUMENTATION//運行時性能檢測PMF_CAPTURE_TIMESTAMP(bl_svc, BL31_ENTRY, PMF_CACHE_MAINT);
#endif#ifdef SUPPORT_UNKNOWN_MPID//處理未知的 MPID(多核標識)if (unsupported_mpid_flag == 0) {NOTICE("Unsupported MPID detected!\n");}
#endif/* Perform platform setup in BL31 *///平臺相關設置bl31_platform_setup();#if USE_GIC_DRIVER/** Initialize the GIC driver as well as per-cpu and global interfaces.* Platform has had an opportunity to initialise specifics.*///初始化 GIC unsigned int core_pos = plat_my_core_pos();gic_init(core_pos); //初始化 GIC 分發器gic_pcpu_init(core_pos); //初始化當前 CPU 的中斷接口gic_cpuif_enable(core_pos); //使能當前 CPU 的中斷接收
#endif /* USE_GIC_DRIVER *//* Initialise helper libraries *///初始化輔助庫bl31_lib_init();#if EL3_EXCEPTION_HANDLING//初始化異常處理框架(EXCEPTION_HANDLING_FRAMEWORK) INFO("BL31: Initialising Exception Handling Framework\n");ehf_init();
#endif/* Initialize the runtime services e.g. psci. *///初始化運行時服務,如PSCI// PSCI(Power State Coordination Interface):負責 CPU 啟動、關閉、掛起等// SPD(Secure Payload Dispatcher):調度 OP-TEE(BL32)// SPMC(Secure Partition Manager Component):用于 FF-A 架構// RMM(Realm Management Monitor):用于 CCA(Confidential Compute Architecture)INFO("BL31: Initializing runtime services\n");runtime_svc_init();/** All the cold boot actions on the primary cpu are done. We now need to* decide which is the next image and how to execute it.* If the SPD runtime service is present, it would want to pass control* to BL32 first in S-EL1. In that case, SPD would have registered a* function to initialize bl32 where it takes responsibility of entering* S-EL1 and returning control back to bl31_main. Similarly, if RME is* enabled and a function is registered to initialize RMM, control is* transferred to RMM in R-EL2. After RMM initialization, control is* returned back to bl31_main. Once this is done we can prepare entry* into BL33 as normal.*///所有在主 CPU 上的冷啟動操作已經完成。//我們現在需要決定下一個要運行的鏡像以及如何執行它。//如果存在 SPD(Secure Payload Dispatcher)運行時服務,//它會希望先將控制權交給運行在 S-EL1 的 BL32。//在這種情況下,SPD 會注冊一個用于初始化 BL32 的函數,//該函數負責進入 S-EL1 并在完成后將控制權交還給 bl31_main。//同樣地,如果啟用了 RME(Realm Management Extension)//并注冊了用于初始化 RMM(Realm Management Monitor)的函數,//則會將控制權轉移到運行在 R-EL2 的 RMM。RMM 初始化完成后,//控制權也會返回到 bl31_main。完成這些步驟后,我們就可以像往常一樣準備進入 BL33。// S-EL安全世界 中運行的異常等級。// R-EL普通世界 中運行的異常等級。/** If SPD had registered an init hook, invoke it.*/if (bl32_init != NULL) {INFO("BL31: Initializing BL32\n");console_flush();int32_t rc = (*bl32_init)();if (rc == 0) {WARN("BL31: BL32 initialization failed\n");}}/** If RME is enabled and init hook is registered, initialize RMM* in R-EL2.*/
#if ENABLE_RMEif (rmm_init != NULL) {INFO("BL31: Initializing RMM\n");console_flush();int32_t rc = (*rmm_init)();if (rc == 0) {WARN("BL31: RMM initialization failed\n");}}
#endif/** We are ready to enter the next EL. Prepare entry into the image* corresponding to the desired security state after the next ERET.*///我們已經準備好進入下一個異常級別。//請準備在下一次 ERET 指令后,進入對應目標安全狀態的鏡像 bl31_prepare_next_image_entry();/** Perform any platform specific runtime setup prior to cold boot exit* from BL31*/// 在從 BL31 冷啟動退出之前,執行任何必要的平臺特定運行時設置bl31_plat_runtime_setup();#if ENABLE_RUNTIME_INSTRUMENTATIONconsole_flush(); //運行時性能檢測PMF_CAPTURE_TIMESTAMP(bl_svc, BL31_EXIT, PMF_CACHE_MAINT);
#endif//切換控制臺狀態并刷新輸出console_flush();console_switch_state(CONSOLE_FLAG_RUNTIME); //將控制臺切換到“運行時”模式
}
runtime_svc_init函數會將各種安全監控模式調用的處理函數的指針注冊到EL3中,并通過service init函數來進行初始化,將TEE OS鏡像的入口函數賦值給bl32_init,通過執行bl32_init指向的函數進入到TEE OS的啟動過程。待TEE OS啟動完成之后就會去查找bl33的鏡像文件,即REE側的鏡像文件,開始進入REE側鏡像的啟動。
runtime_svc_init函數
該函數主要用來建立安全監控模式調用處理函數的索引表,并執行EL3中提供的服務項的初始化操作,獲取TEE OS的入口地址并賦值給bl32_init變量,以備啟動TEE OS。而這些處理函數是通過DECLARE_RT_SVC宏定義被編譯到鏡像文件的rt_svc_descs段中的。
/root/optee/trusted-firmware-a/common/runtime_svc.c
void __init runtime_svc_init(void)
{int rc = 0;uint8_t index, start_idx, end_idx;rt_svc_desc_t *rt_svc_descs;/* Assert the number of descriptors detected are less than maximum indices *///檢查是否描述符的結束地址 ≥ 開始地址//檢查是否注冊的服務數量 < MAX_RT_SVCS(防止數組越界)assert((RT_SVC_DESCS_END >= RT_SVC_DESCS_START) &&(RT_SVC_DECS_NUM < MAX_RT_SVCS));/* If no runtime services are implemented then simply bail out *///如果沒有實現運行時服務,則直接退出if (RT_SVC_DECS_NUM == 0U) {return;}/* Initialise internal variables to invalid state *///初始化內部變量為無效狀態(void)memset(rt_svc_descs_indices, -1, sizeof(rt_svc_descs_indices));//獲取服務描述符數組指針rt_svc_descs = (rt_svc_desc_t *) RT_SVC_DESCS_START;for (index = 0U; index < RT_SVC_DECS_NUM; index++) {rt_svc_desc_t *service = &rt_svc_descs[index];/** An invalid descriptor is an error condition since it is* difficult to predict the system behaviour in the absence* of this service.*///檢查描述符是否合法rc = validate_rt_svc_desc(service);if (rc != 0) {ERROR("Invalid runtime service descriptor %p\n",(void *) service);panic();}/** The runtime service may have separate rt_svc_desc_t* for its fast smc and yielding smc. Since the service itself* need to be initialized only once, only one of them will have* an initialisation routine defined. Call the initialisation* routine for this runtime service, if it is defined.*///調用服務初始化函數(如果存在) if (service->init != NULL) {rc = service->init();if (rc != 0) {ERROR("Error initializing runtime service %s\n",service->name);continue;}}/** Fill the indices corresponding to the start and end* owning entity numbers with the index of the* descriptor which will handle the SMCs for this owning* entity range.*/// 為每個服務分配一個OEN范圍,用來處理所有的SMC調用start_idx = (uint8_t)get_unique_oen(service->start_oen,service->call_type);end_idx = (uint8_t)get_unique_oen(service->end_oen,service->call_type);assert(start_idx <= end_idx);assert(end_idx < MAX_RT_SVCS);//填充查找表for (; start_idx <= end_idx; start_idx++) {rt_svc_descs_indices[start_idx] = index;}}
}
DECLARE_RT_SVC
該宏用來在編譯時將EL3中的service編譯進rt_svc_descs段中。該宏定義如下:
/root/optee/trusted-firmware-a/include/common/runtime_svc.h
/** Convenience macros to declare a service descriptor*/
// ##:將 __svc_desc_ 和 _name 拼接成一個唯一變量名。
// #:將 __svc_desc_ 和 _name 拼接成一個唯一變量名。
// .rt_svc_descs: 將該變量放入名為 .rt_svc_descs 的自定義鏈接段(section)中。
// __used:告訴編譯器“這個變量雖然可能沒被顯式引用,但也別優化掉”
#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) \}
該宏中的各種參數說明如下:
- start_oen:該service的起始內部編號
- end.oen:該service的末尾編號
- call_type:調用的smc的類型
- name:該service的名字
- init:該service在執行之前需要被執行的初始化操作
- handle:當觸發了call type的調用時調用的處理該請求的函數
REE側鏡像文件的啟動
在bl31_main中啟動完TEE OS之后通過調用bl31_prepare_next_image_entry函數來獲取下一個階段需要被加載的鏡像文件,即REE側的鏡像文件,并配置好REE側鏡像的運行環境。bl31_main執行完成之后會跳轉到bl31_entrypoint中繼續執行,計算出需要被加載的鏡像文件的數據段大小和起始地址并清空BSS端中的數據,從EL3進入到EL1-NS開始執行REE側的代碼。
TEE側的加載在bl31_prepare_next_image_entry前,通過runtime_svc_init初始化的函數進行加載,將在下一節進行介紹。
參考資料:
- 《手機安全和可信應用開發指南:TrustZone與OP-TEE技術詳解》