O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Imagem Genérica do Kernel

O Android Núcleo Comum (ACKs) são a base para todos os kernels produtos Android. Os kernels do fornecedor e do dispositivo são posteriores aos ACKs. Os fornecedores adicionam suporte para SoCs e dispositivos periféricos, modificando o código-fonte do kernel e adicionando drivers de dispositivo. Essas modificações podem ser extensa, a tal ponto que, tanto quanto 50% do código em execução em um dispositivo é código de out-of-árvore (não de Linux a montante ou a partir AOSP kernels comuns).

Assim, um kernel de dispositivo é composto por:

  • Upstream: O kernel Linux de kernel.org
  • AOSP: patches específicos para Android adicionais de kernels comuns AOSP
  • Fornecedor: SoC e patches de ativação e otimização de periféricos de fornecedores
  • OEM / dispositivo: drivers de dispositivo adicionais e personalizações

Quase todos os dispositivos possuem um kernel personalizado. Esta é a fragmentação do kernel.

Hierarquia do kernel do Android leva à fragmentação

Figura 1. hierarquia do kernel leva Android à fragmentação

Os custos da fragmentação

A fragmentação do kernel tem vários efeitos negativos na comunidade Android.

As atualizações de segurança são trabalhosas

Patches de segurança citados no Boletim de Segurança Android (ASB) deve ser portado para cada um dos núcleos do dispositivo. No entanto, devido à fragmentação do kernel, é proibitivamente caro propagar correções de segurança para dispositivos Android em campo.

Difícil de mesclar atualizações com suporte de longo prazo

A longo prazo suportados (LTS) lançamentos incluem correções de segurança e outras correções de erros críticos. Manter-se atualizado com os lançamentos LTS provou ser a maneira mais eficaz de fornecer correções de segurança. Em dispositivos Pixel, foi descoberto que 90% dos problemas de segurança do kernel relatados no ASB já haviam sido corrigidos para dispositivos que se mantêm atualizados.

No entanto, com todas as modificações personalizadas nos kernels do dispositivo, é difícil apenas mesclar as correções LTS nos kernels do dispositivo.

Inibe atualizações de versão da plataforma Android

A fragmentação dificulta a adição de novos recursos do Android que exigem alterações de kernel aos dispositivos em campo. O código do Android Framework deve assumir que até cinco versões de kernel são suportadas e que nenhuma alteração de kernel foi feita para o lançamento da nova plataforma (Android 10 suporta os kernels 3.18, 4.4, 4.9, 4.14 e 4.19, que em alguns casos não foram aprimorado com novos recursos desde o Android 8 em 2017).

É difícil contribuir com mudanças do kernel para o Linux upstream

Com todas as mudanças feitas no kernel, a maioria dos dispositivos principais vem com uma versão do kernel que já tem pelo menos 18 meses. Por exemplo, o kernel 4.14 foi lançado pela kernel.org em Novembro de 2017 e os primeiros telefones Android usando 4.14 kernels enviados na Primavera de 2019.

Esse longo atraso entre o lançamento do kernel upstream e os produtos torna difícil para a comunidade Android alimentar os recursos e drivers necessários nos kernels upstream, por isso é um desafio corrigir o problema de fragmentação.

Corrigindo a fragmentação: Imagem Genérica do Kernel

Os endereços do projeto kernel genérico Imagem (GKI) do kernel fragmentação, unificando o kernel do núcleo e movendo SoC e suporte de placa para fora do kernel do núcleo em módulos carregáveis. Apresenta a GKI kernel um estáveis Kernel Module Interface (KMI) para os módulos do kernel, por isso, módulos e kernel pode ser atualizado de forma independente.

O GKI:

  • É construído a partir das fontes ACK.
  • É um single-binário do kernel mais associado módulos carregáveis por arquitetura, por versão LTS (atualmente apenas arm64 para android11-5.4 e android12-5.4 ).
  • É testado com todos os lançamentos da plataforma Android que são suportados para o ACK associada. Não há suspensão de uso de recursos durante o tempo de vida de uma versão do kernel GKI
  • Expõe um estábulo KMI aos condutores dentro de um determinado LTS.
  • Não contém SoC- ou código específico de bordo.

Esta é a aparência de um dispositivo Android com GKI implementado.

Arquitetura GKI

Arquitetura Figura 2. GKI

GKI é uma mudança complexa que será lançada em vários estágios, começando com os kernels v5.4 no lançamento da plataforma Android 11.

GKI 1.0 - requisitos de compatibilidade GKI

Para alguns dispositivos lançados com o lançamento da plataforma Android 11, a compatibilidade do Treble requer o teste GKI para dispositivos que executam kernels v5.4.

Partições para teste de compatibilidade GKI

Figura 3. As divisórias para testes de compatibilidade GKI

GKI meio de compatibilidade que o dispositivo passa o VTS e CTS-em-GSI testes + GKI com a imagem do sistema genérico (GSI) e o kernel GKI instalado através do escoamento da imagem de arranque GKI para a boot da partição e imagem do sistema GSI no system partição. Os dispositivos podem enviar com um kernel produto diferente e pode usar módulos carregáveis que GKI não fornece. No entanto, tanto o produto como kernels GKI deve carregar módulos a partir dos mesmos vendor_boot e vendor partições. Portanto, todos os kernels do produto devem ter a mesma interface de módulo de kernel binário (KMI). Os fornecedores podem estender o KMI para kernels de produtos, desde que permaneça compatível com o GKI KMI. Não há nenhum requisito de que os módulos do fornecedor sejam descarregáveis ​​no GKI 1.0.

GKI 1.0 gols

  • Não introduza regressões no VTS ou CTS quando o kernel do produto é substituído pelo kernel GKI.
  • Reduza a carga de manutenção do kernel para OEMs e fornecedores para se manterem atualizados com os kernels comuns do AOSP.
  • Inclui as principais alterações do Android nos kernels, quer um dispositivo seja atualizado para uma nova versão da plataforma Android ou recém-lançado.
  • Nunca interrompa o espaço de usuário do Android.
  • Separe os componentes específicos de hardware do kernel central como módulos carregáveis.

GKI 2.0 - produtos GKI

Alguns dispositivos que lançamento com o Android 12 (2021) liberação plataforma usando versões do kernel V5.10 ou superior são necessários para navio com o kernel GKI. Imagens de inicialização certificadas serão disponibilizadas e atualizadas regularmente com LTS e correções de bugs críticos. Como a estabilidade binária será mantida para o KMI, essas imagens de inicialização podem ser instaladas sem alterações nas imagens do fornecedor.

Metas GKI 2.0

  • Não introduza desempenho significativo ou regressões de potência com GKI.
  • Permita que os OEMs forneçam correções de segurança do kernel e correções de bugs (LTS) sem o envolvimento do fornecedor.
  • Reduza o custo de atualização da versão principal do kernel para dispositivos (por exemplo, da v5.10 para o kernel 2021 LTS).
  • Mantenha apenas um binário do kernel GKI por arquitetura, atualizando as versões do kernel com um processo claro de atualização.

Integração GKI 2.0 boot.img

Defina as seguintes variáveis de tabuleiro para incorporar uma GKI certificado boot.img em sua base de código. A configuração mostrada abaixo é apenas um exemplo a ser ajustado por dispositivo.

# Uses a prebuilt boot.img
TARGET_NO_KERNEL := true
BOARD_PREBUILT_BOOTIMAGE := device/${company}/${board}/boot.img

# Enables chained vbmeta for the boot.img so it can be updated independently,
# without updating the vbmeta.img. The following configs are optional.
# When they're absent, the hash of the boot.img will be stored then signed in
# the vbmeta.img.
BOARD_AVB_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem
BOARD_AVB_BOOT_ALGORITHM := SHA256_RSA4096
BOARD_AVB_BOOT_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
BOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION := 2

Design GKI

Ramos do kernel KMI

Kernels GKI são construídos a partir dos ramos do kernel ACK KMI, que são descritos em detalhes no Android Núcleo Comum . O KMI é unicamente identificado pela versão do kernel eo lançamento da plataforma Android, de modo que os ramos são nomeados <androidRelease>-<kernel version> . Por exemplo, o KMI ramo 5.4 do kernel para Android 11 é nomeado android11-5.4. Espera-se que para o Android 12 (não cometeu e mostrados apenas para demonstrar como o novo modelo de ramificação vai progredir no futuro) haverá dois KMI adicional kernels, android12-5.4 e um segundo kernel baseado no 5.10 LTS kernel, lançado em dezembro 2020.

Hierarquia de kernel KMI

A Figura 4 mostra a hierarquia de ramificações para os kernels KMI 5.10. android12-5.10 é o kernel KMI correspondente ao Android 12 e android13-5.10 corresponde a T Android (AOSP experimental) (não comprometida e mostrada apenas para demonstrar como o novo modelo de ramificao vai progredir no futuro).

Hierarquia de kernel KMI para 5.10

Hierarquia Figura 4. KMI kernel para 5,10

Como mostrado na Figura 4, o KMI ramos ciclo através de três fases: desenvolvimento (dev), estabilização (facada), e congelado. Essas fases são descritas em detalhe no Android Núcleo Comum .

Depois que o kernel KMI é congelado, nenhuma alteração de quebra de KMI é aceita, a menos que um problema de segurança sério seja identificado e não possa ser mitigado sem afetar o KMI estável. O galho permanece congelado durante toda a sua vida.

Correções de bugs e recursos de parceiros podem ser aceitos em um branch congelado, desde que o KMI existente não seja quebrado. O KMI pode ser estendido com novos símbolos exportados, desde que as interfaces que compõem o KMI atual não sejam afetadas. Quando novas interfaces são adicionadas ao KMI, elas se tornam estáveis ​​imediatamente e não podem ser interrompidas por mudanças futuras.

Por exemplo, uma alteração que adiciona um campo a uma estrutura usada por uma interface KMI não é permitida porque altera a definição da interface:

struct foo {
  int original_field1;
  int original_field2;
  int new_field; // Not allowed
};

int do_foo(struct foo &myarg)
{
  do_something(myarg);
}
EXPORT_SYMBOL_GPL(do_foo);

No entanto, não há problema em adicionar uma nova função:

struct foo_ext {
  struct foo orig_foo;
  int new_field;
};

int do_foo2(struct foo_ext &myarg)
{
  do_something_else(myarg);
}
EXPORT_SYMBOL_GPL(do_foo2);

Estabilidade KMI

Para realizar os objetivos da GKI, é fundamental manter um KMI estável para os motoristas. O kernel GKI é construído e distribuído na forma binária, mas os módulos carregáveis ​​pelo fornecedor são construídos em uma árvore separada. O kernel GKI resultante e os módulos devem funcionar como se fossem construídos juntos. Para o teste de compatibilidade GKI, a imagem de inicialização contendo o kernel é substituída por uma imagem de inicialização contendo o kernel GKI, e os módulos carregáveis ​​na imagem do fornecedor devem funcionar corretamente com qualquer um dos kernel.

O KMI não inclui todos os símbolos do kernel ou mesmo todos os mais de 30.000 símbolos exportados. Em vez disso, os símbolos que podem ser usados ​​pelos módulos são explicitamente listados em um conjunto de arquivos de lista de símbolos mantidos publicamente na raiz da árvore do kernel. A união de todos os símbolos em todos os arquivos da lista de símbolos define o conjunto de símbolos KMI mantidos como estáveis. Um arquivo de lista de símbolos exemplo é abi_gki_aarch64_db845c , que declara os símbolos necessários para a 845c DragonBoard .

Apenas os símbolos listados em uma lista de símbolos e suas estruturas e definições relacionadas são considerados parte do KMI. Você pode publicar alterações em suas listas de símbolos se os símbolos de que você precisa não estiverem presentes. Depois que as novas interfaces estão em uma lista de símbolos e, portanto, fazem parte da descrição KMI, elas são mantidas como estáveis ​​e não devem ser removidas da lista de símbolos ou modificadas depois que o branch é congelado.

Cada árvore do kernel KMI tem seu próprio conjunto de listas de símbolos. Nenhuma tentativa é feita para fornecer estabilidade ABI entre diferentes ramos do kernel KMI. Por exemplo, o KMI para android11-5.4 é completamente independente da KMI para android12-5.4 .

Geralmente, a comunidade Linux tem franziu a testa sobre a noção de em-kernel estabilidade ABI para o kernel. Diante de diferentes conjuntos de ferramentas, configurações e um kernel do Linux em constante evolução, não é viável manter um KMI estável na linha principal. No entanto, é possível no ambiente GKI altamente restrito. As restrições são:

  • O KMI só é estável dentro de um mesmo núcleo LTS (por exemplo, android11-5.4 ).
    • Estabilidade não KMI é mantida para android-mainline .
  • Apenas o específica Clang conjunto de ferramentas fornecido em AOSP e definido para o ramo correspondente é usado para a construção de kernel e módulos.
  • Somente os símbolos conhecidos por serem usados ​​pelos módulos, conforme especificado em uma lista de símbolos, são monitorados quanto à estabilidade e considerados símbolos KMI.
    • O corolário é que os módulos devem usar apenas símbolos KMI. Isso é reforçado por cargas de módulo com falha se símbolos não KMI forem necessários.
  • Depois que a ramificação KMI é congelada, nenhuma alteração pode interromper o KMI, incluindo:
    • Mudanças de configuração
    • Mudanças no código do kernel
    • Mudanças no conjunto de ferramentas (incluindo atualizações)

Monitoramento KMI

Ferramentas de monitoramento ABI monitorar a estabilidade KMI durante os testes presubmit. As alterações que quebram o KMI falham no teste de pré-envio e devem ser retrabalhadas para serem compatíveis. Essas ferramentas estão disponíveis aos parceiros e ao público para integração em seus processos de construção. A equipe do kernel do Android usa essas ferramentas para encontrar quebras de KMI durante o desenvolvimento e ao mesclar versões LTS. Se uma quebra de KMI for detectada durante uma fusão LTS, o KMI será preservado removendo os patches ofensivos ou refatorando os patches para serem compatíveis.

O KMI é considerado quebrado se um símbolo KMI existente for modificado de forma incompatível. Exemplos:

  • Novo argumento adicionado a uma função KMI
  • Novo campo adicionado a uma estrutura usada por uma função KMI
  • Novo valor de enum adicionado que altera os valores de enums usados ​​por uma função KMI
  • Mudança de configuração que altera a presença de membros de dados que afetam o KMI

Adicionar novos símbolos não quebra necessariamente o KMI, mas os novos símbolos usados ​​devem ser adicionados a uma lista de símbolos e à representação ABI. Caso contrário, as alterações futuras nesta parte do KMI não serão reconhecidas.

Espera-se que os parceiros usem as ferramentas de monitoramento ABI para comparar o KMI entre o kernel do produto e o kernel GKI e garantir que sejam compatíveis.

A última android11-5.4 binário GKI kernel pode ser descarregado a partir ci.android.com ( kernel_aarch64 compilações).

Compilador único

Uma alteração do compilador pode alterar o layout da estrutura de dados do kernel interno que afeta a ABI. Por ser crítico para manter o KMI estável, o conjunto de ferramentas usado para construir o kernel GKI deve ser completamente compatível com o conjunto de ferramentas usado para construir módulos do fornecedor. O kernel GKI é construído com o conjunto de ferramentas LLVM incluído no AOSP.

A partir do Android 10, todos os kernels do Android devem ser construídos com um conjunto de ferramentas LLVM. Com o GKI, o conjunto de ferramentas LLVM usado para construir kernels de produtos e módulos do fornecedor deve gerar o mesmo ABI que o conjunto de ferramentas LLVM do AOSP e os parceiros devem garantir que o KMI seja compatível com o kernel GKI.

A documentação de construção do kernel Android descreve como o kernel GKI referência é construído. Em particular, o procedimento documentado garante que o conjunto de ferramentas e a configuração corretos sejam usados ​​para construir o kernel. Os parceiros downstream são incentivados a usar as mesmas ferramentas para produzir o kernel final para evitar incompatibilidades causadas por toolchain ou outras dependências de tempo de construção.

Configurações de kernel

O kernel GKI é construído usando arch/arm64/configs/gki_defconfig . Como essas configurações afetam o KMI, as configurações GKI são gerenciadas com muito cuidado. Para os kernels KMI congelados, as configurações só podem ser adicionadas ou removidas se não houver impacto no KMI.

O kernel GKI deve ser configurado para rodar em diversos conjuntos de dispositivos. Portanto, ele deve ter suporte integrado para todos os subsistemas e opções necessárias para todos esses dispositivos, não incluindo os módulos carregáveis ​​que são usados ​​para habilitar o hardware. Os parceiros devem solicitar as alterações de configuração necessárias para kernels dev.

Mudanças de inicialização

Para facilitar uma clara separação entre os componentes GKI e fornecedores, a boot partição contém apenas componentes genéricos, incluindo o kernel e um ramdisk com módulos GKI. Uma nova versão do cabeçalho de inicialização (v3) é definida para indicar conformidade com a arquitetura GKI. A versão GKI das imagens de inicialização é fornecida pelo Google e substitui a versão do fornecedor da imagem de inicialização ao testar a compatibilidade do GKI.

O disco em memória para uma primeira fase init , recovery , e fastbootd é um initramfs imagem composta de dois arquivos CPIO que são concatenados pelo carregador de arranque. O primeiro arquivo CPIO vem da nova vendor_boot partição. A segunda vem da boot partição.

Um resumo das mudanças é fornecido aqui. Veja partição de boot do fornecedor para mais detalhes.

Partição de inicialização

A boot da partição inclui um cabeçalho, núcleo, e uma CPIO arquivar da porção genérica do disco em memória de inicialização.

Com uma v3 cabeçalho de inicialização boot da partição, estas secções da prévia boot da partição não está mais presente são os seguintes:

  • Carregador de boot de segundo estágio: Se um dispositivo tiver um carregador de boot de segundo estágio, ele deve ser armazenado em sua própria partição.
  • DTB: O DTB é armazenado na partição de inicialização do fornecedor .

A boot partição contém um arquivo CPIO com os componentes GKI:

  • Módulos do kernel GKI localizados em /lib/modules/
  • first_stage_init e bibliotecas que depende
  • fastbootd e recovery (usado em A / B e virtual A / dispositivos B)

A imagem de inicialização GKI é fornecido pelo Google e deve ser usado para testes de compatibilidade GKI .

A última arm64 android11-5.4 boot.img está disponível para download a partir ci.android.com nas aosp_arm64 artefatos de construção da aosp-master ramo.

A última arm64 android11-5.4 imagem de kernel ( Image.gz ) pode ser baixado do ci.android.com nas kernel_aarch64 artefatos de construção do aosp_kernel-common-android11-5.4 ramo.

Partição de inicialização do fornecedor

O vendor_boot partição é introduzido com GKI. É A / B'd com Virtual A / B e consiste em um cabeçalho, o ramdisk do fornecedor e o blob da árvore de dispositivos. O ramdisk do fornecedor é um arquivo CPIO que contém os módulos do fornecedor necessários para a inicialização do dispositivo. Isso inclui módulos para habilitar a funcionalidade SoC crítica, bem como drivers de armazenamento e exibição necessários para inicializar o dispositivo e exibir telas iniciais.

O arquivo CPIO contém:

  • Primeira fase init fornecedor do kernel módulos localizados em /lib/modules/
  • modprobe configuração arquivos localizados em /lib/modules
  • modules.load arquivo indicando os módulos de carga durante o primeiro estágio- init

Requisito de bootloader

O bootloader deve carregar a imagem ramdisk CPIO genérico (a partir do boot partição ) na memória imediatamente após a imagem CPIO fornecedor ramdisk (do vendor_boot partição ). Após a descompactação, o resultado é o ramdisk genérico sobreposto à estrutura de arquivos do ramdisk do fornecedor.

Teste de compatibilidade GKI

Para o lançamento da plataforma Android 11, os dispositivos lançados com um kernel v5.4 devem executar os testes VTS e CTS-on-GSI usando a imagem de inicialização GKI fornecida pelo Google.

Contribuindo para o kernel GKI

O kernel GKI é construído a partir da AOSP Núcleo Comum começando com android11-5.4 . Todos os patches apresentados devem estar em conformidade com estas diretrizes de contribuição , que documentam duas estratégias:

  1. Melhor: Faça todas as suas alterações para montante Linux. Se apropriado, faça backport para as versões estáveis. Esses patches são mesclados automaticamente nos kernels comuns AOSP correspondentes. Se o patch já estiver no Linux upstream, poste um backport do patch que esteja de acordo com os requisitos de patch abaixo.
  2. Menos boa: Desenvolver seus patches fora da árvore (a partir de um ponto de Linux a montante do ponto de vista). A menos que os patches são a fixação de um bug específico do Android, eles são muito improvável de ser aceita a menos que tenha sido coordenada com kernel-team@android.com .

Para parceiros que implementam GKI, pode haver razões válidas pelas quais patches fora da árvore são necessários (especialmente se houver cronogramas de silício que devem ser cumpridos). Para esses casos, apresentar uma Buganizer questão.

Enviar alterações GKI através Gerrit ao android-mainline ramo primeiro e depois portado para outros ramos de liberação, conforme necessário.

O código-fonte associado aos módulos carregáveis ​​não precisa ser contribuído para a árvore de origem do kernel GKI, no entanto, é altamente recomendável enviar todos os drivers para o Linux upstream.

Solicitando backports da linha principal

Geralmente, os patches da linha principal necessários aos parceiros GKI podem ser portados para os kernels GKI. Carregar o patch para Gerrit seguindo as diretrizes de contribuição .

Se o patch foi postado no upstream, mas ainda não foi aceito, é recomendável esperar até que ele seja aceito. Se a programação não permitir esperar que o patch seja aceito no upstream, registre um problema com o Buganizer.

Adicionar e remover configurações GKI

Para solicitar a adição ou remoção de configurações em arch/arm64/configs/gki_defconfig , apresentar uma questão Buganizer afirmando claramente o pedido e a justificação. Se não houver um projeto Buganizer acessível, poste o patch com a mudança de configuração no Gerrit e certifique-se de que a mensagem de confirmação indique claramente o motivo pelo qual a configuração é necessária.

As alterações de configuração em kernels KMI congelados não devem afetar o KMI.

Modificando o código do núcleo do kernel

Modificações no código do núcleo do kernel em kernels comuns AOSP são desencorajadas. Primeiro, envie patches para o Linux upstream e depois faça o backport deles. Se houver um bom motivo para uma mudança no kernel principal, registre um problema do Buganizer declarando claramente a solicitação e a justificativa. Nenhum recurso específico do Android é aceito sem um problema com o Buganizer. Envie um email para kernel-team@android.com se você não pode criar um problema Buganizer.

Exportando símbolos usando via EXPORT_SYMBOL_GPL ()

Não envie patches que contenham apenas exportações de símbolos. Para ser considerado para Linux upstream, adições de EXPORT_SYMBOL_GPL() requer um driver modular in-árvore que usa o símbolo, por isso, incluir o novo driver ou mudanças para um driver existente no mesmo patchset como a exportação.

Ao enviar patches para o upstream, a mensagem de confirmação deve conter uma justificativa clara de porque o patch é necessário e benéfico para a comunidade. Habilitar exportações para beneficiar drivers ou funcionalidade fora da árvore não é um argumento convincente para os mantenedores originais.

Se por algum motivo, o patch não pode ser enviado upstream, registre um problema do Buganizer e explique por que o patch não pode ser enviado upstream. Geralmente, os símbolos que são a interface com suporte para um subsistema do kernel provavelmente são aceitos como exportações. No entanto, é improvável que funções auxiliares internas aleatórias sejam aceitas e você será solicitado a refatorar seu código para evitar usá-las.

Adicionando um símbolo a uma lista de símbolos KMI

As listas de símbolos KMI devem ser atualizadas com os símbolos do kernel GKI usados ​​pelos módulos do fornecedor. Como apenas os símbolos KMI são mantidos estáveis, o GKI não permite que os módulos sejam carregados se eles dependerem de um símbolo não KMI.

O extract_symbols roteiro extrai os símbolos relevantes de uma árvore de compilação do kernel e pode ser usado para atualizar uma lista símbolo KMI. Consulte a documentação símbolo de listagem para obter mais detalhes.