בדף הזה מוסבר על אופטימיזציות שאפשר לבצע בהטמעה של שכבת-על של עץ המכשירים (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 (מקור AOSP בכתובת platform/system/libufdt). libufdt יוצר מבנה עץ אמיתי (עץ מכשירים לא שטוח או ufdt) מעץ מכשירים שטוח (FDT), כך שהוא יכול לשפר את המיזוג של שני קובצי .dtb מ-O(N2) ל-O(N), כאשר N הוא מספר הצמתים בעץ.
בדיקות ביצועים
בבדיקות פנימיות של Google, שימוש ב-libufdt ב-2405
.dtb וב-283 צמתים של .dtbo DT מוביל לגדלי קבצים של 70,618 ו-8,566 בייטים אחרי ההידור. בהשוואה להטמעה של DTO שהועברה מ-FreeBSD (זמן ריצה של 124ms), זמן הריצה של DTO הוא 10ms.libufdt
בדיקות ביצועים במכשירי Pixel בהשוואה ל-libufdt ול-libfdt. ההשפעה של מספר צמתי הבסיס דומה, אבל כוללת את ההבדלים הבאים:
- 500 פעולות של שכבת-על (הוספה או החלפה) – ההבדל בזמן הוא פי 6 עד פי 8
- 1,000 פעולות של שכבת-על (הוספה או החלפה) – ההבדל בזמן הוא פי 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 { ... }; ... }; |
שימוש בשכבות-על דחוסות
ב-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
- כולל ספריות. כדי להשתמש ב-
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:
- כדי לקבל את כותרת ה-FDT של ה-DT הראשי, משתמשים בפקודה
ufdt_install_blob():main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- מתקשרים אל
ufdt_apply_overlay()כדי לקבל את ה-DTO הממוזג בפורמט FDT: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);
- כדי לקבל את כותרת ה-FDT של ה-DT הראשי, משתמשים בפקודה
קוד 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); }