U-Boot 和 Linux 内核的关系及设备树详解
U-Boot 和 Linux 内核的关系及设备树详解
一、U-Boot 和 Linux 内核的关系
系统启动流程全景图
┌─────────────────────────────────────────────────────┐
│ 嵌入式系统启动流程 │
├─────────────────────────────────────────────────────┤
│ 阶段 1:硬件复位 → BootROM(固化在芯片中) │
│ ↓ │
│ 阶段 2:U-Boot(第一阶段:SPL) │
│ ↓ │
│ 阶段 3:U-Boot(第二阶段:主程序) │
│ ↓ │
│ 阶段 4:Linux 内核(内核初始化) │
│ ↓ │
│ 阶段 5:根文件系统 → 用户空间 │
└─────────────────────────────────────────────────────┘
AI写代码
1
2
3
4
5
6
7
8
9
10
11
12
13
U-Boot 的作用(系统引导程序)
U-Boot(Universal Bootloader) 相当于嵌入式系统的"启动管家",主要负责:
主要功能:
硬件初始化 - CPU、内存、时钟、串口等
加载内核 - 从存储设备(eMMC、SD卡、Flash)读取内核镜像
传递参数 - 通过设备树和命令行参数告诉内核硬件信息
引导启动 - 跳转到内核入口点,移交控制权
类比理解:建筑工地开工
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 毛坯房 │━━━━▶│ 施工队 │━━━━▶│ 精装房 │
│ (硬件) │ │ (U-Boot) │ │ (Linux) │
└─────────┘ └─────────┘ └─────────┘
│ │ │
只有水泥墙 通水通电、 装修完成
和地基 搬运建材 可入住
AI写代码
1
2
3
4
5
6
7
启动过程详细时序
// 简化版的启动过程代码示意
void boot_process(void) {
// 1. 硬件复位(不可控,芯片自动执行)
// 2. U-Boot第一阶段(SPL)
chip_hardware_init(); // 初始化最基础硬件
load_uboot_image(); // 加载U-Boot主程序
jump_to_uboot(); // 跳转到U-Boot
// 3. U-Boot第二阶段
init_all_hardware(); // 初始化所有外设
load_device_tree(); // 加载设备树
load_kernel_image(); // 加载Linux内核
set_boot_args(); // 设置启动参数
jump_to_kernel(0x80008000);// 跳转到内核
// 4. Linux内核接管
// U-Boot的使命结束,生命周期终止
}
AI写代码
c
运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
二、设备树(Device Tree)全面解析
什么是设备树?
设备树是一种描述硬件配置的数据结构,相当于硬件的"身份证"和"说明书"。
设备树的演进历史
2005年前:代码硬编码 → 2010年后:设备树标准
├─ ARM平台:板级文件 ├─ 一个.dts文件描述硬件
├─ 大量arch/arm/mach-*目录 ├─ 内核通用,无需修改
├─ 内核臃肿,移植困难 ├─ 内核精简,易于移植
└─ 每个板子需要内核修改 └─ 只需替换设备树文件
AI写代码
1
2
3
4
5
设备树文件类型
├── 源文件(人类可读可编辑)
│ ├── dts(Device Tree Source) - 具体板子的设备树
│ └── dtsi(Device Tree Source Include)- 公共部分,可被包含
│
├── 中间文件(编译过程生成)
│ └── dtb(Device Tree Blob) - 二进制格式,由dts编译
│
└── 运行时(内存中)
└── FDT(Flattened Device Tree) - dtb加载到内存后的结构
AI写代码
1
2
3
4
5
6
7
8
9
三、U-Boot设备树 vs Linux内核设备树
详细对比表格
特性 U-Boot 设备树 Linux 内核 设备树 说明
主要目的 硬件初始化和配置 内核驱动识别硬件 U-Boot用来"点亮"硬件,内核用来"驱动"硬件
生命周期 启动阶段使用 整个系统运行期使用 U-Boot完成任务后销毁,内核持续使用
修改权限 可修改、可调整 只读参考 U-Boot可动态修改DTB再传给内核
包含内容 基础硬件描述+U-Boot专用节点 完整硬件描述+内核驱动绑定
典型差异 可能包含内存测试节点、引导参数 包含中断控制器、时钟、DMA等复杂外设
文件位置 U-Boot源码:arch/*/dts/*.dts Linux源码:arch/*/boot/dts/*.dts 通常同名但内容有差异
场景示例:SD卡控制器配置
// U-Boot 的 SD 卡设备树片段 (简化)
sdhci: sdhci@fe330000 {
compatible = "snps,dwcmshc-sdhci";
reg = <0x0 0xfe330000 0x0 0x10000>;
clocks = <&cru SCLK_SDMMC>;
clock-names = "core";
u-boot,dm-spl; // ← U-Boot专用属性
status = "okay";
};
// Linux 内核的 SD 卡设备树片段
sdhci: sdhci@fe330000 {
compatible = "snps,dwcmshc-sdhci";
reg = <0x0 0xfe330000 0x0 0x10000>;
interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>; // ← 内核需要中断
clocks = <&cru SCLK_SDMMC>, <&cru TMCLK_SDMMC>;
clock-names = "core", "timeout";
resets = <&cru SRST_SDMMC>; // ← 内核需要复位控制
reset-names = "reset";
status = "okay";
};
AI写代码
dts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
四、设备树的传递流程
完整传递过程图示
┌─────────────────────────────────────────────────────────────┐
│ 设备树从源码到内核的完整流程 │
├─────────────┬─────────────┬─────────────┬─────────────┤
│ 阶段1 │ 阶段2 │ 阶段3 │ 阶段4 │
│ 源码准备 │ 编译阶段 │ U-Boot │ 内核使用 │
├─────────────┼─────────────┼─────────────┼─────────────┤
│ 开发板.dts │ dtc编译器 │ 加载DTB │ 解析DTB │
│ + │ ↓ │ 到内存 │ ↓ │
│ SoC.dtsi │ board.dtb │ ↓ │ 创建platform│
│ + │ (二进制) │ 可选:修改 │ 设备 │
│ common.dtsi │ │ 设备树 │ ↓ │
│ │ │ ↓ │ 匹配驱动 │
│ │ │ 传递给内核 │ ↓ │
│ │ │ │ 初始化硬件 │
└─────────────┴─────────────┴─────────────┴─────────────┘
AI写代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
实际启动示例:Raspberry Pi 4
# 编译过程
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs
# 生成: bcm2711-rpi-4-b.dtb
# U-Boot 加载流程
U-Boot> load mmc 0:1 ${kernel_addr_r} Image
U-Boot> load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb
U-Boot> fdt addr ${fdt_addr_r} # 设置设备树地址
U-Boot> fdt resize 8192 # 调整大小(可选)
U-Boot> fdt set /chosen bootargs "console=ttyAMA0" # 修改参数
U-Boot> booti ${kernel_addr_r} - ${fdt_addr_r}
# Linux内核启动日志片段(可以看到设备树解析)
[ 0.000000] OF: fdt: Machine model: Raspberry Pi 4 Model B
[ 0.000000] printk: console [ttyAMA0] enabled
[ 0.123456] mmc0: SDHCI controller on fe340000.mmc [fe340000.mmc]
AI写代码
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
五、常见问题和调试技巧
1. 设备树不匹配的症状
症状 可能原因
──────────────────────────────────────────────────────
内核panic,找不到根文件系统 内存地址配置错误
某个外设不工作 设备树节点缺失或配置错误
内核无法启动,卡在early boot 设备树格式错误或版本不兼容
AI写代码
1
2
3
4
5
2. 调试命令和工具
U-Boot 中的设备树操作:
# 查看设备树
U-Boot> fdt print /soc/mmc@fe330000
# 修改设备树(临时)
U-Boot> fdt set /soc/mmc@fe330000 status "disabled"
# 保存修改后的设备树
U-Boot> fdt save ${fdt_addr_r}
# 检查设备树完整性
U-Boot> fdt checks
AI写代码
bash
1
2
3
4
5
6
7
8
9
10
11
Linux 内核中的设备树查看:
# 查看系统中的设备树
$ ls /proc/device-tree/
# 查看特定设备属性
$ cat /proc/device-tree/soc/mmc@fe330000/compatible
# 使用dtc工具反编译DTB
$ dtc -I dtb -O dts -o output.dts /boot/bcm2711-rpi-4-b.dtb
AI写代码
bash
1
2
3
4
5
6
7
8
3. 设备树覆盖(Device Tree Overlay)
适用于动态修改硬件配置:
原设备树DTB + 叠加overlay.dtbo = 新配置
↓ ↓
基础硬件配置 特定扩展板配置
AI写代码
1
2
3
六、最佳实践和开发建议
设备树编写原则:
复用原则:相同SoC使用同一个.dtsi,具体板子.dts包含它
最小差异:板级设备树只描述与参考设计不同的部分
属性规范:严格按照bindings文档编写属性
版本控制:设备树与内核版本、U-Boot版本匹配
工作流程图解:
U-Boot测试
内核测试
硬件设计完成
编写设备树.dts
编译测试
U-Boot能初始化硬件
内核能识别所有外设
验证通过
产品发布
硬件变更
仅修改.dts文件
重新编译dtb
更新启动介质
无需重新编译内核
总结:核心要点回顾
U-Boot是引导程序,内核是操作系统,两者接力完成启动
设备树是硬件描述文件,避免内核代码硬编码硬件信息
U-Boot和内核各有设备树,前者用于初始化,后者用于驱动
设备树可以传递和修改,U-Boot可调整后再传给内核
设备树使内核通用化,同一内核支持不同硬件只需换设备树
一句话概括:
U-Boot用设备树初始化硬件,然后把"硬件说明书"(设备树)交给Linux内核,内核根据说明书加载驱动、管理硬件。
这种设计实现了硬件描述与内核代码的分离,大大提高了嵌入式系统的可移植性和可维护性,是现代嵌入式Linux系统的标准架构。
————————————————
版权声明:本文为CSDN博主「一个平凡而乐于分享的小比特」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_44647100/article/details/155951738
版权声明:
作者:SE_Gai
链接:https://www.cnesa.cn/10015.html
来源:CNESA
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论