可加載內核模塊

作為 Android 8.0 中引入的模塊內核要求的一部分,所有片上系統 (SoC) 內核都必須支持可加載內核模塊。

內核配置選項

為了支持可加載內核模塊,所有常見內核中的android-base.cfg都包含以下內核配置選項(或其內核版本等效項):

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

所有設備內核都必須啟用這些選項。內核模塊還應盡可能支持卸載和重新加載。

模塊簽名

GKI 供應商模塊不支持模塊簽名。在需要支持驗證啟動的設備上,Android 要求內核模塊位於啟用了 dm-verity 的分區中。這消除了對單個模塊的真實性進行簽名的需要。

文件位置

雖然 Android 7.x 及更低版本不強制要求內核模塊(包括對insmodrmmod的支持),但 Android 8.x 及更高版本建議在生態系統中使用內核模塊。下表顯示了三種 Android 啟動模式所需的潛在板特定外設支持。

引導模式貯存展示鍵盤電池PMIC觸摸屏NFC、無線網絡、
藍牙
傳感器相機
恢復
充電器
安卓

除了在 Android 啟動模式下的可用性之外,內核模塊還可以按擁有者(SoC 供應商或 ODM)進行分類。如果使用內核模塊,它們在文件系統中的放置要求如下:

  • 所有內核都應該具有對引導和掛載分區的內置支持。
  • 內核模塊必須從只讀分區加載。
  • 對於需要驗證啟動的設備,內核模塊應該從驗證分區加載。
  • 內核模塊不應位於/system
  • 完整 Android 或 Charger 模式所需的 SoC 供應商的內核模塊應位於/vendor/lib/modules中。
  • 如果存在 ODM 分區,完整 Android 或 Charger 模式所需的 ODM 內核模塊應位於/odm/lib/modules中。否則,這些模塊應位於/vendor/lib/modules中。
  • 恢復模式所需的 SoC 供應商和 ODM 的內核模塊應位於/lib/modules的恢復ramfs中。
  • 恢復模式和完整 Android 或 Charger 模式所需的內核模塊應該存在於恢復rootfs/vendor/odm分區中(如上所述)。
  • 在恢復模式下使用的內核模塊不應依賴於僅位於/vendor/odm的模塊,因為這些分區不會在恢復模式下掛載。
  • SoC 供應商內核模塊不應依賴於 ODM 內核模塊。

在 Android 7.x 及更低版本中, /vendor/odm分區不會提前掛載。在 Android 8.x 及更高版本中,為了使從這些分區加載模塊成為可能,已經為非 A/B 和 A/B 設備提前安裝了分區。這也確保了分區在 Android 和 Charger 模式下都掛載。

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以在/vendor/lib/modules/lib/modules ( recovery ramfs ) 中生成所需的modules.dep文件。

模塊加載和版本控制

通過調用modprobe -ainit.rc*一次性加載所有內核模塊。這避免了為modprobe二進製文件重複初始化 C 運行時環境的開銷。可以修改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_SCHEDSTATSCONFIG_TASK_DELAY_ACCT時發生)。如果這些配置選項改變了狀態, task_struct結構的佈局也會改變,並且任何從內核導出的使用task_struct的接口都會改變(例如kernel/sched/core.c set_cpus_allowed_ptr 。與使用這些接口的先前編譯的內核模塊的兼容性中斷,需要使用新的內核配置重新構建這些模塊。

有關CONFIG_MODVERSIONS的更多詳細信息,請參閱位於Documentation/kbuild/modules.rst的內核樹中的文檔。