Bootpartitionen des Anbieters

Mit Android 11 wurde das Konzept des Generic Kernel Image (GKI) eingeführt. Damit ein beliebiges Gerät mit dem GKI gestartet werden kann, können Android 11-Geräte die Boot-Image-Header-Version 3 verwenden. In Version 3 werden alle anbieterspezifischen Informationen aus der boot-Partition herausgenommen und in eine neue vendor_boot-Partition verschoben. Ein ARM64-Gerät, das mit Android 11 auf dem Linux-Kernel 5.4 eingeführt wird, muss die Partition vendor_boot und das aktualisierte Partitionsformat boot unterstützen, um die Tests mit dem GKI zu bestehen.

Auf Geräten mit Android 12 kann die Boot-Image-Header-Version 4 verwendet werden, die die Einbindung mehrerer Vendor-Ramdisks in der vendor_boot-Partition unterstützt. Mehrere Anbieter-Ramdisk-Fragmente werden im Anbieter-Ramdisk-Abschnitt nacheinander verkettet. Eine Vendor-Ramdisk-Tabelle wird verwendet, um das Layout des Vendor-Ramdisk-Abschnitts und die Metadaten jedes Vendor-Ramdisk-Fragments zu beschreiben.

Partitionsstruktur

Die Vendor-Bootpartition wird mit Virtual A/B A/B-getestet und durch den Android-Bootmodus mit Verifikation geschützt.

Version 3

Die Partition besteht aus einem Header, der Ramdisk des Anbieters und dem Gerätebaum-Blob (Device Tree Blob, DTB).

Abschnitt Anzahl der Seiten
Boot-Header des Anbieters (n Seiten) n = (2112 + page_size - 1) / page_size
Anbieter-Ramdisk (o Seiten) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p Seiten) p = (dtb_size + page_size - 1) / page_size

Version 4

Die Partition besteht aus einem Header, dem Vendor-Ramdisk-Abschnitt (der aus allen verketteten Vendor-Ramdisk-Fragmenten besteht), dem Gerätebaum-Blob (Device Tree Blob, DTB) und der Vendor-Ramdisk-Tabelle.

Abschnitt Anzahl der Seiten
Boot-Header des Anbieters (n Seiten) n = (2128 + page_size - 1) / page_size
Ramdisk-Fragmente des Anbieters (o Seiten) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p Seiten) p = (dtb_size + page_size - 1) / page_size
Anbieter-Ramdisk-Tabelle (q Seiten) q = (vendor_ramdisk_table_size + page_size - 1) / page_size
Bootconfig (r Seiten) r = (bootconfig_size + page_size - 1) / page_size

Boot-Header des Anbieters

Der Inhalt des Headers der Vendor-Bootpartition besteht hauptsächlich aus Daten, die aus dem Boot-Image-Header dorthin verschoben wurden. Sie enthält auch Informationen zur Vendor-Ramdisk.

Version 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 */

};

Version 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 ist die Gesamtgröße aller Vendor-Ramdisk-Fragmente.
  • ramdisk_type gibt den Typ der Ramdisk an. Mögliche Werte sind:
    • VENDOR_RAMDISK_TYPE_NONE gibt an, dass der Wert nicht angegeben ist.
    • VENDOR_RAMDISK_TYPE_PLATFORM-Ramdisks enthalten plattformspezifische Bits. Der Bootloader muss diese immer in den Arbeitsspeicher laden.
    • VENDOR_RAMDISK_TYPE_RECOVERY-Ramdisks enthalten Wiederherstellungsressourcen. Der Bootloader muss diese beim Booten in den Wiederherstellungsmodus in den Arbeitsspeicher laden.
    • VENDOR_RAMDISK_TYPE_DLKM-Ramdisks enthalten dynamisch ladbare Kernelmodule.
  • ramdisk_name ist ein eindeutiger Name der Ramdisk.
  • board_id ist ein Vektor von vom Anbieter definierten Hardware-IDs.

Bootloader-Unterstützung

Da die Vendor-Boot-Partition Informationen (z. B. Flash-Seitengröße, Kernel, Ramdisk-Ladeadressen, den Gerätebaum selbst) enthält, die zuvor in der Boot-Partition vorhanden waren, muss der Bootloader sowohl auf die Boot- als auch auf die Vendor-Boot-Partition zugreifen, um genügend Daten für den Startvorgang zu haben.

Der Bootloader muss die generische Ramdisk unmittelbar nach der Anbieter-Ramdisk in den Arbeitsspeicher laden. Die Formate CPIO, Gzip und lz4 unterstützen diese Art der Verkettung. Richten Sie das generische Ramdisk-Image nicht an der Seite aus und fügen Sie keinen anderen Speicherplatz zwischen ihm und dem Ende der Anbieter-Ramdisk im Arbeitsspeicher ein. Nachdem der Kernel dekomprimiert wurde, wird die verkettete Datei in initramfs extrahiert. Dadurch entsteht eine Dateistruktur, die eine generische Ramdisk ist, die über die Dateistruktur der Vendor-Ramdisk gelegt wird.

Da die generische Ramdisk und die Anbieter-Ramdisk verkettet werden, müssen sie dasselbe Format haben. Das GKI-Boot-Image verwendet eine lz4-komprimierte generische Ramdisk. Ein GKI-kompatibles Gerät muss daher eine lz4-komprimierte Anbieter-Ramdisk verwenden. Die Konfiguration dafür ist unten zu sehen.

Die Bootloader-Anforderungen für die Unterstützung von Bootconfig werden unter Bootconfig implementieren erläutert.

Mehrere Anbieter-Ramdisks (Version 4)

Mit der Boot-Image-Header-Version 4 kann der Bootloader entweder eine Teilmenge oder alle Vendor-Ramdisks auswählen, die zur Bootzeit als initramfs geladen werden sollen. Die Vendor-Ramdisk-Tabelle enthält die Metadaten jeder Ramdisk und kann dem Bootloader helfen, zu entscheiden, welche Ramdisks geladen werden sollen. Der Bootloader kann die Reihenfolge festlegen, in der die ausgewählten Anbieter-Ramdisks geladen werden, solange die generische Ramdisk zuletzt geladen wird.

Der Bootloader kann beispielsweise das Laden von Anbieter-Ramdisks vom Typ VENDOR_RAMDISK_TYPE_RECOVERY während des normalen Starts auslassen, um Ressourcen zu sparen. So werden nur Anbieter-Ramdisks vom Typ VENDOR_RAMDISK_TYPE_PLATFORM und VENDOR_RAMDISK_TYPE_DLKM in den Arbeitsspeicher geladen. Andererseits werden Vendor-Ramdisks vom Typ VENDOR_RAMDISK_TYPE_PLATFORM, VENDOR_RAMDISK_TYPE_RECOVERY und VENDOR_RAMDISK_TYPE_DLKM beim Booten in den Wiederherstellungsmodus in den Arbeitsspeicher geladen.

Alternativ kann der Bootloader die Anbieter-Ramdisk-Tabelle ignorieren und den gesamten Anbieter-Ramdisk-Abschnitt laden. Dies hat denselben Effekt wie das Laden aller Vendor-Ramdisk-Fragmente in der Partition vendor_boot.

Unterstützung gewinnen

So implementieren Sie die Unterstützung des Anbieter-Bootvorgangs für ein Gerät:

  • Legen Sie BOARD_BOOT_HEADER_VERSION auf 3 oder höher fest.

  • Setzen Sie BOARD_RAMDISK_USE_LZ4 auf true, wenn Ihr Gerät GKI-kompatibel ist oder eine lz4-komprimierte generische Ramdisk verwendet.

  • Legen Sie BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE auf eine für Ihr Gerät geeignete Größe fest und berücksichtigen Sie dabei die Kernelmodule, die auf die Vendor-Ramdisk gehören.

  • Aktualisieren Sie AB_OTA_PARTITIONS, um vendor_boot und alle anbieterspezifischen Listen von OTA-Partitionen auf dem Gerät einzuschließen.

  • Kopieren Sie Ihr Gerät fstab in /first_stage_ramdisk in der Partition vendor_boot, nicht in der Partition boot. Beispiel: $(LOCAL_PATH)/fstab.hardware:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.$(PRODUCT_PLATFORM).

So fügen Sie vendor_boot mehrere Anbieter-Ramdisks hinzu:

  • Setzen Sie BOARD_BOOT_HEADER_VERSION auf 4.
  • Legen Sie BOARD_VENDOR_RAMDISK_FRAGMENTS auf eine Liste logischer Vendor-Ramdisk-Fragmentnamen fest, die in vendor_boot enthalten sein sollen.

  • Wenn Sie eine vorgefertigte Vendor-Ramdisk hinzufügen möchten, legen Sie BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILT auf den vorgefertigten Pfad fest.

  • Wenn Sie eine DLKM-Anbieter-Ramdisk hinzufügen möchten, legen Sie BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRS auf die Liste der einzuschließenden Kernelmodulverzeichnisse fest.

  • Legen Sie für BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGS die mkbootimg-Argumente fest. Dies sind die Argumente --board_id[0-15] und --ramdisk_type für das Vendor-Ramdisk-Fragment. Für die DLKM-Anbieter-Ramdisk wäre der Standardwert für --ramdisk_type DLKM, sofern nichts anderes angegeben ist.

So erstellen Sie Wiederherstellungsressourcen als eigenständige recovery-Ramdisk in vendor_boot:

  • Setzen Sie BOARD_BOOT_HEADER_VERSION auf 4.
  • Setzen Sie BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT auf true.
  • Setzen Sie BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT auf true.
  • Dadurch wird ein Anbieter-Ramdisk-Fragment hinzugefügt, dessen ramdisk_name recovery und dessen ramdisk_type VENDOR_RAMDISK_TYPE_RECOVERY ist. Die Ramdisk enthält dann alle Wiederherstellungsdateien, die unter $(TARGET_RECOVERY_ROOT_OUT) installiert sind.

mkbootimg-Argumente

Argument Beschreibung
--ramdisk_type Der Typ der Ramdisk kann NONE, PLATFORM, RECOVERY oder DLKM sein.
--board_id[0-15] Geben Sie den board_id-Vektor an. Der Standardwert ist 0.

Hier sehen Sie eine Beispielkonfiguration:

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

Die resultierende vendor_boot würde zwei Vendor-Ramdisk-Fragmente enthalten. Die erste ist die „Standard“-Ramdisk, die das DLKM-Verzeichnis baz und die restlichen Dateien in $(TARGET_VENDOR_RAMDISK_OUT) enthält. Die zweite ist die dlkm_foobar-Ramdisk, die die DLKM-Verzeichnisse foo und bar enthält. Der --ramdisk_type-Standardwert ist DLKM.