בדף הזה מוסבר איך לבצע אופטימיזציה להטמעה של שכבת-על של עץ המכשירים (DTO), מתוארות הגבלות על שכבת-על של צומת הבסיס ומוסבר איך להגדיר שכבות-על דחוסות בתמונת ה-DTBO. בנוסף, יש בו הוראות להטמעה וקוד לדוגמה.
שורת הפקודה של הליבה
שורת הפקודה המקורית של הליבה בעץ המכשיר (DT) נמצאת בצומת chosen/bootargs
. מנהל האתחול צריך לשרשר את המיקום הזה עם מקורות אחרים של שורת הפקודה של הליבה:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
ל-DTO אין אפשרות לשרשר ערכים מ-DT הראשי ומ-DT שכבת-העל, לכן צריך להוסיף את שורת הפקודה של הליבה של ה-DT הראשי לקובץ chosen/bootargs
ואת שורת הפקודה של הליבה של ה-DT שכבת-העל לקובץ chosen/bootargs_ext
. לאחר מכן, Bootloader יכול לשרשר את המיקומים האלה ולהעביר את התוצאה לליבה.
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
ב-2,405 צמתים מסוג .dtb
וב-283 צמתים מסוג .dtbo
DT מניב קבצים בגודל 70,618 ו-8,566 בייטים אחרי הידור. בהשוואה להטמעת DTO שהועברה מ-FreeBSD (זמן ריצה של 124 אלפיות השנייה), זמן הריצה של libufdt
הוא 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 ראשי. יצרני ציוד מקורי (OEM) או יצרני ציוד מקורי בהתאמה אישית (ODM) יכולים רק להוסיף צמתים או לשנות אותם באמצעות תוויות שהוגדרו על ידי ספק ה-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 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); }