بهینه‌سازی 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

While the latest libfdt supports DTO, is it recommended to use libufdt to implement DTO (AOSP source at platform/system/libufdt). libufdt builds a real tree structure (un-flattened device tree, or ufdt) from the flattened device tree (FDT), so it can improve the merging of two .dtb files from O(N2) to O(N), where N is the number of nodes in the tree.

آزمایش عملکرد

در آزمایش داخلی Google، استفاده از libufdt در 2405 .dtb و 283 .dtbo گره DT منجر به اندازه فایل ۷۰٬۶۱۸ و ۸٬۵۶۶ بایت پس‌از کامپایل می‌شود. درمقایسه با پیاده‌سازی DTO منتقل‌شده از FreeBSD (زمان اجرای ۱۲۴ میلی‌ثانیه)، libufdt زمان اجرای DTO‏ ۱۰ میلی‌ثانیه است.

آزمایش عملکرد برای دستگاه‌های Pixel در مقایسه با libufdt و libfdt. تعداد گره‌های پایه اثر مشابه است، اما شامل تفاوت‌های زیر است:

  • ‫۵۰۰ عملیات رونهاد (الحاق یا ملغی کردن) ۶ تا ۸ برابر تفاوت زمانی دارد
  • ‫۱۰۰۰ عملیات هم‌پوشانی (افزودن یا لغو) ۸ تا ۱۰ برابر تفاوت زمانی دارند

مثال با تعداد پیوست تنظیم‌شده روی X:

شکل ۱. تعداد پیوست‌ها X است.

مثال با تعداد لغو تنظیم‌شده روی X:

شکل ۲. تعداد لغو X است.

‫libufdt با برخی‌از داده‌ها و ساختارهای میانای برنامه‌سازی کاربردی libfdt توسعه یافته است. هنگام استفاده از libufdt، باید libfdt را اضافه و پیوند دهید (بااین‌حال، در کدتان می‌توانید از libfdt API برای عملکرد DTB یا DTBO استفاده کنید).

libufdt DTO API

میانای برنامه‌سازی کاربردی اصلی برای DTO در libufdt به‌صورت زیر است:

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 پیوست یا ملغی کنند. به‌عنوان راه‌حل، می‌توانید گره odm را زیر گره ریشه در DT پایه تعریف کنید، تا همه گره‌های ODM در DT هم‌پوشان بتوانند گره‌های جدید اضافه کنند. یا می‌توانید همه گره‌های مربوط به SoC را در DT پایه در گره 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 هنگام استفاده از نسخه ۱ سرصفحه جدول DT، از استفاده از رونهادهای فشرده در تصویر DTBO پشتیبانی می‌کند. هنگام استفاده از DTBO header 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

  1. کتابخانه‌ها را اضافه کنید. برای استفاده از libufdt، 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() برای دریافت سرایند FDT برای DT اصلی استفاده کنید:
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
    2. برای دریافت DT ادغام‌شده در قالب FDT ، با ufdt_apply_overlay() به DTO تماس بگیرید:
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
    3. برای دریافت اندازه dtc_totalsize() از merged_fdt استفاده کنید:
      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);
}