بهینه سازی DTO ها

این صفحه بهینه‌سازی‌هایی را که می‌توانید برای اجرای DTO خود انجام دهید، توضیح می‌دهد، محدودیت‌هایی را در برابر همپوشانی گره ریشه، و نحوه پیکربندی همپوشانی‌های فشرده در تصویر DTBO را شرح می‌دهد. همچنین نمونه دستورالعمل ها و کدهای پیاده سازی را ارائه می دهد.

خط فرمان هسته

خط فرمان اصلی هسته در درخت دستگاه در گره 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 (منبع AOSP در platform/system/libufdt ) توصیه می شود. libufdt یک ساختار درختی واقعی (درخت دستگاه بدون مسطح یا ufdt ) از درخت دستگاه پهن شده (FDT) می‌سازد، بنابراین می‌تواند ادغام دو فایل .dtb را از O(N 2 ) به O(N) بهبود بخشد. تعداد گره های درخت است.

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

در آزمایش داخلی Google، استفاده از libufdt روی گره‌های 2405 .dtb و 283 .dtbo DT منجر به اندازه‌های فایل 70618 و 8566 بایت پس از کامپایل می‌شود. در مقایسه با اجرای DTO پورت شده از FreeBSD (124 میلی ثانیه زمان اجرا)، زمان اجرا libufdt DTO 10 میلی ثانیه است.

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

  • 500 عملیات همپوشانی (افزودن یا لغو) دارای 6 تا 8 برابر اختلاف زمانی هستند
  • 1000 عملیات همپوشانی (افزودن یا لغو) دارای 8 تا 10 برابر اختلاف زمانی هستند.

مثال با اضافه کردن شمارش به X:

شکل 1. تعداد اضافه شده X است

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

شکل 2. شمارش بازدارنده X است

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

libufdt DTO API

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

استفاده از پوشش های فشرده

اندروید 9 هنگام استفاده از نسخه 1 هدر جدول درختی دستگاه، پشتیبانی از استفاده از پوشش های فشرده در تصویر DTBO را اضافه می کند. هنگام استفاده از DTBO header v1، چهار بیت کم اهمیت فیلد flags در 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' 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 */
};

در حال حاضر، فشرده سازی zlib و gzip پشتیبانی می شود.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

اندروید 9 پشتیبانی از آزمایش همپوشانی های فشرده را به تست VtsFirmwareDtboVerification اضافه می کند تا به شما در تأیید صحت برنامه همپوشانی کمک کند.

نمونه اجرای DTO

دستورالعمل های زیر شما را از طریق اجرای نمونه DTO با libufdt راهنمایی می کند (کد نمونه زیر).

نمونه دستورالعمل های DTO

  1. شامل کتابخانه ها برای استفاده از libufdt ، libfdt برای ساختارهای داده و APIها اضافه کنید:
    #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() برای دریافت هدر 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. از 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);
}