Modules de noyau chargeables

Conformément aux exigences du noyau de module introduites dans Android 8.0, tous les noyaux de système sur puce (SoC) doivent prendre en charge les modules de noyau pouvant être chargés.

Options de configuration du noyau

Pour prendre en charge les modules de kernel pouvant être chargés, android-base.config dans tous les noyaux courants inclut les options de configuration du kernel suivantes (ou leur équivalent de version du kernel):

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

Tous les noyaux d'appareil doivent activer ces options. Les modules du noyau doivent également être compatibles avec le déchargement et le rechargement dans la mesure du possible.

Signature de module

La signature de module n'est pas prise en charge pour les modules de fournisseurs GKI. Sur les appareils qui doivent prendre en charge le démarrage validé, Android exige que les modules du noyau se trouvent dans les partitions pour lesquelles dm-verity est activé. Cela évite d'avoir à signer des modules individuels pour leur authenticité. Android 13 a introduit le concept de modules GKI. Les modules GKI utilisent l'infrastructure de signature au moment de la compilation du noyau pour différencier les GKI des autres modules au moment de l'exécution. Les modules non signés sont autorisés à se charger tant qu'ils n'utilisent que des symboles figurant sur la liste d'autorisation ou fournis par d'autres modules non signés. Pour faciliter la signature des modules GKI lors de la compilation GKI à l'aide de la paire de clés de compilation du kernel, la configuration du kernel GKI a activé CONFIG_MODULE_SIG_ALL=y. Pour éviter de signer des modules autres que GKI lors de la compilation du kernel de l'appareil, vous devez ajouter # CONFIG_MODULE_SIG_ALL is not set dans vos fragments de configuration du kernel.

Emplacements de fichiers

Bien qu'Android 7.x et versions antérieures n'interdisent pas les modules de kernel (et incluent la prise en charge de insmod et rmmod), Android 8.x et versions ultérieures recommandent l'utilisation de modules de kernel dans l'écosystème. Le tableau suivant présente la prise en charge potentielle des périphériques spécifiques à la carte requise pour trois modes de démarrage Android.

Mode de démarrage Stockage Écran Clavier Batterie PMIC Écran tactile NFC, Wi-Fi,
Bluetooth
Capteurs Appareil photo
Récupération
Chargeur
Android

En plus de la disponibilité dans les modes de démarrage Android, les modules du noyau peuvent également être classés en fonction de leur propriétaire (le fournisseur de SoC ou l'OEM). Si des modules de kernel sont utilisés, les exigences concernant leur emplacement dans le système de fichiers sont les suivantes:

  • Tous les noyaux doivent être compatibles avec le démarrage et le montage des partitions.
  • Les modules du noyau doivent être chargés à partir d'une partition en lecture seule.
  • Pour les appareils nécessitant un démarrage validé, les modules du noyau doivent être chargés à partir de partitions validées.
  • Les modules du noyau ne doivent pas se trouver dans /system.
  • Les modules GKI requis pour l'appareil doivent être chargés à partir de /system/lib/modules, qui est un lien symbolique vers /system_dlkm/lib/modules.
  • Les modules du noyau du fournisseur de SoC requis pour les modes Android complet ou Chargeur doivent se trouver dans /vendor/lib/modules.
  • Si une partition ODM existe, les modules du noyau de l'ODM requis pour les modes Android complet ou Chargeur doivent se trouver dans /odm/lib/modules. Sinon, ces modules doivent se trouver dans /vendor/lib/modules.
  • Les modules du noyau du fournisseur de SoC et de l'ODM requis pour le mode de récupération doivent se trouver dans le ramfs de récupération à /lib/modules.
  • Les modules du noyau requis pour le mode de récupération et les modes Android complet ou Chargeur doivent exister à la fois dans le rootfs de récupération et dans les partitions /vendor ou /odm (comme décrit ci-dessus).
  • Les modules de kernel utilisés en mode Récupération ne doivent pas dépendre de modules situés uniquement dans /vendor ou /odm, car ces partitions ne sont pas montées en mode Récupération.
  • Les modules de kernel du fournisseur de SoC ne doivent pas dépendre des modules de kernel de l'ODM.

Dans Android 7.x et versions antérieures, les partitions /vendor et /odm ne sont pas montées à l'avance. Sous Android 8.x et versions ultérieures, pour permettre le chargement de modules à partir de ces partitions, des dispositions ont été prises pour monter les partitions à l'avance à la fois pour les appareils non A/B et A/B. Cela garantit également que les partitions sont montées en mode Android et en mode Chargeur.

Compatibilité avec le système de compilation Android

Dans BoardConfig.mk, le build Android définit une variable BOARD_VENDOR_KERNEL_MODULES qui fournit la liste complète des modules du kernel destinés à l'image du fournisseur. Les modules listés dans cette variable sont copiés dans l'image du fournisseur à /lib/modules/ et, après avoir été installés dans Android, apparaissent dans /vendor/lib/modules (conformément aux exigences ci-dessus). Exemple de configuration des modules du noyau du fournisseur:

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

Dans cet exemple, un dépôt précompilé de module de kernel du fournisseur est mappé dans le build Android à l'emplacement indiqué ci-dessus.

L'image de récupération peut contenir un sous-ensemble des modules du fournisseur. La compilation Android définit la variable BOARD_RECOVERY_KERNEL_MODULES pour ces modules. Exemple :

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 compilation Android s'occupe d'exécuter depmod pour générer les fichiers modules.dep requis dans /vendor/lib/modules et /lib/modules (recovery ramfs).

Chargement et gestion des versions des modules

Chargez tous les modules du noyau en une seule fois à partir de init.rc* en appelant modprobe -a. Cela évite d'avoir à initialiser à plusieurs reprises l'environnement d'exécution C pour le binaire modprobe. L'événement early-init peut être modifié pour appeler modprobe:

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

En règle générale, un kernel module doit être compilé avec le kernel avec lequel il doit être utilisé (sinon, le kernel refuse de charger le module). CONFIG_MODVERSIONS fournit une solution de contournement en détectant les pannes dans l'interface binaire de l'application (ABI). Cette fonctionnalité calcule une valeur de contrôle de redondance cyclique (CRC) pour le prototype de chaque symbole exporté dans le noyau et stocke les valeurs dans le noyau. Pour les symboles utilisés par un module de noyau, les valeurs sont également stockées dans le module de noyau. Lorsque le module est chargé, les valeurs des symboles utilisés par le module sont comparées à celles du noyau. Si les valeurs correspondent, le module est chargé. Sinon, la charge échoue.

Pour activer la mise à jour de l'image du noyau séparément de l'image du fournisseur, activez CONFIG_MODVERSIONS. Cela permet d'apporter de petites mises à jour du kernel (telles que des corrections de bugs à partir de LTS) tout en conservant la compatibilité avec les modules de kernel existants dans l'image du fournisseur. Toutefois, CONFIG_MODVERSIONS ne corrige pas une erreur ABI par lui-même. Si le prototype d'un symbole exporté dans le noyau change, soit en raison d'une modification de la source, soit en raison d'une modification de la configuration du noyau, la compatibilité avec les modules du noyau qui utilisent ce symbole est rompue. Dans ce cas, le kernel module doit être recompilé.

Par exemple, la structure task_struct du noyau (définie dans include/linux/sched.h) contient de nombreux champs inclus de manière conditionnelle en fonction de la configuration du noyau. Le champ sched_info n'est présent que si CONFIG_SCHED_INFO est activé (ce qui se produit lorsque CONFIG_SCHEDSTATS ou CONFIG_TASK_DELAY_ACCT sont activés). Si ces options de configuration changent d'état, la mise en page de la structure task_struct change et toutes les interfaces exportées du noyau qui utilisent task_struct sont modifiées (par exemple, set_cpus_allowed_ptr dans kernel/sched/core.c). La compatibilité avec les modules de noyau précédemment compilés qui utilisent ces interfaces est interrompue, ce qui nécessite de recompiler ces modules avec la nouvelle configuration du noyau.

Pour en savoir plus sur CONFIG_MODVERSIONS, consultez la documentation dans l'arborescence du kernel à l'adresse Documentation/kbuild/modules.rst.