本頁討論您可以對 DTO 實作進行的最佳化,描述對覆蓋根節點的限制,並詳細說明如何在 DTBO 映像中配置壓縮覆蓋。它還提供範例實作說明和程式碼。
內核命令列
設備樹中的原始核心命令列位於chosen/bootargs
節點中。引導程式必須將此位置與核心命令列的其他來源連接起來:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
DTO無法連接主 DT 和覆蓋 DT 的值,因此您必須將主 DT 的內核命令列放在chosen/bootargs
中,將覆蓋 DT 的內核命令列放在chosen/bootargs_ext
中。然後引導程式可以連接這些位置並將結果傳遞給核心。
主文件 | 覆蓋.dts |
---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; | /dts-v1/; /plugin/; &chosen { bootargs_ext = "..."; }; |
庫夫特
雖然最新的libfdt
支援 DTO,但建議使用libufdt
來實作 DTO(AOSP 原始碼位於platform/system/libufdt
)。 libufdt
從扁平化設備樹 (FDT) 建立真正的樹結構(非扁平化設備樹,或ufdt ),因此它可以將兩個.dtb
檔案的合併從 O(N 2 ) 提高到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 的範例:
覆蓋計數設定為 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分配要覆蓋標籤的節點,所以不能為根節點指定標籤(因此無法覆蓋根節點)。
SoC廠商必須定義主DT的疊加能力; ODM/OEM 只能附加或覆寫帶有 SoC 供應商定義的標籤的節點。作為解決方法,您可以在基礎 DT 中的根節點下定義一個odm
節點,使覆蓋 DT 中的所有 ODM 節點都可以新增節點。或者,您可以將基礎DT中所有與SoC相關的節點放入根節點下的soc
節點中,如下所述:
主文件 | 覆蓋.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中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' will be 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
,請包含用於資料結構和 API 的libfdt
:#include <libfdt.h> #include <ufdt_overlay.h>
- 載入主設備識別碼和覆蓋設備識別碼。將
.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 格式的合併 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); }