本页讨论了您可以对设备树叠加层 (DTO) 实施进行的优化、描述了针对叠加根节点的限制,并详细介绍了如何在 DTBO 映像中配置压缩叠加层。它还提供了示例实施说明和代码。
内核命令行
设备树 (DT) 中的原始内核命令行位于 chosen/bootargs
节点中。引导加载程序必须将此位置与其他内核命令行源连接起来
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
DTO 无法连接主 DT 和叠加层 DT 中的值,因此您必须将主 DT 的内核命令行放在 chosen/bootargs
中,并将叠加层 DT 的内核命令行放在 chosen/bootargs_ext
中。然后,引导加载程序可以连接这些位置并将结果传递给内核。
main.dts | overlay.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 的内部测试中,在 2405 个 .dtb
和 283 个 .dtbo
DT 节点上使用 libufdt
后,编译后的文件大小分别为 70,618 字节和 8,566 字节。与从 FreeBSD 移植的 DTO 实现(运行时为 124 毫秒)相比,libufdt
DTO 运行时为 10 毫秒。
Pixel 设备的性能测试比较了 libufdt
和 libfdt
。基本节点数量的影响相似,但包括以下差异
- 500 个叠加层(追加或覆盖)操作有 6 倍到 8 倍的时间差异
- 1000 个叠加层(追加或覆盖)操作有 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.dts | overlay.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 增加了对在 DTBO 映像中使用压缩覆盖层的支持,当使用 DT 表头版本 1 时。当使用 DTBO 标头 v1 时,*dt_table_entry* 中 flags 字段的最低有效四位指示 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 bits of 'flags' are used to indicate the compression format 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 示例说明
- 包含库。要使用
libufdt
,请包含libfdt
以获取数据结构和 API#include <libfdt.h> #include <ufdt_overlay.h>
- 加载主 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);
- 覆盖 DT
- 使用
ufdt_install_blob()
获取主 DT 的 FDT 标头main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- 调用
ufdt_apply_overlay()
以 DTO 获取 FDT 格式的合并 DTmerged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size);
- 使用
merged_fdt
获取dtc_totalsize()
的大小merged_fdt_size = dtc_totalsize(merged_fdt);
- 传递合并后的 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); }