[第2课时 · 7/8] 设备树与启动过程的关系

0. 这一节要解决什么

第 1 课时的启动链讲过:U-Boot 交接给内核的是四个对象——Kernel、DTB、bootargs、(可选 initrd)。DTB 是其中之一。本节把「源码侧的 DTS」与「启动侧的 DTB」接起来:boot 分区里到底放了什么、U-Boot 怎么决定加载哪个 DTB、如何确认板子用的正是期望的版本。

1. DTB 在启动时间线上的位置

boot 分区(extboot.img)        U-Boot                    Kernel
┌───────────────────────┐   ┌───────────────────┐   ┌──────────────────┐
│ Image                 │──>│ 读 extlinux.conf  │──>│ 解析 DTB 建设备    │
│ <board>.dtb           │   │ 加载 Image+DTB    │   │ 从 /chosen 取     │
│ extlinux/extlinux.conf│   │ bootargs→/chosen  │   │ bootargs(1课时) │
└───────────────────────┘   └───────────────────┘   └──────────────────┘

回顾第 1 课时的两条结论,在这里都有了着落:

  • 交接三要素:booti <Image 地址> <initrd 地址> <DTB 地址>——DTB 地址是显式参数
  • bootargs 的传递媒介:U-Boot 把参数写进 DTB 的 /chosen 节点——DTB 同时是硬件描述和参数的载体

2. 本 SDK:extboot 封装与 extlinux 配置

本 SDK 板级配置带 USE_EXTBOOT=y,./build.sh extboot 产出的 kernel/extboot.img(及同内容的 kernel/extboot/ 目录)就是 boot 分区的全部内容:

kernel/extboot/
├── Image                        内核镜像
├── rk3576-firefly-roc-rk3576-pc.dtb
└── extlinux/extlinux.conf       U-Boot 的启动配置

extlinux.conf 是 U-Boot 侧的「启动菜单」:它声明加载哪个内核、哪个 DTB、附加什么 bootargs。U-Boot 启动时读取它,而不是写死在环境变量里——改 DTB 选择,改的就是这个文件。

观念辨析(第 1 课时的延伸):extboot.img 只是封装容器。启动阶段仍是 U-Boot → Kernel,封装不是链路中的一棒——分区存在 ≠ 已被读取,读取 ≠ 已正确加载。

3. 「用哪个 DTB」由谁决定

三层,从上到下:

层 内容 谁控制
源码层 板级入口 defconfig 的 RK_KERNEL_DTS_NAME="rk3576-firefly-roc-rk3576-pc" 编译配置
打包层 extlinux.conf 的内核与 fdt 行 build.sh extboot 生成
运行层 U-Boot 实际读取 boot 分区哪个文件 boot 分区内容 + U-Boot 环境

三层要一致,闭环才算走通——任何一层指向别处,板子用的就不是期望的 DTB。这正是「改了没生效」最常发生的地方。

4. DTB 与板卡不匹配会发生什么

症状 原因
早期 panic / 卡死在 Starting kernel ... 之后 DTB 描述与实际硬件差异过大(如内存、控制台地址)
系统能起,某外设缺失或行为异常 节点缺失或配置不符(如换屏未换对应 dtsi)
一切正常但改动无效 加载的是旧 DTB(部署没到位 / extlinux 指向别处)

5. 双侧验证

U-Boot 侧(打断倒计时进命令行):

ls mmc 0:4 /                    # boot 分区根目录(分区号以 part list 结果为准)
ls mmc 0:4 /extlinux
load mmc 0:4 ${loadaddr} /extlinux/extlinux.conf && cat ${loadaddr}

内核侧(进入系统后):

cat /proc/device-tree/model
tr -d '\0' < /proc/device-tree/compatible
ls /sys/firmware/devicetree/base/      # 与 /proc/device-tree 同源

对齐判断:U-Boot 读到的 extlinux.conf 里 fdt 指向的文件名,应与内核侧 /proc/device-tree/model 反映的 DTS 一致——两侧对上了,才证明「编译的 = 部署的 = 运行的」。

6. Lab:读出 boot 分区的真实内容

# 1) U-Boot 侧
#    打断倒计时后:
ls mmc 0:4 /extlinux
load mmc 0:4 ${loadaddr} /extlinux/extlinux.conf && cat ${loadaddr}
#    记录:fdt 行指向的 dtb 文件名、append 行的 bootargs

# 2) 内核侧(等系统起来后)
cat /proc/device-tree/model
tr -d '\0' < /proc/device-tree/compatible

# 3) 对照:三者的 DTS 名称是否一致?

7. 本节小结

  • DTB 是 U-Boot 交给内核的对象之一;同时承担 /chosen 传参的载体
  • 本 SDK 的 boot 分区 = Image + DTB + extlinux/extlinux.conf,由 ./build.sh extboot 打包
  • 「用哪个 DTB」有三层:编译配置 → extlinux.conf → U-Boot 实际读取,三层一致才生效
  • 验证靠双侧对齐:U-Boot 读到的配置 vs /proc/device-tree 的内容

8. 本节思考

Q1:extlinux.conf 里的 bootargs 和 U-Boot 环境变量 bootargs 是什么关系?

查看答案

extlinux.conf 的 append 行由 extlinux 启动路径解析并写进 DTB 的 /chosen,效果与手工 setenv bootargs 一致;两者是同一对象的两个来源。优先级以实际走的启动路径为准——手工 booti 前设置的 bootargs 直接生效,extlinux 自动启动则用 append 行。排障时用 printenv bootargs 与 extlinux.conf 互相印证。

Q2:为什么 /proc/device-tree 能反映「板子实际在用哪个 DTB」,而看 boot 分区文件不能?

查看答案

boot 分区里可能同时存在多个 dtb / 多份配置,文件在那里不等于被加载。/proc/device-tree 是内核解析传入的那个 DTB 后在内存中的映射,它反映的是事实而非库存。这就是「分区存在 ≠ 已找到 ≠ 已生效」三段式的应用——验证必须看生效侧的证据。

Q3:如果想让同一块板子在两块屏幕之间切换,按本节模型应该动哪一层?

查看答案

动源码层的板级配置:本板的屏幕相关节点位于单独的 dtsi(rk3576-firefly-roc-rk3576-pc-mipi101-....dtsi),Firefly 的做法是不同屏配对应 dts/dtsi 变体,由 RK_KERNEL_DTS_NAME 选定后重新 ./build.sh extboot 并部署。也可以在 DTB 成品上做 overlay(dtbo)热切换,那属于进阶用法,本课先掌握「重新编译完整 DTB」的路径。