Módulos de kernel cargables

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

Opciones de configuración del núcleo

Para admitir módulos de kernel cargables, android-base.cfg 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 núcleos de dispositivos deben habilitar estas opciones. Los módulos del núcleo también deben admitir la descarga y 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 arranque verificado, Android requiere que los módulos del núcleo estén en las particiones que tienen habilitado dm-verity. Esto elimina la necesidad de firmar módulos individuales para verificar su autenticidad.

Ubicaciones de archivos

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

El modo de inicio Almacenamiento Monitor teclado Batería PMIC Pantalla táctil NFC, WiFi,
Bluetooth
Sensores Cámara
Recuperación
Cargador
Androide

Además de la disponibilidad en los modos de arranque de Android, los módulos del núcleo también pueden clasificarse según su propietario (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 estar ubicados en /system .
  • Los módulos del kernel del proveedor de SoC que se requieren para los modos completos de Android o cargador 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 cargador deben ubicarse en /odm/lib/modules . De lo contrario, estos módulos deben ubicarse en /vendor/lib/modules .
  • Los módulos de 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 requeridos tanto para el modo de recuperación como para los modos completos de Android o cargador deben existir tanto en el 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 los 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 kernel del proveedor de SoC no deberían depender de los módulos kernel ODM.

En Android 7.x y versiones anteriores, las particiones /vendor y /odm no se montan antes. En Android 8.x y superior, para hacer posible la carga de módulos desde estas particiones, se han tomado medidas para montar particiones antes para dispositivos que no son A/B y A/B . Esto también asegura que las particiones estén montadas en los modos Android y Cargador.

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 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 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 versionado de módulos

Cargue todos los módulos del núcleo en una sola pasada desde init.rc* invocando modprobe -a . Esto evita la sobrecarga de inicializar repetidamente el entorno de tiempo 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 ...

Por lo general, un módulo del kernel debe compilarse con el kernel con el que se utilizará el módulo (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 núcleo, los valores también se almacenan en el módulo del núcleo. Cuando se carga el módulo, los valores de los símbolos utilizados por el módulo se comparan con los del núcleo. Si los valores coinciden, el módulo se carga; 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 en el kernel (como corrección de errores de LTS) mientras se mantiene 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í mismo. Si el prototipo de un símbolo exportado en el kernel cambia, ya sea debido a la 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, el módulo del núcleo debe volver a compilarse.

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 los módulos del kernel compilados previamente que usan estas interrupciones de interfaz, 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 .