4. 设备树覆盖(Overlay):概念、语法、编译与动态加载
设备树覆盖(Device Tree Overlay,简称 DTO)是嵌入式 Linux 中实现硬件功能动态组合与热插拔的关键机制。它允许在不重新编译主设备树(Base DTB)的前提下,通过加载一个或多个叠加片段(Overlay DTB)来修改、添加或禁用设备节点。这一特性在 FPGA 动态重配置、扩展板热插拔、模块化 SoC 设计中尤为重要。
核心价值: 将硬件描述与硬件实例化分离。主 DTB 描述 SoC 固定资源,Overlay DTB 描述可插拔或可重配置外设,两者在运行时通过内核的
configfs 接口合并。
4.1 设备树覆盖的概念与工作原理
设备树覆盖的本质是“片段(Fragment)”的合并。每个 Overlay 由一个或多个 fragment@N 节点组成,每个片段包含一个 target(指向主 DTB 中的目标节点)和一个 __overlay__ 子节点(包含要合并的属性或子节点)。内核在加载 Overlay 时,解析这些片段,将 __overlay__ 中的内容合并到 target 指定的节点下。
4.2 Overlay 语法与 DTS 编写规范
Overlay 的 DTS 文件使用 /dts-v1/; 和 /plugin/; 作为文件头,以标识这是一个可叠加的片段。核心语法要素如下:
| 语法元素 | 说明 | 示例 |
|---|---|---|
/plugin/; | 声明文件为 Overlay 插件 | /dts-v1/; /plugin/; |
fragment@N | 每个叠加片段,N 从 0 开始 | fragment@0 { ... }; |
target | 指定合并目标(使用引用标签) | target = &i2c0; |
__overlay__ | 包含要添加/修改的属性或子节点 | __overlay__ { status = "okay"; }; |
target-path | 使用字符串路径替代引用标签 | target-path = "/soc/i2c@7e804000"; |
__fixups__ | 自动生成的符号解析表(编译时) | 由 dtc 编译器自动填充 |
编写要点: Overlay 中引用的标签(如
&i2c0)必须在主 DTB 中已定义。编译器通过 -@ 选项生成 __symbols__ 节点,使 Overlay 能够解析这些外部引用。
4.2.1 完整 Overlay DTS 示例
// temperature-sensor.dts
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = &i2c0;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
temperature@48 {
compatible = "ti,tmp102";
reg = <0x48>;
status = "okay";
};
};
};
fragment@1 {
target = &gpio;
__overlay__ {
alert_pin {
gpios = <&gpio 17 0>;
interrupt-parent = <&gpio>;
interrupts = <17 2>;
};
};
};
};
4.3 编译方法:从 DTS 到 DTO
设备树覆盖的编译使用与主 DTB 相同的 dtc 工具,但需要额外指定 -@ 选项以生成符号表。编译流程如下:
- 编写 Overlay DTS:遵循
/plugin/和fragment语法。 - 编译为 DTO:
dtc -@ -I dts -O dtb -o temperature-sensor.dtbo temperature-sensor.dts - 验证输出:使用
fdtdump temperature-sensor.dtbo查看结构。 - 反编译检查:
dtc -I dtb -O dts temperature-sensor.dtbo确认片段正确。
| 编译选项 | 作用 | 是否必须 |
|---|---|---|
-@ | 生成 __symbols__ 节点,支持外部引用解析 | 是 |
-I dts | 指定输入格式为 DTS 源码 | 是 |
-O dtb | 指定输出格式为 DTB 二进制 | 是 |
-o | 指定输出文件名(通常使用 .dtbo 后缀) | 是 |
-@ -@ | 部分旧版本需要双 -@ 强制生成符号 | 视版本而定 |
4.4 动态加载与卸载
Linux 内核从 4.4 版本开始,通过 configfs 提供标准的 Overlay 动态管理接口。加载路径为 /sys/kernel/config/device-tree/overlays。操作步骤如下:
4.4.1 动态加载流程
- 挂载 configfs(如未自动挂载):
mount -t configfs configfs /sys/kernel/config - 创建 Overlay 目录:
mkdir /sys/kernel/config/device-tree/overlays/my-overlay - 写入 DTB 数据:
cat temperature-sensor.dtbo > /sys/kernel/config/device-tree/overlays/my-overlay/dtbo - 检查状态:
cat /sys/kernel/config/device-tree/overlays/my-overlay/status应返回applied。 - 验证设备:
ls /sys/bus/i2c/devices/或dmesg | tail查看新设备枚举。
4.4.2 动态卸载流程
- 移除 Overlay 目录:
rmdir /sys/kernel/config/device-tree/overlays/my-overlay - 内核自动处理:内核会撤销合并,移除对应设备节点,并触发驱动移除回调。
- 验证卸载:
ls /sys/bus/i2c/devices/确认设备已消失。
注意事项:
- 卸载 Overlay 时,如果对应设备正在被使用(如文件打开、中断处理中),卸载可能失败或导致内核警告。建议先停用相关服务。
- 多次加载同一 Overlay 需要不同的目录名,否则会冲突。
- Overlay 的加载顺序可能影响资源分配,依赖关系需在设计中考虑。
4.5 调试与故障排查
当 Overlay 加载失败时,可以从以下角度排查:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
加载后 status 为 unapplied | target 引用无效或符号解析失败 | 检查主 DTB 是否包含 __symbols__;使用 dtc -@ 重新编译主 DTB |
内核报 duplicate label | Overlay 中定义了与主 DTB 重复的标签 | 避免在 Overlay 中定义全局标签,使用局部引用 |
| 设备节点未出现 | 兼容性字符串不匹配或驱动未加载 | 检查 compatible 值;确认驱动已编译进内核 |
| 卸载时内核崩溃 | 设备有未释放的资源或引用计数不为零 | 使用 lsof 检查占用;确保驱动实现了正确的 remove 回调 |
编译报 label not found | Overlay 引用了主 DTB 中不存在的标签 | 检查主 DTS 中是否定义了该标签;确认编译时使用了正确的 -@ 选项 |
4.6 实践案例:Raspberry Pi 扩展板 Overlay
以 Raspberry Pi 上加载一个 I2C 温度传感器为例,展示完整操作链:
# 1. 编写 Overlay DTS (tmp102-overlay.dts)
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2835";
fragment@0 {
target = &i2c1;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
tmp102@48 {
compatible = "ti,tmp102";
reg = <0x48>;
};
};
};
};
# 2. 编译
dtc -@ -I dts -O dtb -o tmp102.dtbo tmp102-overlay.dts
# 3. 加载
sudo mkdir -p /sys/kernel/config/device-tree/overlays/tmp102
sudo cat tmp102.dtbo > /sys/kernel/config/device-tree/overlays/tmp102/dtbo
# 4. 验证
cat /sys/kernel/config/device-tree/overlays/tmp102/status
# 输出: applied
i2cdetect -y 1
# 应看到 0x48 地址
# 5. 卸载
sudo rmdir /sys/kernel/config/device-tree/overlays/tmp102
关键总结: 设备树覆盖通过
/plugin/ + fragment + target + __overlay__ 四要素实现运行时硬件描述扩展。编译时使用 dtc -@ 生成符号表,运行时通过 configfs 接口动态加载/卸载。掌握 Overlay 机制是调试模块化嵌入式系统、FPGA 动态重配置和热插拔外设的必备技能。