В рамках требований к ядру модулей, представленных в 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
.