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

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 ระบบจะต่อกันชิ้นส่วน ramdisk ของผู้ให้บริการหลายรายทีละชิ้น ในส่วน ramdisk ของผู้ให้บริการ ตาราง Vendor Ramdisk ใช้เพื่ออธิบายเลย์เอาต์ของส่วน Vendor Ramdisk และข้อมูลเมตาของแต่ละ ส่วน Vendor Ramdisk

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

พาร์ติชันการบูตของผู้ขายเป็นแบบ A/B ที่มี A/B เสมือนและได้รับการปกป้องโดยการบูตที่ยืนยันแล้วของ Android

เวอร์ชัน 3

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

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

เวอร์ชัน 4

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

ส่วน จำนวนหน้า
ส่วนหัวการบูตของผู้ให้บริการ (n หน้า) n = (2128 + page_size - 1) / page_size
Vendor ramdisk fragments (o pages) 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
Bootconfig (หน้า r) r = (bootconfig_size + page_size - 1) / page_size

ส่วนหัวการบูตของผู้ให้บริการ

เนื้อหาของส่วนหัวของพาร์ติชันการบูตของผู้ให้บริการประกอบด้วยข้อมูลเป็นหลัก ซึ่งย้ายมาจาก ส่วนหัวของอิมเมจการบูต นอกจากนี้ ยังมีข้อมูลเกี่ยวกับ Vendor 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 ระบุประเภทของ Ramdisk โดยค่าที่เป็นไปได้มีดังนี้
    • VENDOR_RAMDISK_TYPE_NONE แสดงว่าไม่ได้ระบุค่า
    • VENDOR_RAMDISK_TYPE_PLATFORM ramdisk มีบิตที่เฉพาะเจาะจงของแพลตฟอร์ม Bootloader ต้องโหลดสิ่งเหล่านี้ลงในหน่วยความจำเสมอ
    • VENDOR_RAMDISK_TYPE_RECOVERY แรมดิสก์มีแหล่งข้อมูลการกู้คืน โปรแกรมโหลดต้องโหลดไฟล์เหล่านี้ลงในหน่วยความจำเมื่อบูตเข้าสู่การกู้คืน
    • VENDOR_RAMDISK_TYPE_DLKM แรมดิสก์มีโมดูลเคอร์เนลที่โหลดได้แบบไดนามิก
  • ramdisk_name เป็นชื่อที่ไม่ซ้ำกันของ Ramdisk
  • board_id คือเวกเตอร์ของตัวระบุฮาร์ดแวร์ที่ผู้ให้บริการกำหนด

การรองรับ Bootloader

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

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

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

ข้อกำหนดของ Bootloader สำหรับการรองรับ Bootconfig อธิบายไว้ใน Implement Bootconfig

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

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

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

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

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

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

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

  • ตั้งค่า BOARD_RAMDISK_USE_LZ4 เป็น true หากอุปกรณ์เป็นไปตามข้อกำหนดของ GKI หรือหาก ใช้อย่างอื่นที่ใช้ ramdisk ทั่วไปที่บีบอัดด้วย lz4

  • ตั้งค่า BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE ให้มีขนาดที่เหมาะสมสำหรับ อุปกรณ์ โดยพิจารณาโมดูลเคอร์เนลที่ต้องอยู่ใน Vendor 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 Ramdisk หลายรายการใน vendor_boot ให้ทำดังนี้

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

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

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