אופטימיזציה של DTO

בדף הזה מפורטות האופטימיזציות שאתם יכולים לבצע בהטמעת שכבת-העל של עץ המכשיר (DTO), הסבר על הגבלות מפני שכבות-על של צומת השורש ופרטים איך להגדיר שכבות-על דחוסות בתמונה של DTBO. הוא גם מספק דוגמה את ההוראות להטמעה ואת הקוד.

שורת הפקודה בליבה (Kernel)

שורת הפקודה המקורית של הליבה בעץ המכשיר (DT) ממוקמת צומת chosen/bootargs. תוכנת האתחול צריכה לשרשר את המחרוזת המיקום שלו עם מקורות אחרים של שורת הפקודה בליבה (kernel):

/dts-v1/;

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

ל-DTO לא ניתן לשרשר ערכים מה-DT הראשי ומשכבת ה-DT, כך צריך להכניס את שורת הפקודה הבסיסית (kernel) של ה-DT הראשי chosen/bootargs ושורת הפקודה בליבה של שכבת-העל DT chosen/bootargs_ext עכשיו תוכנת האתחול יכולה לשרשר את מיקומים ומעבירים את התוצאה לליבה.

Main.dts שכבת-על.dts
/dts-v1/;

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

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

ליבופט

אומנם הגרסה העדכנית ביותר libfdt תומך ב-DTO, האם מומלץ להשתמש ב-libufdt כדי להטמיע DTO (מקור AOSP ב- platform/system/libufdt). libufdt בונה מבנה עץ אמיתי (עץ מכשיר לא שטוח, או ufdt) מעץ המכשיר השטוחה (FDT), כדי שהוא יכול לשפר מיזוג של שני קובצי .dtb מ-O(N2) ל-O(N), כאשר N הוא מספר הצמתים בעץ.

בדיקת ביצועים

בבדיקה הפנימית של Google, באמצעות libufdt בתאריך 2405 צמתי DT של .dtbo ו-.dtb ו-283 מובילים לגודל הקבצים של הקבצים 70,618 ו-8,566 בייטים לאחר הידור. בהשוואה למדד DTO הטמעה נויד מ-FreeBSD (זמן ריצה של 124 אלפיות שנייה), libufdt זמן הריצה של DTO הוא 10 אלפיות השנייה.

בדיקות הביצועים במכשירי Pixel בהשוואה ל-libufdt ול- libfdt. ההשפעה של מספר צמתים הבסיסיים דומה אבל כוללת את ההבדלים הבאים:

  • 500 פעולות של שכבת-על (הוספה או שינוי מברירת המחדל) מקבלות פי 6 עד פי 8 הפרש
  • לפעולות של 1,000 שכבת-על (צירוף או שינוי מברירת המחדל) יש זמן של פי 8 עד פי 10 הפרש

דוגמה שבה הספירה מצורפת מוגדרת ל-X:

איור 1. המספר שמצורף הוא X.

דוגמה עם ערך עקיפה מוגדר ל-X:

איור 2. הספירה לשינוי היא X.

האפליקציה libufdt פותחה עם כמה ממשקי API ונתונים של libfdt מבנים. כשמשתמשים ב-libufdt, צריך לכלול ולקשר libfdt (עם זאת, בקוד שלכם אפשר להשתמש ברכיב libfdt API להפעלת DTB או DTBO).

ממשק API של 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 בשכבת-העל מקצה את הצמתים לשכבת-על של תוויות, לא ניתן להוסיף תווית לצומת הרמה הבסיסית (root) (ולכן לא ניתן ליצור שכבת-על של הרמה הבסיסית (root) ).

ספקי SoC חייבים להגדיר את יכולת היצירה של שכבות-על של השידור הראשי. יצרני ציוד מקורי (ODM)/יצרני ציוד מקורי יכולים (OEM) בלבד להוסיף או לשנות צמתים עם תוויות שהוגדרו על ידי ספק ה-SoC. בתור כפתרון זמני, אפשר להגדיר צומת odm מתחת צומת הרמה הבסיסית (root) ב-DT הבסיסי, הפעלת כל צומתי ה-ODM בשכבת-העל של DT כדי להוסיף צמתים חדשים. לחלופין, אפשר להכניס את כל הצמתים שקשורים ל-SoC ב-DT הבסיסי לתוך צומת soc מתחת לצומת של הרמה הבסיסית (root), כפי שמתואר בהמשך:

Main.dts שכבת-על.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 נוספה תמיכה בשימוש בשכבות-על דחוסות בתמונה של DTBO כשמשתמשים בגרסה 1 של כותרת הטבלה של ה-DT. כשמשתמשים בכותרת DTBO גרסה 1, ארבע הביטים המינימליים ביותר של שדה הדגלים ב-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 למבני נתונים ולממשקי 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. שכבות-על של העברת הנתונים:
    1. משתמשים ב-ufdt_install_blob() כדי לקבל את כותרת ה-FDT ל-DT הראשי:
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
      
    2. אפשר להתקשר אל ufdt_apply_overlay() אל DTO כדי לקבל DT ממוזג ב-FDT פורמט:
      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);
}