Согласно требованиям к ядру, введенным в Android 8.0, все ядра систем на кристалле должны поддерживать загружаемые модули ядра.
Варианты конфигурации ядра
Чтобы поддерживать загружаемые модули ядра, файл 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.
| Режим загрузки | Хранилище | Экран | Клавиатура | Батарея | PMIC | Сенсорный экран | NFC, Wi-Fi, Bluetooth |
Датчики | Камера |
|---|---|---|---|---|---|---|---|---|---|
| Восстановление | |||||||||
| Зарядное устройство | |||||||||
| Android |
Помимо доступности в режимах загрузки Android, модули ядра также можно классифицировать по их владельцу (поставщик SoC или ODM). Если используются модули ядра, их размещение в файловой системе должно соответствовать следующим требованиям:
- Все ядра должны поддерживать загрузку и монтирование разделов.
- Модули ядра должны загружаться из раздела, доступного только для чтения.
- На устройствах, для которых требуется проверка при запуске, модули ядра должны загружаться из проверенных разделов.
- Модули ядра не должны находиться в каталоге
/system. - Модули GKI, необходимые для устройства, должны загружаться из каталога
/system/lib/modules, который является символической ссылкой на каталог/system_dlkm/lib/modules. - Модули ядра от поставщика SoC, необходимые для работы Android или режима зарядки, должны находиться в каталоге
/vendor/lib/modules. - Если раздел ODM существует, модули ядра из ODM, необходимые для работы Android или режима зарядки, должны находиться в
/odm/lib/modules. В противном случае эти модули должны находиться в/vendor/lib/modules. - Модули ядра от поставщика SoC и ODM, необходимые для режима восстановления, должны находиться в
ramfsвосстановления по пути/lib/modules. - Модули ядра, необходимые для режима восстановления, а также для полной версии Android или режима зарядки, должны быть в разделах
rootfsи/vendorили/odm(как описано выше). - Модули ядра, используемые в режиме восстановления, не должны зависеть от модулей, расположенных только в
/vendorили/odm, поскольку эти разделы не монтируются в режиме восстановления. - Модули ядра поставщика процессора не должны зависеть от модулей ядра ODM.
В Android 7.x и более ранних версий разделы /vendor и /odm
не монтируются на раннем этапе. В Android 8.x и более поздних версий для загрузки модулей из этих разделов предусмотрено раннее монтирование разделов как для устройств с 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.