3、启动引导程序:U-Boot与鸿蒙启动引导流程,从上电到加载内核

大家好,我是老张。今天我们来聊聊鸿蒙系统启动的第一个关键环节——启动引导程序。说白了,就是从上电那一刻起,到内核开始运行之前,芯片到底在忙活些什么。

很多初学者觉得这步很简单,不就是把内核从闪存拷贝到内存嘛。嗯,如果你真这么想,那在实际项目中可能会踩不少坑。我当年第一次调鸿蒙启动时,就因为U-Boot的配置问题,折腾了整整三天。

3.1 上电后的第一件事:固化代码

芯片一上电,CPU的PC指针会指向一个固定的地址。这个地址里存的是固化在ROM里的代码,我们叫它BootROM。这部分代码是芯片出厂就写死的,你改不了,我也不建议你去改。

BootROM的任务很简单:

  • 初始化最基本的硬件(时钟、内存控制器)
  • 从指定的存储介质(eMMC、NAND Flash、SD卡)读取下一级引导程序
  • 验证签名(如果开启了安全启动)
  • 跳转到引导程序入口

为什么会先做这些?因为这时候内存还没初始化,你连C语言都跑不了,只能用汇编。我刚开始做嵌入式时,总觉得这部分可有可无,直到有一次发现BootROM里有个bug,导致特定批次芯片无法从eMMC启动……嗯,从那以后我再也不敢小看这几十KB的代码了。

3.2 U-Boot:鸿蒙的看门人

U-Boot是目前鸿蒙系统最常用的引导程序。它是个开源项目,支持上百种架构,从ARM到RISC-V都能跑。我个人习惯把U-Boot比作「看门人」——它负责检查系统状态,然后决定要不要放内核进来。

U-Boot的启动流程大致分两个阶段:

阶段 代码位置 主要任务
Stage 1 (SPL) 通常小于32KB 初始化CPU、时钟、DDR控制器,加载完整的U-Boot
Stage 2 (U-Boot proper) 几百KB 初始化外设、文件系统、网络,加载内核

这里有个关键点:SPL(Secondary Program Loader)是必须的。因为完整的U-Boot太大了,BootROM那点缓存根本装不下。SPL就像个瘦身版的U-Boot,只做最核心的事。

核心要点:SPL必须足够小,小到能塞进芯片内部的SRAM里。如果SRAM只有64KB,那SPL编译出来就不能超过60KB。我在项目中遇到过SPL超了2KB,结果死活起不来,最后靠裁剪调试信息才搞定。

3.3 鸿蒙的启动引导流程

鸿蒙在U-Boot的基础上做了一些定制。我们来看看从上电到加载内核的完整链路:

提示:下面的流程图是我用SVG画的,展示了鸿蒙启动引导的核心步骤。你可以把它当作一张地图,对照着看后面的文字说明。

鸿蒙启动引导流程(从上电到加载内核) 1. 芯片上电 2. BootROM执行 3. 加载SPL 4. SPL初始化DDR 5. 加载U-Boot到DDR 6. U-Boot加载鸿蒙内核 固化代码,不可修改 初始化最小硬件 从eMMC/NAND读取 验证签名(安全启动) SPL ≤ 64KB 运行在SRAM中 配置DDR控制器 校准时序参数 U-Boot proper 初始化文件系统 解析bootargs 跳转到内核入口

这张图里,蓝色部分是芯片厂商负责的,橙色是SPL阶段,绿色是U-Boot proper阶段。你想想看,从按下电源键到内核跑起来,中间经过了6个步骤,每一步都可能出问题。

3.4 U-Boot的启动参数与设备树

U-Boot加载内核时,需要告诉内核三样东西:

  1. 启动参数(bootargs):比如控制台设备、根文件系统位置、内存大小
  2. 设备树(DTB):描述硬件拓扑结构,告诉内核有哪些外设、地址怎么映射
  3. 内核镜像(zImage/Image):编译好的内核二进制文件

我见过很多新手直接复制网上的bootargs,结果板子死活起不来。其实每个板子的内存地址、串口编号都不一样,你得对着原理图配。

警告:设备树和内核版本必须匹配。我曾经把Linux 5.10的设备树用在鸿蒙内核上,结果GPIO驱动全部失效,查了两天才发现是设备树里中断控制器兼容字符串对不上。

3.5 实战:优化U-Boot启动速度

在实际产品中,启动速度是个硬指标。用户按了电源键,如果5秒内看不到Logo,可能就觉得这设备坏了。我分享几个优化经验:

  • 裁剪不必要的驱动:U-Boot默认编译会带很多驱动,你只需要保留板子用的那几个。比如你的板子用eMMC启动,那把NAND、SPI Flash的驱动都删掉。
  • 关闭调试输出:U-Boot的串口打印很慢,115200波特率下,每打印一行就要花几毫秒。量产版本建议只打印关键信息。
  • 使用Fastboot模式:如果支持USB下载,可以跳过U-Boot的存储设备扫描,直接从USB加载内核。我在一个平板项目里用这招,启动时间从4.2秒降到了1.8秒。

技巧:在U-Boot的配置菜单里(make menuconfig),找到"Boot delay"选项,把它设为0。这样U-Boot就不会等待用户按键,直接启动内核。省下的那几秒,用户感知很明显。

3.6 避坑指南:我踩过的三个坑

做启动引导这么多年,我总结了几条血泪教训:

  • DDR时序参数别乱改:SPL里初始化DDR的时序参数,是芯片厂商测试过的。我试过为了超频改了CAS延迟,结果10次启动有3次死机。
  • 环境变量别存错位置:U-Boot的环境变量默认存在Flash里。如果你改了环境变量但没保存,下次启动还是旧配置。我曾经在调试时改了bootargs没保存,反复重启都进不了系统,还以为是内核坏了。
  • 签名验证要留后门:安全启动开启后,U-Boot会校验内核签名。但开发阶段建议先关掉,或者留一个串口中断入口。否则你每次改内核都要重新签名,很麻烦。

好了,关于U-Boot和鸿蒙启动引导流程,今天就聊这么多。这部分内容虽然基础,但却是整个系统的基石。你想想看,如果引导程序都跑不起来,后面再好的内核、再牛的驱动都是白搭。