Android的DTBO详解
文章目录
- 1. 核心概念:什么是 DTB/DTBO?
- 2. DTBO 要解决什么问题?(为什么需要它?)
- 3. DTBO 的工作原理:叠加(Overlay)
- 4. 技术细节:DTBO 镜像格式
- 5. 如何创建和操作 DTBO?
- 6. 总结与重要性
今天详细深入地介绍一下 Android 的 DTBO。
1. 核心概念:什么是 DTB/DTBO?
要理解 DTBO,首先要了解它的基础:设备树(Device Tree)。
- 设备树(Device Tree): 是一种描述硬件配置的数据结构。它以一种与平台无关的格式,详细说明了处理器类型、内存大小、总线、外设(如I2C、SPI设备)、中断线、GPIO引脚等硬件信息。它的出现是为了解决内核需要为成千上万种不同的硬件板卡提供支持的冗余代码问题。简单说,它就是一个“硬件配置清单”。
- 设备树二进制文件(DTB, Device Tree Blob): 设备树源文件(
.dts
)被编译后生成的二进制文件。Bootloader 会将它加载到内存中,并传递给 Linux 内核。内核解析这个 DTB 文件来了解当前设备的硬件布局,从而正确地初始化驱动程序。 - 设备树叠加层二进制文件(DTBO, Device Tree Overlay Blob): 这是 Android 引入的概念。它是一个增量或补丁形式的设备树二进制片段。它的目的不是描述整个硬件,而是为了动态地修改主设备树(DTB)的内容。
2. DTBO 要解决什么问题?(为什么需要它?)
在传统的嵌入式Linux中,一个设备对应一个特定的DTB文件。但Android设备,特别是现代手机,面临更复杂的场景:
-
硬件碎片化与通用系统映像(GSI):
- Google 推出了 Project Treble,要求将 Android 操作系统框架与设备特定的底层硬件驱动(Vendor Implementation)分离。
- 为了实现一个通用系统映像(GSI) 能在多个不同硬件的设备上运行,GSI 内核必须是通用的。但这个通用内核的 DTB 不可能包含所有设备的硬件信息。
- 解决方案: GSI 使用一个通用的基础 DTB,而每个特定设备则提供一个 DTBO 文件。在启动时,Bootloader 将基础 DTB 和针对该设备的 DTBO 合并,形成一个完整的、描述本机硬件的设备树。
-
A/B 系统更新(Seamless Updates):
- 许多设备支持 A/B 分区,有两个系统分区(
system_a
,system_b
)以便无缝更新。 - 虽然系统分区可以不同,但
boot
分区(包含内核和DTB)通常是被共享的。如果两个系统槽位需要不同的硬件配置(例如,一个槽位用于正常启动,另一个用于调试或特殊模式),单一的 DTB 就无法满足需求。 - 解决方案: 共享的
boot
分区包含基础 DTB,而每个系统槽位(vendor_a
,vendor_b
)可以有自己的 DTBO 文件。Bootloader 根据当前激活的槽位,选择对应的 DTBO 与基础 DTB 合并。
- 许多设备支持 A/B 分区,有两个系统分区(
-
单一系统映像支持多种硬件变体(SKU):
- 同一款手机型号可能有多种硬件变体(例如,不同地区的版本使用的 NFC 芯片、摄像头传感器或屏幕可能不同)。
- 为每个变体都编译一个独立的
boot.img
会增加维护和测试的复杂度。 - 解决方案: 所有变体共享一个基础的
boot.img
(包含基础 DTB)。每个硬件变体有一个独特的 DTBO 文件。Bootloader 通过读取硬件ID(如PCB版本号),动态选择正确的 DTBO 进行叠加,从而让同一份内核和系统能够适配不同的硬件。
3. DTBO 的工作原理:叠加(Overlay)
DTBO 的核心思想是“叠加”。你可以把它想象成在一张基础地图(基础DTB)上贴上一张透明的修正贴纸(DTBO)。
- 新增节点: DTBO 可以添加一个在基础 DTB 中不存在的全新设备节点。
- 例如: 为基础 DTB 中添加一个新的
nfc@78
I2C 设备节点。
- 例如: 为基础 DTB 中添加一个新的
- 修改属性: DTBO 可以修改基础 DTB 中已有节点的属性值。
- 例如: 修改
memory
节点的size
属性,以适配不同内存大小的版本。 - 例如: 修改
gpio-keys
的中断号,因为不同硬件版本的按键所连接的GPIO引脚可能不同。
- 例如: 修改
- 删除节点/属性(理论上支持,但较少使用): 可以标记基础 DTB 中的某个节点或属性为已删除。
工作流程:
- Bootloader 加载通用的基础 DTB(通常来自
boot.img
或dtb.img
)。 - Bootloader 根据当前的硬件ID或系统槽位,从
dtbo.img
(一个DTBO分区)或vendor
分区中加载对应的 DTBO 文件。 - Bootloader 中的设备树叠加层支持(libufdt 或 fdtoverlay)将 DTBO 应用到基础 DTB 上,合并生成一个最终的、完整的设备树。
- Bootloader 将这个合并后的设备树传递给 Linux 内核。
- 内核解析这个最终的设备树,初始化硬件。
4. 技术细节:DTBO 镜像格式
多个 DTBO 文件通常被打包在一个专用的 dtbo.img
镜像文件中。这个镜像有特定的格式:
- 文件头(dt_table_header): 包含魔数、总DTBO条目数、每个DTBO条目的大小等。
- 条目表(dt_table_entry): 一个数组,每个元素描述一个 DTBO 片段在镜像中的偏移量、大小、以及相关的ID或版本信息。
- DTBO Blobs: 实际的多个 DTBO 二进制数据块。
Bootloader 会解析这个 dtbo.img
的头,然后根据匹配条件(如硬件版本 dtbo.board_id
)找到正确的那个 DTBO 块。
5. 如何创建和操作 DTBO?
-
编写设备树源文件:
- 基础设备树:
base.dts
- 叠加层设备树:
overlay.dts
。它的开头必须有一个/plugin/;
标签,这表明它是一个叠加层片段,而不是一个完整的设备树。/dts-v1/; /plugin/;&i2c_3 { // 这是一个引用,指向基础DTB中的i2c_3节点status = "okay";#address-cells = <1>;#size-cells = <0>;nfc@28 {compatible = "nxp,pn544";reg = <0x28>;interrupt-parent = <&tlmm>;interrupts = <61 0>;enable-gpios = <&tlmm 62 0>;}; };
- 基础设备树:
-
编译:
- 使用设备树编译器(DTC)来编译它们。
# 编译基础 DTB dtc -O dtb -o base.dtb base.dts# 编译叠加层 DTBO dtc -O dtb -o overlay.dtbo overlay.dts
-
打包成 dtbo.img:
- 使用
mkdtboimg.py
(AOSP 中的工具)将多个.dtbo
文件打包。
mkdtboimg.py create dtbo.img \--id=0x1000 overlay_v1.dtbo \--id=0x1001 overlay_v2.dtbo
- 使用
-
在 Bootloader 中配置:
- 设备制造商需要在其 Bootloader(如 U-Boot 或 Little Kernel)中实现叠加逻辑,通常通过
libufdt
库来解析和应用叠加层。
- 设备制造商需要在其 Bootloader(如 U-Boot 或 Little Kernel)中实现叠加逻辑,通常通过
6. 总结与重要性
特性 | 传统单一 DTB | Android DTBO + DTB |
---|---|---|
灵活性 | 低,一机一镜像 | 高,一镜像多机型,支持GSI |
维护性 | 差,每个硬件变更都需新boot.img | 好,仅需更新独立的DTBO文件 |
A/B 系统支持 | 困难 | 天然支持,不同槽位可配不同DTBO |
镜像大小 | 较大(多个boot.img) | 较小(共享基础boot.img) |
DTBO 是 Project Treble 和 Generic System Image (GSI) 能够成功实现的基石技术之一。它通过将硬件配置从核心内核镜像中解耦出来,极大地提升了 Android 生态系统的模块化、可维护性和更新效率,让 OEM 厂商能够更灵活地应对多种硬件配置,也让开发者能够更容易地为设备构建通用的系统。
结束语
Flutter是一个由Google开发的开源UI工具包,它可以让您在不同平台上创建高质量、美观的应用程序,而无需编写大量平台特定的代码。我将学习和深入研究Flutter的方方面面。从基础知识到高级技巧,从UI设计到性能优化,欢饮关注一起讨论学习,共同进入Flutter的精彩世界!