英语

设备树覆盖说明

本文档描述了位于 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 ---------------------------------------------------------------

当加载时 (并按照 [1] 中描述的方式解析) ,覆盖 bar.dts

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

应产生 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.dts 示例被修改为使用目标路径语法如下

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

内核中的覆盖 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 中的变更集通知器是第二种类型的通知器,可以通过应用或删除覆盖来触发。不允许这些通知器在覆盖或其内容中存储指向设备树节点的指针。覆盖代码不保护在删除覆盖时包含覆盖的内存被释放时仍然处于活动状态的此类指针。

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

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