Android 11 a introduit le concept d'image de kernel générique (GKI). Pour permettre le démarrage d'un appareil arbitraire avec le GKI, les appareils Android 11 peuvent utiliser la version 3 de l'en-tête d'image de démarrage. Dans la version 3, toutes les informations spécifiques au fournisseur sont exclues de la partition boot et déplacées vers une nouvelle partition vendor_boot. Un appareil ARM64 lancé avec Android 11 sur le kernel Linux 5.4 doit prendre en charge la partition vendor_boot et le format de partition boot mis à jour pour réussir les tests avec le GKI.
Les appareils Android 12 peuvent utiliser la version 4 de l'en-tête d'image de démarrage, qui permet d'inclure plusieurs ramdisks de fournisseurs dans la partition vendor_boot. Plusieurs fragments de ramdisk du fournisseur sont concatenatés les uns après les autres dans la section du ramdisk du fournisseur. Une table de ramdisk du fournisseur permet de décrire la mise en page de la section de ramdisk du fournisseur et les métadonnées de chaque fragment de ramdisk du fournisseur.
Structure de partition
La partition de démarrage du fournisseur est A/B avec A/B virtuel et protégée par le démarrage validé Android.
Version 3
La partition se compose d'un en-tête, du ramdisk du fournisseur et du blob de l'arborescence des périphériques (DTB).
| Section | Nombre de pages |
|---|---|
| En-tête de démarrage du fournisseur (n pages) | n = (2112 + page_size - 1) / page_size |
| Ramdisk du fournisseur (pages o) | o = (vendor_ramdisk_size + page_size - 1) / page_size |
| DTB (p pages) | p = (dtb_size + page_size - 1) / page_size |
Version 4
La partition se compose d'un en-tête, de la section du ramdisk du fournisseur (comprenant tous les fragments de ramdisk du fournisseur, concatenatés), du blob de l'arborescence des appareils (DTB) et de la table du ramdisk du fournisseur.
| Section | Nombre de pages |
|---|---|
| En-tête de démarrage du fournisseur (n pages) | n = (2128 + page_size - 1) / page_size |
| Fragments de ramdisk du fournisseur (pages o) | o = (vendor_ramdisk_size + page_size - 1) / page_size |
| DTB (p pages) | p = (dtb_size + page_size - 1) / page_size |
| Tableau du ramdisk du fournisseur (pages q) | q = (vendor_ramdisk_table_size + page_size - 1) / page_size |
| Bootconfig (pages r) | r = (bootconfig_size + page_size - 1) / page_size |
En-tête de démarrage du fournisseur
Le contenu de l'en-tête de la partition de démarrage du fournisseur se compose principalement de données qui y ont été déplacées depuis l'en-tête de l'image de démarrage. Il contient également des informations sur le ramdisk du fournisseur.
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_sizecorrespond à la taille totale de tous les fragments de ramdisk du fournisseur.ramdisk_typeindique le type de ramdisk. Les valeurs possibles sont les suivantes :VENDOR_RAMDISK_TYPE_NONEindique que la valeur n'est pas spécifiée.- Les ramdisks
VENDOR_RAMDISK_TYPE_PLATFORMcontiennent des bits spécifiques à la plate-forme. Le bootloader doit toujours les charger en mémoire. - Les ramdisks
VENDOR_RAMDISK_TYPE_RECOVERYcontiennent des ressources de récupération. Le bootloader doit les charger en mémoire lors du démarrage en mode récupération. - Les ramdisks
VENDOR_RAMDISK_TYPE_DLKMcontiennent des modules de kernel à chargement dynamique.
ramdisk_nameest un nom unique du ramdisk.board_idest un vecteur d'identifiants matériels définis par le fournisseur.
Compatibilité avec le bootloader
Étant donné que la partition de démarrage du fournisseur contient des informations (telles que la taille de page flash, le noyau, les adresses de chargement du ramdisk et le DTB lui-même) qui existaient auparavant dans la partition de démarrage, le bootloader doit accéder à la fois à la partition de démarrage et à la partition de démarrage du fournisseur pour disposer de suffisamment de données pour terminer le démarrage.
Le bootloader doit charger le ramdisk générique dans la mémoire immédiatement après le ramdisk du fournisseur (les formats CPIO, Gzip et lz4 sont compatibles avec ce type de concatenaison). N'alignez pas l'image ramdisk générique sur la page ni n'ajoutez d'autre espace entre elle et la fin du ramdisk du fournisseur en mémoire. Une fois le noyau décompressé, il extrait le fichier concaténé dans un initramfs, ce qui donne une structure de fichiers qui est un ramdisk générique superposé à la structure de fichiers ramdisk du fournisseur.
Étant donné que le ramdisk générique et le ramdisk du fournisseur sont concatenatés, ils doivent être au même format. L'image de démarrage GKI utilise un ramdisk générique compressé lz4. Par conséquent, un appareil conforme à GKI doit utiliser un ramdisk du fournisseur compressé lz4. La configuration correspondante est illustrée ci-dessous.
Les exigences du bootloader pour la prise en charge de bootconfig sont expliquées dans la section Implémenter Bootconfig.
Plusieurs ramdisks de fournisseurs (version 4)
Avec la version 4 de l'en-tête de l'image de démarrage, le bootloader peut sélectionner un sous-ensemble ou l'ensemble des ramdisks du fournisseur à charger en tant que initramfs au démarrage. La table de ramdisk du fournisseur contient les métadonnées de chaque ramdisk et peut aider le bootloader à choisir les ramdisks à charger. Le bootloader peut décider de l'ordre de chargement des ramdisks du fournisseur sélectionné, à condition que le ramdisk générique soit chargé en dernier.
Par exemple, le bootloader peut omettre de charger les ramdisks du fournisseur de type VENDOR_RAMDISK_TYPE_RECOVERY lors du démarrage normal pour économiser des ressources. Seules les ramdisks du fournisseur de type VENDOR_RAMDISK_TYPE_PLATFORM et VENDOR_RAMDISK_TYPE_DLKM sont donc chargées en mémoire. En revanche, les ramdisks du fournisseur de type VENDOR_RAMDISK_TYPE_PLATFORM, VENDOR_RAMDISK_TYPE_RECOVERY et VENDOR_RAMDISK_TYPE_DLKM sont chargés en mémoire lors du démarrage en mode récupération.
Le bootloader peut également ignorer la table de ramdisk du fournisseur et charger l'intégralité de la section ramdisk du fournisseur. Cela a le même effet que le chargement de tous les fragments de ramdisk du fournisseur dans la partition vendor_boot.
Assistance de compilation
Pour implémenter la prise en charge du démarrage du fournisseur pour un appareil:
Définissez
BOARD_BOOT_HEADER_VERSIONsur3ou une version ultérieure.Définissez
BOARD_RAMDISK_USE_LZ4surtruesi votre appareil est conforme à GKI ou s'il utilise un ramdisk générique compressé lz4.Définissez
BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZEsur une taille appropriée pour votre appareil, en tenant compte des modules du noyau qui doivent être placés sur le ramdisk du fournisseur.Mettez à jour
AB_OTA_PARTITIONSpour inclurevendor_bootet toutes les listes de partitions OTA spécifiques au fournisseur sur l'appareil.Copiez le
fstabde votre appareil dans/first_stage_ramdiskdans la partitionvendor_boot, et non dans la partitionboot. Exemple :$(LOCAL_PATH)/fstab.hardware:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.$(PRODUCT_PLATFORM).
Pour inclure plusieurs ramdisks de fournisseurs dans vendor_boot:
- Définissez
BOARD_BOOT_HEADER_VERSIONsur4. Définissez
BOARD_VENDOR_RAMDISK_FRAGMENTSsur une liste de noms de fragments de ramdisk logiques du fournisseur à inclure dansvendor_boot.Pour ajouter un ramdisk de fournisseur prédéfini, définissez
BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILTsur le chemin prédéfini.Pour ajouter un ramdisk de fournisseur DLKM, définissez
BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRSsur la liste des répertoires de modules du kernel à inclure.Définissez
BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGSsur les argumentsmkbootimg. Il s'agit des arguments--board_id[0-15]et--ramdisk_typepour le fragment de ramdisk du fournisseur. Pour le ramdisk du fournisseur DLKM, la valeur--ramdisk_typepar défaut estDLKM, sauf indication contraire.
Pour créer des ressources de récupération en tant que ramdisk recovery autonome dans vendor_boot:
- Définissez
BOARD_BOOT_HEADER_VERSIONsur4. - Définissez
BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOTsurtrue. - Définissez
BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOTsurtrue. - Un fragment de ramdisk du fournisseur est ajouté, dont
ramdisk_nameestrecoveryetramdisk_typeestVENDOR_RAMDISK_TYPE_RECOVERY. Le ramdisk contient ensuite tous les fichiers de récupération, qui sont des fichiers installés sous$(TARGET_RECOVERY_ROOT_OUT).
Arguments mkbootimg
| Argument | Description |
|---|---|
--ramdisk_type |
Le type de ramdisk peut être NONE, PLATFORM, RECOVERY ou DLKM.
|
--board_id[0-15] |
Spécifiez le vecteur board_id, qui est défini par défaut sur 0. |
Voici un exemple de configuration:
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
Le fichier vendor_boot obtenu contiendrait deux fragments de ramdisk du fournisseur. Le premier est le ramdisk "par défaut", qui contient le répertoire DLKM baz et le reste des fichiers dans $(TARGET_VENDOR_RAMDISK_OUT). Le second est le ramdisk dlkm_foobar, qui contient les répertoires DLKM foo et bar, et le --ramdisk_type est défini par défaut sur DLKM.