Módulos de kernel cargables

Como parte de los requisitos del kernel del módulo introducidos en Android 8.0, todos los kernels del sistema en chip (SoC) deben admitir módulos del kernel cargables.

Opciones de configuración del núcleo

Para admitir módulos de kernel cargables, android-base.config en todos los kernels comunes incluye las siguientes opciones de configuración del kernel (o su equivalente en la versión del kernel):

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

Todos los núcleos de dispositivos deben habilitar estas opciones. Los módulos del kernel también deberían admitir la descarga y recarga siempre que sea posible.

Firma de módulo

La firma de módulos no es compatible con los módulos de proveedores de GKI. En los dispositivos necesarios para admitir el arranque verificado, Android requiere que los módulos del kernel estén en las particiones que tienen dm-verity habilitado. Esto elimina la necesidad de firmar módulos individuales para garantizar su autenticidad. Android 13 introdujo el concepto de módulos GKI. Los módulos GKI utilizan la infraestructura de firma en tiempo de compilación del kernel para diferenciar entre GKI y otros módulos en tiempo de ejecución. Los módulos sin firmar pueden cargarse siempre que solo utilicen símbolos que aparecen en la lista de permitidos o proporcionados por otros módulos sin firmar. Para facilitar la firma de módulos GKI durante la compilación de GKI utilizando el par de claves de tiempo de compilación del kernel, la configuración del kernel de GKI ha habilitado CONFIG_MODULE_SIG_ALL=y . Para evitar firmar módulos que no sean GKI durante la compilación del kernel del dispositivo, debe agregar # CONFIG_MODULE_SIG_ALL is not set como parte de los fragmentos de configuración del kernel.

Ubicaciones de archivos

Si bien Android 7.x y versiones anteriores no exigen módulos del kernel (e incluyen soporte para insmod y rmmod ), Android 8.x y versiones posteriores recomiendan el uso de módulos del kernel en el ecosistema. La siguiente tabla muestra la posible compatibilidad con periféricos específicos de la placa requerida en tres modos de inicio de Android.

El modo de inicio Almacenamiento Mostrar Teclado Batería PMIC Pantalla táctil NFC, Wi-Fi,
Bluetooth
Sensores Cámara
Recuperación
Cargador
Androide

Además de la disponibilidad en los modos de arranque de Android, los módulos del kernel también pueden clasificarse según quién los posee (el proveedor de SoC o el ODM). Si se utilizan módulos del kernel, los requisitos para su ubicación en el sistema de archivos son los siguientes:

  • Todos los núcleos deben tener soporte integrado para arrancar y montar particiones.
  • Los módulos del kernel deben cargarse desde una partición de solo lectura.
  • Para los dispositivos que requieren un arranque verificado, los módulos del kernel deben cargarse desde particiones verificadas.
  • Los módulos del kernel no deben ubicarse en /system .
  • Los módulos GKI necesarios para el dispositivo deben cargarse desde /system/lib/modules , que es un enlace simbólico a /system_dlkm/lib/modules .
  • Los módulos del kernel del proveedor de SoC que se requieren para los modos completos de Android o Charger deben ubicarse en /vendor/lib/modules .
  • Si existe una partición ODM, los módulos del kernel del ODM que se requieren para los modos completos de Android o Charger deben ubicarse en /odm/lib/modules . De lo contrario, estos módulos deben ubicarse en /vendor/lib/modules .
  • Los módulos del kernel del proveedor de SoC y ODM que se requieren para el modo de recuperación deben ubicarse en las ramfs de recuperación en /lib/modules .
  • Los módulos del kernel necesarios tanto para el modo de recuperación como para los modos completos de Android o Charger deben existir tanto en los rootfs de recuperación como en las particiones /vendor o /odm (como se describe anteriormente).
  • Los módulos del kernel utilizados en el modo de recuperación no deberían depender de módulos ubicados solo en /vendor o /odm , ya que esas particiones no están montadas en el modo de recuperación.
  • Los módulos del kernel del proveedor de SoC no deberían depender de los módulos del kernel ODM.

En Android 7.x y versiones anteriores, las particiones /vendor y /odm no se montan anticipadamente. En Android 8.x y versiones posteriores, para hacer posible la carga de módulos desde estas particiones, se han tomado medidas para montar particiones anticipadamente para dispositivos A/B y no A/B . Esto también garantiza que las particiones estén montadas tanto en modo Android como en modo Cargador.

Soporte del sistema de compilación Android

En BoardConfig.mk , la compilación de Android define una variable BOARD_VENDOR_KERNEL_MODULES que proporciona una lista completa de los módulos del kernel destinados a la imagen del proveedor. Los módulos enumerados en esta variable se copian en la imagen del proveedor en /lib/modules/ y, después de montarse en Android, aparecen en /vendor/lib/modules (de acuerdo con los requisitos anteriores). Ejemplo de configuración de los módulos del kernel del proveedor:

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

En este ejemplo, un repositorio prediseñado del módulo del kernel del proveedor se asigna a la compilación de Android en la ubicación indicada anteriormente.

La imagen de recuperación puede contener un subconjunto de los módulos del proveedor. La compilación de Android define la variable BOARD_RECOVERY_KERNEL_MODULES para estos módulos. Ejemplo:

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

La compilación de Android se encarga de ejecutar depmod para generar los archivos modules.dep necesarios en /vendor/lib/modules y /lib/modules ( recovery ramfs ).

Carga y versionado de módulos

Cargue todos los módulos del kernel de una sola vez desde init.rc* invocando modprobe -a . Esto evita la sobrecarga de inicializar repetidamente el entorno de ejecución de C para el binario modprobe . El evento early-init se puede modificar 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, un módulo del kernel debe compilarse con el kernel con el que se va a utilizar (de lo contrario, el kernel se niega a cargar el módulo). CONFIG_MODVERSIONS proporciona una solución al detectar roturas en la interfaz binaria de la aplicación (ABI). Esta característica calcula un valor de verificación de redundancia cíclica (CRC) para el prototipo de cada símbolo exportado en el kernel y almacena los valores como parte del kernel; para los símbolos utilizados por un módulo del kernel, los valores también se almacenan en el módulo del kernel. Cuando se carga el módulo, los valores de los símbolos utilizados por el módulo se comparan con los del kernel. Si los valores coinciden, se carga el módulo; de lo contrario, la carga falla.

Para habilitar la actualización de la imagen del kernel por separado de la imagen del proveedor, habilite CONFIG_MODVERSIONS . Hacerlo permite realizar pequeñas actualizaciones del kernel (como correcciones de errores de LTS) manteniendo la compatibilidad con los módulos del kernel existentes en la imagen del proveedor. Sin embargo, CONFIG_MODVERSIONS no soluciona una rotura de ABI por sí solo. Si el prototipo de un símbolo exportado en el kernel cambia, ya sea debido a una modificación de la fuente o porque la configuración del kernel cambió, esto rompe la compatibilidad con los módulos del kernel que usan ese símbolo. En tales casos, se debe volver a compilar el módulo del kernel.

Por ejemplo, la estructura task_struct en el kernel (definida en include/linux/sched.h ) contiene muchos campos incluidos condicionalmente dependiendo de la configuración del kernel. El campo sched_info está presente solo si CONFIG_SCHED_INFO está habilitado (lo que ocurre cuando CONFIG_SCHEDSTATS o CONFIG_TASK_DELAY_ACCT están habilitados). Si estas opciones de configuración cambian de estado, el diseño de la estructura task_struct cambia y cualquier interfaz exportada desde el kernel que use task_struct se modifica (por ejemplo, set_cpus_allowed_ptr en kernel/sched/core.c ). La compatibilidad con módulos del kernel previamente compilados que utilizan estas interfaces se interrumpe, lo que requiere que esos módulos se reconstruyan con la nueva configuración del kernel.

Para obtener más detalles sobre CONFIG_MODVERSIONS , consulte la documentación en el árbol del kernel en Documentation/kbuild/modules.rst .