4. Overlay语法详解
设备树 Overlay 的本质,是“把一段增量 DTS 片段在运行时合并到已加载的基设备树上”。它之所以能精确落到某个节点,靠的不是路径字符串的模糊匹配,而是一套由 fragment、target、target-path、__overlay__、__symbols__、__fixups__ 共同构成的“定位—挂载—重定位”机制。理解这些关键字的语义,是写出可编译、可加载、可维护 Overlay 的前提。
一、核心原理:fragment 是容器,target 是锚点
一个 Overlay 源文件(.dts)在结构上由若干 fragment 子节点组成,每个 fragment 代表一次独立的“修改动作”。fragment 内部必须包含两个关键信息:改哪里(target 或 target-path)和改什么(__overlay__)。
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&uart0>;
__overlay__ {
status = "okay";
pinctrl-0 = <&uart0_pins>;
};
};
fragment@1 {
target-path = "/soc/i2c@40012000";
__overlay__ {
clock-frequency = <400000>;
};
};
};
其中 /plugin/ 是 Overlay 的编译标记,告诉 dtc 这是一个插件而非完整设备树。fragment 的编号(@0、@1)只是节点名,不参与语义,但建议保持连续以便阅读。
二、target 与 target-path:两种定位方式
target 使用标签引用(phandle 形式),例如 target = <&uart0>;。它依赖基设备树在编译时导出的标签,最终通过 __symbols__ 解析为绝对路径。优点是路径变化时 Overlay 无需修改,可移植性强。
target-path 使用绝对路径字符串,例如 target-path = "/soc/i2c@40012000";。它不依赖标签,但一旦基设备树节点路径或地址发生变化,Overlay 就会失效。两者对比如下:
| 对比项 | target(标签引用) | target-path(绝对路径) |
|---|---|---|
| 书写形式 | target = <&uart0>; |
target-path = "/soc/uart@..."; |
| 依赖条件 | 基 DTB 含 __symbols__ |
路径必须完全一致 |
| 可移植性 | 高,路径变化不影响 | 低,路径变化即失效 |
| 适用场景 | 常规板级扩展、量产维护 | 调试、临时验证、无标签场景 |
target。只有当基设备树未导出所需标签,或需要临时定位一个没有标签的节点时,才使用 target-path。三、__overlay__:真正被合并的内容
__overlay__ 是 fragment 的“载荷节点”。它下面的所有属性与子节点,会被合并到 target 指向的节点中。合并规则遵循设备树通用语义:
- 同名属性:Overlay 中的值覆盖基树中的值;
- 新属性:直接追加;
- 同名子节点:递归合并;
- 新子节点:追加到目标节点下。
因此 __overlay__ 不是普通节点名,而是 dtc 与内核 Overlay 解析器约定的特殊标记。若把内容直接写在 fragment 下而不放入 __overlay__,编译或加载会失败。
四、__symbols__ 与 __fixups__:标签如何变成路径
基设备树编译时若使用 dtc -@,会生成 __symbols__ 节点,记录“标签 → 绝对路径”的映射。Overlay 编译时,dtc 会把 target = <&uart0> 这类引用转换为 __fixups__ 节点中的占位记录,例如:
__fixups__ {
uart0 = "/fragment@0:target:0";
};
加载 Overlay 时,内核先读取 __fixups__,再查基树的 __symbols__,把标签解析为真实 phandle 或路径,最后完成 fragment 的合并。这条链路可以概括为:
五、典型场景与实操片段
以启用一块扩展板上的 SPI 屏为例,Overlay 需要同时修改 SPI 控制器状态、添加屏幕子节点、并引用 GPIO 标签:
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&spi1>;
__overlay__ {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "vendor,spi-panel";
reg = <0>;
reset-gpios = <&gpio1 12 0>;
dc-gpios = <&gpio1 13 0>;
};
};
};
};
编译与加载命令如下:
dtc -@ -I dts -O dtb -o panel.dtbo panel-overlay.dts
mkdir -p /sys/kernel/config/device-tree/overlays/panel
cat panel.dtbo > /sys/kernel/config/device-tree/overlays/panel/dtbo
cat /sys/kernel/config/device-tree/overlays/panel/status
若加载失败,dmesg 通常会给出 OF: overlay: failed to apply 或 find target node 相关提示,可据此判断是标签缺失还是路径错误。
六、常见误区与复盘
误区一:基 DTB 未加 -@,导致没有 __symbols__,所有 target = <&xxx> 全部解析失败。
误区二:把属性直接写在 fragment 下,忘记包一层 __overlay__,编译虽过但加载无效。
误区三:同时写 target 和 target-path,语义冲突,应二选一。
误区四:用 target-path 时路径写错一个字符,节点定位失败,且错误信息不如标签方式直观。
复盘建议:先确认基 DTB 是否带 __symbols__,再检查 Overlay 是否带 /plugin/,最后核对 fragment 内是否严格使用 __overlay__。这三步能覆盖绝大多数 Overlay 语法类故障。
一句话总结:fragment 定义“改哪一次”,target/target-path 定义“改哪个节点”,__overlay__ 定义“改成什么”,__symbols__ 与 __fixups__ 负责把标签翻译成真实路径。四者配合,Overlay 才能在运行时安全、精确地合并进基设备树。