本頁面將討論如何最佳化裝置樹狀結構疊加 (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 的內部測試中,使用 libufdt
2405 .dtb
和 283 .dtbo
DT 節點時,編譯後的檔案大小分別為 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
中從主要 API 到 DTO 的對應如下:
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 { ... }; ... }; |
使用壓縮的疊加層
使用第 1 版 DT 資料表標頭時,Android 9 支援在 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 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 實作範例
下列操作說明將逐步介紹 DTO 的範例實作方式,並提供 libufdt
的範例程式碼。
DTO 指示範例
- 包含程式庫。如要使用
libufdt
,請加入資料結構和 API 的libfdt
:#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 格式取得合併的 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); }