手撕ARM64启动栈(四):QEMU + TF-A(ATF) BL2 详解
系列文章目录手撕ARM64启动栈一QEMU TF-A → OP-TEE → U-Boot → Linux 全链路总览手撕ARM64启动栈二QEMU TF-A(ATF) BL1 执行上下文——GDB 实测复位到 BL2手撕ARM64启动栈三QEMU TF-A(ATF) BL1 核心职责——bl1_main()逐函数拆解与 BL2 加载手撕ARM64启动栈四QEMU TF-A(ATF) BL2 详解文章目录系列文章目录1. 引言2. BL2 运行特权级S-EL13. BL2 汇编入口GDB 实测入口寄存器对比4. BL2 主流程bl2_main()GDB 实测SMC 重入 BL1 异常向量5. image_desc 列表的来源6. entry_point_info 传递给 BL31GDB 实测三节点入口信息链表7. 本篇小结日志与代码的对应关系1. 引言前两篇第二、三篇走完了 BL1 的全部流程——执行上下文复位向量、异常向量表、退出前状态与核心职责bl1_main()逐函数拆解、加载 BL2。本篇进入BL2 阶段BL2 运行在哪个特权级、它的汇编入口、bl2_main()主流程如何加载 BL31/BL32/BL33 这一串镜像以及加载完成后如何把entry_point_info链表交还给 BL1 再跳往 BL31。和前两篇一样本篇同样使用 GDB 断点方式分析 BL2 的关键节点。涉及关键词ATF / TF-A / ARM Trusted Firmware、QEMU virt aarch64、BL2、S-EL1、bl2_entrypoint、bl2_main、bl2_load_images、OP-TEE 镜像头、parse_optee_header、entry_point_info。2. BL2 运行特权级S-EL1本篇中 BL2 运行在Secure EL1ENABLE_RME1时才改到 EL3本篇未启用。第二篇中介绍BL1交给控制权给BL2时 使用GDB 断点抓到 BL1 构造 BL2 的entry_point_info时next_bl_ep-spsr 0x3c5ERET用它决定 BL2 起跑时的 PSTATE。0x3c5的M[4:0]0b00101即EL1hAArch64、EL1、用 EL1 自己的 SP所以 BL1 是以 EL1 状态把控制权ERET给 BL2 的。3. BL2 汇编入口BL2 也有自己的汇编入口bl2/aarch64/bl2_entrypoint.S角色和第二篇拆解的bl1_entrypoint.S相同——都是CPU/上一级固件跳过来的第一条指令负责在跳进 C 函数之前把运行环境搭好。但它比 BL1 入口简单BL1 被硬件复位向量直接拉起要从复位态初始化搭建 EL3 环境而 BL2 是 BL1 用ERET跳过来的运行在 S-EL1只初始化自己EL1运行环境即可。完整代码trusted-firmware-a/bl2/aarch64/bl2_entrypoint.Sfunc bl2_entrypoint /* 保存 BL1 传来的参数后面转发给 bl2_main */ mov x20, x0 mov x21, x1 mov x22, x2 mov x23, x3 /* 设EL1异常向量表 */ adr x0, early_exceptions msr vbar_el1, x0 isb msr daifclr, #DAIF_ABT_BIT /* 开指令 cache、栈指针对齐检查、数据访问对齐检查 */ mov x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT) mrs x0, sctlr_el1 orr x0, x0, x1 bic x0, x0, #SCTLR_DSSBS_BIT msr sctlr_el1, x0 isb /* 清理 BL2 自身 RW 段的cache */ adr x0, __RW_START__ adr x1, __RW_END__ sub x1, x1, x0 bl inv_dcache_range /* .bss 清零 */ adrp x0, __BSS_START__ ... bl zeromem /* 分配栈 */ bl plat_set_my_stack #if STACK_PROTECTOR_ENABLED bl update_stack_protector_canary #endif /* 把保存的 x0-x3 还原为 bl2_main 的四个参数跳转 */ mov x0, x20 mov x1, x21 mov x2, x22 mov x3, x23 bl bl2_main no_ret plat_panic_handler endfunc bl2_entrypointGDB 实测入口寄存器对比bl2_entrypoint断点处bl2_main断点处汇编收尾已跑完SP/VBAR/SCTLR 都已就位两处关键寄存器对比寄存器bl2_entrypoint断点处bl2_main断点处说明$pc0xe05b0000xe05b25c与第三篇 GDB 实测的desc-image_info.image_base 0xe05b000对应——BL2 入口地址$VBAR即当前 EL 的异常向量基址0x00xe060800EL1异常向量初始化 –adr x0, early_exceptions; msr vbar_el1, x0$SCTLR当前 EL 的系统控制寄存器0x30d008000x30d0180aI指令 cachebit12/SA栈指针对齐检查bit3/A数据访问对齐检查bit1位被置上对应mov x1,#(SCTLR_I_BIT|SCTLR_A_BIT|SCTLR_SA_BIT)那几行$sp0x00xe064440从空BL1 未传递 S-EL1 栈指针变为plat_set_my_stack分配好的 BL2 栈顶$x1(arg1)0xe0010000xe001000BL1 传给 BL2 的meminfo_t指针BL2 可用安全 RAM 的 信息4. BL2 主流程bl2_main()bl2_main.c核心序列voidbl2_main(u_register_targ0,u_register_targ1,u_register_targ2,u_register_targ3){plat_setup_early_console();bl2_early_platform_setup2(arg0,arg1,arg2,arg3);bl2_plat_arch_setup();auth_mod_init();next_bl_ep_infobl2_load_images();// ← 核心加载 BL31/BL32/BL33/* 通过 SMC 把 next_bl_ep_info 交还 BL1 */smc(BL1_SMC_RUN_IMAGE,(unsignedlong)next_bl_ep_info,0,0,0,0,0,0);}smc(BL1_SMC_RUN_IMAGE, ...)会触发第二篇看到的 BL1smc_handler64bl1/aarch64/bl1_exceptions.SBL2 加载完 BL31/BL32/BL33 后并不直接跳转而是通过Mac call把执行权交还 BL1由 BL1 跳进 BL31。bl2_main()的主序列以及它调用的bl2_load_images()加载循环流程见下图GDB 实测SMC 重入 BL1 异常向量在smc(...)那行bl2_main.c打断点看即将交还给 BL1 的next_bl_ep_infopc 0xe090000(BL31_BASE)、spsr 0x3cd(EL3h)——链表头正是 BL31 的入口信息与 §6 的 handoff 链一致。args.arg0 0xe064450 是 §6 那条入口信息链表的头指针随 SMC 一起交给 BL31.紧接着单步过smcCPU 会陷入 EL3见下图 – 执行到BL1 的smc_handler64且$x0 0x4BL1_SMC_RUN_IMAGE5. image_desc 列表的来源bl2_load_images()具体要加载哪些镜像是通过平台层查询得到的bl2_load_infoplat_get_bl_image_load_info();// 平台层实现qemu_image_load.cbl2_node_infobl2_load_info-head;while(bl2_node_info!NULL){if(attrIMAGE_ATTRIB_PLAT_SETUP){bl2_platform_setup();}// BL2: Doing platform setupif(attrIMAGE_ATTRIB_SKIP_LOADING){INFO(BL2: Skip loading image id %u\n,...);// 对应 id22}else{INFO(BL2: Loading image id %u\n,...);// 对应 id3/4/21/5load_auth_image(bl2_node_info-image_id,bl2_node_info-image_info);}bl2_node_infobl2_node_info-next_load_info;}而这份列表的静态定义在 QEMU 平台代码里plat/qemu/common/qemu_bl2_mem_params_desc.c是一个bl_mem_params_node_t数组关键条目{.image_idBL31_IMAGE_ID,.ep_info.pcBL31_BASE,.ep_info.spsrSPSR_64(MODE_EL3,MODE_SP_ELX,...),.image_info.image_baseBL31_BASE,.image_info.image_max_sizeBL31_LIMIT-BL31_BASE,.next_handoff_image_idBL32_IMAGE_ID,// 加载完 BL31 后下一个是 BL32},{.image_idBL32_IMAGE_ID,// ← id4OP-TEE header.ep_info.pcBL32_BASE,.image_info.image_max_sizeBL32_LIMIT-BL32_BASE,.next_handoff_image_idBL33_IMAGE_ID,},{.image_idBL32_EXTRA1_IMAGE_ID,// ← id21OP-TEE pager.image_info{...,IMAGE_ATTRIB_SKIP_LOADING},// 初始标记跳过.image_info.image_baseBL32_BASE,.next_handoff_image_idINVALID_IMAGE_ID,},{.image_idBL32_EXTRA2_IMAGE_ID,// ← id22OP-TEE pageable.image_info{...,IMAGE_ATTRIB_SKIP_LOADING},// 初始标记跳过.image_info.image_baseQEMU_OPTEE_PAGEABLE_LOAD_BASE,.next_handoff_image_idINVALID_IMAGE_ID,},id ↔ 宏的对应关系在本系列第一篇有介绍。在bl2_load_images()加载循环bl2_image_load_v2.c打断点展开bl2_node_info链表可以直接看到这份静态数组转成的运行时链表——image_id沿next_load_info依次是0x3 → 0x4 → 0x15(21) → 0x16(22) → 0x5链尾next_load_info 0x0其中 BL31id3的image_info.image_base 0xe090000BL31_BASE、BL32id4的image_base 0xe100000BL32_BASE。关键点BL32_EXTRA1/BL32_EXTRA2在这份静态表里默认都标记IMAGE_ATTRIB_SKIP_LOADING日志里能看到 id21 被加载说明这个标记在运行时被动态清除了——下一篇 OP-TEE header 解析会详细介绍这部分。这份数组通过REGISTER_BL_IMAGE_DESCS()宏注册。需要注意的是OP-TEEBL32在这份列表里并非一个镜像而是占了id4/21/22 三项id4 在本篇是一个 28 字节的 OP-TEE 专用镜像头id21(pager)/id22(paged) 才是真正的镜像体。上面 提到的 id21/22 的 skip 标记之所以能在运行时被动态清除、以及 BL32 的最终入口从哪来都由解析 id4 这个头决定——这部分下一篇会详细拆解本篇不再展开。6. entry_point_info 传递给 BL31BL2 把 BL31/BL32/BL33 全部加载到位后如何告诉下一级这些镜像各自从哪进入执行实现是——BL2 一次性把三个镜像的入口信息entry_point_info串成一条链表交给 BL1BL1 再原样转交 BL31。bl2_load_images()结尾就是这段构造 交接bl2_to_next_bl_paramsplat_get_next_bl_params();// 把静态数组构造成一条入口信息链表/* 把链表头指针塞进头节点 ep_info 的 arg0随后传给 BL31 */if(bl2_to_next_bl_params-head-ep_info-args.arg00)bl2_to_next_bl_params-head-ep_info-args.arg0(u_register_t)bl2_to_next_bl_params;plat_flush_next_bl_params();// 刷 cache确保 BL31 能读到returnbl2_to_next_bl_params-head-ep_info;// 返回链表头BL31 的 ep_infoplat_get_next_bl_params()把 §5 那份静态数组串成一条链表每个节点 image_id 该镜像的ep_infoentry_point_info含 pc/spsr/args 指向下一节点的指针节点顺序按数组里的next_handoff_image_id连成 BL31→BL32→BL33。控制权传递路径 入口信息链表结构见下图GDB 实测三节点入口信息链表在bl2_load_images()的return那行此时 已把链表构造完打断点展开bl2_to_next_bl_params就能把 §6 图里画的三节点链表逐字段对上——沿head→next_params_info依次是 BL31→BL32→BL33链尾next_params_info 0x0节点image_idep_info-pcep_info-spsr对应宏head第 1 个0x30xe0900000x3cdEL3hBL31_BASE第 2 个0x40xe1000000x0BL32OP-TEEpager 入口第 3 个0x50x600000000x3c5EL1h/EL2hNS_IMAGE_OFFSETU-Boot7. 本篇小结日志与代码的对应关系日志行 / 关键事实代码位置NOTICE: BL2: v2.14.0(release)...bl2_main.cINFO: BL2: Doing platform setupbl2_image_load_v2.c调用bl2_platform_setup()INFO: BL2: Loading image id 3/4/21/5bl2_image_load_v2.cload_auth_image()前打印INFO: BL2: Skip loading image id 22bl2_image_load_v2.cINFO: OPTEE header info: ...parse_optee_header()解析 id4 头字段级拆解关注下一篇NOTICE: BL1: Booting BL31BL1 收到 BL2 的 SMC 后打印第五篇会从本篇第 5 节点到的 OP-TEE 专用镜像头id4/21/22继续深入用真实tee-header_v2.bin的十六进制 dump 和实测日志逐字段拆解optee_header_t与parse_optee_header()的三种模式。第六篇BL31 详解会讲 BL31 拿到这条entry_point_info链表之后如何完成 GICv3/运行时服务初始化并通过SPDopteed把控制权切换到 S-EL1 的 OP-TEE以及 OP-TEE 执行完毕后如何再切回 BL33U-Boot。最后留个小问题BL1 跑在 EL3BL2 却被降到了 S-EL1。为啥不让 BL2 也在 EL3运行这样还省掉后面那次 SMC 陷回 EL3 的往返呢你怎么看欢迎评论区聊聊

相关新闻

Safari MCP服务器:AI驱动的跨浏览器调试新范式

Safari MCP服务器:AI驱动的跨浏览器调试新范式

如果你是一名 Web 开发者,特别是需要处理跨浏览器兼容性问题的开发者,那么你一定经历过这样的痛苦:在 Chrome 上完美运行的页面,在 Safari 上却出现了布局错乱、样式失效或者功能异常。传统的调试流程通常是:发现 Safa…

2026/10/17 4:21:12 阅读更多 →
基于YOLOv8-seg与注意力机制的芒果叶片病害检测系统

基于YOLOv8-seg与注意力机制的芒果叶片病害检测系统

1. 项目背景与核心价值 芒果作为热带地区重要的经济作物,其叶片健康状况直接影响果实品质与产量。传统的人工巡检方式效率低下且依赖经验,而基于深度学习的图像分割技术为芒果叶片损伤检测提供了自动化解决方案。这个项目通过融合YOLOv8-seg与CSWinTrans…

2026/10/17 4:21:33 阅读更多 →
Windows服务器使用Certbot配置SSL证书全攻略

Windows服务器使用Certbot配置SSL证书全攻略

1. 项目背景与需求解析在当今互联网环境中,SSL证书已成为网站安全的基础配置。对于个人开发者和小型项目而言,使用Lets Encrypt的certbot工具获取免费证书是最经济实惠的选择。但实际操作中,很多开发者会遇到一个典型困境:手头只有…

2026/10/17 4:21:50 阅读更多 →

最新新闻

基于STM32H750与SOEM的EtherCAT主站开发实战解析

基于STM32H750与SOEM的EtherCAT主站开发实战解析

1. 为什么我最终选了"H750SOEM"这套组合做EtherCAT主站开发,绕不开一个问题:用哪颗芯片、跑哪套协议栈。市面上主流的方案分两派,一派走ASIC专用芯片,另一派走"MCU软件协议栈"。我这次选的是后者,…

2026/10/18 7:29:04 阅读更多 →
AI辅助学术论文写作书籍(2026.10.17)

AI辅助学术论文写作书籍(2026.10.17)

1、秒懂AI写作:让你轻松成为写作高手(2023.06) 2、玩转ChatGPT:秒变AI写作高手(2024.01) 3、挑战疑难病:SCI论文背后的故事.第二辑(2024.04) 4、AI辅助论文写作——DeepS…

2026/10/18 7:29:04 阅读更多 →
BLDC方波控制SIL测试实战:Simulink模型到代码回环验证

BLDC方波控制SIL测试实战:Simulink模型到代码回环验证

1. 这个项目到底在做什么:BLDC方波控制与SIL的定位先说一个我最近做完的项目,链路是完整的:控制对象是无刷直流电机(BLDC),控制算法是带霍尔传感器的方波控制(也叫六步换相、梯形波控制&#xf…

2026/10/18 7:29:04 阅读更多 →
hom_mat2d_rotate 的 Px 和 Py 参数

hom_mat2d_rotate 的 Px 和 Py 参数

hom_mat2d_translate 里也有 Tx, Ty,那里的 T 是 Translation(平移),表示"平移量";而 P 表示"一个点的位置"。hom_mat2d_rotate 的 Px 和 Py 参数,代表的是旋转中心(也称作…

2026/10/18 7:29:04 阅读更多 →
Vue 3 基础:模板语法、响应式、指令

Vue 3 基础:模板语法、响应式、指令

前言上一篇讲了 JavaScript、DOM 和 ES6,这一篇进入 Vue 3。Vue 是目前最主流的前端框架之一,它把"数据驱动视图"这个理念做到了极致:你只管改数据,页面自动更新。这篇覆盖 Vue 3 的基础三件套:模板语法、响…

2026/10/18 7:29:04 阅读更多 →
全球电商数据接口一键接入:技术要点与选型指南

全球电商数据接口一键接入:技术要点与选型指南

上个季度我帮一个做跨境电商的朋友整理运营流程,他手上有5个平台账号,每天早晨雷打不动要花两小时导出各后台的订单和库存,再做一轮手工汇总。有一回因为某个平台库存同步晚了半天,一单爆款直接超卖,赔了运费还被买家差…

2026/10/18 7:28:03 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →