
很多人搜 UFS boot 是带着两种完全不同的目的来的一种是后端工程师想搜 Spring Boot 的启动流程另一种是做嵌入式、做手机、做存储的工程师想搞清楚 UFS 这个存储器件到底怎么把系统拉起来。今天这篇聊的是后者Universal Flash Storage 的 boot 功能也就是 UFS boot。说白了它回答三个问题UFS 内部的启动分区怎么组织SoC 怎么通过 UFS 启动以及实际调试的时候你会踩到哪些坑。我会尽量把协议层的概念和硬件电路上的表现都糅在一起讲因为这两件事在 UFS boot 里根本分不开。1. 先泼盆冷水UFS boot 不是 Spring Boot但它确实很重要1.1 同名搜索下的两个世界“UFS boot” 这个关键词非常容易让人迷惑。搜出来一大半是 Spring Boot 的教程、Java MyBatis 搭配、Spring Boot 4.x 的配置问题剩下才是 JEDEC UFS、UFS 协议、boot 配置电路这类存储领域的内容。原因很简单UFS 和 Spring Boot 都有 “Boot” 这个词搜索引擎不关心你到底是哪个领域。但如果你点进来想学 Spring Boot可以先关掉页面了这里一整篇都是关于 Universal Flash Storage 的启动机制。UFS 全称 Universal Flash Storage是 JEDEC 制定的闪存存储标准目前广泛用在手机、平板、车载、服务器启动盘、数据中心等场景。它最大的特点就是性能比 eMMC 高一个量级支持全双工传输、命令队列、多 LULogical Unit并发访问。而 UFS boot 则是这套标准里专门定义的一块能力让 SoC 能够在最早期、内存还没初始化的时候直接从 UFS 设备上把引导代码读出来执行。1.2 为什么 UFS boot 是绕不开的设计点存储设备不是接上电就能启动系统的。SoC 的 ROM Code 在启动初期几乎没有可用的外部资源它必须先找到一个稳定、可访问的介质把引导代码读进内部 SRAM 里去执行。eMMC 时代这件事靠 BOOT 分区和 BOOT 引脚完成UFS 时代UFS 器件同样需要承担这个角色只不过它的实现方式跟 eMMC 有本质区别。UFS boot 的重要性在于它决定了整个系统的安全启动链能不能建立起来。一级引导加载程序比如高通平台的 xbl、abl通常放在 UFS 的 Boot LU 里ROM Code 读出它之后先做签名校验再一级级引导下去。所以 UFS boot 不只是一个 “能不能启动” 的问题还涉及防回滚、信任链、分区保护等一系列安全能力。1.3 很多人把两个 “boot” 混为一谈这里必须做一个区分UFS boot 指的是 UFS 器件提供的启动分区和启动流程而 Android 里的 boot.img 是 GPT 分区表里的一个普通分区主要放 kernel 和 ramdisk。两者在启动链路里分工完全不同UFS Boot LU 放的是最底层的 bootloaderboot.img 是 bootloader 去读取的下一级镜像。我第一次看平台启动流程时也犯了糊涂以为把 UFS Boot LU 烧成 boot.img 就能启动结果自然是点不亮。后面会专门把这条链路拆开讲。2. Boot 区域是怎么长出来的从 eMMC Boot Partition 到 UFS Boot LUN2.1 先理解 UFS 内部的 Logical UnitUFS 设备内部不是一块简单的裸闪存它被逻辑地划分成多个 Logical Unit每个 LU 相当于一个独立的块设备有自己的大小、读保护属性、写保护属性甚至可以是不同类型普通存储、Code Flash、RPMB 等。主机侧看到的是一个或若干个 LUN通过 SCSI 命令集访问。这跟 SSD 里做多命名空间有点像但粒度更细。在这些 LU 之外UFS 协议还定义了一组 Well-Known LUN。这组 LUN 不是普通的存储区域而是有特殊功能的逻辑入口比如 REPORT LUNS、UFS DEVICE、RPMB以及今天的主角 Boot Well-Known LUN。Boot 相关的 Well-Known LUN 固定分配给两个启动分区分别叫 Boot 1 和 Boot 2对应的 LUN 地址是 0xB0 和 0xB1。2.2 Boot LU 的使能与选择机制UFS 标准规定Boot LU 并不是默认就开启的需要通过设备描述符里的 Boot Enable 字段和对应的 Boot LUN ID 字段来配置。生产时SoC 厂商会用工具把某个普通 LU 配置成 Boot LU指定它当前是 Boot 1 还是 Boot 2然后打开 Boot Enable这个 LU 才会在启动阶段以 Boot Well-Known LUN 的形式暴露给主机。这里有个很容易忽略的点Boot 1 和 Boot 2 都可以独立使能也可以同时使能。很多平台的做法是Boot 1 放一级引导程序Boot 2 放备份的引导程序启动时先尝试 Boot 1失败再尝试 Boot 2。这种冗余设计在 eMMC 时代就有UFS 时代延续了下来。如果生产时只配置了 Boot 1Boot 2 没有打开后面想通过 Boot 2 做备份恢复就会遇到访问不到的问题。2.3 和 eMMC Boot Partition 对比很多工程师对 eMMC 的 BOOT 模式比较熟理解 UFS 时可以拿它作参照。两者都有两个专门用于启动的物理区域但实现方式差别很大。对比项eMMC Boot PartitionUFS Boot LU物理区域BOOT1/BOOT2 两个固定分区Boot LU 1 / Boot LU 2由描述符配置进入方式硬件 BOOT 引脚拉高进入 Boot Mode无专门引脚启动阶段通过 Well-Known LUN 访问访问命令受限于 Boot Mode 下的特定命令标准 SCSI READ/WRITE 命令分区大小通常 4MB/8MB可配置可配置常见 4MB 到 16MB写保护引脚和寄存器组合控制LU 级写保护属性控制启动顺序ROM 检测 BOOT 引脚后直接读 BOOT1ROM 通过描述符确认 Boot Enable 后读对应 LUN这个表格的信息量很大尤其是“进入方式”这一行。eMMC 是靠引脚拉高低来选择 Boot 模式所以电路上确实有 BOOT 配置电路的讲究UFS 没有这个引脚启动分区的选择完全靠设备里的描述符所以很多从 eMMC 转过来的人会下意识去主板原理图里找“BOOT 引脚”结果找了半天没有。2.4 UFS Boot 不等于 boot.img再强调一次UFS Boot LU 和存储介质上的 boot.img 是两层概念。UFS Boot LU 是一个物理启动区域放 bootloader 的早期镜像而 boot.img 是 Android 系统分区里的一个 GPT 分区通常位于普通 LU 上由 bootloader 在后续阶段读取。你通过 fastboot 烧写 boot.img 的时候烧的是普通 GPT 分区通过量产工具烧写 bootloader 的时候烧的才是 UFS Boot LU。这两个东西搞混轻则启动不起来重则把引导链搞坏只能重新烧写整个 UFS。3. 想让 SoC 从 UFS 启动硬件上要先满足这些条件3.1 没有 BOOT 引脚那硬件管什么搜索热词里有“boot配置电路”很多刚接触 UFS 的硬件工程师会以为 UFS 像 eMMC 一样有 BOOT 引脚需要上拉下拉。实际上UFS 的启动选择不靠硬件引脚而是靠设备描述符里的配置硬件上要保证的反而是另外几件事参考时钟、电源、复位和差分信号链路。UFS 器件需要一组稳定的参考时钟通常是 MHz 级别的差分时钟或单端时钟具体频率由平台决定最常见的是 19.2 MHz。这个时钟在 UFS PHY 做 Link Startup 时必须已经稳定如果时钟没起来后续所有命令都白搭。我曾经遇到一块板子UFS 启动时好时坏最后用示波器一测发现参考时钟源的上电时序比 UFS 器件电源晚了 10 ms锁定问题后调整时钟使能顺序问题消失。3.2 电源轨和复位时序不能乱UFS 器件一般有多路供电轨包括主电源和 IO 电源。不同平台、不同 UFS 版本的电压值不完全一样严格以器件数据手册和 SoC 参考设计为准。硬件上最常见的问题是上电时序不满足要求主电源还没稳定复位就已经释放导致 UFS Link Startup 失败。复位信号同样重要。UFS 器件有复位输入SoC 通常在启动流程开始时拉低复位等电源稳定后再释放。如果复位释放太快或者复位信号上有毛刺UFS PHY 可能进入异常状态。调试时不要只看能不能正常跑系统要拿示波器量一下复位释放点是不是晚于最后一组电源纹波稳定的时间点。3.3 PHY 差分对的信号完整性UFS 数据通道是高速差分信号PCB 布线时对差分阻抗有明确要求一般是 85 欧姆左右。走线太长、过孔太多、参考平面被割断都会影响信号眼图导致 Link Startup 训练失败。启动阶段的链路速率可能相对低有时候看起来能 Link 上但进系统后高负载一跑就崩这种问题很可能是信号完整性裕量不足。如果条件允许做硬件调试时优先看 PHY 的差分眼图。UFS 高速模式下的时序容限比 eMMC 严格得多不能用万用表量通断的方式来排除问题。之前有个项目板子量产到第二批突然出现一批无法开机的机器最后定位到是代工厂换了一款阻抗偏高的连接器差分阻抗跑到 95 欧姆UFS 眼的余量直接被吃掉了。3.4 硬件排查的一套土办法很多 UFS boot 失败其实是硬件层面引起的。我给你一个排查顺序先确认参考时钟有没有再量电源轨和复位时序然后测差分信号是否连通最后再去看软件日志。如果 UFS PHY Link 都建立不起来ROM Code 根本走不到读 Boot LU 那一步。很多软件工程师习惯一上来抓串口日志看到没有输出就懵了其实前面还有很大一截硬件工作要做。4. 一次 UFS 启动的完整生命旅程ROM Code 到 Linux Kernel4.1 ROM Code 阶段Link Startup 与 Boot LUN 读取SoC 上电后ROM Code 是第一段代码。它本身就放在 SoC 内部的 ROM 里不需要外部存储。这个阶段要做的事情非常有限初始化最基础的系统时钟配置必要的 GPIO然后开始跟 UFS 器件做交互。UFS 的 Link Startup 类似两个设备之间的“握手”包括速率协商、PHY 校准、链路层初始化等。Link Startup 完成后UFS Host Controller 才能向器件发送命令。此时 ROM Code 会通过 Boot Well-Known LUN 去读 Boot LU 里的内容。它不需要知道 GPT 分区表也不需要懂文件系统就是按固定偏移把一段足够的代码读进 SRAM然后执行。这个阶段对代码大小有严格限制因为 SRAM 通常只有几十到几百 KB。4.2 SBL 和 ABL 阶段从“能读存储”到“能读文件”ROM Code 加载起来的第一段代码叫 SBLSecondary Boot Loader或类似概念它比 ROM Code 复杂一些但依旧很靠底层。SBL 的任务是初始化 DRAM、继续配置 UFS 控制器、把链路速率提升到高速模式然后从 UFS Boot LU 里加载第二段引导代码 ABLApplication Boot Loader或引导系统所需的下一级镜像。到了 ABL 阶段才有能力去解析 GPT 分区表。它会去普通 LU 上的某个分区读取 boot.img在 Android 平台上一般叫 boot 或 init_boot校验完整性后加载到内存最后跳转到内核入口。整个链路到这里UFS Boot LU 的使命就基本结束了剩下的启动都是软件层面的事情。4.3 Kernel 阶段UFS 驱动接管内核启动后UFS 驱动会重新初始化一遍 UFS 控制器和设备。这个初始化和 ROM Code 阶段的初始化不一定是同一个流程因为内核要支持动态电源管理、多 LU、命令队列、RPMB 等完整功能。内核通过 Linux SCSI 子系统把每个 UFS LU 映射成块设备之后就能挂载 rootfs、加载系统服务了。从这里也能看到UFS boot 的核心战场其实是最早那几百毫秒。ROM Code、SBL、ABL 三个阶段里任何一步出了问题用户看到的现象都是“启动失败”但真正的原因可能差别很大。4.4 启动流程与 eMMC 的对比eMMC 启动时ROM Code 通过 BOOT 引脚选择 BOOT1/BOOT2然后读固定 blockUFS 则是通过描述符使能的 Boot LU 来读。看起来差别不大但 eMMC 的 Boot Mode 是一个受限模式读了一段时间后要切回正常模式重新初始化UFS 的 Boot LU 本质上就是一个普通可访问的 LU只是地址比较特殊访问模型更干净。性能上UFS 的优势在双通道和命令队列ROM Code 阶段可能还发挥不出来但进入 ABL 后加载大镜像时会明显比 eMMC 快。这也是现在旗舰手机都转 UFS 的原因之一。不过快是一回事调试复杂度是另一回事UFS 的协议栈明显比 eMMC 深出问题后的排查链路也长得多。5. 动手实践把自己的 UFS Boot 分区完整备份一份5.1 先分清楚你要备份的是哪个分区很多人在网上搜“提取 boot 软件下载”“android 提取 boot”以为能直接提取 UFS Boot LU。但大多数情况下你通过系统工具提取到的是 boot.img也就是 GPT 分区里的 boot 分区不是 UFS 的 Boot LU。Boot LU 一般在系统起来后不会被主动暴露成标准块设备普通用户没有 root 权限也看不到。如果你用的是开发板或者自己有权限的工程机想在 Linux 下确认有哪些分区可以这样ls /dev/block/by-name/如果是高通平台的工程机你可能会看到 xbl、abl、aop、tz 等一系列 bootloader 分区这些通常是 GPT 分区但它们的镜像源往往就是从 UFS Boot LU 拷出来的。真正直接操作 UFS Boot LU一般要走 SoC 厂商的烧录工具或者 UFS 量产工具。5.2 通过 dd 备份普通 boot 分区对于普通 boot.img 分区root 权限下最直接的方式就是 ddadb root adb shell dd if/dev/block/by-name/boot of/data/local/tmp/boot.img bs4M adb pull /data/local/tmp/boot.img注意这里的 by-name/boot 是不是对应你想要的 boot 分区要看平台的分区表。有些设备叫 boot_a、boot_b对应 A/B 槽位。dd 前最好先看一下分区的实际大小dd 一整块可能比实际镜像大很多。5.3 访问 UFS Boot LU 的工程方法真正读写 UFS Boot LU最简单可靠的方法是进 SoC 的紧急下载模式或量产烧录模式。比如高通平台的 EDL 模式、瑞芯微的 Loader 模式、MTK 的 BROM 模式这类模式下主机会跳过正常启动流程直接跟 UFS 建立底层连接然后通过工具读写包括 Boot LU 在内的所有区域。不同厂商工具的界面和协议差别很大但底层操作都差不多扫描存储设备、识别 Boot LU、选择分区、进行整块读写。如果你是做存储模组验证的开发人员可能还会用 UFS 协议分析仪或 JEDEC 自带的测试工具直接下发命令去读 Boot LU 的 Descriptor确认 Boot Enable 是否打开。这个过程不复杂但需要工具链支持。5.4 备份下来之后怎么验证备份完 Boot LU 或 boot.img 后不要急着存网盘先做一次最基本的完整性验证hexdump -C boot.img | head -n 20boot.img 头部通常有 Android 的 boot image header 魔数比如ANDROID!而 UFS Boot LU 里的早期 bootloader 镜像则是有厂商私有格式的。如果 hexdump 出来全是 0 或乱码很可能备份失败了或者在 dd 之前分区已经被破坏过。更稳妥的做法是记录分区大小、SHA256 校验值跟量产原厂镜像比对。提示量产消费类设备通常有签名校验、防回滚、熔丝等安全机制随意烧写 Boot LU 可能直接变砖。这里讨论的范围仅限于你有权限的开发板、工程机或实验室设备。6. 量产与调试中的常见坑Boot Enable 丢失、启动失败与恢复思路6.1 Boot Enable 被固件覆盖最隐蔽的一个坑是UFS Boot LU 的配置在量产时是正常的但后续做固件升级或系统更新时某些升级脚本会重新写配置描述符把 Boot Enable 清掉或改掉。表现就是设备本身没坏但下一次开机找不到启动介质。这种情况在量产验证阶段最容易发现也最容易排查用工具读一下 Device Descriptor 里的 Boot Enable 字段确认状态就行。6.2 遇到 “default boot device missing” 怎么查网上很多热词比如“acer 笔记本电脑 boot 启动项找不到硬盘怎么办”“default boot device missing or boot failed insert recovery media”这类报错在传统 BIOS/UEFI 平台也常见。如果机器用的是 UFS 存储出现这个报错说明 UEFI 固件没有找到可用的 UFS 启动分区。排查顺序建议是进 BIOS/固件设置确认存储设备是否被识别到。如果 UFS 设备都没枚举出来先查硬件链路时钟、复位、电源。确认固件里的启动顺序是否优先从 UFS 启动。如果之前能启动突然报错优先怀疑 Boot LU 配置被改动或者引导镜像损坏。用厂商工具连接设备读取 Boot LU 状态确认 Boot Enable 和 Boot LUN ID 是否正常。注意这事要一步一步来不要一上来就全盘擦除否则可能把所有引导数据都清掉。6.3 Boot LU 的写保护策略UFS Boot LU 通常建议在量产完成后开启写保护防止运行时被误写。LU 级写保护在 UFS 里有专门的属性配置JEDEC 标准定义得很明确。如果量产时没有设置写保护后面的量产工具、OTA 脚本、用户态程序只要能正确下发命令就有机会误擦 Boot LU造成设备变砖。我自己习惯的做法是把一级引导放在 Boot 1把用于恢复的镜像放在 Boot 2正常启动只从 Boot 1 读Boot 1 做完签名验证后Boot 2 可以作为升级失败时的备用引导。最后给两个 Boot LU 都加上写保护需要更新引导时再临时解除保护更新完重新加锁。6.4 关于 “UFS 有 TRIM 命令吗” 的延伸热词里有个问题是“ufs 有 trim 命令吗”。UFS 标准支持主机通过 SCSI UNMAP 命令对普通 LU 做块回收这就是 UFS 的 TRIM。但 UFS Boot LU 不是普通的数据存储区域它承担的是引导功能通常不支持也无需做 TRIM。如果你把 UNMAP 命令发给 Boot LU轻则命令报错重则破坏引导数据生产测试用例里一般会明确避开 Boot LU 做 UNMAP 操作。这个问题看似跟 boot 无关但恰恰反应了很多人对 Boot LU 的特殊性认识不足。6.5 启动失败恢复的一套兜底流程如果你的设备已经因为 Boot LU 问题变砖恢复思路不外乎几种进入 SoC 的强制下载模式重新烧写 Boot LU用支持的烧录器连接 UFS Flash 进行离线烧录或者从备份镜像恢复。前提是你提前做过备份并且手上有正确的原厂镜像。所以我一直强调Boot LU 这种区域量产前一定要留存黄金镜像最好同时保存一份不带签名校验的开发版镜像调试会省很多事。我在实际项目里最深的体会是UFS boot 出问题时现象往往很简单就是开不了机但背后可能横跨硬件、固件、工具链三个领域。多一份备份、多一步验证、多一次示波器测试都比事后猜原因有价值。最后再分享一个小习惯拿到新平台的第一天先把 UFS Boot LU 的 Descriptor 完整 dump 一份留档版本号、Boot Enable 状态、Boot LU 大小都记录下来后面任何一个环节变了都能第一时间发现。