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

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

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

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

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

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

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

Подписание модулей не поддерживается для модулей поставщика GKI. На устройствах, требующих поддержки проверенной загрузки, Android требует, чтобы модули ядра находились в разделах, в которых включена функция dm-verity. Это устраняет необходимость подписи отдельных модулей на предмет их аутентичности. В Android 13 появилась концепция модулей GKI. Модули GKI используют инфраструктуру подписи времени сборки ядра, чтобы различать GKI и другие модули во время выполнения. Неподписанные модули разрешено загружать при условии, что они используют только символы, указанные в разрешенном списке или предоставленные другими неподписанными модулями. Чтобы облегчить подписание модулей GKI во время сборки GKI с использованием пары ключей времени сборки ядра, в конфигурации ядра GKI включен CONFIG_MODULE_SIG_ALL=y . Чтобы избежать подписания модулей, отличных от GKI, во время сборки ядра устройства, вы должны добавить # CONFIG_MODULE_SIG_ALL is not set как часть фрагментов конфигурации вашего ядра.

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

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

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

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

  • Все ядра должны иметь встроенную поддержку загрузки и монтирования разделов.
  • Модули ядра должны загружаться из раздела, доступного только для чтения.
  • Для устройств, которым требуется проверенная загрузка, модули ядра следует загружать из проверенных разделов.
  • Модули ядра не должны располагаться в /system .
  • Модули GKI, необходимые для устройства, должны быть загружены из /system/lib/modules , который является символической ссылкой на /system_dlkm/lib/modules .
  • Модули ядра от поставщика SoC, необходимые для полноценных режимов Android или Charger, должны находиться в /vendor/lib/modules .
  • Если раздел ODM существует, модули ядра из ODM, необходимые для полноценных режимов Android или Charger, должны находиться в /odm/lib/modules . В противном случае эти модули должны находиться в /vendor/lib/modules .
  • Модули ядра от поставщика SoC и ODM, необходимые для режима восстановления, должны быть расположены в ramfs восстановления по адресу /lib/modules .
  • Модули ядра, необходимые как для режима восстановления, так и для полного режима Android или зарядного устройства, должны существовать как в 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 .