Trusted Firmware-A 加载 U-Boot 过程详解本文档详细描述了 Trusted Firmware-A (TFA) 如何加载 U-Boot 的完整过程基于 bitmain/bm1684 平台的实现。1. 引导流程概述TFA 采用分阶段引导的方式从 BL1 到 BL2再到 BL31最终跳转到 BL33U-Boot。整个流程如下BL1从 SPI Flash 加载初始化基本硬件然后加载 BL2 到安全 SRAMBL2加载 BL31EL3 运行时和 BL33U-Boot非安全到指定内存位置BL31初始化 EL3 运行时环境然后跳转到 BL33U-BootBL33U-Boot 启动继续引导操作系统2. 关键组件和文件2.1 主要代码文件bl2/bl2_main.cBL2 的主函数协调镜像加载过程bl2/bl2_image_load_v2.c实现bl2_load_images()函数负责加载 BL31 和 BL33plat/bitmain/bm1684/bm_bl2_mem_params_desc.c定义 BL33U-Boot的加载参数plat/bitmain/bm1684/include/platform_def.h定义内存布局和地址偏移bl31/bl31_main.cBL31 主函数负责初始化并跳转到 BL332.2 内存布局根据platform_def.h的定义相关内存布局如下内存区域基地址大小用途NS_DRAM_BASE0x300000000256MB非安全 DRAM 基地址NS_IMAGE_OFFSET0x308000000-U-Boot 加载地址偏移 128MBNS_IMAGE_MAX_SIZE0x8000000128MBU-Boot 最大大小BL31_BASE0x300000000256KBBL31 加载地址3. 详细加载过程3.1 BL2 加载 BL33U-BootBL2 主函数执行在bl2_main()函数中调用bl2_load_images()加载后续镜像获取镜像加载信息bl2_load_images()调用plat_get_bl_image_load_info()获取镜像加载信息对于 BM1684 平台此函数会将镜像描述符复制到安全位置遍历并加载镜像遍历bl2_mem_params_descs数组中的每个镜像对于 BL31安全镜像和 BL33非安全镜像执行加载操作BL33 加载配置在bm_bl2_mem_params_desc.c中BL33 的配置如下{.image_idBL33_IMAGE_ID,SET_STATIC_PARAM_HEAD(ep_info,PARAM_EP,VERSION_2,entry_point_info_t,NON_SECURE|EXECUTABLE),.ep_info.pcNS_IMAGE_OFFSET,SET_STATIC_PARAM_HEAD(image_info,PARAM_EP,VERSION_2,image_info_t,0),.image_info.image_baseNS_IMAGE_OFFSET,.image_info.image_max_sizeNS_IMAGE_MAX_SIZE,.next_handoff_image_idINVALID_IMAGE_ID,}执行加载调用load_auth_image()加载 BL33 镜像到NS_IMAGE_OFFSET地址执行平台特定的后处理操作bl2_plat_handle_post_image_load()准备跳转参数调用plat_get_next_bl_params()获取传递给下一个镜像的参数刷新参数到内存确保下一个镜像可以访问跳转到 BL31BL2 完成加载后跳转到 BL31 执行3.2 BL31 跳转到 BL33U-BootBL31 初始化在bl31_main()函数中执行平台初始化、运行时服务初始化等操作准备跳转到下一个镜像调用bl31_prepare_next_image_entry()准备跳转到 BL33获取 BL33 入口信息调用bl31_plat_get_next_image_ep_info(NON_SECURE)获取 BL33 的入口点信息验证入口点信息的安全性状态配置 EL3 退出调用cm_init_my_context(next_image_info)初始化上下文调用cm_prepare_el3_exit_ns()准备从 EL3 退出到非安全世界执行 ERET 指令执行 ERET 指令跳转到 BL33U-Boot的入口点U-Boot 开始执行4. 关键函数调用链4.1 BL2 加载流程bl2_main() └── bl2_load_images() ├── plat_get_bl_image_load_info() │ └── get_bl_load_info_from_mem_params_desc() ├── load_auth_image(BL31_IMAGE_ID, ...) ├── load_auth_image(BL33_IMAGE_ID, ...) ├── plat_get_next_bl_params() │ └── get_next_bl_params_from_mem_params_desc() ├── plat_flush_next_bl_params() └── return next_bl_ep_info (指向 BL31)4.2 BL31 跳转流程bl31_main() ├── bl31_platform_setup() ├── runtime_svc_init() ├── bl31_prepare_next_image_entry() │ ├── bl31_get_next_image_type() (返回 NON_SECURE) │ ├── bl31_plat_get_next_image_ep_info(NON_SECURE) │ ├── cm_init_my_context(next_image_info) │ └── cm_prepare_el3_exit_ns() └── 执行 ERET 跳转到 BL335. 配置和定制5.1 内存布局配置U-Boot 的加载地址和大小可以通过修改platform_def.h中的以下宏来调整NS_IMAGE_OFFSETU-Boot 的加载地址NS_IMAGE_MAX_SIZEU-Boot 的最大大小5.2 镜像加载配置BL33 的加载参数可以在bm_bl2_mem_params_desc.c中修改包括入口点地址镜像基地址镜像最大大小安全状态标记6. 调试和排错6.1 常见问题U-Boot 加载失败检查NS_IMAGE_OFFSET是否正确确认 U-Boot 镜像大小不超过NS_IMAGE_MAX_SIZE检查 SPI Flash 中的 U-Boot 镜像是否正确跳转失败检查 BL33 的入口点地址是否正确确认 BL31 到 BL33 的参数传递是否正确内存冲突确保 BL31 和 BL33 的内存区域不重叠检查其他组件是否使用了相同的内存区域6.2 调试技巧在 BL2 和 BL31 中添加调试信息输出镜像加载的详细过程使用 JTAG 调试器跟踪引导过程检查 U-Boot 的反汇编代码确认入口点逻辑正确7. 总结TFA 加载 U-Boot 的过程是一个精心设计的多阶段引导流程确保了系统的安全性和可靠性。通过 BL2 加载 BL33 到指定内存位置然后由 BL31 负责安全地跳转到 U-Boot整个过程实现了从安全世界到非安全世界的平稳过渡。这种设计不仅提供了安全的引导环境还为 U-Boot 和后续的操作系统启动做好了充分准备是现代 ARM 系统引导架构的重要组成部分。