最佳化 DTO

本頁討論您可以對 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 裝置的效能測試比較了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中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 */
};

目前支援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. 載入主設備識別碼和覆蓋設備識別碼。將.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);
}