首页/新闻资讯/正文详情

手撕ARM64启动栈(四):QEMU + TF-A(ATF) BL2 详解

发布时间:2026/11/12 0:30:35 来源:云帆数科 栏目:资讯中心
手撕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/11/12 0:30:35

Linux文件系统核心结构与实用命令详解
Linux文件系统核心结构与实用命令详解

1. Linux文件系统基础认知 刚接触Linux时,最让人头疼的就是那个与Windows截然不同的文件系统结构。记得我第一次用 cd / 命令进入根目录时,面对密密麻麻的目录名完全摸不着头脑。经过多年运维工作,现在我把这个"目录迷宫"的规律总… · 2026/11/12 0:30:35

实测2026最新可用主流网盘解析提速偏方,从此不再限速
实测2026最新可用主流网盘解析提速偏方,从此不再限速

在日常的电子文件传输与云端存储使用中,许多人常常会产生疑问:数据接收的快慢究竟是随机决定的,还是能够通过特定的方式进行人为干预与调节?答案是肯定的。网络数据的传输速率并非一个不可改变的固定数值,而是一个受多… · 2026/10/31 22:04:02

工地太忙根本没时间复习考试 电工证上岗证考试试题怎么报名全攻略
工地太忙根本没时间复习考试 电工证上岗证考试试题怎么报名全攻略

工地太忙根本没时间复习考试 电工证上岗证考试试题怎么报名全攻略 工地太忙根本没时间复习考试,这是很多一线施工人员的真实写照。你可能正忙着赶工期,连吃饭的时间都紧张,更别提抽出时间来准备电工证上岗证考试试题了。别担心,今天就带你从零开始,看看… · 2026/11/12 0:30:03

收电工证流程复审延期怎么办理 官方回应来了
收电工证流程复审延期怎么办理 官方回应来了

收电工证流程复审延期怎么办理 官方回应来了 证书过期了不知道怎么复审延期,你不是一个人。最近,很多持证人反映, 收电工证流程… · 2026/11/12 0:30:03

扶绥电工证培训机构官方回应:学历不高也能考,薪资前景有保障
扶绥电工证培训机构官方回应:学历不高也能考,薪资前景有保障

扶绥电工证培训机构官方回应:学历不高也能考,薪资前景有保障 学历不高怕报不上名?别急, 扶绥电工证培训机构 的官方回应来了, 学历不是硬门槛… · 2026/11/12 0:30:03

漯河哪里可以办电工证保姆级教程:怕考不过白交培训费?这份攻略帮你搞定
漯河哪里可以办电工证保姆级教程:怕考不过白交培训费?这份攻略帮你搞定

漯河哪里可以办电工证保姆级教程:怕考不过白交培训费?这份攻略帮你搞定 怕考不过白交培训费?你不是一个人!在漯河,电工证已经成为很多建筑工人、电工从业者、维修人员的“刚需”。尤其是郑州金水区的在职建筑工人,经常需要接触高低压电气设备,没有电工… · 2026/11/12 0:30:03

q3是什么特种工证报考条件与考试时间
q3是什么特种工证报考条件与考试时间

之前考的证在外省能不能转过来?Q3特种工证和电子证书全解析 之前考的证在外省能不能转过来?我刚接手一个鹤壁水利工程项目的工地,有位工人拿着河南考的Q3特种工证,说在本地项目上被要求重新考证,理由是证书系统显示“证书状态异常”。这事儿让我意识… · 2026/11/12 0:30:03

电工证归哪些部门审核值不值得考 上岗必备指南
电工证归哪些部门审核值不值得考 上岗必备指南

电工证归哪些部门审核值不值得考 上岗必备指南 怕考不过白交培训费,尤其是电工证这种 上岗必备 的证书,很多人担心花了几千块培训费,结果考试不过,钱打水漂。别急,下面这些干货能帮你少走弯路。 电工证归哪些部门审核? 电工证归哪些部门审核… · 2026/11/12 0:29:03

电工证丢失还能补吗?千万别踩坑,丢了证等于失业
电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/11 12:18:07

电力电缆作业证直播报名入口与办理流程良心建议
电力电缆作业证直播报名入口与办理流程良心建议

电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/11 19:07:25

英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑

英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/11 17:14:54

电工证设年审有效期多久官方回应来了,工地太忙根本没时间复习考试
电工证设年审有效期多久官方回应来了,工地太忙根本没时间复习考试

电工证设年审有效期多久官方回应来了,工地太忙根本没时间复习考试 工地太忙根本没时间复习考试,这是很多一线电工、焊工、高处作业人员的真实写照。尤其是像你这样刚毕业的鹤壁工程类毕业生,一边要找工作,一边要考证,时间紧张,复习效率低。但你可能不知… · 2026/11/12 0:00:56

新乡电工证实操怕挂科?电工作业动漫图+报考避坑全解析
新乡电工证实操怕挂科?电工作业动漫图+报考避坑全解析

新乡电工证实操怕挂科?电工作业动漫图+报考避坑全解析 实操考试心里没底怕挂科?你不是一个人。新乡的水利工程从业者,很多在报考电工证的时候都担心自己操作不熟练、考试不通过, 千万别踩坑 ,选对资料和方法真的能提高通过率。… · 2026/11/12 0:00:56

网电工证千万别踩坑:实操考试心里没底怕挂科怎么破?
网电工证千万别踩坑:实操考试心里没底怕挂科怎么破?

网电工证千万别踩坑:实操考试心里没底怕挂科怎么破? 实操考试心里没底怕挂科,这是很多准备报考【网电工证】的学员最真实的感受。尤其是在高压电工、低压电工等工种上,操作环节的容错率极低,一旦失误就可能直接被判定不合格。很多人因为担心挂科而放弃考… · 2026/11/12 0:00:56

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

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

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

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

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

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

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码