Módulos de kernel cargables

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

Opciones de configuración del kernel

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 de versión del kernel):

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

Todos los kernels del dispositivo deben habilitar estas opciones. Los módulos de kernel también deben admitir la descarga y la recarga siempre que sea posible.

Firma de módulos

La firma de módulos no es compatible con los módulos de proveedores de GKI. En los dispositivos que deben admitir el inicio verificado, Android requiere que los módulos del kernel estén en las particiones que tienen habilitado dm-verity. Esto quita la necesidad de firmar módulos individuales para su autenticidad. Android 13 introdujo el concepto de módulos de GKI. Los módulos de GKI usan la infraestructura de firma del tiempo de compilación del kernel para diferenciar entre GKI y otros módulos en el tiempo de ejecución. Los módulos sin firmar pueden cargarse, siempre y cuando solo usen símbolos que aparezcan en la lista de entidades permitidas o que proporcionen otros módulos sin firmar. Para facilitar la firma de módulos de GKI durante la compilación de GKI con el par de claves del tiempo de compilación del kernel, la configuración del kernel de GKI habilitó CONFIG_MODULE_SIG_ALL=y. Para evitar firmar módulos que no sean de GKI durante las compilaciones del kernel del dispositivo, debes 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 imponen módulos de kernel (y admiten insmod y rmmod), Android 8.x y versiones posteriores recomiendan el uso de módulos de kernel en el ecosistema. En la siguiente tabla, se muestra la posible compatibilidad con periféricos específicos de la placa que se requiere en los tres modos de inicio de Android.

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

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

  • Todos los kernels deben tener compatibilidad integrada para el inicio y el acceso a las particiones.
  • Los módulos de kernel se deben cargar desde una partición de solo lectura.
  • En el caso de los dispositivos que deben tener un inicio verificado, los módulos de kernel deben cargarse desde particiones verificadas.
  • Los módulos de kernel no deben estar ubicados en /system.
  • Los módulos de GKI necesarios para el dispositivo deben cargarse desde /system/lib/modules, que es un vínculo simbólico a /system_dlkm/lib/modules.
  • Los módulos de kernel del proveedor del SoC que se requieren para los modos de cargador o Android completos deben ubicarse en /vendor/lib/modules.
  • Si existe una partición de ODM, los módulos de kernel del ODM que se requieren para los modos de Android o cargador completos deben ubicarse en /odm/lib/modules. De lo contrario, estos módulos deben estar ubicados en /vendor/lib/modules.
  • Los módulos de kernel del proveedor del SoC y del ODM que se requieren para el modo de recuperación deben ubicarse en el ramfs de recuperación en /lib/modules.
  • Los módulos de kernel necesarios para el modo de recuperación y los modos de Android o cargador completos deben existir en el rootfs de recuperación y en las particiones /vendor o /odm (como se describió anteriormente).
  • Los módulos de kernel que se usan en el modo de recuperación no deben depender de módulos ubicados solo en /vendor o /odm, ya que esas particiones no se activan en el modo de recuperación.
  • Los módulos de kernel del proveedor del SoC no deben depender de los módulos de kernel del ODM.

En Android 7.x y versiones anteriores, las particiones /vendor y /odm no se activan antes. En Android 8.x y versiones posteriores, para que sea posible la carga de módulos desde estas particiones, se realizaron provisiones para activar las particiones con anticipación en dispositivos no A/B y A/B. Esto también garantiza que las particiones se monten en los modos Android y Charger.

Compatibilidad con el sistema de compilación de 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 que se enumeran 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 precompilado de un módulo de kernel del proveedor se asigna a la compilación de Android en la ubicación mencionada 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 control de versiones de módulos

Carga todos los módulos de kernel en un solo pase desde init.rc* invocando modprobe -a. Esto evita la sobrecarga de inicializar repetidamente el entorno de ejecución de C para el objeto 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 ...

Por lo general, un módulo de kernel se debe compilar con el kernel con el que se usará (de lo contrario, el kernel se negará a cargar el módulo). CONFIG_MODVERSIONS proporciona una solución alternativa mediante la detección de fallas en la interfaz binaria de la aplicación (ABI). Esta función 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. En el caso de los símbolos que usa un módulo de kernel, los valores también se almacenan en el módulo de kernel. Cuando se carga el módulo, los valores de los símbolos que usa 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, habilita CONFIG_MODVERSIONS. Esto permite realizar pequeñas actualizaciones del kernel (como correcciones de errores de LTS) y, al mismo tiempo, mantener la compatibilidad con los módulos de kernel existentes en la imagen del proveedor. Sin embargo, CONFIG_MODVERSIONS no corrige una ruptura de ABI por sí sola. Si cambia el prototipo de un símbolo exportado en el kernel, ya sea debido a la modificación de la fuente o porque cambió la configuración del kernel, se inhabilita la compatibilidad con los módulos del kernel que usan ese símbolo. En esos 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 que se incluyen de forma condicional según la configuración del kernel. El campo sched_info solo está presente 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 se alteran las interfaces exportadas del kernel que usan task_struct (por ejemplo, set_cpus_allowed_ptr en kernel/sched/core.c). Se rompe la compatibilidad con los módulos de kernel compilados anteriormente que usan estas interfaces, lo que requiere que esos módulos se vuelvan a compilar con la nueva configuración del kernel.

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