Moduli kernel caricabili

Nell'ambito dei requisiti del kernel del modulo introdotti in Android 8.0, tutti i kernel System-on-Chip (SoC) devono supportare i moduli del kernel caricabili.

Opzioni di configurazione del kernel

Per supportare i moduli del kernel caricabili, android-base.config in tutti i kernel comuni include le seguenti opzioni kernel-config (o il relativo equivalente kernel-version):

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

Tutti i kernel del dispositivo devono attivare queste opzioni. I moduli del kernel devono inoltre supportare lo scaricamento e il ricaricamento, se possibile.

Firma del modulo

La firma del modulo non è supportata per i moduli del fornitore GKI. Sui dispositivi che devono supportare l'avvio verificato, Android richiede che i moduli del kernel si trovino nelle partizioni in cui è abilitato dm-verity. In questo modo non è più necessario firmare i singoli moduli per verificarne l'autenticità. Android 13 ha introdotto il concetto di moduli GKI. I moduli GKI utilizzano l'infrastruttura di firma al momento della compilazione del kernel per distinguere il GKI da altri moduli in fase di esecuzione. I moduli non firmati sono consentiti se utilizzano solo i simboli presenti nella lista consentita o forniti da altri moduli non firmati. Per facilitare la firma dei moduli GKI durante la compilazione del GKI utilizzando la coppia di chiavi del momento di compilazione del kernel, la configurazione del kernel GKI ha attivato CONFIG_MODULE_SIG_ALL=y. Per evitare di firmare i moduli non GKI durante le build del kernel del dispositivo, devi aggiungere # CONFIG_MODULE_SIG_ALL is not set come parte dei frammenti di configurazione del kernel.

Posizioni dei file

Sebbene Android 7.x e versioni precedenti non vietino i moduli del kernel (e includano il supporto per insmod e rmmod), Android 8.x e versioni successive consigliano l'utilizzo di moduli del kernel nell'ecosistema. La tabella seguente mostra il potenziale supporto delle periferiche specifico della scheda richiesto nelle tre modalità di avvio di Android.

Modalità di avvio Spazio di archiviazione Display Tastierino Batteria PMIC Touchscreen NFC, Wi-Fi,
Bluetooth
Sensori Fotocamera
Ripristino
Caricabatterie
Android

Oltre alla disponibilità nelle modalità di avvio di Android, i moduli del kernel possono anche essere classificati in base a chi li possiede (il fornitore del SoC o l'ODM). Se vengono utilizzati moduli del kernel, i requisiti per il loro posizionamento nel file system sono i seguenti:

  • Tutti i kernel devono avere il supporto integrato per l'avvio e il montaggio delle partizioni.
  • I moduli del kernel devono essere caricati da una partizione di sola lettura.
  • Per i dispositivi che devono avere l'avvio verificato, i moduli del kernel devono essere caricati dalle partizioni verificate.
  • I moduli del kernel non devono trovarsi in /system.
  • I moduli GKI richiesti per il dispositivo devono essere caricati da /system/lib/modules, che è un link simbolico a /system_dlkm/lib/modules.
  • I moduli del kernel del fornitore del SoC necessari per le modalità Android o caricabatterie complete devono trovarsi in /vendor/lib/modules.
  • Se esiste una partizione ODM, i moduli del kernel dell'ODM richiesti per le modalità Android o Caricabatterie complete devono trovarsi in /odm/lib/modules. In caso contrario, questi moduli devono trovarsi in /vendor/lib/modules.
  • I moduli del kernel del fornitore del SoC e dell'ODM necessari per la modalità di recupero devono trovarsi nella cartella ramfs di recupero all'indirizzo /lib/modules.
  • I moduli del kernel richiesti sia per la modalità di ripristino sia per le modalità Android o caricabatterie complete devono esistere sia nella partizione rootfs di ripristino sia nelle partizioni /vendor o /odm (come descritto sopra).
  • I moduli del kernel utilizzati in modalità di recupero non devono dipendere da moduli situati solo in /vendor o /odm, poiché queste partizioni non sono montate in modalità di recupero.
  • I moduli del kernel del fornitore SoC non devono dipendere dai moduli del kernel ODM.

In Android 7.x e versioni precedenti, le partizioni /vendor e /odm non vengono montate in anticipo. In Android 8.x e versioni successive, per consentire il caricamento dei moduli da queste partizioni, sono state prese disposizioni per il montaggio anticipato delle partizioni sia per i dispositivi non A/B che per quelli A/B. In questo modo, viene garantito che le partizioni vengano montate sia in modalità Android sia in modalità caricabatterie.

Supporto del sistema di build Android

In BoardConfig.mk, la build Android definisce una variabile BOARD_VENDOR_KERNEL_MODULES che fornisce un elenco completo dei moduli del kernel destinati all'immagine del fornitore. I moduli elencati in questa variabile vengono copiati nell'immagine del fornitore in /lib/modules/ e, dopo essere stati montati in Android, vengono visualizzati in /vendor/lib/modules (in conformità ai requisiti sopra indicati). Configurazione di esempio dei moduli del kernel del fornitore:

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

In questo esempio, un repository precompilato del modulo del kernel del fornitore viene mappato nella compilazione Android nella posizione sopra indicata.

L'immagine di ripristino potrebbe contenere un sottoinsieme dei moduli del fornitore. La compilazione Android definisce la variabile BOARD_RECOVERY_KERNEL_MODULES per questi moduli. Esempio:

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 compilazione Android si occupa di eseguire depmod per generare i file modules.dep richiesti in /vendor/lib/modules e /lib/modules (recovery ramfs).

Caricamento e controllo delle versioni dei moduli

Carica tutti i moduli del kernel in un passaggio da init.rc* richiamando modprobe -a. In questo modo si evita il sovraccarico dell'inizializzazione ripetuta dell'ambiente di runtime C per il file binario modprobe. L'evento early-init può essere modificato per richiamare modprobe:

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

In genere, un modulo del kernel deve essere compilato con il kernel con cui deve essere utilizzato (in caso contrario, il kernel rifiuta di caricare il modulo). CONFIG_MODVERSIONS fornisce una soluzione alternativa rilevando le interruzioni nell'interfaccia a bit dell'applicazione (ABI). Questa funzionalità calcola un valore di controllo di ridondanza ciclica (CRC) per il prototipo di ogni simbolo esportato nel kernel e memorizza i valori all'interno del kernel. Per i simboli utilizzati da un modulo del kernel, i valori vengono memorizzati anche nel modulo del kernel. Quando il modulo viene caricato, i valori dei simboli utilizzati dal modulo vengono confrontati con quelli del kernel. Se i valori corrispondono, il modulo viene caricato; in caso contrario, il caricamento non va a buon fine.

Per abilitare l'aggiornamento dell'immagine del kernel separatamente dall'immagine del fornitore, attiva CONFIG_MODVERSIONS. In questo modo è possibile apportare piccoli aggiornamenti al kernel (ad esempio correzioni di bug dall'LTS) mantenendo la compatibilità con i moduli del kernel esistenti nell'immagine del fornitore. Tuttavia, CONFIG_MODVERSIONS non corregge da solo un'interruzione dell'ABI. Se il prototipo di un simbolo esportato nel kernel cambia, a causa della modifica del codice sorgente o della configurazione del kernel, la compatibilità con i moduli del kernel che utilizzano quel simbolo viene interrotta. In questi casi, il modulo del kernel deve essere ricompilato.

Ad esempio, la struttura task_struct nel kernel (definita in include/linux/sched.h) contiene molti campi inclusi in modo condizionale a seconda della configurazione del kernel. Il campo sched_info è presente solo se è attivato CONFIG_SCHED_INFO (cosa che accade quando sono attivati CONFIG_SCHEDSTATS o CONFIG_TASK_DELAY_ACCT). Se queste opzioni di configurazione cambiano stato, il layout della struttura task_struct cambia e le eventuali interfacce esportate dal kernel che utilizzano task_struct vengono alterate (ad esempio set_cpus_allowed_ptr in kernel/sched/core.c). La compatibilità con i moduli del kernel compilati in precedenza che utilizzano queste interfacce viene interrotta, pertanto è necessario ricostruire questi moduli con la nuova configurazione del kernel.

Per ulteriori dettagli su CONFIG_MODVERSIONS, consulta la documentazione nell'albero del kernel all'indirizzo Documentation/kbuild/modules.rst.