בדף הזה מוסבר על אופטימיזציות שאפשר לבצע בהטמעה של שכבת העל של עץ המכשירים (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 (זמן ריצה של 124 מילי-שניות), זמן הריצה של DTO הוא 10 מילי-שניות.libufdt
בדיקות הביצועים במכשירי 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).
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 שכבת-העל מקצה את התוויות לצמתים שיוצגו כשכבת-על, אי אפשר לתת תווית לצומת הבסיס (ולכן אי אפשר להציג את צומת הבסיס כשכבת-על).
ספקי 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()כדי לקבל את הנתונים המאוחדים בפורמט 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); }