可載入的核心模組

根據 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 以下版本並未強制使用核心模組 (並且包含對 insmodrmmod 的支援),但 Android 8.x 以上版本則建議在生態系統中使用核心模組。下表列出三種 Android 啟動模式所需的可能板卡專屬周邊支援功能。

啟動模式 儲存空間 螢幕 撥號鍵盤 電池 PMIC 觸控螢幕 NFC、Wi-Fi、
藍牙
感應器 相機
復原
充電器
Android

除了在 Android 開機模式中可用之外,核心模組也可能會依擁有者 (SoC 供應商或 ODM) 分類。如果使用核心模組,則其在檔案系統中的位置如下:

  • 所有核心都應內建支援開機和掛載分割區的功能。
  • 核心模組必須從唯讀分區載入。
  • 如果裝置需要驗證開機程序,則應從已驗證的分區載入核心模組。
  • 核心模組不應位於 /system 中。
  • 裝置所需的 GKI 模組應從 /system/lib/modules 載入,這是 /system_dlkm/lib/modules 的符號連結。
  • 完整 Android 或充電器模式所需的 SoC 供應商核心模組應位於 /vendor/lib/modules 中。
  • 如果有 ODM 分區,則 ODM 提供的核心模組 (適用於完整 Android 或充電模式) 應位於 /odm/lib/modules 中。否則,這些模組應位於 /vendor/lib/modules 中。
  • 復原模式所需的 SoC 供應商和 ODM 核心模組應位於 /lib/modules 的 recovery ramfs 中。
  • 復原模式和完整 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,以便在 /vendor/lib/modules/lib/modules (recovery ramfs) 中產生所需的 modules.dep 檔案。

模組載入和版本管理

請呼叫 modprobe -a,從 init.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 的核心樹狀結構說明文件。