O Android 11 introduziu o conceito de imagem genérica
do kernel (GKI, na sigla em inglês). Para ativar a inicialização de um dispositivo arbitrário com a GKI, os dispositivos Android
11 podem usar a versão 3 do cabeçalho da imagem de inicialização. Na
versão 3, todas as informações específicas do fornecedor são desconsideradas da partição boot
e realocadas para uma nova partição vendor_boot. Um dispositivo ARM64
lançado com o Android 11 no kernel Linux 5.4 precisa
ser compatível com a partição vendor_boot e o formato atualizado da partição boot para
passar no teste com a GKI.
Os dispositivos Android 12 podem usar a versão 4 do cabeçalho da imagem de inicialização,
que oferece suporte à inclusão de vários ramdisks de fornecedores na partição vendor_boot. Vários fragmentos de ramdisk do fornecedor são concatenados um após o outro
na seção de ramdisk do fornecedor. Uma tabela do ramdisk do fornecedor é usada para descrever o
layout da seção do ramdisk e os metadados de cada fragmento
do ramdisk.
Estrutura da partição
A partição de inicialização do fornecedor é A/B com A/B virtual e protegida pela Inicialização verificada do Android.
Versão 3
A partição consiste em um cabeçalho, o ramdisk do fornecedor e o blob da árvore de dispositivos (DTB).
| Seção | Número de páginas |
|---|---|
| Cabeçalho de inicialização do fornecedor (n páginas) | n = (2112 + page_size - 1) / page_size |
| Ramdisk do fornecedor (páginas o) | o = (vendor_ramdisk_size + page_size - 1) / page_size |
| DTB (páginas p) | p = (dtb_size + page_size - 1) / page_size |
Versão 4
A partição consiste em um cabeçalho, a seção do ramdisk do fornecedor (composta por todos os fragmentos do ramdisk do fornecedor, concatenados), o blob da árvore de dispositivos (DTB) e a tabela do ramdisk do fornecedor.
| Seção | Número de páginas |
|---|---|
| Cabeçalho de inicialização do fornecedor (n páginas) | n = (2128 + page_size - 1) / page_size |
| Fragmentos de ramdisk do fornecedor (páginas o) | o = (vendor_ramdisk_size + page_size - 1) / page_size |
| DTB (páginas p) | p = (dtb_size + page_size - 1) / page_size |
| Tabela de ramdisk do fornecedor (q páginas) | q = (vendor_ramdisk_table_size + page_size - 1) / page_size |
| Bootconfig (páginas r) | r = (bootconfig_size + page_size - 1) / page_size |
Cabeçalho de inicialização do fornecedor
O conteúdo do cabeçalho da partição de inicialização do fornecedor consiste principalmente em dados que foram realocados do cabeçalho da imagem de inicialização. Ele também contém informações sobre o ramdisk do fornecedor.
Versão 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 */
};
Versão 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é o tamanho total de todos os fragmentos do ramdisk do fornecedor.ramdisk_typeindica o tipo de ramdisk. Os valores possíveis são:VENDOR_RAMDISK_TYPE_NONEindica que o valor não foi especificado.- Os ramdisks
VENDOR_RAMDISK_TYPE_PLATFORMcontêm bits específicos da plataforma. O carregador de inicialização sempre precisa carregar esses dados na memória. - Os ramdisks
VENDOR_RAMDISK_TYPE_RECOVERYcontêm recursos de recuperação. O carregador de inicialização precisa carregar esses arquivos na memória ao inicializar a recuperação. - Os
VENDOR_RAMDISK_TYPE_DLKMramdisks contêm módulos do kernel carregáveis dinamicamente.
ramdisk_nameé um nome exclusivo do ramdisk.board_idé um vetor de identificadores de hardware definidos pelo fornecedor.
Suporte ao carregador de inicialização
Como a partição de inicialização do fornecedor contém informações (como tamanho da página de flash, kernel, endereços de carga do ramdisk e o próprio DTB) que antes existiam na partição de inicialização, o carregador de inicialização precisa acessar as partições de inicialização e de inicialização do fornecedor para ter dados suficientes para concluir a inicialização.
O carregador de inicialização precisa carregar o ramdisk genérico na memória imediatamente após
o ramdisk do fornecedor. Os formatos CPIO, Gzip e lz4 são compatíveis com esse tipo de
concatenação. Não faça alinhamento de página na imagem do ramdisk genérico nem introduza qualquer
outro espaço entre ela e o fim do ramdisk do fornecedor na memória. Depois que o
kernel é descompactado, ele extrai o arquivo concatenado em um initramfs,
resultando em uma estrutura de arquivos que é um ramdisk genérico sobreposto à
estrutura de arquivos ramdisk do fornecedor.
Como o ramdisk genérico e o ramdisk do fornecedor são concatenados, eles precisam estar no mesmo formato. A imagem de inicialização da GKI usa um ramdisk genérico compactado com lz4. Portanto, um dispositivo compatível com a GKI precisa usar um ramdisk do fornecedor compactado com lz4. A configuração para isso é mostrada abaixo.
Os requisitos do carregador de inicialização para oferecer suporte ao bootconfig são explicados em Implementar o bootconfig.
Vários ramdisks do fornecedor (versão 4)
Com o cabeçalho da imagem de inicialização versão 4, o carregador de inicialização pode selecionar um subconjunto ou
todos os ramdisks do fornecedor para carregar como initramfs durante a inicialização. A tabela do ramdisk do fornecedor contém os metadados de cada ramdisk e pode ajudar o carregador de inicialização a decidir quais ramdisks carregar. O carregador de inicialização pode decidir a ordem de carregamento dos ramdisks do fornecedor selecionados, desde que o ramdisk genérico seja carregado por último.
Por exemplo, o carregador de inicialização pode omitir o carregamento de ramdisks do fornecedor do tipo
VENDOR_RAMDISK_TYPE_RECOVERY durante a inicialização normal para economizar recursos. Assim, apenas
ramdisks do fornecedor do tipo VENDOR_RAMDISK_TYPE_PLATFORM e
VENDOR_RAMDISK_TYPE_DLKM são carregados na memória. Por outro lado, os ramdisks do fornecedor do tipo VENDOR_RAMDISK_TYPE_PLATFORM, VENDOR_RAMDISK_TYPE_RECOVERY e VENDOR_RAMDISK_TYPE_DLKM são carregados na memória ao inicializar no modo de recuperação.
Como alternativa, o carregador de inicialização pode ignorar a tabela do ramdisk do fornecedor e carregar toda a seção do ramdisk do fornecedor. Isso tem o mesmo efeito que carregar todos os fragmentos de ramdisk do fornecedor na partição vendor_boot.
Crie uma base de apoio
Para implementar o suporte à inicialização do fornecedor em um dispositivo:
Defina
BOARD_BOOT_HEADER_VERSIONcomo3ou maior.Defina
BOARD_RAMDISK_USE_LZ4comotruese o dispositivo for compatível com GKI ou se ele usar um ramdisk genérico compactado com lz4.Defina
BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZEcom um tamanho adequado para seu dispositivo, considerando os módulos do kernel que precisam estar no ramdisk do fornecedor.Atualize
AB_OTA_PARTITIONSpara incluirvendor_boote todas as listas de partições OTA específicas do fornecedor no dispositivo.Copie o dispositivo
fstabpara/first_stage_ramdiskna partiçãovendor_boot, não na partiçãoboot. Por exemplo:$(LOCAL_PATH)/fstab.hardware:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.$(PRODUCT_PLATFORM).
Para incluir vários ramdisks do fornecedor em vendor_boot:
- Defina
BOARD_BOOT_HEADER_VERSIONcomo4. Defina
BOARD_VENDOR_RAMDISK_FRAGMENTScomo uma lista de nomes de fragmentos de ramdisk lógicos do fornecedor a serem incluídos emvendor_boot.Para adicionar um ramdisk pré-criado do fornecedor, defina
BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILTcomo o caminho pré-criado.Para adicionar um ramdisk do fornecedor DLKM, defina
BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRScomo a lista de diretórios de módulos do kernel a serem incluídos.Defina
BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGScomo argumentosmkbootimg. Esses são os argumentos--board_id[0-15]e--ramdisk_typepara o fragmento do ramdisk do fornecedor. Para o ramdisk do fornecedor DLKM, o--ramdisk_typepadrão seriaDLKMse não fosse especificado de outra forma.
Para criar recursos de recuperação como um ramdisk recovery independente em vendor_boot:
- Defina
BOARD_BOOT_HEADER_VERSIONcomo4. - Defina
BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOTcomotrue. - Defina
BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOTcomotrue. - Isso adiciona um fragmento de ramdisk do fornecedor cujo
ramdisk_nameérecoveryeramdisk_typeéVENDOR_RAMDISK_TYPE_RECOVERY. O ramdisk contém todos os arquivos de recuperação, que são instalados em$(TARGET_RECOVERY_ROOT_OUT).
Argumentos mkbootimg
| Argumento | Descrição |
|---|---|
--ramdisk_type |
O tipo do ramdisk, que pode ser NONE,
PLATFORM, RECOVERY ou DLKM.
|
--board_id[0-15] |
Especifique o vetor board_id, que tem como padrão 0. |
Confira a seguir um exemplo de configuração:
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
O vendor_boot resultante teria dois fragmentos de ramdisk do fornecedor. O primeiro é o ramdisk "padrão", que contém o diretório DLKM baz e o restante dos arquivos em $(TARGET_VENDOR_RAMDISK_OUT). O segundo é o ramdisk dlkm_foobar, que contém os diretórios DLKM foo e bar, e o --ramdisk_type tem como padrão DLKM.