设备树叠加层

设备树覆盖

设备树 (DT)是描述不可发现硬件的命名节点和属性的数据结构。内核(例如 Android 中使用的 Linux 内核)使用 DT 来支持 Android 设备使用的各种硬件配置。硬件供应商提供他们自己的设备树源 (DTS)文件,这些文件使用设备树编译器编译成设备树 Blob (DTB)文件。引导加载程序随后使用这些文件。 DTB 文件包含一个二进制格式的扁平化设备树

设备树覆盖 (DTO)使中央设备树 blob (DTB) 能够覆盖在设备树上。使用 DTO 的引导加载程序可以维护片上系统 (SoC) DT 并动态覆盖特定于设备的 DT,向树中添加节点并更改现有树中的属性。

DTBO用于覆盖的设备树 Blob

Android 9 版本中的更新

在 Android 9 中,引导加载程序在将统一设备树 blob 传递给内核之前不得修改设备树覆盖中定义的属性。

加载设备树

在引导加载程序中加载设备树涉及构建、分区和运行。

图 1.在引导加载程序中加载设备树的典型实现。

  1. 要创建并刷新设备树 blob:

    1a.使用设备树编译器 ( dtc >) 将设备树源代码 ( .dts ) 编译成设备树 blob ( .dtb )。设备树 blob 被格式化为扁平设备树。 1b.将.dtb文件闪存到引导加载程序运行时可访问的位置(详见下文)。

  2. 要分区,请确定闪存中引导加载程序运行时可访问且受信任的位置以放置.dtb 。示例位置:

    引导分区

    图 2.通过附加到image.gz并作为“ kernel ”传递给mkbootimg ,将.dtb放入启动分区。

    唯一分区

    图 3..dtb放在一个唯一的分区中(例如dtb分区)。

  3. 要加载设备树 blob 并启动内核:

    3a. .dtb从存储加载到内存中。 3b.使用加载的 DT 的内存地址启动内核。

实现 DTO

实现 DTO 包括分割设备树、构建、分区和运行。在实现可以正常工作之后,您还必须保持两个 DT 之间的兼容性,并确定用于确保每个 DT 分区安全性的策略。

分割 DT

首先将设备树分割成两 (2) 部分:

  • 主 DT。由 SoC 供应商提供的仅限 SoC 访问的部分和默认配置。
  • 叠加 DT。由原始设计制造商 (ODM)/原始设备制造商 (OEM) 提供的设备专用配置。

分割设备树之后,您必须确保主 DT 和叠加 DT 之间的兼容性,以便通过合并主 DT 和叠加 DT 为设备生成完整的 DT。有关 DTO 格式和规则的详细信息,请参阅 DTO 语法。如需详细了解多个设备树,请参阅多个 DT。

构建主 DT 和叠加 DT

如需构建主 DT,请执行以下操作:

  1. 将主 DT .dts 编译为 .dtb 文件。
  2. 将 .dtb 文件刷写到引导加载程序在运行时可访问的分区(详见下文)。

如需构建叠加 DT,请执行以下操作:

  1. 将叠加 DT .dts 编译为 .dtbo 文件。虽然此文件格式与格式设为扁平化设备树的 .dtb 文件相同,但是用不同的文件扩展名可以将其与主 DT 区分开来。
  2. 将 .dtbo 文件刷写到引导加载程序在运行时可访问的分区(详见下文)。

如需详细了解如何使用 DTC 进行编译并在主机上验证 DTO 结果,请参阅编译和验证。

对 DT 进行分区

在闪存中确定引导加载程序在运行时可访问和可信的位置信息以放入 .dtb 和 .dtbo

主 DT 的示例位置:

  • 启动分区的一部分,已附加到内核 (image.gz)。
  • 单独的 DT blob (.dtb),位于专用分区 (dtb)。

叠加 DT 的示例位置:

专属分区

图 1. 将 .dtbo 放在专属的分区(例如 dtbo 分区)中。

ODM 分区

图 2. 将 .dtbo 放入 odm 分区中(仅在您的引导加载程序能够从 odm 分区的文件系统中加载数据时才这样做)。

注意:叠加 DT 分区的大小取决于设备和主 DT blob 上所需的更改量。通常,8 MB 已足够当前使用并已为未来扩展留出了空间(如果需要的话)。

对于支持无缝 (A/B) 更新的设备,请用 A/B 来标识主 DT 和叠加 DT 分区:

示例 1

图 3. DTBO 分区 A/B,示例 1。

示例 2

图 4. DTBO 分区 A/B,示例 2。

在引导加载程序中运行

如需运行,请执行以下操作:

图 5. 引导加载程序中设备树叠加层的典型运行时实现。

  1. 将 .dtb 从存储空间加载到内存中。
  2. 将 .dtbo 从存储空间加载到内存中。
  3. 用 .dtb 叠加 .dtbo 以形成合并的 DT。
  4. 启动内核(已给定合并 DT 的内存地址)。

保持兼容性

主 DTB(来自 SoC 供应商)会被视为 DTBO 的 API 表面。将设备树分离为 SoC 通用部件和设备专用部件后,您必须确保这两个部件以后相互兼容,包括:

  • 主 DT 中的 DT 定义(例如,节点、属性、标签)。主 DT 中的任何定义更改都可能会触发叠加 DT 中的更改。例如,如需更正主 DT 中的某个节点名称,请定义映射到原始节点名称的“别名”标签(以免更改叠加 DT)。
  • 叠加 DT 的存储位置(例如,分区名称、存储格式)。

确保安全

引导加载程序必须确保 DTB/DTBO 安全无虞、未被修改且未被损坏。您可以使用任何解决方案来保护 DTB/DTBO,例如,VBoot 1.0 中的启动映像签名或 AVB 哈希页脚 (VBoot 2.0)。

  • 如果 DTB/DTBO 位于专属的分区中,您可以将该分区添加到 AVB 的信任链。信任链从硬件保护的信任根开始,并进入引导加载程序,从而验证 DTB/DTBO 分区的完整性和真实性。
  • 如果 DTB/DTBO 位于现有分区(如 odm 分区)中,该分区应位于 AVB 的信任链中。(DTBO 分区可以与 odm 分区共享一个公钥)。

DTO 语法

 

设备树源 (DTS) 格式是设备树的文本表示形式。设备树编译器 (DTC) 可将这种格式处理为二进制设备树,这是 Linux 内核要求的形式。

使用引用

DTC(设备树编译器 + 叠加补丁程序)项目在 dtc-format.txt 和 manual.txt 中说明了 DTS 格式。在 dt-object-internal.txt 中说明了 DTO 格式和规则。这些文档说明了如何使用叠加 DT 中的节点 fragment@x 和语法 __overlay__ 更新主 DT。例如:

/ {fragment@0 {target = <&some_node>;__overlay__ {some_prop = "okay";...};};
};

不过,Google 强烈建议您不要使用 fragment@x 和语法 __overlay__,而应使用引用语法。例如:

&some_node {some_prop = "okay";...
};

dtc 会将引用语法编译成与上述使用语法 __overlay__ 所生成的对象相同的对象。此语法不强制您对片段进行编号,让您能够轻松地读取和写入叠加 DTS。如果您的 dtc 不支持此语法糖,请使用 AOSP 中的 dtc。

使用标签

为了允许对编译时不存在的节点进行未定义的引用,叠加 DT .dts 文件的头文件中必须带有 /plugin/ 标签。例如:

/dts-v1/;
/plugin/;

在这里,您可以使用引用定位要叠加的节点,该引用是以“和”符号 (&) 作为前缀的绝对节点路径。例如,对于主 DT 中的 node@0

在主 DT 中定义标签......然后使用标签。

[my_main_dt.dts]/dts-v1/;/ {my_node: node@0 {status = "disabled";my_child: child@0 {value = <0xffffffff>;};};
};

[my_overlay_dt.dts]/dts-v1/;
/plugin/;&my_node {status = "okay";
};&my_child {value = <0x1>;
};

叠加

如果引用目标属性存在于主 DT 中,则在 DTO 之后被叠加;否则,系统会对其进行附加。例如:

main.dtsoverlay.dts合并结果

[my_main_dt.dts]/dts-v1/;/ {compatible = "corp,foo";my_node: node@0 {status = "disabled";};
};

[my_overlay_dt.dts]/dts-v1/;
/plugin/;&my_node {status = "okay";
};

/dts-v1/;/ {compatible = "corp,foo";...node@0 {linux,phandle = <0x1>;phandle = <0x1>;status = "okay";};
};

附加

如果引用目标属性不存在于主 DT 中,则在 DTO 之后被附加。例如:

main.dtsoverlay.dts合并结果

[my_main_dt.dts]/dts-v1/;/ {compatible = "corp,foo";my_node: node@0 {status = "okay";};
};

[my_overlay_dt.dts]/dts-v1/;
/plugin/;&my_node {new_prop = "bar";
};

/dts-v1/;/ {compatible = "corp,foo";...node@0 {linux,phandle = <0x1>;phandle = <0x1>;status = "okay";new_prop = "bar";};
};

子节点

子节点语法示例:

main.dtsoverlay.dts合并结果

[my_main_dt.dts]/dts-v1/;/ {compatible = "corp,foo";my_nodes: nodes {compatible = "corp,bar";node@0 {status = "disabled";};};
};

[my_overlay_dt.dts]/dts-v1/;
/plugin/;&my_nodes {new_prop1 = "abc";node@0 {status = "okay";new_prop2 = "xyz";};
};

/dts-v1/;/ {compatible = "corp,foo";...nodes {linux,phandle = <0x1>;phandle = <0x1>;compatible = "corp,bar";new_prop1 = "abc";node@0 {linux,phandle = <0x2>;phandle = <0x2>;status = "okay";new_prop2 = "xyz";};};
};

编译和验证

您可以使用设备树编译器 (DTC) 编译设备树源文件。不过,在将叠加 DT 应用到目标主 DT 之前,您还应该通过模拟 DTO 的行为来验证结果。

通过 DTC 进行编译

使用 dtc 编译 .dts 时,您必须添加选项 -@ 以在生成的 .dtbo 中添加 __symbols__ 节点。__symbols__ 节点包含所有带标签节点的列表,DTO 库可使用这个列表作为参考。

构建主 DT .dts 的示例命令:

<span style="color:var(--devsite-code-color)">dtc -@ -O dtb -o my_main_dt.dtb my_main_dt.dts
</span>

构建叠加 DT .dts 的示例命令:

<span style="color:var(--devsite-code-color)">dtc -@ -O dtb -o my_overlay_dt.dtbo my_overlay_dt.dts
</span>

注意:如果遇到 DTC 构建错误 invalid option --'@',则表示您可能需要更新 DTC 版本。在 AOSP 上游,官方 DTC 对 DTO 的支持从版本 1.4.4 开始,而且大部分补丁程序在 2016 年 12 月后就完成了合并。为了支持 DTO,建议您使用 AOSP 中的 external/dtc,它已与最新的 DTC 同步(已视需要合并 DTO 补丁程序)。

在主机上验证 DTO 结果

验证流程可以帮助您确认将叠加 DT 放在主 DT 上时可能出现的错误。更新目标之前,您可以在 .dts 中使用 /include/ 来模拟 DTO 行为,通过这种方式在主机上验证叠加 DT 的结果。

注意/include/ 不支持在叠加 DT 源中使用 __overlay__

图 1. 使用语法 /include/ 模拟主机上的 DTO

  1. 创建叠加层 .dts 的副本。在副本中,移除第一行头文件。示例:
    /dts-v1/;
    /plugin/;
    
    将文件另存为 my_overlay_dt_wo_header.dts(或您想使用的任何文件名)。
  2. 创建主 .dts 的副本。在副本中的最后一行后,为您在第 1 步中创建的文件附加 include 语法。例如:
    /include/ "my_overlay_dt_wo_header.dts"
    
    将文件另存为 my_main_dt_with_include.dts(或您希望的任何文件名)。
  3. 使用 dtc 编译 my_main_dt_with_include.dts 以获得合并的 DT,这应该与使用 DTO 进行编译所得到的结果相同。例如:
    <span style="color:var(--devsite-code-color)">dtc -@ -O dtb -o my_merged_dt.dtb my_main_dt_with_include.dts
    </span>
  4. 使用 dtc 转储 my_merged_dt.dto
    <span style="color:var(--devsite-code-color)">dtc -O dts -o my_merged_dt.dts my_merged_dt.dtb
    </span>

在 Android 9 中验证 DTO

Android 9 需要具有设备树 Blob 叠加层 (DTBO) 分区。要在 SoC DT 中添加节点或更改属性,引导加载程序必须在 SoC DT 之上动态叠加设备专用的 DT。

指示已应用的叠加层

为了保证供应商测试套件 (VTS) 能够评估叠加应用的正确性,供应商必须添加新的内核命令行参数 androidboot.dtbo_idx 来指示从 DTBO 分区中选择的叠加层。在内核版本为 5.10 或更高版本的 Android 12 环境中,此参数会通过 bootconfig 传递。例如,参数 androidboot.dtbo_idx=x,y,z 将 xy 和 z 报告为 DTBO 分区中已由引导加载程序按相同顺序应用于基础设备树 (DT) 的设备树叠加层 (DTO) 的索引,这些索引以零为起点。

叠加层可以应用于主设备树中的节点,也可以添加新节点,但不能引用之前叠加层中添加的节点。这种限制是必要的,因为叠加应用不会将叠加层符号表与主 DT 符号表合并(不合并的做法既可避免符号名称出现冲突,也可避免叠加层之间的依赖关系复杂化)。

示例:无效叠加层

在此示例中,overlay_2.dts 引用了由 overlay_1.dts 添加的节点 e。在将 overlay_1 应用于主 DT 之后,如果尝试将 overlay_2 应用于生成的 DT,叠加应用将会运行失败,并显示基础 DT 的符号表中不存在符号 e 的错误。

main.dtsoverlay_1.dtsoverlay_2.dts
[main.dts]/dts-v1/;/ {a: a {};b: b {};c: c {};
};
[overlay_1.dts]/dts-v1/;
/plugin/;&b { ref1 =  <&a>;e: e {prop = <0x0a>;phandle = <0x04>;};
};
[overlay_2.dts]/dts-v1/;
/plugin/;/* invalid! */
&e {prop = <0x0b>;
};

示例:有效叠加层

在此示例中,overlay_2.dts 仅引用了主 DTS 中的节点 b。将 overlay_1 和 overlay_2 依次应用于基础 DT 之后,节点 e 中属性 prop 的值(由 overlay_1.dts 设置)将被 overlay_2.dts 设置的值覆盖。

main.dtsoverlay_1.dtsoverlay_2.dts
[final.dts]/dts-v1/;/ {a: a {};b: b {};c: c {};
};
[overlay_1.dts]/dts-v1/;
/plugin/;&b { ref1 =  <&a>;e {prop = <0x0c>;};
};
[overlay_2.dts]/dts-v1/;
/plugin/;/* valid */
&b { ref1 =  <&c>;e {prop = <0x0d>;};
};

实现 DTBO 分区

要实现所需的 DTBO 分区,请确保引导加载程序可以执行以下操作:

  1. 识别它正在哪个开发板上运行,并选择要应用的相应叠加层。
  2. 将 androidboot.dtbo_idx 参数附加到内核命令行。
    • 该参数必须指示 DTBO 分区映像中按相同顺序应用于基础 DT 的 DTO 的索引,这些索引以零为起点。
    • 这些索引必须引用叠加层在 DTBO 分区中的位置。

要详细了解 DTBO 分区结构,请访问 source.android.com 上的设备树叠加层。

验证 DTBO 分区

您可以使用 VTS 验证以下内容:

  • 内核命令行参数 androidboot.dtbo_idx 是否存在(方法:检查 Init 是否已自动设置相应的 ro.boot.dtbo_idx 系统属性)。
  • ro.boot.dtbo_idx 系统属性的有效性(方法:检查该属性是否至少指定了一个有效的 DTBO 映像索引)。
  • DTBO 分区的有效性(还应验证 DTBO 分区中应用于基础 DT 的叠加层的有效性)。
  • 生成的 DT 中的其他节点或属性更改是否已呈现给 Linux 内核。

例如,在以下叠加层和最终 DT 中,将 androidboot.dtbo_idx=5,3 添加到内核命令行可通过验证,而将 androidboot.dtbo_idx=3,5 添加到内核命令行不能通过验证。

索引 3 处的叠加层 DT索引 5 处的叠加层 DT
[overlay_1.dts]/dts-v1/;
/plugin/;&c { prop = <0xfe>; };
[overlay_2.dts]/dts-v1/;
/plugin/;&c { prop = <0xff>; };
最终 DT
/dts-v1/;
/ {a {phandle = <0x1>;};b {phandle = <0x2>;};c {phandle = <0x3>;prop = <0xfe>;};__symbols__ {a = "/a";b = "/b";c = "/c";};
};

 

 使用多个 DT

很多 SoC 供应商和 ODM 都支持在一台设备上使用多个 DT,从而使一个映像能够为多个 SKU/配置提供支持。在这种情况下,引导加载程序会在运行时识别硬件,并加载相应的 DT:

图 1. 引导加载程序中的多个设备树叠加层。

注意:使用多个 DT 不是强制性要求。

设置

如需向 DTO 模型添加对多个 DT 的支持,请设置一个主 DT 列表和另一个叠加 DT 列表。

图 2. 多个 DT 的运行时 DTO 实现。

引导加载程序应该能够:

  • 读取 SoC ID 并选择相应的主设备树,并
  • 读取板 ID 并选择相应的叠加设备树。

只能选择一个主 DT 供在运行时使用。可选择多个叠加 DT,但它们必须与选定的主 DT 兼容。使用多个叠加层有助于避免 DTBO 分区内的每块板上都存储一个叠加层,并能让引导加载程序根据板 ID 或通过探测外设来确定所需叠加层的子集。例如,板 A 可能需要通过叠加层 1、3 和 5 添加的设备,而板 B 可能需要通过叠加层 1、4 和 5 添加的设备。

分区

要进行分区,请在闪存中确定引导加载程序在运行时可访问和可信的位置,以存储 DTB 和 DTBO(引导加载程序必须能够在匹配的进程中找到这些文件)。请记住,DTB 和 DTBO 不能存在于同一个分区中。如果您的 DTB/DTBO 位于 dtb/dtbo 分区中,请使用 DTB/DTBO 分区格式中详细列出的表结构和头文件格式。

在引导加载程序中运行

要运行,请执行以下操作:

  1. 标识 SoC 并将相应的 .dtb 从存储空间加载到内存中。
  2. 标识板并将相应的 .dtbo 从存储空间加载到内存中。
  3. 用 .dtbo 叠加 .dtb 形成合并的 DT。
  4. 启动内核(已给定合并 DT 的内存地址)。

DTB/DTBO 分区

如果您的 DTB/DTBO 位于专属的分区(例如 dtb 和 dtbo 分区)中,请使用以下表格结构和头文件格式:

图 1. dtb/dtbo 分区布局示例(如需了解 AVB 签名相关信息,请参阅安全性)。

数据结构

dt_table_header 适用于 dtb/dtbo 分区;您不能在 image.gz 末尾处附加此格式。如果您有一个 DTB/DTBO,则仍必须使用此格式(并且,dt_table_header 中的 dt_entry_count 为 1)。

#define DT_TABLE_MAGIC 0xd7b7ab1estruct dt_table_header {uint32_t magic;             // DT_TABLE_MAGICuint32_t total_size;        // includes dt_table_header + all dt_table_entry// and all dtb/dtbouint32_t header_size;       // sizeof(dt_table_header)uint32_t dt_entry_size;     // sizeof(dt_table_entry)uint32_t dt_entry_count;    // number of dt_table_entryuint32_t dt_entries_offset; // offset to the first dt_table_entry// from head of dt_table_headeruint32_t page_size;         // flash page size we assumeuint32_t version;       // DTBO image version, the current version is 0.// The version will be incremented when the// dt_table_header struct is updated.
};struct dt_table_entry {uint32_t dt_size;uint32_t dt_offset;         // offset from head of dt_table_headeruint32_t id;                // optional, must be zero if unuseduint32_t rev;               // optional, must be zero if unuseduint32_t custom[4];         // optional, must be zero if unused
};

如需读取所有 dt_table_entry,请使用 dt_entry_sizedt_entry_count 和 dt_entries_offset。例如:

my_read(entries_buf,header_addr + header->dt_entries_offset,header->dt_entry_size * header->dt_entry_count);

dt_table_entry 中的 idrevcustom 是设备树的可选硬件标识,引导加载程序可以使用这些标识有效地识别要加载的 DTB/DTBO。如果引导加载程序需要获取更多信息,请将其放在 DTB/DTBO 中,引导加载程序可在这里解析 DTB/DTBO,从而读取这些信息(参见下面的示例代码)。

示例代码

以下示例代码可检查引导加载程序中的硬件标识。

  • check_dtbo() 函数用于检查硬件标识。 首先它会检查结构 dt_table_entry 中的数据(idrev 等)。如果这些数据未能提供充足的信息,它会将 dtb 数据加载到内存中,并检查 dtb 中的值。
  • my_hw_information 和 soc_id 属性的值会在根节点中进行解析(请参见 my_dtbo_1.dts 中的示例)。

    [my_dtbo_1.dts]
    /dts-v1/;
    /plugin/;/ {/* As DTS design, these properties only for loader, won't overlay */compatible = "board_manufacturer,board_model";/* These properties are examples */board_id = <0x00010000>;board_rev = <0x00010001>;another_hw_information = "some_data";soc_id = <0x68000000>;...
    };&device@0 {value = <0x1>;status = "okay";
    };[my_bootloader.c]
    int check_dtbo(const dt_table_entry *entry, uint32_t header_addr) {...if (entry->id != ... || entry->rev != ...) {...}...void * fdt_buf = my_load_dtb(header_addr + entry->dt_offset, entry->dt_size);int root_node_off = fdt_path_offset(fdt_buf, "/");...const char *my_hw_information =(const char *)fdt_getprop(fdt_buf, root_node_off, "my_hw_information", NULL);if (my_hw_information != NULL && strcmp(my_hw_information, ...) != 0) {...}const fdt32_t *soc_id = fdt_getprop(fdt_buf, root_node_off, "soc_id", NULL);if (soc_id != NULL && *soc_id != ...) {...}...
    }
    

mkdtimg

mkdtimg 是用于创建 dtb/dtbo 映像的工具(源代码 位于 AOSP 中的 system/libufdt 下)。mkdtimg 支持多个命令,包括 createcfg_create 和 dump

create

使用 create 命令创建 dtb/dtbo 映像:

mkdtimg create <image_filename> (<global-option>...) \<ftb1_filename> (<entry1_option>...) \<ftb2_filename> (<entry2_option>...) \...

ftbX_filename 会在映像中生成一个 dt_table_entryentryX_option 是分配给 dt_table_entry 的值。这些值可以是以下任一值:

--id=<number|path>
--rev=<number|path>
--custom0=<number|path>
--custom1=<number|path>
--custom2=<number|path>
--custom3=<number|path>

数字值可以是 32 位数字(如 68000)或十六进制数字(如 0x6800)。或者,您也可以使用以下格式指定路径:

<full_node_path>:<property_name>

例如,/board/:idmkdtimg 从 DTB/DTBO 文件中的路径读取值,并将值(32 位)分配给 dt_table_entry 中的相对属性。或者,您也可以将 global_option 作为所有条目的默认选项。dt_table_header 中 page_size 的默认值为 2048;可使用 global_option --page_size=<number> 分配其他值。

例如:

[board1.dts]
/dts-v1/;
/plugin/;/ {compatible = "board_manufacturer,board_model";board_id = <0x00010000>;board_rev = <0x00010001>;another_hw_information = "some_data";...
};&device@0 {value = <0x1>;status = "okay";
};mkdtimg create dtbo.img --id=/:board_id --custom0=0xabc \board1.dtbo \board2.dtbo --id=0x6800 \board3.dtbo --id=0x6801 --custom0=0x123
  • 第一个 dt_table_entry (board1.dtboid 为 0x00010000custom[0] 为 0x00000abc
  • 第二个 id 为 0x00006800custom[0] 为 0x00000abc
  • 第三个 id 为 0x00006801custom[0] 为 0x00000123
  • 所有其他项均使用默认值 (0)。

cfg_create

cfg_create 命令使用以下格式的配置文件创建映像:

# global options<global_option>...
# entries
<ftb1_filename>     # comment<entry1_option>   # comment...
<ftb2_filename><entry2_option>...
...

选项 global_option 和 entryX_option 必须以一个或多个空格字符开头(这些选项与 create 选项相同,不带 -- 前缀)。空行或者以 # 开头的行将被忽略。

例如:

[dtboimg.cfg]
# global optionsid=/:board_idrev=/:board_revcustom0=0xabcboard1.dtboboard2.dtboid=0x6800       # override the value of id in global optionsboard2.dtboid=0x6801       # override the value of id in global optionscustom0=0x123   # override the value of custom0 in global optionsmkdtimg cfg_create dtbo.img dtboimg.cfg

mkdtimg 不会处理 .dtb/.dtbo 文件的对齐方式,而是将它们附加到映像上。使用 dtc 将 .dts 编译为 .dtb/.dtbo 时,必须添加选项 -a。例如,添加选项 -a 4 会增加内边距,使得 .dtb/.dtbo 的大小调整为 4 个字节。

多个 DT 表格条目可以共享一个 .dtb/.dtbo。如果您为不同的条目使用同一个文件名,则系统只会在具有相同 dt_offset 和 dt_size 的映像中存储一份内容。使用具有相同 DT 的不同硬件时,这种方式非常有用。

dump

对于 dtb/dtbo 映像,请使用 dump 命令打印映像中的信息。例如:

mkdtimg dump dtbo.img
dt_table_header:magic = d7b7ab1etotal_size = 1300header_size = 32dt_entry_size = 32dt_entry_count = 3dt_entries_offset = 32page_size = 2048version = 0
dt_table_entry[0]:dt_size = 380dt_offset = 128id = 00010000rev = 00010001custom[0] = 00000abccustom[1] = 00000000custom[2] = 00000000custom[3] = 00000000(FDT)size = 380(FDT)compatible = board_manufacturer,board_model
...

优化 DTO

本页介绍了可以对 DTO 实现进行的优化,描述了针对叠加根节点的限制,并详细介绍了如何在 DTBO 映像中配置经过压缩的叠加层。此外还提供了示例实现说明和代码。

内核命令行

设备树中的原始内核命令行位于 chosen/bootargs 节点中。引导加载程序必须将此位置与内核命令行的其他源位置串联起来:

/dts-v1/;/ {chosen: chosen {bootargs = "...";};
};

DTO 无法串联主 DT 和叠加 DT 的值,因此您必须将主 DT 的内核命令行置入 chosen/bootargs 中,并将叠加 DT 的内核命令行置入 chosen/bootargs_ext 中。然后,引导加载程序才能串联这些位置,并将结果传递到内核。

main.dtsoverlay.dts

/dts-v1/;/ {chosen: chosen {bootargs = "...";};
};

/dts-v1/;
/plugin/;&chosen {bootargs_ext = "...";
};

libufdt

虽然最新的 libfdt 支持 DTO,但建议使用 libufdt 来实现 DTO(AOSP 源代码位于 platform/system/libufdt 下)。libufdt 可通过扁平化设备树 (FDT) 构建真实的树结构(非扁平化设备树,简称“ufdt”),从而改善合并两个 .dtb 文件的效果(从 O(N2) 到 O(N),其中 N 是树中的节点数)。

性能测试

在 Google 的内部测试中,分别在 2,405 个 .dtb 和 283 个 .dtbo DT 节点上使用 libufdt,在编译后分别会生成 70,618 字节和 8,566 字节的文件。从 FreeBSD 移植的 DTO 实现的运行时为 124 毫秒,相比之下 libufdt DTO 的运行时为 10 毫秒,差异非常明显。

在 Pixel 设备的性能测试中,我们比较了 libufdt 和 libfdt。基本节点数量带来的影响相似,但包含以下差异:

  • 500 次叠加(附加或覆盖)操作具有 6-8 倍的时间差异
  • 1,000 次叠加(附加或覆盖)操作具有 8-10 倍的时间差异

附加计数设置为 X 的示例:

图 1. 附加计数为 X

覆盖计数设置为 X 的示例:

图 2. 覆盖计数为 X

libufdt 是使用一些 libfdt API 和数据结构开发的。使用 libufdt 时,必须包含并关联 libfdt(然而,您可以在代码中使用 libfdt API 来操作 DTB 或 DTBO)。

libufdt DTO API

libufdt 中适用于 DTO 的主要 API 如下:

struct fdt_header *ufdt_apply_overlay(struct fdt_header *main_fdt_header,size_t main_fdt_size,void *overlay_fdt,size_t overlay_size);

参数 main_fdt_header 是主 DT,overlay_fdt 是包含 .dtbo 文件内容的缓冲区。返回值是一个包含合并 DT 的新缓冲区(如果出现错误,系统会返回 null)。合并 DT 采用 FDT 格式,您可以在启动内核时将其传递到内核。

返回值的新缓冲区由 dto_malloc() 创建,该缓冲区应在将 libufdt 移植到引导加载程序时实现。有关参考实现,请参阅 sysdeps/libufdt_sysdeps_*.c

根节点限制

您无法将新节点或属性叠加到主 DT 的根节点中,因为叠加操作依赖于标签。由于主 DT 必须定义一个标签,而叠加 DT 则会分配要叠加标签的节点,因此,您无法为根节点提供标签(因而无法叠加根节点)。

SoC 供应商必须定义主 DT 的叠加能力;ODM/OEM 只能使用 SoC 供应商定义的标签附加或覆盖节点。如需解决这个问题,您可以在基础 DT 中的根节点下定义一个 odm 节点,使叠加 DT 中的所有 ODM 节点都能够添加新节点。或者,您也可以将基础 DT 中的所有 SoC 相关节点放在根节点下的 soc 节点中,如下所述:

main.dtsoverlay.dts

/dts-v1/;/ {compatible = "corp,bar";...chosen: chosen {bootargs = "...";};/* nodes for all soc nodes */soc {...soc_device@0: soc_device@0 {compatible = "corp,bar";...};...};odm: odm {/* reserved for overlay by odm */};
};

/dts-v1/;
/plugin/;/ {
};&chosen {bootargs_ex = "...";
};&odm {odm_device@0 {...};...
};

使用经过压缩的叠加层

Android 9 增加了以下支持:在使用第 1 版设备树表格头文件时,在 DTBO 映像中使用经过压缩的叠加层。 使用 DTBO 头文件 v1 时,dt_table_entry 中标记字段的四个最低有效位会指明 DT 条目的压缩格式。

struct dt_table_entry_v1 {uint32_t dt_size;uint32_t dt_offset;  /* offset from head of dt_table_header */uint32_t id;         /* optional, must be zero if unused */uint32_t rev;        /* optional, must be zero if unused */uint32_t flags;      /* For version 1 of dt_table_header, the 4 least significant bitsof 'flags' will be used to indicate the compressionformat of the DT entry as per the enum 'dt_compression_info' */uint32_t custom[3];  /* optional, must be zero if unused */
};

目前,系统支持 zlib 和 gzip 压缩。

enum dt_compression_info {NO_COMPRESSION,ZLIB_COMPRESSION,GZIP_COMPRESSION
};

Android 9 增加了以下支持:在 VtsFirmwareDtboVerification 测试中测试经过压缩的叠加层,以帮助您验证叠加应用的正确性。

DTO 实现示例

以下说明介绍了使用 libufdt 实现 DTO 的示例过程(示例代码如下)。

示例 DTO 说明

  1. 提供库。如需使用 libufdt,请提供 libfdt 以用于数据结构和 API:

    #include <libfdt.h>
    #include <ufdt_overlay.h>
    
  2. 加载主 DT 和叠加 DT。将 .dtb 和 .dtbo 从存储空间加载到内存中(确切的步骤取决于您的设计)。此时,您应该设置 .dtb/.dtbo 的缓冲区和大小:

    main_size = my_load_main_dtb(main_buf, main_buf_size)
    

    overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
    
  3. 叠加 DT:
    1. 使用 ufdt_install_blob() 获取主 DT 的 FDT 头文件:

      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
      
    2. 对 DTO 调用 ufdt_apply_overlay() 以获取采用 FDT 格式的合并 DT:

      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,overlay_buf, overlay_size);
      
    3. 使用 merged_fdt 获取 dtc_totalsize() 的大小:

      merged_fdt_size = dtc_totalsize(merged_fdt);
      
    4. 传递合并的 DT 以启动内核:

      my_kernel_entry(0, machine_type, merged_fdt);
      

示例 DTO 代码

#include <libfdt.h>
#include <ufdt_overlay.h>…{struct fdt_header *main_fdt_header;struct fdt_header *merged_fdt;/* load main dtb into memory and get the size */main_size = my_load_main_dtb(main_buf, main_buf_size);/* load overlay dtb into memory and get the size */overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);/* overlay */main_fdt_header = ufdt_install_blob(main_buf, main_size);main_fdt_size = main_size;merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,overlay_buf, overlay_size);merged_fdt_size = dtc_totalsize(merged_fdt);/* pass to kernel */my_kernel_entry(0, machine_type, merged_fdt);
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/136166.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

合并两个升序链表,合并后也是升序的

开始时也要判断是否有一个链表本来就是空&#xff0c;如果是&#xff0c;直接返回另外一个链表 代码&#xff1a; struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){if(list1NULL){return list2;}if(list2NULL){return list1;} struct ListN…

练习敲代码速度

2023年9月18日&#xff0c;周一晚上 今晚不想学习&#xff0c;但又不想玩游戏&#xff0c;于是找了一些练习敲代码的网站来玩玩&#xff0c;顺便练习一下敲代码的速度 目录 参考资料个人推荐第一个 第二个第三个 参考资料 电脑打字慢&#xff0c;有哪些比较好的练打字软件&a…

子网的划分

强化计算机网络发现王道没有这一块的内容&#xff0c;导致做题稀里糊涂。于是个人调研补充。 子网划分是将一个大型IP网络划分成更小的子网&#xff0c;以实现更有效的网络管理和资源分配。 原因&#xff1a; 提高网络性能&#xff1a;子网划分可以减少广播域的大小&#xff…

在qml中将一个16进制表示的颜色加上透明度

在qml中&#xff0c;我们在指定控件的颜色时&#xff0c;可以直接通过16进制的字符串来表示&#xff0c;比如"#ff0000"; 这种方式也比较符合UI设计人员的使用习惯。 但是假如要在此颜色的基础上&#xff0c;加个透明度的话&#xff0c;就要重新计算一番&#xff0c;比…

Python基础指令(上)

Python基础指令上 常量和表达式变量和类型1. 什么是变量2. 变量的语法2.1 定义变量2.2 使用变量 3. 变量的类型4. 为什么要有这么多类型5. 动态类型特性 注释输入输出1. 程序与用户的交互2. 通过控制台输出3. 通过控制台输入 运算符1. 算术运算符2. 关系运算符3. 逻辑运算符4. …

python虚拟环境(venv)

一、什么是python环境 首先要知道什么是python环境&#xff1f; Python环境主要包括以下内容&#xff1a; 解释器 python.exe (python interpreter&#xff0c;使用的哪个解释看环境配置) Lib目录 标准库 第三方库&#xff1a;site-pakages目录&#xff0c;默认安装第三方…

用无代码搭建数据中台,竟做到如此丝滑

文章目录 需求背景系统介绍配置说明1 菜单导航2 系统自带组件导入页面&#xff08;1&#xff09;数据集成相关组件&#xff08;2&#xff09;数据服务相关组件 3 由系统组件路径添加页面&#xff08;1&#xff09;数据资产管理&#xff08;2&#xff09;数据标准管理&#xff0…

VMware Fusion 13+Ubuntu ARM Server 22.04.3在M2芯片的Mac上共享文件夹

因为Server版没有桌面&#xff0c;VMware Tools不能直接装&#xff0c;导致没办法共享文件。 Ubuntu中的包如果需要更新&#xff0c;先执行下面的步骤 sudo apt update 再执行 sudo apt upgrade 不需要更新的话&#xff0c;直接执行下面的步骤 先把open-vm-tools卸载了 …

typescript 高级类型-class类详解

class 简介 typescript 全面支持es2015中引入的class关键字,并为其添加了类型注解,和其它语法(比如,可见性修饰符等), class 基本使用,如下 tips 1. 根据ts中的类型推论,可以知道Person的实例对象p的类型是Person 2. ts中的class,不仅提供了class的语法功能,也作为一种类型存…

【云计算】虚拟私有云 VPC

虚拟私有云 VPC 1.前言1.1 基本介绍1.2 VPC 的作用1.3 VPC 的适用人群 2.VPC 基本概念2.1 VPC 相关基本概念2.2 其他相关基本概念 3.VPC 通信场景3.1 VPC 内部互通3.2 VPC 间互通3.2.1 对等连接3.2.2 Transit Gateway 或者云联网 3.3 访问 Internet3.3.1 Internet 网关3.3.2 NA…

阿里云服务器价格更新,轻量应用服务器108元,云服务器182.04元起

阿里云服务器价格更新了&#xff0c;不同时期阿里云服务器的租用价格不同&#xff0c;目前阿里云在官网活动中新增加了一款经济型e实例规格的云服务器&#xff0c;现在购买阿里云轻量应用服务器最低为108元&#xff0c;购买云服务器最低为182.04元&#xff0c;换算到每天只要0.…

2023.9.8 基于传输层协议 UDP 和 TCP 编写网络通信程序

目录 UDP 基于 UDP 编写网络通信程序 服务器代码 客户端代码 TCP 基于 TCP 编写网络通信程序 服务器代码 客户端代码 IDEA 打开 支持多客户端模式 UDP 特点&#xff1a; 无连接性&#xff1a;发送端和接收端不需要建立连接也可相互通信&#xff0c;且每个 UDP 数据包都…

自定义实现:头像上传View

看看效果&#xff1a; 非常简单&#xff1a;代码直接贴在下面&#xff0c;有需要的直接带走 /*** 带有自定义字体TextView。*/ class EditAvatarUploadView : AppCompatTextView {lateinit var paint:Paintconstructor(context: Context) : this(context, null){iniPaint()}con…

字符串函数和内存函数详解(1)

&#x1f435;本文将通过函数原型、用法、模拟实现等多个方面全面讲解字符串的库函数 1.strlen&#x1f4da; 1.1函数用法&#x1f4d7; strlen函数用来计算字符串的长度&#xff0c;它会从接收到字符的地址开始读取直到遇到\0&#xff0c;每读取一个非\0的字符长度1&#xff…

Harmony Codelab 样例—弹窗基本使用

一、介绍 本篇 Codelab 主要基于 dialog 和 button 组件&#xff0c;实现弹窗的几种自定义效果&#xff0c;具体效果有&#xff1a; 1. 警告弹窗&#xff0c;点击确认按钮弹窗关闭。 2. 确认弹窗&#xff0c;点击取消按钮或确认按钮&#xff0c;触发对应操作。 3. 加载…

Java基于SpringBoot的在线考试系统的研究与实现(附源码,教程)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 第一章第二章.主要技术第三章第四章 系统设计4.1功能结构4.2 数据库设计4.2.1 数据库E/R图4.2.2 数…

数据变换:数据挖掘的准备工作之一

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

《Python趣味工具》——自制emoji3

今日目标 在上次&#xff0c;我们绘制了静态的emoji图。并且总结了turtle中的常用函数。 本次我们将尝试制作一个动态的emoji&#xff0c;让你的表情包动起来&#xff01; 文章目录 一、动画原理&#xff1a;二、制作动画&#xff1a;1. 修改eyes_black()函数&#xff1a;2. 绘…

软考和PMP哪个含金量更高?

软考中&#xff0c;能和pmp一起来比较的是软考高项&#xff0c;软考高级信息系统项目管理师&#xff0c;和PMP的共同点&#xff0c;基本来说都是项目管理类的证书。本质也都是适用于项目经理岗位的证书&#xff0c;软考高项中大部分考试内容是PMPIT技术两部分&#xff0c;其中项…

【STL容器】list

文章目录 一、list定义二、list的迭代器三、list的元素操作四&#xff0c;list的优缺点 一、list定义 list本质是一个双向带头循环链表 template<class T> struct list_node {list_node* prev;T val;list_node* next; };template<class T> class list {typedef lis…