בדף הזה מוסבר איך לבצע אופטימיזציה להטמעה של שכבת-על של עץ המכשירים (DTO), מתוארות הגבלות על שכבת-על של צומת הבסיס ומוסבר איך להגדיר שכבות-על דחוסות בתמונת ה-DTBO. בנוסף, יש בו הוראות להטמעה וקוד לדוגמה.
שורת הפקודה של הליבה
שורת הפקודה המקורית של הליבה בעץ המכשיר (DT) נמצאת בצומת chosen/bootargs
. תוכנת האתחול צריכה לשרשר את המיקום הזה עם מקורות אחרים של שורת הפקודה בליבה (kernel):
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
ל-DTO אין אפשרות לשרשר ערכים מ-DT הראשי ומ-DT שכבת-העל, לכן צריך להוסיף את שורת הפקודה של הליבה של ה-DT הראשי לקובץ chosen/bootargs
ואת שורת הפקודה של הליבה של ה-DT שכבת-העל לקובץ chosen/bootargs_ext
. לאחר מכן, Bootloader יכול לשרשר את המיקומים האלה ולהעביר את התוצאה לליבה.
main.dts | שכבת-על.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
ב-2,405 צמתים של .dtb
וב-283 צמתים של .dtbo
DT מניב קבצים בגודל 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
(עם זאת, בקוד אפשר להשתמש ב-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
מתחת לצומת הרמה הבסיסית (root) ב-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 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
- לכלול ספריות. כדי להשתמש ב-
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);
- מניחים שכבת-על על ה-DTs:
- משתמשים ב-
ufdt_install_blob()
כדי לקבל את כותרת ה-FDT ל-DT הראשי:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- קוראים ל-
ufdt_apply_overlay()
ב-DTO כדי לקבל DT ממוזג בפורמט 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);
- משתמשים ב-
קוד 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); }