设备树覆盖说明

本文档描述了位于 drivers/of/overlay.c 中的内核设备树覆盖功能的实现,是设备树动态解析器说明[1]的配套文档。

覆盖是如何工作的

设备树覆盖的目的是修改内核的实时树,并使修改以反映这些更改的方式影响内核的状态。由于内核主要处理设备,任何导致产生活动设备的新设备节点都应该被创建,而如果设备节点被禁用或完全移除,则受影响的设备应该被注销。

让我们以一个具有以下基础树的 foo 开发板为例

---- foo.dts ---------------------------------------------------------------
    /* FOO platform */
    /dts-v1/;
    / {
            compatible = "corp,foo";

            /* shared resources */
            res: res {
            };

            /* On chip peripherals */
            ocp: ocp {
                    /* peripherals that are always instantiated */
                    peripheral1 { ... };
            };
    };
---- foo.dts ---------------------------------------------------------------

覆盖文件 bar.dtso,

---- bar.dtso - overlay target location by label ---------------------------
    /dts-v1/;
    /plugin/;
    &ocp {
            /* bar peripheral */
            bar {
                    compatible = "corp,bar";
                    ... /* various properties and child nodes */
            };
    };
---- bar.dtso --------------------------------------------------------------

加载(并按[1]中所述解析)后,应该得到 foo+bar.dts

---- foo+bar.dts -----------------------------------------------------------
    /* FOO platform + bar peripheral */
    / {
            compatible = "corp,foo";

            /* shared resources */
            res: res {
            };

            /* On chip peripherals */
            ocp: ocp {
                    /* peripherals that are always instantiated */
                    peripheral1 { ... };

                    /* bar peripheral */
                    bar {
                            compatible = "corp,bar";
                            ... /* various properties and child nodes */
                    };
            };
    };
---- foo+bar.dts -----------------------------------------------------------

作为覆盖的结果,一个新的设备节点(bar)被创建,因此一个 bar 平台设备将被注册;如果加载了匹配的设备驱动程序,该设备将按预期被创建。

如果基础 DT 在编译时没有使用 -@ 选项,那么“&ocp”标签将无法用于将覆盖节点解析到基础 DT 中的正确位置。在这种情况下,可以提供目标路径。首选通过标签语法来定位目标位置,因为无论标签出现在 DT 中的何处,覆盖都可以应用于任何包含该标签的基础 DT。

上面修改为使用目标路径语法的 bar.dtso 示例如下

---- bar.dtso - overlay target location by explicit path -------------------
    /dts-v1/;
    /plugin/;
    &{/ocp} {
            /* bar peripheral */
            bar {
                    compatible = "corp,bar";
                    ... /* various properties and child nodes */
            }
    };
---- bar.dtso --------------------------------------------------------------

内核中的覆盖 API

该 API 非常易于使用。

  1. 调用 of_overlay_fdt_apply() 来创建并应用一个覆盖变更集。返回值是一个错误代码或用于标识此覆盖的 cookie。

  2. 调用 of_overlay_remove() 来移除并清理之前通过调用 of_overlay_fdt_apply() 创建的覆盖变更集。不允许移除被另一个变更集堆叠的覆盖变更集。

最后,如果您需要一次性移除所有覆盖,只需调用 of_overlay_remove_all(),它将按正确的顺序移除每一个覆盖。

可以选择注册在覆盖操作时被调用的通知器。有关详细信息,请参阅 of_overlay_notifier_register/unregister 和 enum of_overlay_notify_action

用于 OF_OVERLAY_PRE_APPLY、OF_OVERLAY_POST_APPLY 或 OF_OVERLAY_PRE_REMOVE 的通知器回调可能会存储指向覆盖中或其内容中的设备树节点的指针,但这些指针在 OF_OVERLAY_POST_REMOVE 的通知器回调结束后不得继续存在。包含覆盖的内存将在 OF_OVERLAY_POST_REMOVE 通知器被调用后被 kfree() 释放。请注意,即使 OF_OVERLAY_POST_REMOVE 的通知器返回错误,该内存也会被 kfree() 释放。

drivers/of/dynamic.c 中的变更集通知器是第二种类型的通知器,可以通过应用或移除覆盖来触发。这些通知器不允许存储指向覆盖中或其内容中的设备树节点的指针。覆盖代码无法防止当包含覆盖的内存因移除覆盖而被释放时,此类指针仍然有效的情况。

任何其他保留指向覆盖节点或数据的指针的代码都被认为是 bug,因为在移除覆盖后,该指针将指向已释放的内存。

覆盖的使用者必须特别注意系统上发生的整体操作,以确保其他内核代码不会保留任何指向覆盖节点或数据的指针。此类指针被无意使用的例子是:在应用覆盖后加载了驱动程序或子系统模块,并且该驱动程序或子系统扫描了整个设备树或其中的大部分内容(包括覆盖节点)。