news 2026/9/6 3:18:32

ARM可信固件ATF深度解析:源码架构、安全审计与平台移植实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ARM可信固件ATF深度解析:源码架构、安全审计与平台移植实战

1. 内容整体设计与思路拆解

1.1 一文说清ATF到底是什么

很多从事嵌入式Linux或Android底层开发的工程师,第一次接触ARM Trusted Firmware(简称ATF,现在官方常称TF-A)时,通常是一脸懵的。市面上讲U-Boot、讲Linux内核的资料一大把,但系统讲ATF的资料却少得可怜。这导致不少人有个误解:以为ATF是个类似U-Boot的引导程序,把U-Boot加载起来就完事了。

实际完全不是这样。

ATF是一套运行在ARM处理器安全侧(Secure World)的固件集合,它不仅仅负责上电引导,更核心的职责是提供安全运行时服务、可信启动流程、以及在不同特权级和安全状态之间安全切换的机制。你在设备上看到的armv8/armv9架构、TrustZone、PSCI电源管理、安全启动(Secure Boot)、TEE(可信执行环境)这类词,最终落地都绕不开ATF。

为什么很多人看得懂U-Boot却看不懂ATF?最根本的原因是视角不同。U-Boot解决的是“怎么把内核加载到内存并跳过去”的问题,而ATF解决的是“在处理器刚上电、还处于最高特权级EL3时,如何以安全可控的方式建立整个系统的执行环境”的问题。它站在系统启动的最顶端,是安全信任链的根。

这篇文章的来源,是我对一个基于Rockchip RK3588平台的商业项目做ATF基线审计和移植时的完整复盘。从架构阅读、安全功能审计,到平台适配和启动调试,前前后后花了几周时间。我会把整个思路、关键代码路径、和踩过的坑全部梳理出来,给准备接触ATF或者正在做平台移植的工程师一条相对完整的路线。

1.2 ATF在整个启动链路中的位置

要理解ATF,首先得把它放进完整的启动链路里看。以经典的ARMv8-A平台为例,典型启动顺序是:

  1. 芯片内部的BootROM(固化在硅片里的程序)执行,根据拨码或烧录状态决定从eMMC、SD、USB等介质读取下一级镜像。
  2. 读取到的第一份镜像就是ATF的BL1(Boot Loader stage 1)。BL1做基础硬件初始化(时钟、DDR、串口等的最小集),然后从可信存储位置加载BL2。
  3. BL2继续初始化,加载BL31(EL3 Runtime Firmware)、可选的BL32(TEE OS)、以及BL33(通常是U-Boot或UEFI)。
  4. BL31常驻内存,一直留在EL3,向非安全侧的Linux内核提供PSCI等运行时服务。
  5. Linux内核在EL2(虚拟化场景)或EL1(非虚拟化场景)正常运行,所有安全相关请求通过SMC(Secure Monitor Call)指令陷入EL3交给BL31处理。

这个流程里最容易被忽略的一点是:ATF不止在启动阶段干活,它是一个“启动后依然活跃”的运行时组件。BL31常驻DDR里一个被保护的region,类似你的电脑里有一个独立于操作系统之外的安全固件一直在跑——这么理解就贴切了。

对于做系统移植的工程师来说,你的任务时间线一般是这样:芯片原厂给你一套完整可启动的ATF源码和烧录包——你验证它在你的板子上能跑——然后根据你的外设、内存颗粒、调试口差异做适配——最后把安全启动和TEE方案集成进去。看起来步骤不多,但每一步都有很多暗坑。

1.3 为什么需要专门做一次“工程审计”

我在很多项目里见过一种危险做法:直接把芯片原厂提供的ATF二进制丢进烧录工具,能开机就认为万事大吉。短期看没毛病,长期看隐患极大。因为ATF是整个系统安全信任链的地基,它的错误不像应用层软件那样崩溃几次就能暴露,往往是在特定攻击场景或者极端边界条件下才会触发。

所以我建议,凡是涉及安全功能的设备,都应该把ATF源码做一次系统性的审计。审计的核心目标包括:

  • 确认工程量级和组成部分,知道谁在什么时候执行、访问哪些硬件资源。
  • 梳理信任链,搞清楚每个镜像的加载和校验方式,确认是否存在被绕过验证的可能。
  • 检查TOCTOU(Time-of-check Time-of-use)问题,即“校验时是好的、运行时被换了”的经典攻击面。
  • 验证BL31提供的SMC接口是否严格做了调用来源和参数校验,这部分是攻击者最容易接触到的接口。
  • 评估平台配置的合理性,比如BL31运行基址、内存权限配置、PSCI功能是否完整。

这听起来像安全团队的活,但实际做平台移植的工程师也同样需要掌握。因为你不知道什么时候就要自己改一处平台代码,不懂审计原则的人很容易随手引入一个安全隐患。

2. ARM Trusted Firmware源码架构全景拆解

2.1 源码目录结构与各目录职责

拿到TF-A源码后,首先要做的是把目录结构看懂。TF-A的代码仓库地址是https://github.com/ARM-software/arm-trusted-firmware(后来被迁移到了TrustedFirmware.org托管),官方的参考实现是主线master分支。整个仓库的顶层目录结构大致如下:

  • bl1/:BL1阶段源码,负责最基础的启动。
  • bl2/:BL2阶段源码,负责加载和认证后续镜像。
  • bl31/:BL31运行时固件源码,包含SMC分发、PSCI实现、运行时服务框架。
  • bl32/:BL32(TEE OS)的启动集成代码,实际TEE OS本体(如OP-TEE)是独立仓库。
  • plat/:平台相关代码,这是你做移植时最常动的地方,按厂商分目录存放。
  • drivers/:各类驱动,包括GIC、UART、DDR、IO存储器等。
  • include/:通用头文件,包括架构定义、平台接口定义、常量定义等。
  • lib/:通用库,如标准C库子集、跳转表、内存操作等。
  • common/:通用代码,如启动流程的基本框架、Blob验证逻辑等。
  • services/:各类运行时服务,包括标准SMC服务、PSCI服务、SPD(Secure Payload Dispatcher)等。
  • tools/:各种工具,包括证书生成工具cert_create、FIP打包工具fiptool等。
  • fdts/:设备树源文件,ATF也使用设备树描述平台硬件信息。

初次接触的人容易犯一个错误:钻进bl1的汇编代码里出不来。其实你不必逐行抠汇编。ATF强调可移植性,通用流程代码用C写的部分很多,只有极小部分例如CPU reset、异常向量表、上下文切换才用汇编。你真正需要精读汇编的地方并不多。

2.2 从编译脚本理解构建系统

TF-A的构建系统是精修的Makefiles体系,顶层Makefile是入口。通常的编译方式是:

make PLAT=versal BL33=/path/to/u-boot.bin DEBUG=1 V=1 all

这个命令里几个关键参数:

  • PLAT:指定平台目录,比如versal对应Xilinx versal平台,rk3588对应Rockchip平台,fvp是ARM官方提供的固定虚拟平台。
  • BL33:指定Non-Secure侧下一级镜像路径。ARM标准流程里BL33通常是U-Boot或UEFI。
  • DEBUG:设为1时会生成带调试符号、开启LOG_LEVEL高等级输出的版本。
  • V=1:显示完整编译命令,排查编译错误时非常有用。

编译产生的镜像文件:

  • bl1.bin:BL1镜像,烧录到片内SRAM或可信ROM区域。
  • bl2.bin:BL2镜像。
  • bl31.bin:BL31镜像,完全独立的裸机固件。
  • bl32.bin:如果配置了OPTEE则产出。
  • fip.bin:将BL2、BL31、BL33等打包在一起的FIP格式镜像。
  • bl1-bl2.bin:一种BL1+BL2合并镜像,烧录方案更简单。

我强烈建议你用DEBUG=1做开发调试,用LOG_LEVEL=50查看详细日志。但要注意,正式发布版本务必关掉DEBUG,否则日志输出本身就是严重的信息泄露风险。

2.3 启动流程中的信任链传递机制

ATF的启动流程核心就是“分级加载、逐级认证”。这套机制是我审计时花时间最多的部分,因为它直接决定了“到底什么能信”。

整体思路很简单:每一级引导程序在加载下一级之前,都必须先对下一级的镜像做安全校验,校验通过才会跳转。这形成了一条信任链:

  1. 芯片BootROM是第一信任根,它验证BL1。BootROM的代码在硅片出厂时固化了,无法修改,理论上最可信。
  2. BL1验证BL2。BL1中内置了平台根公钥的hash,用这个公钥验证BL2的签名。
  3. BL2验证BL31、BL32、BL33。BL2中包含了证书解析和验签逻辑。

用生活化类比就是:银行金库的钥匙在手,开门,验证押运员(BL2),验明正身后再让他开第二道库门,放行长进来(BL31),行长再验证押运的那箱钱是不是真钞(BL33)。

实际代码里,bl2_plat_handle_post_image_load等接口就是BL2完成镜像加载后的挂载点。如果你看到这些接口里有额外的平台校验逻辑,要注意是否弱化了原有的安全策略。

2.4 关键数据结构:上下文切换与Boot Layout

ATF里最核心的数据结构,一是cpu_context,二是boot_params

cpu_context保存了CPU寄存器状态,用于EL3与Normal World(非安全侧)之间的切换。你可以把它想成一个银行金库的交接记录表:每次访问金库(进EL3)和离开金库(回Non-secure)都要记录当前状态,下次回来能快速恢复现场。

boot_params则描述了每个BL阶段的加载位置和大小。在ARMv8下,BL1通常放在片内SRAM或特殊的ROM地址,BL2可能放在SRAM或DDR可信区域,BL31常驻DDR保留区域。

平台移植时你最重要的任务之一,就是合理规划这些内存布局。以RK3588为例,BL31运行在DDR顶部保留的特定区域,BL2则放在DDR安全区域。如果内存布局没规划好,轻则启动卡死,重则某个安全功能静默失效——后一种情况尤其危险。

3. 安全固件工程审计要点与实操方法

3.1 审计清单:从代码层面找问题

经验来看,工程级的安全审计不必像安全研究那样追求0-day发现,更应该关注“可用性、配置合理性和最小化攻击面”。按这个目标,我整理的审计重点如下:

  • 默认配置是否开启完整的安全启动链?
  • 每个SMC接口是否做了调用来源校验(即调用者的Safety/EL级别)?
  • 全局变量是否都初始化?是否有敏感的调试打印?
  • 平台内存映射表(MMU Table)是否存在过度授权的映射区域?
  • 是否有绕过认证后直接加载镜像的代码路径?
  • BL31中是否存在未使用的、但暴露给Normal World的调试接口?
  • PSCI API是否符合ARM FFH规范?是否暴露了不必要的扩展接口?

我习惯用grep快速搜索危险调用模式,比如ssh相关的打印、callbackdebug等关键词,然后逐步跟踪函数调用路径。但是单纯grep很难发现逻辑漏洞,更有效的方式是分别对关键函数走“白盒阅读+动态验证”。

3.2 信任根与固件验证机制的深度解析

信任根通常以两种形式存在:一是芯片BootROM内的固定公钥,二是BL1内部的公钥hash(即建立信任根的公钥列表)。在ATF源码中,BL1的验证逻辑定义在drivers/auth/目录下的认证框架中。

在认证的过程中,trusted board boot(TBB)配置很关键。当你开启TRUSTED_BOARD_BOOT编译选项时,BL2会通过auth_mod_verify_signature对所有加载镜像执行RSA或ECDSA验签。验证内容包括:

  1. 镜像头格式是否正确。
  2. 签名是否有效(基于平台证书的验签)。
  3. 镜像哈希是否与证书中声明一致。
  4. 镜像是否过期或不在允许的版本序列(如果是防回滚场景)。

代码里plat_get_rotpk_info就是平台获取Root Of Trust Public Key的接口。这里坑很多——有些平台对可重编程存储区(比如eMMC RPMB分区)的信任处理得很模糊,如果你把ROTPK存在可写存储中,攻击面就被放大了。

3.3 审计发现的典型安全薄弱点

在这类审计中,我总结出几类容易被忽略的风险点,这里列一下:

  • 调试接口:许多原厂代码在定义平台宏时保留了大量调试日志,包含DDR初始化Details、内存地址、启动参数。攻击者如果拿到串口访问权限,这些信息能显著降低攻击门槛。
  • SMC参数未校验完全:BL31里的某些SMC接口,对参数来源的exception level判断不严格,或缺少对SMC调用者身份(caller_sec_state)的校验。理论上,Normal World的普通应用(EL0)也可能触发某些本应只允许EL1/EL2调用的接口。
  • 信息泄露:BL31处理SMC失败时,部分错误码直接回显寄存器内容或缓冲区内容,可能导致地址布局泄露。
  • 内存设置RORW权限混乱:有些平台为省事将BL31的部分数据段映射为可读写,但这些数据一旦可被写入,可能覆盖安全策略表或跳转指针。
  • 未禁用的弱密码算法:部分代码还残留对SHA-1的支持,虽然未必被实际使用,但如果你审计粒度不足,很容易忽略。

从工程角度说,我建议做审计时同步开启Secure Boot,并在目标板上跑一轮TF-A Tests,或者直接跑Linux内核的KASAN级别压力。只有动态+静态结合才能发现这类交叉问题。

3.4 一份适合团队执行的审计流程参考

审计不是一个人天马行空读代码,建议按下面的流程来:

  1. 先读文档:docs/目录下的firmware-design.rsttrusted-board-boot.rst、以及platform-porting-guide.rst是必读。这一层先建立全貌。
  2. 拉一个干净的tag版本:不要审计一堆本地修改,CI拉主线或厂商release tag即可。
  3. 按启动顺序逐模块审查:BL1 → BL2 → BL31 → SPD/TEE。
  4. 对每个平台函数(plat_xxx)标出调用点,并记录函数职责。
  5. 采用code review工具(如Gerrit、GitLab MR review)把审计过程记录下来,方便追溯。
  6. 最后跑实际硬件验证:启动、休眠唤醒、热插拔CPU、TEE功能、安全启动失败注入测试。

另外,ATF官方有一个docs/下“security center”的指引,整理了最近公布的漏洞列表,建议每隔几个月对照检查自己平台是否需要同步更新补丁。

4. 平台移植完整落地指南

4.1 移植的前置条件和环境准备

做ATF平台移植,你手上最好先有这几样东西,缺一不可:

  • 目标板的芯片手册,特别是存储映射、GIC配置、串口基地址、DDR控制器寄存器。
  • 原厂提供的可启动uboot镜像和内核镜像(用来做对照验证)。
  • 一块能稳定供电、有串口输出的目标板。JTAG调试器强烈建议准备,启动阶段的串口输出来得太晚。
  • 主机的交叉编译工具链,通常是aarch64-linux-gnu-前缀的GCC工具链。
  • 如果有TrustZone调试工具(如ARM DS、JTAG),会在调试时省很多时间。

环境上,在Ubuntu 22.04这类常见发行版上安装工具链即可:

sudo apt update sudo apt install gcc-aarch64-linux-gnu make device-tree-compiler git clone https://github.com/ARM-software/arm-trusted-firmware.git

建议选择稳定的版本如v2.8v2.9v2.10或厂商维护分支。如果你用的是老平台,直接切主线可能编译不过,保持“够用即可”的策略。

4.2 新建平台目录和最小启动配置

TF-A平台代码的骨架非常简单,核心是在plat/vendor/board/下创建你的平台目录,并实现一组固定的平台接口。

最小化平台目录结构通常是:

plat/mycompany/myboard/ ├── platform.mk ├── myboard_def.h ├── myboard_common.c ├── myboard_bl2.c ├── myboard_bl31.c └── myboard_helpers.S

第一步看platform.mk,里面指定了平台编译选项、包含路径、源文件列表。一个最基本的内容大致如下:

PLAT_INCLUDES := -Iplat/mycompany/myboard/include PLAT_BL_COMMON_SOURCES := \ plat/mycompany/myboard/myboard_common.c \ plat/mycompany/myboard/myboard_helpers.S \ drivers/arm/gic/v3/gicv3_main.c \ drivers/arm/gic/v3/gicv3_helpers.c BL2_SOURCES += \ plat/mycompany/myboard/myboard_bl2.c BL31_SOURCES += \ plat/mycompany/myboard/myboard_bl31.c

第二步实现平台初始化函数,比如platform_setupplat_get_next_bl_paramsplat_get_stack_info等。对调试来说,第一步要保证串口输出能工作。以ARM PL011串口为例:

void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { /* UART base address from platform definition */ console_pl011_register(PLAT_MYBOARD_UART_BASE, PLAT_MYBOARD_UART_CLK_IN_HZ, PLAT_MYBOARD_UART_BAUDRATE, &console_data); }

这里console_pl011_register就是把寄存器基址注册成标准console。很多新手卡在没有输出,其实就是这一步没做对,或者基址不对,或者UART时钟频率算错了。这个频率参数要查芯片手册的UART时钟树,不是随便写的。

4.3 平台内存布局规划与中断控制器配置

内存布局规划是移植的核心,它直接决定你能不能正常进入BL31。ATF通过链接脚本(.ld.S)来布置镜像段,平台头文件里定义各镜像加载基址。我的建议是:

  • BL1尽量放在片内SRAM,因为DDR控制器还没初始化,代码必须能在不含DDR的环境中运行。
  • BL2可以放在SRAM也可放DDR的安全区域,但BL2运行前DDR已经可用(BL1初始化完DDR)。
  • BL31常驻DDR安全区域,这个区域必须在内存控制器上被保护,防止Non-secure侧的Linux意外改写。

以RK3588为例,它的ATF代码中会把BL31放在系统DRAM的高端保留区。你在看代码时如果看到#define PLAT_RK3588_TRUSTED_SRAM_BASE这类宏,就是这个用途。

中断控制器方面,ATF BL31需要把GIC(Generic Interrupt Controller)配置好,因为PSCI的CPU热插拔、系统挂起/恢复都依赖于GIC的正确初始化。GIC版本以v3为主,初始化逻辑包括:

  • 设置GICD_CTLR的Group0和Group1使能。
  • 配置SPI(Shared Peripheral Interrupt)中断的路由和触发方式。
  • 配置GICC/GICR基址。

代码里主要是gicv3_driver_initgicv3_distif_init函数。如果GIC配错,系统一进BL31就直接死掉。

4.4 标准开机已验证:从编译到进U-Boot

给大家一个我常用的最小验证路径:

  1. 编译ATF:make PLAT=yourplatform DEBUG=1 V=1 BL33=/path/to/u-boot.bin all
  2. 确认产物bl31.binfip.bin存在。
  3. 烧录:按厂商工具烧到启动介质,可能是SPL+eMMC分区或FIP容器。
  4. 通过串口观察启动日志。

如果一切正常,你会看到类似这样的日志:

NOTICE: BL31: v2.9(release):v2.9 NOTICE: BL31: Built : ... INFO: BL31: Initializing runtime services INFO: BL31: Preparing for EL3 exit to normal world INFO: Entry point address = 0x...

然后U-Boot打印出现。到这一步,平台移植的最小闭环已经完成。

4.5 常见移植踩坑实录

坑1:串口只出BL1日志,BL2卡死。大概率是BL2的DDR初始化或内存访问异常。检查BL2的bl2_platform_setup是否访问了尚未就绪的控制器,或者SRAM布局重叠。

坑2:BL31进入后立刻重启。多数是GIC或定时器配置问题。建议在BL31初始化代码里逐步加NOTICE日志,找到具体挂点。

坑3:U-Boot起来但Linux不能启动。往往不是ATF问题,而是传给内核的DTB或启动参数不对,但背锅的往往是ATF。先用U-Boot的booti命令单独验证内核能否启动。

坑4:PSCI调用返回错误码。核心是BL31是否注册了PSCI服务。看启动日志里Registered PSCI信息,没有的话多半是条件宏没配置对。

坑5:安全启动验证失败,设备变砖。这种场景要非常小心,务必开发阶段先关闭验签,验证完再开启。并且保留烧录工具能从MaskROM模式恢复的能力。

5. 深入实战:从审计需求到自定义扩展常见问题与排查

5.1 如何扩展一个自定义SMC服务

很多时候默认的ATF无法满足业务需求,要在EL3加一个自定义SMC接口。举个例子,比如你想实现一个只在安全世界可见的“设备唯一ID”读取接口。

首先你要在BL31的服务框架里注册一个handler。代码长这样:

static uintptr_t my_smc_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3, u_register_t x4, void *cookie, void *handle, u_register_t flags) { switch (smc_fid) { case MY_SVC_GET_UID: /* 做一些安全检查,比如确保调用来自EL1以上 */ if (!is_caller_secure(flags)) { SMC_RET1(handle, SMC_UNK); } write_my_uid_to_buffer(x1); SMC_RET1(handle, 0); default: SMC_RET1(handle, SMC_UNK); } } DECLARE_RT_SVC(my_smc_service, MY_SVC_START, MY_SVC_END, my_smc_handler);

然后在Linux侧,你可以通过smc指令直接发起调用,或者用arm_smccc_smc接口从内核发起。扩展SMC接口时最重要的是做两件事:一是把MY_SVC_START/MY_SVC_END的编号范围定义好,不要跟ARM标准SMC分配段冲突;二是在合法范围内明确调用者的权限和安全状态,否则很容易开个后门。

5.2 基于真实硬件排查启动流程故障

这里分享一个在RK3588平台上的具体排查案例。现象是板子偶尔冷启动卡死,热启动正常。

我先从U-Boot、内核日志出发没找到线索,转向ATF端。用JTAG读寄存器,发现BL31在初始化GIC时卡在一个等待循环里。进一步阅读代码发现有一个平台宏控制GICR基址的偏移,当芯片型号的core数量不同时会差一截。原厂默认配置按“16核”去初始化,但该板子实际只有8核,结果访问了不存在的GICR frame,导致总线hang。

修复方法是把平台宏改成根据DTB解析CPU数量,让GICR基址随核心数偏移。

这种情况是典型的“默认能跑但特定硬件才暴露”的平台问题,也正是为什么每一步启动日志和JTAG都不可少的原因。

5.3 常用调试工具与日志分析方法

ATF代码库内建了NOTICEWARNERRORINFO等日志级别。编译时通过LOG_LEVEL控制输出等级,一般:

#define LOG_LEVEL 50 /* 输出所有消息 */

在BL31中打印时,可以用fmt字符串加参数。要注意在BL1阶段DDR未初始化,串口打印受限于BL1所在介质(通常是片内SRAM的驱动代码),某些外设如DMA不能用。

硬件调试方面强烈推荐使用JTAG,比如ARM官方的Arm Development Studio或开源调试器OpenOCD配合gdb。可以做到:

(gdb) target remote :3333 (gdb) hbreak bl31_main (gdb) continue (gdb) info registers

在启动早期,一条指令就能让整个系统挂死,所以打印日志是基础,JTAG才是“重型武器”。

5.4 平台移植后的必要测试清单

移植完成不是终点,我个人测试时会至少覆盖下列场景:

  • 冷启动和热启动各100次,确认无偶发问题。
  • 进入和退出Suspend(系统休眠)200次,确认PSCI路径稳定。
  • CPU热插拔操作,确认CPU0以外的核能稳定上线和下线。
  • TEE功能(比如OPTEE启动、TA加载)来回切换,确认BL31与BL32的交互正常。
  • 非安全世界恶意SMC调用测试,确认异常接口返回错误而不是挂死。
  • 开启安全启动后,做一次签名错误注入测试,确认设备拒绝启动并进入恢复模式。

每次测试都要记录日志,偶尔会把LOG_LEVEL调到最高跑一轮,排查掉潜在信息过载或格式问题。

5.5 常用问题排查速查表

现象可能原因建议排查步骤
BL1启动卡死SRAM初始化、串口时序检查JTAG读PC值,确认卡在哪条指令
BL2加载失败FIP打包错误、加载地址重叠fiptool查看FIP内容,核对BL2基址
BL31重启GIC初始化错误、定时器配置错误分阶段加日志,确认挂点
PSCICPU_ON失败未正确配置电源域、GICR基址错误检查代码中的psci_validate_power_state实现
安全启动验签失败ROTPK不匹配、根证书不正确检查BL1中ROTPK是否与烧录证书对应
内核启动后无法访问TEESPD未编译进BL31确认编译时SPD选项正确,比如SPD=opteed
板子变砖BL31损坏或验签失败从MaskROM/串口恢复模式重刷,开发期务必保留无校验版本

这张表是我每次换新板子时都会拿出来过一遍的检查清单,所有端口、分区、烧录路径都是按厂商手册逐一核对的,不能凭经验猜。

6. 末尾随想与经验拾遗

说一组我个人的小技巧。很多人面对ATF这种数千行C/汇编的代码库会有畏难情绪,我也一样。但真正上手后会发现,它其实遵循一个很清晰的模式:每个BL阶段都有固定的setup流程,平台代码只要按接口填空就行。心态摆正以后,阅读速度会快很多。

第二个实用习惯是“保持与主线同步”。即使你在做厂商分支,我也建议每隔几个月拉一次上游TF-A的更新,对比一下安全补丁。很多厂商分支长期不合并主线的安全修复,这在产品生命周期很长的情况下尤其危险。

第三个建议是,设计上尽量把安全相关的逻辑集中在一个独立的模块里,不要在平台代码里到处发散。比如你要加自定义SMC服务,就在services/下新建一个目录,不要塞进plat/的某个c文件里。这样做的好处是审计、测试、复用都容易,而且避免平台适配代码和业务逻辑耦合。

最后送大家一句话:ATF的文档和代码永远是最好的老师,遇到问题先查docs/,再搜代码,最后再问社区或者厂商。这个过程本身就是提升嵌入式底层能力最有效的方式。希望这篇基于实践的拆解能帮你少走一点弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 3:16:01

Python第4次作业

第1题: 位运算: 计算56及-18的所有位运算符结果,并使在注释中体现计算过程 【代码】 # 1.位运算: 计算56及-18的所有位运算符结果,并使在注释中体现计算过程 # 数值定义 a 56 b -18 # bin()打印二进制&#xff0c…

作者头像 李华
网站建设 2026/9/6 3:14:34

单相APFC仿真实战:Simulink建模、PI整定与波形优化

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

作者头像 李华
网站建设 2026/9/6 3:09:05

2026代码管理平台选型指南:从Git托管到研发协作升级

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

作者头像 李华
网站建设 2026/9/6 3:09:05

DeepSeek 涨价了,你换了吗?——技术视角下的成本与选型思考

1. 引言:DeepSeek 涨价,开发者圈炸了 最近 DeepSeek 官方宣布调整 API 价格,消息一出,开发者群里立刻炸开了锅。有人连夜对比各家大模型报价,有人开始盘算迁移成本,也有人淡定表示“早就预料到了”。 作为技…

作者头像 李华
网站建设 2026/9/6 3:07:02

SSM+VUE家庭食谱管理系统:前后端分离开发实战指南

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

作者头像 李华