Moduli kernel caricabili

Come parte dei requisiti del kernel del modulo introdotti in Android 8.0, tutti i kernel System-on-chip (SoC) devono supportare i moduli 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 di configurazione del kernel (o la loro versione equivalente del kernel):

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

Tutti i kernel del dispositivo devono abilitare queste opzioni. I moduli del kernel dovrebbero anche supportare lo scaricamento e il ricaricamento quando 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. Ciò elimina la necessità di 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 in fase di compilazione del kernel per distinguere tra GKI e altri moduli in fase di esecuzione. È consentito caricare moduli non firmati purché utilizzino solo simboli visualizzati nella lista consentita o forniti da altri moduli non firmati. Per facilitare la firma dei moduli GKI durante la compilazione GKI utilizzando la coppia di chiavi del momento della compilazione del kernel, la configurazione del kernel GKI ha abilitato CONFIG_MODULE_SIG_ALL=y . Per evitare di firmare moduli non GKI durante la compilazione del kernel del dispositivo, devi aggiungere # CONFIG_MODULE_SIG_ALL is not set come parte dei frammenti di configurazione del kernel.

Posizioni dei file

Mentre Android 7.x e versioni precedenti non impongono l'uso dei moduli kernel (e includono il supporto per insmod e rmmod ), Android 8.x e versioni successive consigliano l'uso dei moduli kernel nell'ecosistema. La tabella seguente mostra il potenziale supporto periferico specifico della scheda richiesto in tre modalità di avvio Android.

Modalità di avvio Magazzinaggio Schermo Tastiera Batteria PMIC Touch screen NFC, Wi-Fi,
Bluetooth
Sensori Telecamera
Recupero
Caricabatterie
Androide

Oltre alla disponibilità nelle modalità di avvio 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 i moduli del kernel, i requisiti per il loro posizionamento nel file system sono i seguenti:

  • Tutti i kernel dovrebbero 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 richiedono un avvio verificato, i moduli del kernel devono essere caricati da partizioni verificate.
  • I moduli del kernel non dovrebbero trovarsi in /system .
  • I moduli GKI richiesti per il dispositivo devono essere caricati da /system/lib/modules che è un collegamento simbolico a /system_dlkm/lib/modules .
  • I moduli del kernel del fornitore del SoC necessari per le modalità Android o Charger complete devono trovarsi in /vendor/lib/modules .
  • Se esiste una partizione ODM, i moduli del kernel dell'ODM richiesti per le modalità Android o Charger complete dovrebbero trovarsi in /odm/lib/modules . Altrimenti, questi moduli dovrebbero trovarsi in /vendor/lib/modules .
  • I moduli del kernel del fornitore del SoC e dell'ODM richiesti per la modalità di ripristino dovrebbero trovarsi nelle ramfs di ripristino in /lib/modules .
  • I moduli del kernel richiesti sia per la modalità di ripristino che per le modalità Android o Charger complete dovrebbero esistere sia nel rootfs di ripristino che nelle partizioni /vendor o /odm (come descritto sopra).
  • I moduli del kernel utilizzati in modalità di ripristino non dovrebbero dipendere da moduli situati solo in /vendor o /odm , poiché tali partizioni non sono montate in modalità di ripristino.
  • I moduli del kernel del fornitore SoC non dovrebbero 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 rendere possibile il caricamento dei moduli da queste partizioni, sono state adottate disposizioni per montare anticipatamente le partizioni sia per i dispositivi non A/B che per quelli A/B . Ciò garantisce inoltre che le partizioni siano montate sia in modalità Android che in modalità Caricatore.

Supporto del sistema di compilazione 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à con i requisiti di cui sopra). Esempio di configurazione 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 precostruito del modulo kernel del fornitore viene mappato nella build Android nella posizione sopra elencata.

L'immagine di ripristino può contenere un sottoinsieme dei moduli del fornitore. La build 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 build 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* invocando modprobe -a . Ciò evita il sovraccarico derivante dall'inizializzazione ripetuta dell'ambiente runtime C per il 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 ...

Tipicamente, un modulo del kernel deve essere compilato con il kernel con cui verrà utilizzato il modulo (altrimenti il ​​kernel si rifiuterà di caricare il modulo). CONFIG_MODVERSIONS fornisce una soluzione alternativa rilevando le interruzioni nell'interfaccia binaria dell'applicazione (ABI). Questa funzionalità calcola un valore di controllo di ridondanza ciclico (CRC) per il prototipo di ciascun simbolo esportato nel kernel e memorizza i valori come parte 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 nel kernel. Se i valori corrispondono, il modulo viene caricato; altrimenti il ​​carico fallisce.

Per abilitare l'aggiornamento dell'immagine del kernel separatamente dall'immagine del fornitore, abilitare CONFIG_MODVERSIONS . Ciò consente di apportare piccoli aggiornamenti al kernel (come le correzioni di bug di LTS) mantenendo la compatibilità con i moduli del kernel esistenti nell'immagine del fornitore. Tuttavia, CONFIG_MODVERSIONS non risolve da solo un'interruzione dell'ABI. Se il prototipo di un simbolo esportato nel kernel cambia, a causa della modifica del sorgente o perché è cambiata la configurazione del kernel, ciò interrompe la compatibilità con i moduli del kernel che utilizzano quel simbolo. 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 condizionatamente a seconda della configurazione del kernel. Il campo sched_info è presente solo se CONFIG_SCHED_INFO è abilitato (che si verifica quando CONFIG_SCHEDSTATS o CONFIG_TASK_DELAY_ACCT sono abilitati). Se queste opzioni di configurazione cambiano stato, il layout della struttura task_struct cambia e tutte le 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 precedentemente compilati che utilizzano queste interfacce si interrompe, richiedendo che tali moduli vengano ricostruiti con la nuova configurazione del kernel.

Per maggiori dettagli su CONFIG_MODVERSIONS , fare riferimento alla documentazione nell'albero del kernel su Documentation/kbuild/modules.rst .