Módulos de kernel carregáveis

Como parte dos requisitos do kernel do módulo introduzidos no Android 8.0, todos os kernels system-on-chip (SoC) devem oferecer suporte a módulos de kernel carregáveis.

Opções de configuração do kernel

Para oferecer suporte a módulos de kernel carregáveis, android-base.cfg em todos os kernels comuns inclui as seguintes opções de configuração do kernel (ou seu equivalente na versão do kernel):

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

Todos os kernels de dispositivos devem habilitar essas opções. Os módulos do kernel também devem suportar descarregamento e recarregamento sempre que possível.

Assinatura do módulo

A assinatura de módulo não é compatível com os módulos do fornecedor GKI. Nos dispositivos necessários para dar suporte à inicialização verificada, o Android exige que os módulos do kernel estejam nas partições que têm o dm-verity ativado. Isso elimina a necessidade de assinar módulos individuais para sua autenticidade.

Locais de arquivos

Embora o Android 7.xe inferior não exija contra módulos do kernel (e inclua suporte para insmod e rmmod ), o Android 8.xe superior recomenda o uso de módulos do kernel no ecossistema. A tabela a seguir mostra o suporte a periféricos específicos da placa em potencial necessário em três modos de inicialização do Android.

Modo de inicialização Armazenar Exibição Teclado Bateria PMIC Tela sensível ao toque NFC, Wi-Fi,
Bluetooth
Sensores Câmera
Recuperação
Carregador
Android

Além da disponibilidade nos modos de inicialização do Android, os módulos do kernel também podem ser categorizados por quem os possui (o fornecedor do SoC ou o ODM). Se os módulos do kernel estiverem sendo usados, os requisitos para sua colocação no sistema de arquivos são os seguintes:

  • Todos os kernels devem ter suporte embutido para inicialização e montagem de partições.
  • Os módulos do kernel devem ser carregados de uma partição somente leitura.
  • Para dispositivos que precisam ter inicialização verificada, os módulos do kernel devem ser carregados de partições verificadas.
  • Os módulos do kernel não devem estar localizados em /system .
  • Os módulos do kernel do fornecedor do SoC que são necessários para os modos Android ou Charger completos devem estar localizados em /vendor/lib/modules .
  • Se existir uma partição ODM, os módulos do kernel do ODM que são necessários para os modos Android ou Charger completos devem estar localizados em /odm/lib/modules . Caso contrário, esses módulos devem estar localizados em /vendor/lib/modules .
  • Os módulos do kernel do fornecedor do SoC e do ODM que são necessários para o modo de recuperação devem estar localizados nas ramfs de recuperação em /lib/modules .
  • Os módulos do kernel necessários para o modo de recuperação e os modos Android ou Charger completos devem existir nos rootfs de recuperação e nas partições /vendor ou /odm (conforme descrito acima).
  • Os módulos do kernel usados ​​no modo de recuperação não devem depender de módulos localizados apenas em /vendor ou /odm , pois essas partições não são montadas no modo de recuperação.
  • Os módulos de kernel do fornecedor de SoC não devem depender de módulos de kernel ODM.

No Android 7.xe inferior, as partições /vendor e /odm não são montadas antecipadamente. No Android 8.xe superior, para possibilitar o carregamento do módulo dessas partições, foram tomadas provisões para montar partições antecipadamente para dispositivos não A/B e A/B . Isso também garante que as partições sejam montadas nos modos Android e Charger.

Suporte ao sistema de compilação do Android

Em BoardConfig.mk , a compilação do Android define uma variável BOARD_VENDOR_KERNEL_MODULES que fornece uma lista completa dos módulos do kernel destinados à imagem do fornecedor. Os módulos listados nesta variável são copiados para a imagem do fornecedor em /lib/modules/ e, após serem montados no Android, aparecem em /vendor/lib/modules (de acordo com os requisitos acima). Exemplo de configuração dos módulos do kernel do fornecedor:

vendor_lkm_dir := device/$(vendor)/lkm-4.x
BOARD_VENDOR_KERNEL_MODULES := \
  $(vendor_lkm_dir)/vendor_module_a.ko \
  $(vendor_lkm_dir)/vendor_module_b.ko \
  $(vendor_lkm_dir)/vendor_module_c.ko

Neste exemplo, um repositório pré-construído do módulo do kernel do fornecedor é mapeado para a compilação do Android no local listado acima.

A imagem de recuperação pode conter um subconjunto dos módulos do fornecedor. A compilação do Android define a variável BOARD_RECOVERY_KERNEL_MODULES para esses módulos. Exemplo:

vendor_lkm_dir := device/$(vendor)/lkm-4.x
BOARD_RECOVERY_KERNEL_MODULES := \
  $(vendor_lkm_dir)/vendor_module_a.ko \
  $(vendor_lkm_dir)/vendor_module_b.ko

A compilação do Android cuida da execução do depmod para gerar os arquivos modules.dep necessários em /vendor/lib/modules e /lib/modules ( recovery ramfs ).

Carregamento e versionamento de módulos

Carregue todos os módulos do kernel em uma passagem do init.rc* invocando modprobe -a . Isso evita a sobrecarga de inicializar repetidamente o ambiente de tempo de execução C para o binário modprobe . O evento early-init pode ser modificado para invocar modprobe :

on early-init
    exec u:r:vendor_modprobe:s0 -- /vendor/bin/modprobe -a -d \
        /vendor/lib/modules module_a module_b module_c ...

Normalmente, um módulo do kernel deve ser compilado com o kernel com o qual o módulo será usado (caso contrário, o kernel se recusa a carregar o módulo). CONFIG_MODVERSIONS fornece uma solução alternativa detectando interrupções na interface binária do aplicativo (ABI). Esse recurso calcula um valor de verificação de redundância cíclica (CRC) para o protótipo de cada símbolo exportado no kernel e armazena os valores como parte do kernel; para símbolos usados ​​por um módulo do kernel, os valores também são armazenados no módulo do kernel. Quando o módulo é carregado, os valores dos símbolos usados ​​pelo módulo são comparados com os do kernel. Se os valores corresponderem, o módulo será carregado; caso contrário, a carga falha.

Para habilitar a atualização da imagem do kernel separadamente da imagem do fornecedor, habilite CONFIG_MODVERSIONS . Isso permite que pequenas atualizações no kernel (como correções de bugs do LTS) sejam feitas, mantendo a compatibilidade com os módulos do kernel existentes na imagem do fornecedor. No entanto, CONFIG_MODVERSIONS não corrige uma quebra de ABI por si só. Se o protótipo de um símbolo exportado no kernel mudar, seja devido à modificação da fonte ou porque a configuração do kernel mudou, isso quebra a compatibilidade com os módulos do kernel que usam esse símbolo. Nesses casos, o módulo do kernel deve ser recompilado.

Por exemplo, a estrutura task_struct no kernel (definida em include/linux/sched.h ) contém muitos campos incluídos condicionalmente dependendo da configuração do kernel. O campo sched_info está presente somente se CONFIG_SCHED_INFO estiver habilitado (o que ocorre quando CONFIG_SCHEDSTATS ou CONFIG_TASK_DELAY_ACCT estão habilitados). Se essas opções de configuração mudarem de estado, o layout da estrutura task_struct alterado e quaisquer interfaces exportadas do kernel que usam task_struct serão alteradas (por exemplo, set_cpus_allowed_ptr em kernel/sched/core.c ). A compatibilidade com módulos do kernel compilados anteriormente que usam essas interfaces quebra, exigindo que esses módulos sejam reconstruídos com a nova configuração do kernel.

Para obter mais detalhes sobre CONFIG_MODVERSIONS , consulte a documentação na árvore do kernel em Documentation/kbuild/modules.rst .