DTO 最佳化

本頁說明您可以對裝置樹狀結構疊加層 (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 (platform/system/libufdt 中的 AOSP 來源)。libufdt 會從扁平化裝置樹狀結構 (FDT) 建立實際的樹狀結構 (未扁平化的裝置樹狀結構或 ufdt),因此可將兩個 .dtb 檔案的合併作業從 O(N2) 改善為 O(N),其中 N 是樹狀結構中的節點數。

效能測試

在 Google 的內部測試中,在 2405 .dtb 和 283 .dtbo DT 節點上使用 libufdt,會導致編譯後的檔案大小為 70,618 和 8,566 位元組。相較於從 FreeBSD (124 毫秒的執行階段) 移植的 DTO 實作libufdtDTO 執行階段為 10 毫秒。

Pixel 裝置效能測試比較 libufdtlibfdt。基底節點數量會產生類似的效果,但有以下差異:

  • 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 格式化,您可以在啟動核心時將其傳遞至核心。

libufdt 移植至系統啟動載入程式時,應實作來自傳回值的新緩衝區。dto_malloc()如需參考實作方式,請參閱 sysdeps/libufdt_sysdeps_*.c

根節點限制

您無法將新節點或屬性重疊至主要 DT 的根節點,因為重疊作業需要依靠標籤。由於主 DT 必須定義標籤,而疊加層 DT 會將節點指派給疊加標籤,因此您無法為根節點提供標籤 (因此無法疊加根節點)。

SoC 供應商必須定義主要 DT 的重疊功能;ODM/OEM 只能附加或覆寫含有 SoC 供應商定義的標籤節點。如要解決這個問題,您可以在基本 DT 的根節點下定義 odm 節點,讓重疊 DT 中的所有 ODM 節點新增節點。或者,您也可以將基礎資料結構中的所有 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 {
        ...
    };
    ...
};

使用壓縮疊加層

使用 DT 資料表標頭第 1 版時,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 */
};

目前支援 zlibgzip 壓縮。

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 現對測試壓縮疊加層的 VtsFirmwareDtboVerification 測試新增支援功能,可協助您驗證疊加層應用程式的正確性。

DTO 實作範例

以下操作說明會逐步引導您使用 libufdt 的 DTO 實作範例 (程式碼範例如下)。

DTO 範例操作說明

  1. 包含程式庫。如要使用 libufdt,請在資料結構和 API 中加入 libfdt
    #include <libfdt.h>
    #include <ufdt_overlay.h>
    
  2. 載入主要 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);
    
  3. 重疊 DT:
    1. 使用 ufdt_install_blob() 取得主 DT 的 FDT 標頭:
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
      
    2. 呼叫 ufdt_apply_overlay() 給 DTO,取得 FDT 格式的合併 DT:
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
      
    3. 使用 merged_fdt 取得 dtc_totalsize() 的大小:
      merged_fdt_size = dtc_totalsize(merged_fdt);
      
    4. 傳遞已合併的 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);
}