מחיצות אתחול של ספקים

ב-Android 11 הושקה רעיון הליבה (kernel) הכללית תמונה (GKI). כדי להפעיל אתחול של מכשיר שרירותי באמצעות GKI, Android 11 מכשירים יכולים להשתמש בגרסה 3 של כותרת תמונת האתחול. לחשבון בגרסה 3, כל המידע הספציפי לספק נלקח מתוך boot מחיצה והועברה למחיצה חדשה של vendor_boot. מכשיר ARM64 ההשקה עם Android 11 בליבה (kernel) של Linux 5.4 חייבת תומכים במחיצה vendor_boot ובפורמט המחיצה המעודכן boot כדי לעבור את הבדיקות ב-GKI.

במכשירי Android 12 יכולים להשתמש בכותרת תמונת הפעלה בגרסה 4, שתומך בהכללת מספר רדיסקים של ספקים בvendor_boot מחיצה. מקטעי ramdisk של ספקים מרובים משורשרים זה אחרי זה בקטע ramdisk של הספק. טבלת ramdisk של ספק משמשת לתיאור הפריסה של החלק ramdisk של הספק והמטא-נתונים של כל ramdisk של ספק מקטע.

מבנה החלוקה

מחיצת האתחול של הספק היא A/B-ed באמצעות A/B וירטואלי ומוגנת על ידי Android הפעלה מאומתת.

גרסה 3

המחיצה מורכבת מכותרת, מ-ramdisk של הספק ומ-blob של עץ המכשיר (DTB).

קטע מספר הדפים
כותרת האתחול של הספק (n דפים) n = (2112 + page_size - 1) / page_size
ramdisk של ספק (דפי O) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (דפי p) p = (dtb_size + page_size - 1) / page_size

גרסה 4

המחיצה מורכבת מכותרת, הקטע ramdisk של הספק (כולל כל מקטעי ה-ramdisk של הספק, משורשרים), את ה-blob של עץ המכשיר (DTB) טבלת ramdisk של ספקים.

קטע מספר הדפים
כותרת האתחול של הספק (n דפים) n = (2128 + page_size - 1) / page_size
קטעי ramdisk של ספק (דפי O) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (דפי p) p = (dtb_size + page_size - 1) / page_size
טבלת ramdisk של ספק (q דפים) q = (vendor_ramdisk_table_size + page_size - 1) / page_size
Butconfig (דפי r) r = (bootconfig_size + page_size - 1) / page_size

כותרת אתחול של הספק

התוכן של כותרת מחיצת האתחול של הספק מורכב בעיקר מנתונים שהועבר לשם הפעלת כותרת תמונה. הוא מכיל גם מידע על ramdisk של הספק.

גרסה 3

struct vendor_boot_img_hdr_v3
{
#define VENDOR_BOOT_MAGIC_SIZE 8
    uint8_t magic[VENDOR_BOOT_MAGIC_SIZE];
    uint32_t header_version;
    uint32_t page_size;           /* flash page size we assume */

    uint32_t kernel_addr;         /* physical load addr */
    uint32_t ramdisk_addr;        /* physical load addr */

    uint32_t vendor_ramdisk_size; /* size in bytes */

#define VENDOR_BOOT_ARGS_SIZE 2048
    uint8_t cmdline[VENDOR_BOOT_ARGS_SIZE];

    uint32_t tags_addr;           /* physical addr for kernel tags */

#define VENDOR_BOOT_NAME_SIZE 16
    uint8_t name[VENDOR_BOOT_NAME_SIZE]; /* asciiz product name */
    uint32_t header_size;         /* size of vendor boot image header in
                                   * bytes */
    uint32_t dtb_size;            /* size of dtb image */
    uint64_t dtb_addr;            /* physical load address */

};

גרסה 4

struct vendor_boot_img_hdr_v4
{
#define VENDOR_BOOT_MAGIC_SIZE 8
    uint8_t magic[VENDOR_BOOT_MAGIC_SIZE];
    uint32_t header_version;
    uint32_t page_size;           /* flash page size we assume */

    uint32_t kernel_addr;         /* physical load addr */
    uint32_t ramdisk_addr;        /* physical load addr */

    uint32_t vendor_ramdisk_size; /* size in bytes */

#define VENDOR_BOOT_ARGS_SIZE 2048
    uint8_t cmdline[VENDOR_BOOT_ARGS_SIZE];

    uint32_t tags_addr;           /* physical addr for kernel tags */

#define VENDOR_BOOT_NAME_SIZE 16
    uint8_t name[VENDOR_BOOT_NAME_SIZE]; /* asciiz product name */
    uint32_t header_size;         /* size of vendor boot image header in
                                   * bytes */
    uint32_t dtb_size;            /* size of dtb image */
    uint64_t dtb_addr;            /* physical load address */

    uint32_t vendor_ramdisk_table_size; /* size in bytes for the vendor ramdisk table */
    uint32_t vendor_ramdisk_table_entry_num; /* number of entries in the vendor ramdisk table */
    uint32_t vendor_ramdisk_table_entry_size; /* size in bytes for a vendor ramdisk table entry */
    uint32_t bootconfig_size; /* size in bytes for the bootconfig section */
};

#define VENDOR_RAMDISK_TYPE_NONE 0
#define VENDOR_RAMDISK_TYPE_PLATFORM 1
#define VENDOR_RAMDISK_TYPE_RECOVERY 2
#define VENDOR_RAMDISK_TYPE_DLKM 3

struct vendor_ramdisk_table_entry_v4
{
    uint32_t ramdisk_size; /* size in bytes for the ramdisk image */
    uint32_t ramdisk_offset; /* offset to the ramdisk image in vendor ramdisk section */
    uint32_t ramdisk_type; /* type of the ramdisk */
#define VENDOR_RAMDISK_NAME_SIZE 32
    uint8_t ramdisk_name[VENDOR_RAMDISK_NAME_SIZE]; /* asciiz ramdisk name */

#define VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE 16
    // Hardware identifiers describing the board, soc or platform which this
    // ramdisk is intended to be loaded on.
    uint32_t board_id[VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE];
};
  • vendor_ramdisk_size הוא הגודל הכולל של כל מקטעי ה-ramdisk של הספק.
  • ramdisk_type מציין את סוג הרדיסק. הערכים האפשריים הם:
    • VENDOR_RAMDISK_TYPE_NONE מציין שהערך לא צוין.
    • קובצי ramdisk של VENDOR_RAMDISK_TYPE_PLATFORM מכילים ביטים ספציפיים לפלטפורמה. תוכנת האתחול חייבת תמיד לטעון אותן לזיכרון.
    • VENDOR_RAMDISK_TYPE_RECOVERY תיקיות ramdisk עם משאבי שחזור. תוכנת האתחול חייבת לטעון אותן לזיכרון במהלך ההפעלה במהלך השחזור.
    • VENDOR_RAMDISK_TYPE_DLKM RAMs מכילים ליבה (kernel) דינמית שניתן לטעון מודולים.
  • ramdisk_name הוא שם ייחודי של הרדיסק.
  • board_id הוא וקטור של מזהי חומרה בהגדרת הספק.

תמיכה בתוכנת אתחול

מכיוון שמחיצת האתחול של הספק מכילה מידע (כגון גודל דף Flash, כתובות ליבה (kernel), לעומסי ramdisk, ה-DTB עצמו) שהיו קיימים בעבר מחיצת האתחול, תוכנת האתחול חייבת לגשת גם לאתחול וגם לאתחול של הספק למחיצות כך שיהיו מספיק נתונים להשלמת האתחול.

תוכנת האתחול חייבת לטעון את ה-ramdisk הגנרי לזיכרון באופן מיידי לאחר ramdisk של הספק (הפורמטים CPIO, Gzip ו-lz4 תומכים בסוג כזה של שרשור מחרוזות). אין ליישר דף לתמונה של ה-Radisk הגנרית או להוסיף רווח אחר ביניהם לבין סוף ה-Radisk של הספק בזיכרון. אחרי מבטל דחיסה של הליבה, הוא מחלץ את הקובץ המשורשר ל-initramfs, שהתוצאה שלו הוא מבנה קובץ שהוא רדיסק גנרי שמוצג כשכבת-על את מבנה קובץ ramdisk של הספק.

מכיוון שה-ramdisk הגנרי ורדיסק הספק משורשרים, הם חייבים להיות אותו פורמט. תמונת האתחול של GKI משתמשת ברדיסק גנרי בדחיסת lz4, תואם ל-GKI חייב להשתמש ב-ramdisk של ספק בדחיסת lz4. בהגדרות האלה מוצגות בהמשך.

הדרישות של תוכנת האתחול לתמיכה בתצורת אתחול מוסברות ב יישום bootconfig.

ramdisk של ספקים מרובים (גרסה 4)

בגרסה 4 של כותרת תמונת האתחול, תוכנת האתחול יכולה לבחור קבוצת משנה או את כל ה-ramdisks של הספקים שייטען כ-initramfs במהלך זמן האתחול. טבלת ramdisk של ספקים מכילה את המטא-נתונים של כל ramdisk, ויכולה לסייע תוכנת אתחול שמחליטה אילו רדיסקים לטעון. תוכנת האתחול יכולה לקבוע כדי לטעון את הרדיסקים של הספק שנבחר, כל עוד ה-Radisk הגנרי נטען אחרונה.

לדוגמה, תוכנת האתחול יכולה להשמיט רדיסקים של ספק טעינה מסוג מסוים VENDOR_RAMDISK_TYPE_RECOVERY במהלך הפעלה רגילה כדי לחסוך במשאבים, לכן רק מערכי נתונים של ספקים מסוג VENDOR_RAMDISK_TYPE_PLATFORM ו VENDOR_RAMDISK_TYPE_DLKM נטענו לזיכרון. לעומת זאת, ספק מקלטים מסוג VENDOR_RAMDISK_TYPE_PLATFORM, VENDOR_RAMDISK_TYPE_RECOVERY וגם VENDOR_RAMDISK_TYPE_DLKM נטענים לזיכרון כשמפעילים את המכשיר לשחזור במצב תצוגה.

לחלופין, תוכנת האתחול יכולה להתעלם מטבלת ה-ramdisk של הספק ולטעון את כל הקטע של ramdisk של הספק. הפעולה הזו משפיעה על הטעינה של כל את מקטעי ה-ramdisk של הספק במחיצה vendor_boot.

בניית תמיכה

כדי להטמיע תמיכה בהפעלה של הספק במכשיר:

  • צריך להגדיר את הערך של BOARD_BOOT_HEADER_VERSION לערך 3 ומעלה.

  • יש להגדיר את BOARD_RAMDISK_USE_LZ4 לערך true אם המכשיר תואם ל-GKI, או אם אחרת הוא משתמש לרדיסק גנרי בדחיסת lz4.

  • הגדרת BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE בגודל שמתאים בהתאם למודולים של הליבה שצריך להפעיל ב-ramdisk של הספק.

  • מעדכנים את AB_OTA_PARTITIONS כך שיכלול את vendor_boot וכל מידע ספציפי לספק. רשימות של מחיצות OTA במכשיר.

  • העתקת המכשיר fstab אל /first_stage_ramdisk בvendor_boot לא את המחיצה boot. לדוגמה, $(LOCAL_PATH)/fstab.hardware:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.$(PRODUCT_PLATFORM).

כדי לכלול רדיסקים של כמה ספקים ב-vendor_boot:

  • מגדירים את BOARD_BOOT_HEADER_VERSION להיות 4.
  • הגדרת BOARD_VENDOR_RAMDISK_FRAGMENTS לרשימה של ramdisk לוגי של ספקים שמות של מקטעים שייכללו ב-vendor_boot.

  • כדי להוסיף ramdisk של ספק מוכן מראש, מגדירים BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILT לתוכן שנוצר מראש נתיב.

  • כדי להוסיף RAMdisk של ספק DLKM, מגדירים BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRS ל- רשימה של ספריות מודולי ליבה (kernel) שצריך לכלול.

  • הגדרה של BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGS לערך mkbootimg ארגומנטים. אלה ה--board_id[0-15] וה--ramdisk_type ארגומנטים למקטע ramdisk של הספק. ב-ramdisk של ספק DLKM, אם לא צוין אחרת, ברירת המחדל של --ramdisk_type תהיה DLKM.

כדי ליצור משאבי שחזור כ-Radisk נפרד ב-recovery ב-vendor_boot:

  • מגדירים את BOARD_BOOT_HEADER_VERSION להיות 4.
  • מגדירים את BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT להיות true.
  • מגדירים את BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT להיות true.
  • הפעולה הזו מוסיפה מקטע ramdisk של ספק שהramdisk_name שלו הוא recovery ו ramdisk_type היא VENDOR_RAMDISK_TYPE_RECOVERY. לאחר מכן ה-ramdisk מכיל את כל קובצי השחזור, שהם קבצים שמותקנים ב: $(TARGET_RECOVERY_ROOT_OUT).

ארגומנטים של mkbootimg

ארגומנט תיאור
--ramdisk_type סוג ה-ramdisk יכול להיות אחד מ-NONE, PLATFORM, RECOVERY או DLKM.
--board_id[0-15] מציינים את הווקטור board_id. ברירת המחדל היא 0.

דוגמה להגדרה:

BOARD_KERNEL_MODULE_DIRS := foo bar baz
BOARD_BOOT_HEADER_VERSION := 4
BOARD_VENDOR_RAMDISK_FRAGMENTS := dlkm_foobar
BOARD_VENDOR_RAMDISK_FRAGMENT.dlkm_foobar.KERNEL_MODULE_DIRS := foo bar
BOARD_VENDOR_RAMDISK_FRAGMENT.dlkm_foobar.MKBOOTIMG_ARGS := --board_id0 0xF00BA5 --board_id1 0xC0FFEE

ה-vendor_boot שמתקבל יכיל שני מקטעים של ramdisk של ספק. הראשון הוא "ברירת המחדל" ramdisk, שמכיל את ספריית DLKM baz וגם שאר הקבצים ב-$(TARGET_VENDOR_RAMDISK_OUT). השנייה היא ramdisk dlkm_foobar, שמכיל את ספריות ה-DLKM foo ו-bar, וגם ברירת המחדל של --ramdisk_type היא DLKM.