Partycje rozruchu dostawcy

Android 11 wprowadził koncepcję Generic Kernel Image (GKI). Aby umożliwić uruchamianie dowolnego urządzenia za pomocą interfejsu GKI, urządzenia z Androidem 11 mogą używać nagłówka obrazu rozruchu w wersji 3. W wersji 3 wszystkie informacje o dostawcy są wykluczane z partycji boot i przenoszone do nowej partycji vendor_boot. Aby można było przeprowadzić testy w GKI, urządzenie ARM64 z Androidem 11 z jądrem systemu Linux 5.4 musi obsługiwać partycję vendor_boot i zaktualizowany format partycji boot.

Urządzenia z Androidem 12 mogą używać nagłówka obrazu rozruchowego w wersji 4, który obsługuje umieszczanie wielu dysków RAM dostawcy na partycji vendor_boot. W sekcji dotyczącej ramdiska dostawcy wiele fragmentów ramdiska jest łączonych jeden po drugim. Tabela ramdisk dostawcy służy do opisania układu sekcji ramdisk dostawcy i metadanych każdego fragmentu ramdisk dostawcy.

Struktura partycji

Partycja rozruchu dostawcy jest skonfigurowana w trybie A/B z wirtualnym trybem A/B i chroniona przez funkcję Verified Boot w Androidzie.

Wersja 3

Partycja składa się z nagłówka, partycji RAM dostawcy i bloba drzewa urządzenia (DTB).

Sekcja Liczba stron
Nagłówek uruchamiania dostawcy (n stron) n = (2112 + page_size - 1) / page_size
Plik Ramdisk dostawcy (strony O) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p stron) p = (dtb_size + page_size - 1) / page_size

Wersja 4

Partycja składa się z nagłówka, sekcji dostawcy ramdisk (która zawiera wszystkie złączone fragmenty ramdisk dostawcy), pliku danych drzewa urządzenia (DTB) i tabeli ramdisk dostawcy.

Sekcja Liczba stron
Nagłówek uruchamiania dostawcy (n stron) n = (2128 + page_size - 1) / page_size
Fragmenty dostawcy ramdisk (o strony) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (strony: p) p = (dtb_size + page_size - 1) / page_size
Tabela ramdisk dostawcy (strony Q) q = (vendor_ramdisk_table_size + page_size - 1) / page_size
Bootconfig (strony r) r = (bootconfig_size + page_size - 1) / page_size

Nagłówek uruchamiania dostawcy

Zawartość nagłówka partycji rozruchowej dostawcy składa się głównie z danych przeniesionych z nagłówka obrazu rozruchowego. Zawiera on też informacje o dysku RAM dostawcy.

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

};

Wersja 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 to łączny rozmiar wszystkich fragmentów dysku RAM dostawcy.
  • ramdisk_type określa typ dysku ramdy. Możliwe wartości to:
    • VENDOR_RAMDISK_TYPE_NONE oznacza, że wartość jest nieokreślona.
    • VENDOR_RAMDISK_TYPE_PLATFORM Ramdiski zawierają bity specyficzne dla platformy. Program rozruchowy musi zawsze wczytywać je do pamięci.
    • VENDOR_RAMDISK_TYPE_RECOVERY Pamięci RAM zawierają zasoby odzyskiwania. Program rozruchowy musi załadować je do pamięci podczas uruchamiania trybu odzyskiwania.
    • VENDOR_RAMDISK_TYPE_DLKM Ramdysk zawiera dynamicznie ładowalne moduły jądra.
  • ramdisk_name to unikalna nazwa dysku RAM.
  • board_id to wektor identyfikatorów sprzętowych zdefiniowanych przez dostawcę.

Obsługa programu rozruchowego

Partycja rozruchowa dostawcy zawiera informacje (takie jak rozmiar strony w formacie Flash, jądro, adresy wczytywanego dysku RAM czy plik DTB), które istniały wcześniej w partycji rozruchowej, więc program rozruchowy musi mieć dostęp zarówno do partycji rozruchowej, jak i od dostawcy, aby mieć wystarczającą ilość danych do zakończenia rozruchu.

Program rozruchowy musi załadować do pamięci ogólny dysk RAM natychmiast po dysku RAM dostawcy (takie konkatenacje obsługują formaty CPIO, Gzip i lz4). Nie wyrównuj strony do ramki pliku ramdysk ogólny ani nie wprowadzaj żadnych innych odstępów między nim a końcem pliku ramdysk dostawcy w pamięci. Po zakończeniu procesu dekompresji jądra plik jest wyodrębniony z konkatenowanego pliku do initramfs, co powoduje powstanie struktury plików, która jest ogólnym dyskiem ramowym nałożonym na strukturę pliku dysku ramowego dostawcy.

Ponieważ ogólny dysk RAM i dysk RAM dostawcy są łączone, muszą mieć ten sam format. Obraz GKI używa ogólnego pliku ramdysk skompresowanego za pomocą lz4, więc urządzenie zgodne z GKI musi używać pliku ramdysk skompresowanego za pomocą lz4. Konfiguracja tego ustawienia jest pokazana poniżej.

Wymagania dotyczące programu rozruchowego związane z obsługą rozruchu zostały opisane w artykule Implementowanie konfiguracji rozruchowej.

Ramdiski wielu dostawców (wersja 4)

W przypadku nagłówka obrazu rozruchowego w wersji 4 bootloader może wybrać podzbiór lub wszystkie ramdysk dostawcy do załadowania jako initramfs podczas rozruchu. Tabela vendor ramdisk zawiera metadane każdego dysku RAM i może pomóc programowi rozruchowemu w podejmowaniu decyzji, które dyski RAM mają być ładowane. Bootloader może decydować o kolejności wczytywania wybranych ramdysków dostawcy, o ile tylko ostatnio wczytany będzie ogólny ramdisk.

Program rozruchowy może na przykład podczas normalnego rozruchu pomijać wczytywanie dysków RAM dostawcy typu VENDOR_RAMDISK_TYPE_RECOVERY, aby oszczędzać zasoby. W rezultacie do pamięci wczytywane są tylko dyski RAM dostawców typu VENDOR_RAMDISK_TYPE_PLATFORM i VENDOR_RAMDISK_TYPE_DLKM. Z drugiej strony podczas uruchamiania w trybie przywracania do pamięci są wczytywane dyski Ramdy dostawcy typu VENDOR_RAMDISK_TYPE_PLATFORM, VENDOR_RAMDISK_TYPE_RECOVERY i VENDOR_RAMDISK_TYPE_DLKM.

Alternatywnie bootloader może zignorować tabelę ramdisk dostawcy i wczytać całą sekcję ramdisk dostawcy. Ma to taki sam efekt jak wczytywanie wszystkich fragmentów pamięci RAM dostarczyciela na partycji vendor_boot.

Pomoc w budowaniu

Aby wdrożyć obsługę uruchamiania przez dostawcę na urządzeniu:

  • Ustaw wartość BOARD_BOOT_HEADER_VERSION na 3 lub większą.

  • Ustaw BOARD_RAMDISK_USE_LZ4 na true, jeśli urządzenie jest zgodne z GKI lub używa ogólnego pliku wymiany z kompresją LZ4.

  • Ustaw BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE na odpowiedni rozmiar dla urządzenia, biorąc pod uwagę moduły jądra, które muszą znajdować się na partycji RAM dostawcy.

  • Zaktualizuj AB_OTA_PARTITIONS, aby uwzględnić vendor_boot oraz wszelkie listy partycji OTA na urządzeniu, które są specyficzne dla danego dostawcy.

  • Skopiuj urządzenie fstab do /first_stage_ramdisk partycji vendor_boot, a nie boot. Na przykład: $(LOCAL_PATH)/fstab.hardware:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.$(PRODUCT_PLATFORM).

Aby uwzględnić w vendor_boot kilka dysków RAM od różnych dostawców:

  • Ustaw BOARD_BOOT_HEADER_VERSION na 4.
  • Ustaw BOARD_VENDOR_RAMDISK_FRAGMENTS na listę nazw fragmentów dysków Ramdisk dostawcy logicznych, które mają być uwzględnione w vendor_boot.

  • Aby dodać gotowy obraz pamięci RAM od dostawcy, ustaw parametr BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILT na ścieżkę do gotowego obrazu.

  • Aby dodać dysk ramdisk dostawcy DLKM, ustaw BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRS na listę katalogów modułu jądra, które chcesz uwzględnić.

  • Ustaw argumenty BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGS na mkbootimg. Są to argumenty --board_id[0-15]--ramdisk_type dla fragmentu ramdisk dostawcy. W przypadku pamięci RAM dostawcy DLKM domyślna wartość --ramdisk_type to DLKM, jeśli nie zostanie określona inna wartość.

Aby utworzyć zasoby do przywracania jako samodzielny dysk RAM recovery w vendor_boot:

  • Ustaw BOARD_BOOT_HEADER_VERSION na 4.
  • Ustaw BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT na true.
  • Ustaw BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT na true.
  • Dodaje fragment ramdisk dostawcy, którego ramdisk_name to recovery, a ramdisk_type to VENDOR_RAMDISK_TYPE_RECOVERY. Ramdysk zawiera wtedy wszystkie pliki przywracania, czyli pliki zainstalowane w systemie $(TARGET_RECOVERY_ROOT_OUT).

Argumenty mkbootimg

Argument Opis
--ramdisk_type Typ dysku RAM może być NONE, PLATFORM, RECOVERY lub DLKM.
--board_id[0-15] Określ wektor board_id, który domyślnie ma wartość 0.

Oto przykładowa konfiguracja:

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

Powstały vendor_boot zawierałby 2 fragmenty ramdisk dostawcy. Pierwszy to „domyślny” dysk twardy w pamięci RAM, który zawiera katalog DLKM baz oraz resztę plików w $(TARGET_VENDOR_RAMDISK_OUT). Drugim jest dysk RAM dlkm_foobar, który zawiera katalogi DLKM foo i bar, a wartość domyślna --ramdisk_type to DLKM.