Bootpartitionen des Anbieters

Mit Android 11 wurde das Konzept des generischen Kernel-Images (GKI) eingeführt. Um das Starten eines beliebigen Geräts mit der GKI zu ermöglichen, können Android 11-Geräte die Version 3 des Boot-Image-Headers verwenden. In Version 3 werden alle anbieterspezifischen Informationen aus der boot-Partition entfernt und in eine neue vendor_boot-Partition verschoben. Ein ARM64-Gerät, das mit Android 11 auf dem Linux-Kernel 5.4 gestartet wird, muss die vendor_boot-Partition und das aktualisierte boot-Partitionsformat unterstützen, um die GKI-Tests zu bestehen.

Auf Android 12-Geräten kann die Version 4 des Boot-Image-Headers verwendet werden, die das Einfügen mehrerer RAM-Disks von Anbietern in die Partition vendor_boot unterstützt. Mehrere Anbieter-Ramdisk-Fragmente werden im Abschnitt „Anbieter-Ramdisk“ nacheinander zusammengefügt. Mit einer Anbieter-Ramdisk-Tabelle wird das Layout des Anbieter-Ramdisk-Segments und die Metadaten jedes Anbieter-Ramdisk-Fragments beschrieben.

Partitionsstruktur

Die Bootpartition des Anbieters ist A/B-gespiegelt mit virtuellem A/B und durch den verifizierten Bootmodus von Android geschützt.

Version 3

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

Abschnitt Seitenzahl
Anbieter-Boot-Header (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 Anbieter-Ramdisk-Abschnitt (bestehend aus allen zusammenhängenden Anbieter-Ramdisk-Fragmenten), dem Device-Tree-Blob (DTB) und der Anbieter-Ramdisk-Tabelle.

Abschnitt Seitenzahl
Anbieter-Boot-Header (n Seiten) n = (2128 + page_size - 1) / page_size
Anbieter-RAM-Disk-Fragmente (o-Seiten) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p-Seiten) p = (dtb_size + page_size - 1) / page_size
Anbieter-RAM-Disk-Tabelle (Q-Seiten) q = (vendor_ramdisk_table_size + page_size - 1) / page_size
Bootconfig (R-Seiten) r = (bootconfig_size + page_size - 1) / page_size

Anbieter-Boot-Header

Der Inhalt des Header der Bootpartition des Anbieters besteht hauptsächlich aus Daten, die dort aus dem Boot-Image-Header verschoben wurden. Außerdem enthält es Informationen zum RAM-Disk des Anbieters.

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 RAM-Disk-Fragmente des Anbieters.
  • ramdisk_type gibt den Typ des RAM-Laufwerks 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 Starten in den Wiederherstellungsmodus in den Arbeitsspeicher laden.
    • VENDOR_RAMDISK_TYPE_DLKM RAM-Disks enthalten dynamisch ladbare Kernelmodule.
  • ramdisk_name ist ein eindeutiger Name für das RAM-Disk.
  • board_id ist ein Vektor von vom Anbieter definierten Hardware-IDs.

Bootloader-Unterstützung

Da die Boot-Partition des Anbieters Informationen wie die Flash-Seitengröße, den Kernel, die Ramdisk-Ladeadressen und das DTB selbst enthält, die zuvor in der Boot-Partition vorhanden waren, muss der Bootloader sowohl auf die Boot- als auch auf die Boot-Partition des Anbieters zugreifen, um genügend Daten für den vollständigen Startvorgang zu haben.

Der Bootloader muss das generisch erstellte RAM-Disk unmittelbar nach dem RAM-Disk des Anbieters in den Arbeitsspeicher laden. Die Formate CPIO, Gzip und lz4 unterstützen diese Art der Koncatenation. Das generische RAM-Disk-Image darf nicht seitenausgerichtet werden und es darf auch keinen anderen Speicherplatz zwischen ihm und dem Ende des RAM-Disks des Anbieters geben. Nachdem der Kernel die Dekomprimierung abgeschlossen hat, wird die zusammengesetzte Datei in eine initramfs extrahiert. Dies führt zu einer Dateistruktur, die ein generisches RAM-Disk ist, das über die RAM-Disk-Dateistruktur des Anbieters gelegt wird.

Da das generische RAM-Disk und das RAM-Disk des Anbieters zusammengeführt werden, müssen sie dasselbe Format haben. Das GKI-Boot-Image verwendet ein LZ4-komprimiertes generisches RAM-Disk. Daher muss ein GKI-kompatibles Gerät ein LZ4-komprimiertes RAM-Disk des Anbieters verwenden. Die Konfiguration dafür ist unten dargestellt.

Die Bootloaderanforderungen für die Unterstützung von bootconfig werden unter Bootconfig implementieren erläutert.

Mehrere RAM-Disks von verschiedenen Anbietern (Version 4)

Bei der Boot-Image-Header-Version 4 kann der Bootloader entweder einen Teil oder alle RAM-Disks des Anbieters auswählen, die während des Bootens als initramfs geladen werden sollen. Die RAM-Disk-Tabelle des Anbieters enthält die Metadaten der einzelnen RAM-Disks und kann dem Bootloader bei der Entscheidung helfen, welche RAM-Disks geladen werden sollen. Der Bootloader kann die Reihenfolge zum Laden der ausgewählten Anbieter-Ramdisks festlegen, solange das generische Ramdisk als letztes geladen wird.

Beispielsweise kann der Bootloader das Laden von RAM-Disks vom Typ VENDOR_RAMDISK_TYPE_RECOVERY beim normalen Starten auslassen, um Ressourcen zu sparen. So werden nur RAM-Disks vom Typ VENDOR_RAMDISK_TYPE_PLATFORM und VENDOR_RAMDISK_TYPE_DLKM in den Arbeitsspeicher geladen. Anbieter-RAM-Disks vom Typ VENDOR_RAMDISK_TYPE_PLATFORM, VENDOR_RAMDISK_TYPE_RECOVERY und VENDOR_RAMDISK_TYPE_DLKM werden dagegen in den Arbeitsspeicher geladen, wenn der Computer im Wiederherstellungsmodus gestartet wird.

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

Unterstützung aufbauen

So implementieren Sie die Unterstützung des Anbieterstarts für ein Gerät:

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

  • Legen Sie BOARD_RAMDISK_USE_LZ4 auf true fest, wenn Ihr Gerät GKI-kompatibel ist oder andernfalls ein lz4-komprimiertes generisches RAM-Disk verwendet.

  • Legen Sie für BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE eine für Ihr Gerät geeignete Größe fest. Berücksichtigen Sie dabei die Kernelmodule, die auf dem RAM-Disk des Anbieters abgelegt werden müssen.

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

  • Kopieren Sie fstab auf dem Gerät 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 mehrere Anbieter-Ramdisks in vendor_boot ein:

  • Legen Sie BOARD_BOOT_HEADER_VERSION auf 4 fest.
  • Legen Sie BOARD_VENDOR_RAMDISK_FRAGMENTS auf eine Liste logischer Namen von RAM-Disk-Fragmenten des Anbieters fest, die in vendor_boot aufgenommen werden sollen.

  • Wenn Sie ein vorkonfiguriertes Anbieter-Ramdisk hinzufügen möchten, setzen Sie BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILT auf den vorkonfigurierten Pfad.

  • Wenn Sie ein DLKM-Anbieter-Ramdisk hinzufügen möchten, setzen Sie BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRS in die Liste der zu berücksichtigenden Kernelmodulverzeichnisse.

  • Legen Sie für BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGS die Argumente mkbootimg fest. Das sind die Argumente --board_id[0-15] und --ramdisk_type für das Anbieter-Ramdisk-Fragment. Für das DLKM-RAM-Disk des Anbieters ist DLKM der Standardwert für --ramdisk_type, sofern nicht anders angegeben.

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

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

mkbootimg-Argumente

Argument Beschreibung
--ramdisk_type Der Typ des RAM-Disks kann NONE, PLATFORM, RECOVERY oder DLKM sein.
--board_id[0-15] Geben Sie den board_id-Vektor an. Standardmäßig ist 0 festgelegt.

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

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