Mengoptimalkan DTO

Halaman ini membahas pengoptimalan yang dapat Anda lakukan pada implementasi overlay hierarki perangkat (DTO), menjelaskan batasan terhadap overlay node root, dan menjelaskan cara mengonfigurasi overlay terkompresi pada gambar DTBO. Halaman ini juga menyediakan contoh petunjuk dan kode implementasi.

Command line kernel

Command line kernel asli di hierarki perangkat (DT) terletak di node chosen/bootargs. Bootloader harus menyambungkan lokasi ini dengan sumber command line kernel lainnya:

/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};

DTO tidak dapat menggabungkan nilai dari DT utama dan DT overlay, sehingga Anda harus menempatkan command line kernel DT utama di chosen/bootargs dan command line kernel dari DT overlay di chosen/bootargs_ext. Bootloader kemudian dapat menyambungkan lokasi ini dan meneruskan hasilnya ke kernel.

main.dts overlay.dts
/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};
/dts-v1/;
/plugin/;

&chosen {
  bootargs_ext = "...";
};

libufdt

Meskipun libfdt terbaru mendukung DTO, sebaiknya gunakan libufdt untuk mengimplementasikan DTO (sumber AOSP pada platform/system/libufdt). libufdt membangun struktur pohon nyata (hierarki perangkat yang tidak diratakan, atau ufdt) dari hierarki perangkat yang diratakan (FDT), sehingga dapat meningkatkan penggabungan dua file .dtb dari O(N2) ke O(N), dengan N adalah jumlah node.

Pengujian performa

Dalam pengujian internal Google, penggunaan libufdt pada 2.405 node DT .dtb dan 283 .dtbo menghasilkan ukuran file 70.618 dan 8.566 byte setelah kompilasi. Dibandingkan dengan penerapan DTO yang di-port dari FreeBSD (runtime 124 md), runtime DTO libufdt adalah 10 md.

Pengujian performa untuk perangkat Pixel dibandingkan libufdt dan libfdt. Jumlah efek node dasar serupa, tetapi mencakup perbedaan berikut:

  • Operasi overlay 500 (tambahkan atau ganti) memiliki perbedaan waktu 6x hingga 8x
  • Operasi overlay 1000 (tambahkan atau ganti) memiliki perbedaan waktu 8x hingga 10x

Contoh dengan jumlah tambahan yang ditetapkan ke X:

Gambar 1. Jumlah penambahan adalah X.

Contoh dengan jumlah penggantian ditetapkan ke X:

Gambar 2. Jumlah penggantian adalah X.

libufdt dikembangkan dengan beberapa libfdt API dan struktur data. Saat menggunakan libufdt, Anda harus menyertakan dan menautkan libfdt (tetapi, dalam kode Anda, Anda dapat menggunakan libfdt API untuk mengoperasikan DTB atau DTBO).

libufdt DTO API

API utama untuk DTO di libufdt adalah sebagai berikut:

struct fdt_header *ufdt_apply_overlay(
        struct fdt_header *main_fdt_header,
        size_t main_fdt_size,
        void *overlay_fdt,
        size_t overlay_size);

Parameter main_fdt_header adalah DT utama dan overlay_fdt adalah buffer yang berisi isi file .dtbo. Nilai yang ditampilkan adalah buffer baru yang berisi DT gabungan (atau null jika terjadi error). DT yang digabungkan diformat dalam FDT, yang dapat Anda teruskan ke kernel saat memulai kernel.

Buffer baru dari nilai yang ditampilkan dibuat oleh dto_malloc(), yang harus Anda terapkan saat melakukan porting libufdt ke bootloader. Untuk implementasi referensi, lihat sysdeps/libufdt_sysdeps_*.c.

Batasan node root

Anda tidak dapat menempatkan node atau properti baru ke node root DT utama karena operasi overlay bergantung pada label. Karena DT utama harus menentukan label dan DT overlay menetapkan node untuk di-overlay dengan label, Anda tidak dapat memberikan label untuk node root (dan karenanya tidak dapat menempatkan node root).

Vendor SoC harus menentukan kemampuan overlay DT utama; ODM/OEM hanya dapat menambahkan atau mengganti node dengan label yang ditentukan oleh vendor SoC. Sebagai solusinya, Anda dapat menentukan node odm di bawah node root di DT dasar, yang memungkinkan semua node ODM di DT overlay untuk menambahkan node baru. Atau, Anda dapat menempatkan semua node terkait SoC di DT dasar ke dalam node soc di bawah node root seperti yang dijelaskan di bawah:

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 {
        ...
    };
    ...
};

Menggunakan overlay yang dikompresi

Android 9 menambahkan dukungan untuk penggunaan overlay terkompresi pada gambar DTBO saat menggunakan header tabel DT versi 1. Saat menggunakan header DTBO v1, empat bit paling tidak signifikan dari kolom flag di dt_table_entry menunjukkan format kompresi entri 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 */
};

Saat ini, kompresi zlib dan gzip didukung.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 menambahkan dukungan untuk menguji overlay terkompresi ke pengujian VtsFirmwareDtboVerification guna membantu Anda memverifikasi ketepatan aplikasi overlay.

Contoh penerapan DTO

Petunjuk berikut akan memandu Anda melakukan contoh penerapan DTO dengan libufdt (kode contoh di bawah).

Contoh petunjuk DTO

  1. Menyertakan library. Untuk menggunakan libufdt, sertakan libfdt untuk struktur data dan API:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
  2. Muat DT utama dan DT overlay. Muat .dtb dan .dtbo dari penyimpanan ke memori (langkah tepatnya bergantung pada desain Anda). Pada tahap ini, Anda akan memiliki buffer dan ukuran .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. Tumpang-tindih DT:
    1. Gunakan ufdt_install_blob() untuk mendapatkan header FDT untuk DT utama:
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
    2. Panggil ufdt_apply_overlay() ke DTO untuk mendapatkan DT gabungan dalam format FDT:
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
    3. Gunakan merged_fdt untuk mendapatkan ukuran dtc_totalsize():
      merged_fdt_size = dtc_totalsize(merged_fdt);
    4. Teruskan DT yang digabungkan untuk memulai kernel:
      my_kernel_entry(0, machine_type, merged_fdt);

Contoh kode 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);
}