Particiones de inicio del proveedor

Android 11 introdujo el concepto de imagen genérica del kernel (GKI). Para habilitar el inicio de un dispositivo arbitrario con el GKI, los dispositivos con Android 11 pueden usar la versión 3 del encabezado de imagen de arranque. En la versión 3, toda la información específica del proveedor se factoriza fuera de la partición boot y se reubica en una partición vendor_boot nueva. Un dispositivo ARM64 que se lanza con Android 11 en el kernel de Linux 5.4 debe admitir la partición vendor_boot y el formato de partición boot actualizado para aprobar las pruebas con el GKI.

Los dispositivos con Android 12 pueden usar la versión 4 del encabezado de imagen de arranque, que admite la inclusión de varios ramdisks del proveedor en la partición vendor_boot. Varios fragmentos de ramdisk del proveedor se concatenan uno tras otro en la sección de ramdisk del proveedor. Se usa una tabla de ramdisk del proveedor para describir el diseño de la sección correspondiente y los metadatos de cada fragmento de ramdisk del proveedor.

Estructura de particiones

La partición de inicio del proveedor se divide en A/B con A/B virtual y se protege con el inicio verificado de Android.

Versión 3

La partición consta de un encabezado, el ramdisk del proveedor y el BLOB del árbol de dispositivos (DTB).

Sección Cantidad de páginas
Encabezado de inicio del proveedor (n páginas) n = (2112 + page_size - 1) / page_size
ramdisk del proveedor (o páginas) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p páginas) p = (dtb_size + page_size - 1) / page_size

Versión 4

La partición consta de un encabezado, la sección de ramdisk del proveedor (que consta de todos los fragmentos de ramdisk del proveedor, concatenados), el BLOB del árbol de dispositivos (DTB) y la tabla de ramdisk del proveedor.

Sección Cantidad de páginas
Encabezado de inicio del proveedor (n páginas) n = (2128 + page_size - 1) / page_size
Fragmentos de ramdisk del proveedor (o páginas) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p páginas) p = (dtb_size + page_size - 1) / page_size
Tabla de ramdisk del proveedor (páginas q) q = (vendor_ramdisk_table_size + page_size - 1) / page_size
Bootconfig (páginas r) r = (bootconfig_size + page_size - 1) / page_size

Encabezado de inicio del proveedor

El contenido del encabezado de la partición de inicio del proveedor consta, en su mayoría, de datos que se reubicaron allí desde el encabezado de imagen de arranque. También contiene información sobre el ramdisk del proveedor.

Versión 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 */

};

Versión 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 es el tamaño total de todos los fragmentos de ramdisk del proveedor.
  • ramdisk_type indica el tipo de ramdisk. Los valores posibles son los siguientes:
    • VENDOR_RAMDISK_TYPE_NONE indica que el valor no se especifica.
    • Los ramdisks VENDOR_RAMDISK_TYPE_PLATFORM contienen bits específicos de la plataforma. El bootloader siempre debe cargarlos en la memoria.
    • Los ramdisks VENDOR_RAMDISK_TYPE_RECOVERY contienen recursos de recuperación. El bootloader debe cargarlos en la memoria cuando se inicia en modo de recuperación.
    • Los VENDOR_RAMDISK_TYPE_DLKM ramdisks contienen módulos de kernel cargables dinámicos.
  • ramdisk_name es un nombre único del ramdisk.
  • board_id es un vector de identificadores de hardware definidos por el proveedor.

Compatibilidad con el bootloader

Debido a que la partición de inicio del proveedor contiene información (como el tamaño de la página de la memoria flash, el kernel, las direcciones de carga de ramdisk y el DTB en sí) que existía anteriormente en la partición de inicio, el bootloader debe acceder a las particiones de inicio y de inicio del proveedor para tener suficientes datos y completar el inicio.

El bootloader debe cargar el ramdisk genérico en la memoria inmediatamente después del ramdisk del proveedor (los formatos CPIO, Gzip y lz4 admiten este tipo de concatenación). No alinees la imagen genérica del ramdisk ni introduzcas ningún otro espacio entre ella y el final del ramdisk del proveedor en la memoria. Después de que el kernel descomprime, extrae el archivo concatenado en un initramfs, lo que genera una estructura de archivos que es un ramdisk genérico superpuesto en la estructura de archivos del ramdisk del proveedor.

Debido a que el disco RAM genérico y el disco RAM del proveedor se concatenan, deben estar en el mismo formato. La imagen de arranque de GKI usa un ramdisk genérico comprimido con lz4, por lo que un dispositivo que cumpla con GKI debe usar un ramdisk del proveedor comprimido con lz4. La configuración para esto se muestra a continuación.

Los requisitos del bootloader para admitir bootconfig se explican en Implementa Bootconfig.

Varios ramdisks del proveedor (versión 4)

Con la versión 4 del encabezado de imagen de arranque, el bootloader puede seleccionar un subconjunto o todos los ramdisks del proveedor para cargarlos como initramfs durante el tiempo de inicio. La tabla de ramdisk del proveedor contiene los metadatos de cada ramdisk y puede ayudar al bootloader a decidir qué ramdisks cargar. El bootloader puede decidir el orden de carga de los ramdisks del proveedor seleccionados, siempre y cuando el ramdisk genérico se cargue en último lugar.

Por ejemplo, el bootloader puede omitir la carga de ramdisks del proveedor de tipo VENDOR_RAMDISK_TYPE_RECOVERY durante el inicio normal para conservar recursos, de modo que solo se carguen en la memoria los ramdisks del proveedor de tipo VENDOR_RAMDISK_TYPE_PLATFORM y VENDOR_RAMDISK_TYPE_DLKM. Por otro lado, los ramdisks del proveedor de tipo VENDOR_RAMDISK_TYPE_PLATFORM, VENDOR_RAMDISK_TYPE_RECOVERY y VENDOR_RAMDISK_TYPE_DLKM se cargan en la memoria cuando se inicia el modo de recuperación.

Como alternativa, el bootloader puede ignorar la tabla de ramdisk del proveedor y cargar toda la sección de ramdisk del proveedor. Esto tiene el mismo efecto que cargar todos los fragmentos de ramdisk del proveedor en la partición vendor_boot.

Cómo obtener asistencia

Para implementar la compatibilidad con el arranque del proveedor para un dispositivo, haz lo siguiente:

  • Establece BOARD_BOOT_HEADER_VERSION en 3 o un valor superior.

  • Establece BOARD_RAMDISK_USE_LZ4 en true si tu dispositivo cumple con GKI o si, de lo contrario, usa un ramdisk genérico comprimido con lz4.

  • Establece BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE en un tamaño adecuado para tu dispositivo, teniendo en cuenta los módulos de kernel que deben ir en el disco RAM del proveedor.

  • Actualiza AB_OTA_PARTITIONS para incluir vendor_boot y cualquier lista de particiones OTA específica del proveedor en el dispositivo.

  • Copia el fstab de tu dispositivo en /first_stage_ramdisk en la partición vendor_boot, no en la partición boot. (por ejemplo, $(LOCAL_PATH)/fstab.hardware:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.$(PRODUCT_PLATFORM)).

Para incluir varios ramdisks del proveedor en vendor_boot, haz lo siguiente:

  • Establece BOARD_BOOT_HEADER_VERSION en 4.
  • Configura BOARD_VENDOR_RAMDISK_FRAGMENTS en una lista de nombres de fragmentos de ramdisk del proveedor lógicos que se incluirán en vendor_boot.

  • Para agregar un ramdisk del proveedor compilado previamente, establece BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILT en la ruta de acceso compilada previamente.

  • Para agregar un disco RAM del proveedor de DLKM, establece BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRS en la lista de directorios de módulos de kernel que se incluirán.

  • Establece BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGS en los argumentos mkbootimg. Estos son los argumentos --board_id[0-15] y --ramdisk_type para el fragmento de ramdisk del proveedor. Para el ramdisk del proveedor de DLKM, el --ramdisk_type predeterminado sería DLKM si no se especifica lo contrario.

Para compilar recursos de recuperación como un ramdisk recovery independiente en vendor_boot, haz lo siguiente:

  • Establece BOARD_BOOT_HEADER_VERSION en 4.
  • Establece BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT en true.
  • Establece BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT en true.
  • Esto agrega un fragmento de ramdisk del proveedor cuyo ramdisk_name es recovery y ramdisk_type es VENDOR_RAMDISK_TYPE_RECOVERY. Luego, el ramdisk contiene todos los archivos de recuperación, que son archivos instalados en $(TARGET_RECOVERY_ROOT_OUT).

Argumentos de mkbootimg

Argumento Descripción
--ramdisk_type El tipo de ramdisk puede ser NONE, PLATFORM, RECOVERY o DLKM.
--board_id[0-15] Especifica el vector board_id, que se establece de forma predeterminada en 0.

A continuación, se muestra una configuración de ejemplo:

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

El vendor_boot resultante contendrá dos fragmentos de ramdisk del proveedor. El primero es el ramdisk "predeterminado", que contiene el directorio DLKM baz y el resto de los archivos en $(TARGET_VENDOR_RAMDISK_OUT). El segundo es el ramdisk dlkm_foobar, que contiene los directorios DLKM foo y bar, y el --ramdisk_type predeterminado es DLKM.