พาร์ติชันสำหรับบูตของผู้ให้บริการ

Android 11 ได้เปิดตัวแนวคิดของ Generic Kernel Image (GKI) หากต้องการเปิดใช้การบูตอุปกรณ์แบบกำหนดเองด้วย GKI อุปกรณ์ Android 11 จะใช้ส่วนหัวของอิมเมจการบูตเวอร์ชัน 3 ได้ ในเวอร์ชัน 3 ระบบจะนำข้อมูลเฉพาะของผู้ให้บริการทั้งหมดออกจากพาร์ติชัน boot และย้ายไปไว้ในพาร์ติชัน vendor_boot ใหม่ อุปกรณ์ ARM64 ที่เปิดใช้ Android 11 ในเคอร์เนล Linux 5.4 ต้องรองรับพาร์ติชัน vendor_boot และรูปแบบพาร์ติชัน boot ที่อัปเดตแล้วจึงจะผ่านการทดสอบด้วย GKI

อุปกรณ์ Android 12 สามารถใช้ส่วนหัวของภาพการบูตเวอร์ชัน 4 ซึ่งรองรับการรวม RAMdisk ของผู้ให้บริการหลายรายการในพาร์ติชัน vendor_boot ระบบจะต่อชิ้นส่วนของ RAM Disk ของผู้ให้บริการหลายรายต่อกันทีละรายการในส่วน RAM Disk ของผู้ให้บริการ ตาราง RAM disk ของผู้ให้บริการใช้เพื่ออธิบายเลย์เอาต์ของส่วน RAM disk ของผู้ให้บริการและข้อมูลเมตาของเศษ RAM disk ของผู้ให้บริการแต่ละราย

โครงสร้างพาร์ติชัน

พาร์ติชันสำหรับบูตของผู้ให้บริการเป็นแบบ A/B กับ A/B เสมือนและได้รับการปกป้องโดย Android Verified Boot

เวอร์ชัน 3

พาร์ติชันประกอบด้วยส่วนหัว, แรดดิสก์ของผู้ให้บริการ และบล็อก Device Tree (DTB)

ส่วน จำนวนหน้า
ส่วนหัวของบูตของผู้ให้บริการ (n หน้า) n = (2112 + page_size - 1) / page_size
แรมดิสก์ของผู้ให้บริการ (o หน้า) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (หน้า p) p = (dtb_size + page_size - 1) / page_size

เวอร์ชัน 4

พาร์ติชันประกอบด้วยส่วนหัว ส่วน RAM disk ของผู้ให้บริการ (ประกอบด้วยเศษ RAM disk ทั้งหมดของผู้ให้บริการที่ต่อเชื่อมกัน) บล็อกต้นไม้อุปกรณ์ (DTB) และตาราง RAM disk ของผู้ให้บริการ

ส่วน จำนวนหน้า
ส่วนหัวของบูตของผู้ให้บริการ (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
ตาราง RAM Disk ของผู้ให้บริการ (หน้า q) q = (vendor_ramdisk_table_size + page_size - 1) / page_size
Bootconfig (หน้า 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 คือขนาดรวมของเศษ RAM Disk ทั้งหมดของผู้ให้บริการ
  • ramdisk_type หมายถึงประเภทของ RAMdisk โดยค่าที่เป็นไปได้มีดังนี้
    • VENDOR_RAMDISK_TYPE_NONE บ่งบอกว่าไม่ได้ระบุค่า
    • VENDOR_RAMDISK_TYPE_PLATFORM แรมดิสก์มีบิตเฉพาะแพลตฟอร์ม บูตโหลดเดอร์ต้องโหลดข้อมูลเหล่านี้ลงในหน่วยความจำเสมอ
    • VENDOR_RAMDISK_TYPE_RECOVERY แรมดิสก์มีทรัพยากรการกู้คืน บูตโหลดเดอร์ต้องโหลดไฟล์เหล่านี้ลงในหน่วยความจำเมื่อบูตเข้าสู่การกู้คืน
    • VENDOR_RAMDISK_TYPE_DLKM แรมดิสก์มีโมดูลเคอร์เนลที่โหลดแบบไดนามิกได้
  • ramdisk_name คือชื่อที่ไม่ซ้ำกันของ RAM disk
  • board_id เป็นเวกเตอร์ของตัวระบุฮาร์ดแวร์ที่ผู้ให้บริการกำหนด

การรองรับ Bootloader

เนื่องจากพาร์ติชันบูตของผู้ให้บริการมีข้อมูล (เช่น ขนาดหน้าของแฟลช เคอร์เนล ที่อยู่การโหลดของ RAMdisk ตัวของ DTB เอง) ที่เคยอยู่ในพาร์ติชันบูต บูตโหลดเดอร์จึงต้องเข้าถึงทั้งพาร์ติชันบูตและพาร์ติชันบูตของผู้ให้บริการเพื่อให้มีข้อมูลเพียงพอที่จะทำการบูตให้เสร็จสมบูรณ์

บูตโหลดเดอร์ต้องโหลด RAM disk ทั่วไปลงในหน่วยความจำทันทีตาม RAM disk ของผู้ให้บริการ (รูปแบบ CPIO, Gzip และ lz4 รองรับการต่อเชื่อมประเภทนี้) อย่าจัดแนวหน้ารูปภาพ RAM ดิสก์ทั่วไปหรือใส่ช่องว่างอื่นๆ ระหว่างรูปภาพดังกล่าวกับส่วนท้ายของ RAM ดิสก์ของผู้ให้บริการในหน่วยความจำ หลังจากระบบแกะไฟล์ที่บีบอัดแล้ว มันจะดึงไฟล์ที่ต่อเชื่อมกันออกมาเป็น initramfs ซึ่งส่งผลให้โครงสร้างไฟล์เป็น RAM disk ทั่วไปที่วางซ้อนกันบนโครงสร้างไฟล์ RAM disk ของผู้ให้บริการ

เนื่องจากระบบจะต่อเชื่อมไฟล์ RAM ทั่วไปและไฟล์ RAM ของผู้ให้บริการเข้าด้วยกัน ไฟล์เหล่านี้จึงต้องอยู่ในรูปแบบเดียวกัน อิมเมจการบูต GKI ใช้ RAM disk ทั่วไปที่บีบอัดด้วย lz4 ดังนั้นอุปกรณ์ที่เป็นไปตามข้อกำหนด GKI ต้องใช้ RAM disk ของผู้ให้บริการที่บีบอัดด้วย lz4 การกําหนดค่าสําหรับการดำเนินการนี้จะแสดงอยู่ด้านล่าง

โปรดดูคำอธิบายข้อกำหนดของ Bootloader ที่รองรับ Bootconfig ในส่วนใช้งาน Bootconfig

แรมดิสก์ของผู้ให้บริการหลายราย (เวอร์ชัน 4)

เมื่อใช้ส่วนหัวของอิมเมจการบูตเวอร์ชัน 4 บูตโหลดเดอร์จะเลือกชุดย่อยหรือแรมดิสก์ทั้งหมดของผู้ให้บริการเพื่อโหลดเป็น initramfs ในระหว่างการบูตได้ ตาราง RAM disk ของผู้ให้บริการมีข้อมูลเมตาของ RAM disk แต่ละรายการ และสามารถช่วยบูตโหลดเดอร์ในการตัดสินใจว่าจะโหลด RAM disk รายการใด บูตโหลดเดอร์จะเลือกลําดับการโหลด RAM disk ของผู้ให้บริการที่เลือกได้ ตราบใดที่ RAM disk ทั่วไปจะโหลดเป็นลําดับสุดท้าย

ตัวอย่างเช่น บูตโหลดเดอร์อาจไม่โหลด RAM disk ของผู้ให้บริการประเภท VENDOR_RAMDISK_TYPE_RECOVERY ในระหว่างการบูตตามปกติเพื่อประหยัดทรัพยากร ดังนั้นระบบจะโหลดเฉพาะ RAM disk ของผู้ให้บริการประเภท VENDOR_RAMDISK_TYPE_PLATFORM และ VENDOR_RAMDISK_TYPE_DLKM ลงในหน่วยความจำ ในทางกลับกัน ระบบจะโหลด RAM disk ประเภท VENDOR_RAMDISK_TYPE_PLATFORM, VENDOR_RAMDISK_TYPE_RECOVERY และ VENDOR_RAMDISK_TYPE_DLKM ของผู้ให้บริการลงในหน่วยความจำเมื่อบูตเข้าสู่โหมดการกู้คืน

หรือ Bootloader อาจไม่สนใจตาราง RAM Disk ของผู้ให้บริการและโหลดส่วน RAM Disk ทั้งหมดของผู้ให้บริการ ซึ่งมีผลเหมือนกับการโหลดเศษ ramdisk ทั้งหมดของผู้ให้บริการในพาร์ติชัน vendor_boot

สร้างการสนับสนุน

วิธีใช้การรองรับการบูตของผู้ให้บริการสำหรับอุปกรณ์

  • ตั้งค่า BOARD_BOOT_HEADER_VERSION เป็น 3 ขึ้นไป

  • ตั้งค่า BOARD_RAMDISK_USE_LZ4 เป็น true หากอุปกรณ์เป็นไปตามข้อกำหนด GKI หรือใช้ RAMdisk ทั่วไปที่บีบอัดด้วย 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)

วิธีรวม RAM Disk ของผู้ให้บริการหลายรายใน vendor_boot

  • ตั้งค่า BOARD_BOOT_HEADER_VERSION เป็น 4
  • ตั้งค่า BOARD_VENDOR_RAMDISK_FRAGMENTS เป็นรายการชื่อแฟรกเมนต์ของ RAMdisk ของผู้ให้บริการเชิงตรรกะที่จะรวมไว้ใน vendor_boot

  • หากต้องการเพิ่ม RAM disk ของผู้ให้บริการที่สร้างไว้ล่วงหน้า ให้ตั้งค่า BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILT เป็นเส้นทางที่สร้างไว้ล่วงหน้า

  • หากต้องการเพิ่ม RAM disk ของผู้ให้บริการ DLKM ให้ตั้งค่า BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRS เป็นรายการไดเรกทอรีของโมดูลเคอร์เนลที่จะรวมไว้

  • ตั้งค่า BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGS เป็นอาร์กิวเมนต์ mkbootimg เหล่านี้คืออาร์กิวเมนต์ --board_id[0-15] และ --ramdisk_type สำหรับแฟรกเมนต์ RAMDisk ของผู้ให้บริการ สำหรับแรมดิสก์ของผู้ให้บริการ DLKM --ramdisk_type เริ่มต้นจะเป็น DLKM หากไม่ได้ระบุไว้เป็นอย่างอื่น

วิธีสร้างทรัพยากรการกู้คืนเป็นแรมดิสก์ 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 จากนั้นไฟล์ RAM จะมีไฟล์การกู้คืนทั้งหมด ซึ่งเป็นไฟล์ที่ติดตั้งภายใต้$(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 ของผู้ให้บริการ 2 รายการ ไฟล์แรกคือ RAM disk "เริ่มต้น" ซึ่งมีไดเรกทอรี DLKM baz และไฟล์ที่เหลือใน $(TARGET_VENDOR_RAMDISK_OUT) ส่วนไฟล์ที่ 2 คือdlkm_foobar แรดิสก์ซึ่งมีไดเรกทอรี DLKM foo และ bar และ--ramdisk_type มีค่าเริ่มต้นเป็น DLKM