可加載內核模塊

作為 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 as part of your kernel config snippets。

文件位置

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

引導模式貯存展示鍵盤電池電源管理IC觸控螢幕NFC、無線網路、
藍牙
感應器相機
恢復
充電器
安卓

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

  • 所有核心都應該具有對引導和安裝分區的內建支援。
  • 內核模組必須從唯讀分區載入。
  • 對於需要驗證啟動的設備,應從驗證的分區載入內核模組。
  • 核心模組不應位於/system中。
  • 設備所需的 GKI 模組應從/system/lib/modules加載,這是/system_dlkm/lib/modules的符號連結。
  • 完整 Android 或充電器模式所需的 SoC 供應商的核心模組應位於/vendor/lib/modules中。
  • 如果存在 ODM 分區,則完整 Android 或 Charger 模式所需的 ODM 核心模組應位於/odm/lib/modules中。否則,這些模組應位於/vendor/lib/modules中。
  • 恢復模式所需的來自 SoC 供應商和 ODM 的核心模組應位於/lib/modules的恢復ramfs中。
  • 恢復模式和完整 Android 或充電器模式所需的核心模組應同時存在於恢復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/modulesrecovery 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中定義)包含許多根據核心配置有條件包含的欄位。僅當啟用CONFIG_SCHED_INFO時, 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的核心樹中的文件。