本页介绍了可以对设备树叠加层 (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 的内部测试中,分别在 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.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 增加了以下支持:在使用第 1 版 DT 表格头文件时,在 DTBO 映像中使用经过压缩的叠加层。使用 DTBO 头文件 v1 时,dt_table_entry 中标记字段的四个最低有效位会指明 DT 条目的压缩格式。dt_table_entry
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;
- 对 DTO 调用
ufdt_apply_overlay()
以获取采用 FDT 格式的合并 DT:merged_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); }