ローダブル カーネル モジュール

Android 8.0 で導入されたカーネル モジュール要件の一環として、すべてのシステム オン チップ(SoC)カーネルはローダブル カーネル モジュールをサポートする必要があります。

カーネル構成オプション

ローダブル カーネル モジュールをサポートするには、すべての共通カーネルの android-base.config が以下の kernel-config オプション(またはこれに相当する kernel-version オプション)を含んでいなければなりません。

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 の 3 つの起動モードのそれぞれで必要とされるボード固有の周辺機器サポートを示しています。

起動モード ストレージ ディスプレイ キーパッド バッテリー PMIC タッチスクリーン NFC、Wi-Fi、
Bluetooth
センサー カメラ
リカバリー
充電
Android

なお、カーネル モジュールは、Android 起動モードでの可用性によって分類されるだけでなく、オーナー(SoC ベンダーまたは ODM)によって分類されることもあります。カーネル モジュールが使用されている場合、ファイル システムでの配置要件は次のようになります。

  • すべてのカーネルにパーティションの起動とマウントのサポートが組み込まれている。
  • カーネル モジュールが読み取り専用パーティションから読み込まれる。
  • 確認付きブートを行う必要があるデバイスでは、カーネル モジュールが確認済みのパーティションから読み込まれる。
  • カーネル モジュールが /system に配置されない。
  • デバイスに必要な GKI モジュールは、/system_dlkm/lib/modules へのシンボリック リンクである /system/lib/modules から読み込む必要がある。
  • 完全な Android モードまたは充電モードに必要な SoC ベンダーのカーネル モジュールは、/vendor/lib/modules に配置される。
  • ODM パーティションが存在する場合、完全な Android モードまたは充電モードに必要な ODM のカーネル モジュールは、/odm/lib/modules に配置される。ODM パーティションが存在しない場合、これらのモジュールは /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 モードと充電モードの両方でパーティションが確実にマウントされます。

Android ビルドシステムのサポート

Android ビルドでは、BoardConfig.mkBOARD_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/modulesrecovery ramfs)に生成します。

モジュールの読み込みとバージョニング

すべてのカーネル モジュールを init.rc* から 1 つのパスで読み込むには、modprobe -a を呼び出します。そうすることで、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_SCHEDSTATS または CONFIG_TASK_DELAY_ACCT が有効な場合に発生)。これらの構成オプションにより状態が変更されると、task_struct 構造のレイアウトが変更され、task_struct を使用するカーネルからエクスポートされたすべてのインターフェースが変更されます(例: kernel/sched/core.cset_cpus_allowed_ptr)。これらのインターフェースを使用するカーネル モジュールのうちすでにコンパイル済みのものについては互換性が失われるため、該当モジュールを新しいカーネル構成で再ビルドする必要があります。

CONFIG_MODVERSIONS の詳細については、カーネルツリー内のドキュメント Documentation/kbuild/modules.rst をご覧ください。