このページでは、デバイスツリー オーバーレイ(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)を構築するため、2 つの .dtb
ファイルのマージが O(N2) から O(N) に改善されます(N は、ツリー内のノードの数)。
パフォーマンス テスト
Google の内部テストでは、2,405 個の .dtb
DT ノードと 283 個の .dtbo
DT ノードで libufdt
を使用すると、コンパイル後のファイルサイズは 70,618 バイトと 8,566 バイトになります。FreeBSD から移植された DTO の実装(124 ms ランタイム)と比較すると、libufdt
DTO のランタイムは 10 ms です。
libufdt
と libfdt
を比較した Pixel デバイスのパフォーマンス テスト。ベースノードの数による影響は似ていますが、次の違いがあります。
- 500 回のオーバーレイ(追加または上書き)操作では、6 倍から 8 倍の時間差があります
- 1,000 回のオーバーレイ(追加または上書き)操作では、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 形式でフォーマットされ、カーネルの起動時にカーネルに渡すことができます。
戻り値からの新しいバッファは、libufdt
をブートローダーに移行するときに実装する必要のある dto_malloc()
によって作成されます。リファレンス実装については、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 { ... }; ... }; |
圧縮オーバーレイの使用
Android 9 では、バージョン 1 の DT テーブル ヘッダーを使用している場合に、DTBO イメージで圧縮オーバーレイを使用できるようになりました。DTBO header v1 を使用する場合、dt_table_entry 内の flags フィールドの最下位 4 ビットは 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 の実装のサンプル
libufdt
を使用した DTO の実装のサンプルを次に示します(以下のサンプルコードを参照)。
DTO のサンプルの手順
- ライブラリを追加します。
libufdt
を使用するには、libfdt
を追加してデータ構造と API を指定します。#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); }