Загружаемые модули ядра

В рамках требований к ядру модуля, представленных в Android 8.0, все ядра системы на кристалле (SoC) должны поддерживать загружаемые модули ядра.

Параметры конфигурации ядра

Для поддержки загружаемых модулей ядра файл android-base.cfg во всех распространенных ядрах включает следующие параметры конфигурации ядра (или их эквивалент версии ядра):

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

Все ядра устройств должны включать эти параметры. Модули ядра также должны поддерживать выгрузку и перезагрузку, когда это возможно.

Подписание модуля

Подписание модулей не поддерживается для модулей поставщика GKI. На устройствах, которые должны поддерживать проверенную загрузку, Android требует, чтобы модули ядра находились в разделах с включенной dm-verity. Это устраняет необходимость подписывать отдельные модули для их подлинности.

Расположение файлов

В то время как Android 7.x и более ранние версии не требуют модулей ядра (и включают поддержку insmod и rmmod ), Android 8.x и более поздние версии рекомендуют использовать модули ядра в экосистеме. В следующей таблице показана потенциальная поддержка периферийных устройств для конкретных плат, необходимая для трех режимов загрузки Android.

Режим загрузки Место хранения Отображать Клавиатура Батарея PMIC Сенсорный экран NFC, Wi-Fi,
блютус
Датчики Камера
Восстановление
Зарядное устройство
Андроид

Помимо доступности в режимах загрузки Android, модули ядра также могут быть классифицированы по тому, кто ими владеет (поставщик SoC или ODM). Если используются модули ядра, требования к их размещению в файловой системе следующие:

  • Все ядра должны иметь встроенную поддержку загрузки и монтирования разделов.
  • Модули ядра должны загружаться из раздела, доступного только для чтения.
  • Для устройств, требующих проверенной загрузки, модули ядра должны быть загружены из проверенных разделов.
  • Модули ядра не должны располагаться в /system .
  • Модули ядра от поставщика SoC, необходимые для полнофункциональных режимов Android или Charger, должны находиться в /vendor/lib/modules .
  • Если раздел ODM существует, модули ядра из ODM, необходимые для полнофункциональных режимов Android или Charger, должны находиться в /odm/lib/modules . В противном случае эти модули должны находиться в /vendor/lib/modules .
  • Модули ядра от поставщика SoC и ODM, необходимые для режима восстановления, должны находиться в ramfs восстановления в /lib/modules .
  • Модули ядра, необходимые как для режима восстановления, так и для полных режимов Android или Charger, должны существовать как в rootfs восстановления, так и в разделах /vendor или /odm (как описано выше).
  • Модули ядра, используемые в режиме восстановления, не должны зависеть от модулей, расположенных только в /vendor или /odm , поскольку эти разделы не монтируются в режиме восстановления.
  • Модули ядра поставщика SoC не должны зависеть от модулей ядра ODM.

В Android 7.x и ниже разделы /vendor и /odm не монтируются заранее. В Android 8.x и более поздних версиях, чтобы сделать возможной загрузку модулей из этих разделов, были созданы условия для раннего монтирования разделов как для устройств, отличных от A/B, так и для устройств A/B . Это также гарантирует, что разделы будут смонтированы как в режиме Android, так и в режиме зарядного устройства.

Поддержка системы сборки Android

В BoardConfig.mk Android определяет переменную BOARD_VENDOR_KERNEL_MODULES , которая предоставляет полный список модулей ядра, предназначенных для образа поставщика. Модули, перечисленные в этой переменной, копируются в образ вендора по адресу /lib/modules/ и после монтирования в Android появляются в /vendor/lib/modules (в соответствии с вышеуказанными требованиями). Пример конфигурации модулей ядра производителя:

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

В этом примере предварительно созданный репозиторий модуля ядра поставщика сопоставляется со сборкой Android в расположении, указанном выше.

Образ восстановления может содержать подмножество модулей поставщика. Сборка Android определяет переменную BOARD_RECOVERY_KERNEL_MODULES для этих модулей. Пример:

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

Сборка Android заботится о запуске depmod для создания необходимых файлов modules.dep в каталогах /vendor/lib/modules и /lib/modules ( recovery ramfs ).

Загрузка модуля и управление версиями

Загрузите все модули ядра за один проход из init.rc* , вызвав modprobe -a . Это позволяет избежать накладных расходов на повторную инициализацию среды выполнения C для двоичного modprobe . Событие early-init можно изменить для вызова modprobe :

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

Как правило, модуль ядра должен быть скомпилирован с ядром, с которым он будет использоваться (иначе ядро ​​отказывается загружать модуль). CONFIG_MODVERSIONS обеспечивает обходной путь, обнаруживая сбои в двоичном интерфейсе приложения (ABI). Эта функция вычисляет значение проверки циклическим избыточным кодом (CRC) для прототипа каждого экспортируемого символа в ядре и сохраняет значения как часть ядра; для символов, используемых модулем ядра, значения также хранятся в модуле ядра. Когда модуль загружается, значения символов, используемых модулем, сравниваются со значениями в ядре. Если значения совпадают, модуль загружается; в противном случае загрузка не выполняется.

Чтобы включить обновление образа ядра отдельно от образа поставщика, включите CONFIG_MODVERSIONS . Это позволяет вносить небольшие обновления ядра (например, исправления ошибок из LTS), сохраняя при этом совместимость с существующими модулями ядра в образе поставщика. Однако CONFIG_MODVERSIONS сама по себе не устраняет поломку ABI. Если прототип экспортируемого символа в ядре изменяется либо из-за модификации исходного кода, либо из-за изменения конфигурации ядра, это нарушает совместимость с модулями ядра, использующими этот символ. В таких случаях модуль ядра необходимо перекомпилировать.

Например, структура task_struct в ядре (определенная в include/linux/sched.h ) содержит множество полей, включенных условно в зависимости от конфигурации ядра. Поле sched_info присутствует, только если включен CONFIG_SCHED_INFO (что происходит, когда включены параметры CONFIG_SCHEDSTATS или CONFIG_TASK_DELAY_ACCT ). Если эти параметры конфигурации изменяют состояние, меняется макет структуры task_struct и изменяются любые экспортированные из ядра интерфейсы, использующие task_struct (например, set_cpus_allowed_ptr в kernel/sched/core.c ). Совместимость с ранее скомпилированными модулями ядра, использующими эти интерфейсы, нарушается, что требует пересборки этих модулей с новой конфигурацией ядра.

Дополнительные сведения о CONFIG_MODVERSIONS см. в документации в дереве ядра по адресу Documentation/kbuild/modules.rst .