汎用カーネル イメージ

Android 共通カーネル(ACK)は、すべての Android プロダクト カーネルの基礎となっています。ベンダー カーネルとデバイス カーネルは、ACK のダウンストリームにあります。ベンダーは、カーネル ソースコードを変更し、デバイス ドライバを追加することで、SoC と周辺機器のサポートを追加します。こうした変更は、デバイス上で実行されるコードの 50% がツリー外のコード(アップストリームの Linux や AOSP 共通カーネルからではない)になるほど、広範囲にわたる可能性があります。

つまり、デバイス カーネルは次の要素で構成されます。

  • アップストリーム: kernel.org の Linux カーネル
  • AOSP: AOSP 共通カーネルからの Android 固有の追加パッチ
  • ベンダー: ベンダーによる SoC と周辺機器の有効化パッチと最適化パッチ
  • OEM / デバイス: 追加のデバイス ドライバとカスタマイズ

ほぼすべてのデバイスにカスタム カーネルがあります。これはカーネルの断片化です。

Android カーネル階層による断片化

図 1. Android カーネル階層による断片化

断片化の代償

カーネルの断片化は、Android コミュニティに悪影響を及ぼします。

セキュリティ アップデートに手間がかかる

Android のセキュリティに関する公開情報(ASB)に記載されているセキュリティ パッチは、各デバイス カーネルにバックポートする必要があります。しかし、カーネルの断片化が原因で、実際に利用中の Android デバイスにセキュリティ修正を反映させるには大きなコストがかかります。

長期サポートのアップデートの統合が困難

長期サポート(LTS)リリースには、セキュリティ修正やその他の重要なバグの修正が含まれています。LTS リリースを最新の状態に保つことは、セキュリティ修正を提供する最も効果的な方法であることが証明されています。Pixel デバイスにおいて、ASB でレポートされたカーネル セキュリティに関する問題の 90% が、最新の状態のデバイスではすでに修正されていることが判明しました。

しかし、デバイス カーネルのカスタム変更をすべて適用しても、LTS 修正をデバイス カーネルに統合することは困難です。

Android プラットフォーム リリースのアップグレードが妨げられる

断片化により、カーネルの変更を必要とする Android の新機能を、利用中のデバイスに追加することが困難になります。Android フレームワークのコードでは、最大 5 つのカーネル バージョンがサポートされ、新しいプラットフォーム リリースのカーネル変更が行われていないことを前提とする必要があります(Android 10 は 3.18、4.4、4.9、4.14、4.19 のカーネルをサポートしていますが、2017 年の Android 8 以降、新機能によって強化されていないものもあります)。

アップストリームの Linux にカーネル変更を反映することが困難

カーネル変更をすべて適用しても、ほとんどのフラッグシップ デバイスは、18 か月以上前のカーネル バージョンで出荷されます。たとえば、4.14 カーネルは kernel.org によって 2017 年 11 月にリリースされましたが、4.14 カーネルを使用した最初の Android スマートフォンが出荷されたのは 2019 年春でした。

アップストリームのカーネル リリースとプロダクトの間のこうした大きな遅延により、Android コミュニティが必要な機能やドライバをアップストリームのカーネルに組み込みにくくなっているため、断片化の問題を解決することは困難です。

断片化の修正: 汎用カーネル イメージ

汎用カーネル イメージ(GKI)プロジェクトは、コアカーネルを統合し、SoC とボードのサポートをコアカーネルから読み込み可能モジュールに移動することで、カーネルの断片化に対処します。GKI カーネルはカーネル モジュールに安定版のカーネル モジュール インターフェース(KMI)を提供するため、モジュールとカーネルを別々に更新できます。

GKI は:

  • ACK ソースからビルドされています。
  • LTS リリースごと(現時点では android11-5.4android12-5.4 の arm64 のみ)、アーキテクチャごとの、シングルカーネル バイナリと、関連する読み込み可能モジュールです。
  • 関連する ACK でサポートされているすべての Android プラットフォーム リリースでテストされています。GKI カーネル バージョンの全期間を通じて、機能のサポートが終了することはありません。
  • 所定の LTS 内のドライバに安定版の KMI を公開します。
  • SoC またはボード固有のコードを含みません。

GKI を実装することで Android デバイスがどのようになるかを次に示します。

GKI アーキテクチャ

図 2. GKI アーキテクチャ

GKI は複雑な変更です。Android 11 プラットフォーム リリースの v5.4 カーネル以降、複数の段階でロールアウトされます。

GKI 1.0 - GKI 互換性要件

Android 11 プラットフォーム リリースの場合、Treble 互換性には、v5.4 カーネルを搭載したデバイスに対する GKI テストが必要です。

GKI 互換性テストのパーティション

図 3. GKI 互換性テストのパーティション

GKI 互換性とは、GKI ブートイメージを boot パーティションにフラッシュし、GKI システム イメージを system パーティションにフラッシュすることで、Generic System Image(GSI)と GKI カーネルをインストールした状態で、デバイスが VTS テストと CTS-on-GSI+GKI テストに合格することを指します。デバイスは異なるプロダクト カーネルで出荷でき、GKI が提供していない読み込み可能モジュールを使用できます。ただし、プロダクト カーネルと GKI カーネルはどちらも、同じ vendor_boot パーティションと vendor パーティションからモジュールを読み込む必要があります。したがって、すべてのプロダクト カーネルで同じバイナリ カーネル モジュール インターフェース(KMI)が必要です。ベンダーは、GKI KMI と互換性がある限り、プロダクト カーネルの KMI を拡張できます。ベンダー モジュールを GKI 1.0 で読み込み不可にする必要はありません。

GKI 1.0 の目標

  • プロダクト カーネルを GKI カーネルに置き換える際に、VTS または CTS での問題を起こさない。
  • OEM とベンダーが AOSP 共通カーネルを最新の状態に保つためのカーネル メンテナンスの負担を軽減する。
  • デバイスが新しい Android プラットフォーム リリースにアップグレードされたか、新たにリリースされたかにかかわらず、カーネルに Android の主要な変更を含める。
  • Android ユーザー空間に影響を及ぼさない。
  • ハードウェア固有のコンポーネントをコアカーネルから読み込み可能モジュールとして分離する。

GKI 2.0 - GKI プロダクト

Android S(2021 年)プラットフォーム リリースでカーネル バージョン v5.x 以降を使用してリリースされるデバイスは、GKI カーネルで出荷する必要があります(5.x は 2020 年末に LTS として選択されたカーネルです)。署名付きブートイメージが利用可能になり、LTS と重要なバグ修正で定期的に更新されます。KMI ではバイナリ安定性が維持されるため、こうしたブートイメージはベンダー イメージを変更せずにインストールできます。

GKI 2.0 の目標

  • GKI でパフォーマンスや電力の大幅な低下が生じない。
  • ベンダーの関与なしで OEM がカーネルのセキュリティ修正とバグの修正(LTS)を提供できるようにする。
  • デバイスのメジャー カーネル バージョンの更新(たとえば v5.x から v5.y)にかかるコストを削減する。
  • アップグレードの明確なプロセスを定めてカーネル バージョンを更新することにより、アーキテクチャごとに GKI カーネル バイナリを 1 つだけ保持する。

GKI の設計

KMI カーネル ブランチ

GKI カーネルは ACK KMI カーネル ブランチからビルドされています(詳細については Android 共通カーネルをご覧ください)。KMI はカーネル バージョンと Android プラットフォーム リリースによって一意に識別されるため、ブランチは <androidRelease>-<kernel version> という名前になります。たとえば、Android 11 用の 5.4 KMI カーネル ブランチは android11-5.4. という名前になります。Android S(コミットされておらず、新しいブランチモデルが今後どのように進歩するかを示すのみ)では、2020 年末の発表が見込まれる新しい LTS カーネルに基づく新しい 2 つの KMI カーネル(android12-5.4 と 2 つ目のカーネル)が追加される予定です。

KMI カーネル階層

5.x KMI カーネルのブランチ階層を図 4 に示します(5.x は 2020 年末に LTS として選択されたカーネルです)。android12-5.x は Android S に対応する KMI カーネルであり、android13-5.x は Android 13(コミットされておらず、新しいブランチモデルが今後どのように進歩するかを示すのみ)に対応します。

5.x の KMI カーネル階層

図 4. 5.x の KMI カーネル階層

図 4 に示すように、KMI ブランチでは、開発(dev)、安定化(stab)、固定の 3 つのフェーズでサイクルが回されます。これらのフェーズの詳細については、Android 共通カーネルをご覧ください。

KMI カーネルが固定された後は、安定版の KMI に影響を及ぼさずには対処できない重大なセキュリティ上の問題が見つかった場合を除き、KMI の互換性を損なう変更は受け入れられません。ブランチはその全期間を通じて固定されたままになります。

バグの修正とパートナー機能は、既存の KMI の互換性が破られない限り、固定ブランチに受け入れられます。現在の KMI を構成するインターフェースに影響が及ばないのであれば、新しくエクスポートされたシンボルで KMI を拡張できます。新しいインターフェースが KMI に追加されると、すぐに安定化し、今後の変更によって破壊されることがなくなります。

たとえば、KMI インターフェースで使用される構造にフィールドを追加しようとしても、インターフェース定義が変更されるため許可されません。

struct foo {
  int original_field1;
  int original_field2;
  int new_field; // Not allowed
};

int do_foo(struct foo &myarg)
{
  do_something(myarg);
}
EXPORT_SYMBOL_GPL(do_foo);

ただし、新しい関数の追加は可能です。

struct foo_ext {
  struct foo orig_foo;
  int new_field;
};

int do_foo2(struct foo_ext &myarg)
{
  do_something_else(myarg);
}
EXPORT_SYMBOL_GPL(do_foo2);

KMI の安定性

GKI の目標を実現するには、ドライバに安定版の KMI を維持することが重要です。GKI カーネルはバイナリ形式でビルドされて出荷されますが、ベンダーの読み込み可能モジュールは別のツリーでビルドされます。生成される GKI カーネルとモジュールは、合わせてビルドされたかのように動作する必要があります。GKI 互換性テストでは、カーネルを含むブートイメージが GKI カーネルを含むブートイメージに置き換えられ、ベンダー イメージ内の読み込み可能モジュールがどちらのカーネルでも正しく機能する必要があります。

KMI には、カーネル内のすべてのシンボルや、30,000 以上のエクスポートされたシンボルがすべて含まれているわけではありません。代わりに、モジュールで使用できるシンボルは、カーネルツリーのルートで公開されているシンボルリスト ファイルのセットに明示的にリストされます。すべてのシンボルリスト ファイル内のすべてのシンボルを合わせたものが、安定版として維持される KMI シンボルのセットを定めます。シンボルリスト ファイルの一例を挙げると abi_gki_aarch64_db845c であり、DragonBoard 845c に必要なシンボルを宣言しています。

シンボルリストにリストされているシンボルと、関連する構造および定義のみが、KMI の一部とみなされます。必要なシンボルが存在しない場合は、シンボルリストに変更を送信できます。新しいインターフェースがシンボルリストに含まれると、KMI の一部として記載され、安定版として維持されます。ブランチが固定された後でシンボルリストから削除または変更されることはありません。

各 KMI カーネルツリーには、独自のシンボルリストのセットがあります。異なる KMI カーネル ブランチ間で ABI の安定性を提供する試みは行われていません。たとえば、android11-5.4 の KMI は、android12-5.4 の KMI から完全に独立しています。

一般に、Linux コミュニティはメインライン カーネルのカーネル内 ABI の安定性という概念に難色を示してきました。さまざまなツールチェーン、構成、進化し続ける Linux メインライン カーネルに対応して、メインラインで安定版の KMI を維持することは不可能です。ただし、高度に制約された GKI 環境では可能です。制約は次のとおりです。

  • KMI は、同じ LTS カーネル内(android11-5.4 など)でのみ安定性が保たれる。
    • android-mainline について、KMI の安定性は維持されない。
  • カーネルとモジュールのビルドには、AOSP で提供され、対応するブランチに対して定義された特定の Clang ツールチェーンのみが使用される。
  • シンボルリストで指定されたモジュールで使用されることがわかっているシンボルのみが、安定性をモニターされ、KMI シンボルとみなされる。
    • 必然的に、モジュールは KMI シンボルのみを使用する必要がある。これは、KMI 以外のシンボルが必要な場合にモジュールの読み込みが失敗することで適用されます。
  • KMI ブランチが固定された後は、以下の KMI の互換性を損なう変更ができなくなる。
    • 構成の変更
    • カーネルコードの変更
    • ツールチェーンの変更(アップデートを含む)

KMI のモニタリング

ABI モニタリング ツールは、送信前テストの際に KMI の安定性をモニターします。KMI の互換性を損なう変更は、送信前テストで不合格となります。互換性を持つように改善する必要があります。これらのツールは、パートナーや一般ユーザーがビルドプロセスに統合するために利用できます。Android カーネルチームは、これらのツールを使用して、開発時や LTS リリースの統合時に KMI の互換性の問題を検出します。LTS の統合中に KMI の互換性の問題が検出された場合、不適切なパッチを削除するか、互換性のあるパッチにリファクタリングすることで、KMI の安定性を保持します。

既存の KMI シンボルが互換性のない方法で変更された場合、KMI の互換性が損なわれたとみなされます。次に例を示します。

  • KMI 関数に新しい引数が追加された
  • KMI 関数で使用する構造に新しいフィールドが追加された
  • KMI 関数で使用する列挙値を変更する新しい列挙値が追加された
  • KMI に影響するデータメンバーの存在を変更する構成変更

新しいシンボルを追加しても KMI の互換性が損なわれるとは限りませんが、使用する新しいシンボルは、シンボルリストと ABI 表現に追加する必要があります。そうしないと、KMI のこの部分に対する今後の変更が認識されません。

パートナーは ABI モニタリング ツールを使用して、プロダクト カーネルと GKI カーネルの KMI を比較し、互換性があることを確認する必要があります。

最新の android11-5.4 バイナリ GKI カーネルは ci.android.com からダウンロードできます(kernel_aarch64 ビルド)。

単一のコンパイラ

コンパイラの変更により、ABI に影響する内部のカーネルデータ構造のレイアウトが変更される可能性があります。KMI の安定性を維持することが重要であるため、GKI カーネルのビルドに使用するツールチェーンは、ベンダー モジュールをビルドするために使用するツールチェーンと完全互換である必要があります。GKI カーネルは、AOSP に含まれる LLVM ツールチェーンでビルドされています。

Android 10 以降、すべての Android カーネルは LLVM ツールチェーンでビルドする必要があります。GKI では、プロダクト カーネルとベンダー モジュールをビルドするために使用する LLVM ツールチェーンで、AOSP の LLVM ツールチェーンと同じ ABI を生成する必要があります。また、パートナーは KMI が GKI カーネルと互換性があることを確認する必要があります。

Android カーネルのビルド ドキュメントでは、リファレンス GKI カーネルのビルド方法について説明しています。特に、文書化された手順に沿うことで、カーネルのビルドに正しいツールチェーンと構成が使用されるようになります。ダウンストリームのパートナーは、ツールチェーンや他のビルド時の依存関係によって生じる非互換性を回避するために、最終的なカーネルを生成する際に同じツールを使用することをおすすめします。

カーネル構成

GKI カーネルは arch/arm64/configs/gki_defconfig を使用してビルドされます。これらの構成は KMI に影響するため、GKI 構成は非常に慎重に管理されています。固定 KMI カーネルでは、KMI に影響がない場合にのみ、構成を追加または削除できます。

GKI カーネルは、さまざまなデバイスで動作するように構成する必要があります。そのため、ハードウェアを有効にするために使用する読み込み可能モジュールを除く、これらすべてのデバイスで必要なすべてのサブシステムとオプションのサポートが組み込まれている必要があります。パートナーは、dev カーネルに必要な構成の変更をリクエストする必要があります。

ブートの変更

GKI コンポーネントとベンダー コンポーネントを明確に分離しやすくするために、boot パーティションには、カーネルや、GKI モジュールを使用した RAM ディスクなどの、汎用コンポーネントのみが含まれています。新しいバージョンのブートヘッダー(v3)は、GKI アーキテクチャを遵守していることを示すために定義されます。GKI バージョンのブートイメージは Google によって提供され、GKI 互換性をテストする際にベンダーのバージョンのブートイメージに置き換えられます。

第 1 ステージの initrecoveryfastbootd の RAM ディスクは、ブートローダーによって連結された 2 つの CPIO アーカイブからなる initramfs イメージです。最初の CPIO アーカイブは、新しい vendor_boot パーティションからのものです。2 番目は、boot パーティションからのものです。

変更点の概要を次に示します。詳細については、ベンダー ブート パーティションをご覧ください。

ブート パーティション

boot パーティションには、ヘッダー、カーネル、またブート RAM ディスクの汎用部分の CPIO アーカイブが含まれます。

ブートヘッダー v3 の boot パーティションでは、以前の boot パーティションの次のセクションはなくなりました。

  • 第 2 ステージのブートローダー: デバイスに第 2 ステージのブートローダーが存在する場合は、専用のパーティションに格納する必要があります。
  • DTB: DTB はベンダー ブート パーティションに格納されます。

boot パーティションには、GKI コンポーネントを持つ CPIO アーカイブが含まれています。

  • /lib/modules/ にある GKI カーネル モジュール
  • first_stage_init とその依存ライブラリ
  • fastbootdrecovery(A/B デバイスと仮想 A/B デバイスで使用)

GKI ブートイメージは Google が提供するものであり、GKI 互換性テストに使用する必要があります。

最新の arm64 android11-5.4 boot.img は、aosp-master ブランチの aosp_arm64 ビルド アーティファクトにある ci.android.com からダウンロードできます。

最新の arm64 android11-5.4 カーネル イメージ(Image.gz)は、aosp_kernel-common-android11-5.4 ブランチの kernel_aarch64 ビルド アーティファクトにある ci.android.com からダウンロードできます。

ベンダー ブート パーティション

vendor_boot パーティションは GKI で導入されました。仮想 A/B で A/B が作成され、ヘッダー、ベンダー RAM ディスク、デバイスツリー blob で構成されています。ベンダー RAM ディスクは、デバイスの起動に必要なベンダー モジュールを含む CPIO アーカイブです。これには、デバイスとディスプレイのスプラッシュ画面を起動するために必要なストレージとディスプレイ ドライバだけでなく、重要な SoC 機能を有効にするためのモジュールも含まれます。

CPIO アーカイブには次のものが含まれます。

  • /lib/modules/ にある第 1 ステージの init ベンダー カーネル モジュール
  • /lib/modules にある modprobe 構成ファイル
  • 第 1 ステージの init で読み込むモジュールを示す modules.load ファイル

ブートローダーの要件

ブートローダーは、boot パーティションにある汎用 RAM ディスク CPIO イメージを、vendor_boot パーティションにあるベンダー RAM ディスク CPIO イメージに続けて、メモリに読み込む必要があります。解凍すると、汎用 RAM ディスクがベンダー RAM ディスクのファイル構造の上にオーバーレイされます。

GKI 互換性テスト

Android 11 プラットフォーム リリースの場合、v5.4 カーネルでリリースされたデバイスは、Google が提供する GKI ブートイメージを使用して VTS テストと CTS-on-GSI テストを実施する必要があります。

GKI カーネルに対する貢献

GKI カーネルは、android11-5.4 以降、AOSP 共通カーネルからビルドされています。送付されるパッチはすべて、貢献に関するガイドラインを遵守する必要があります。このガイドラインには、2 つの戦略が記載されています。

  1. 最適: アップストリームの Linux にすべての変更を加えます。必要に応じて、安定版リリースにバックポートします。これらのパッチは、対応する AOSP 共通カーネルに自動的に統合されます。パッチがすでにアップストリームの Linux にある場合は、下記のパッチ要件を満たすパッチのバックポートを投稿してください。
  2. 不十分: パッチを、アップストリームの Linux のツリー外で作成します。パッチが Android 固有のバグを修正する場合を除き、kernel-team@android.com と調整していない限り、受け入れられる可能性はほとんどありません。

GKI を実装しているパートナーの場合、ツリー外のパッチが必要である正当な理由があることがあります(特に、満たさなければならないシリコン スケジュールがある場合)。そのような場合は、Buganizer で問題を報告してください。

GKI の変更を Gerrit 経由で android-mainline ブランチに送信してから、必要に応じて他のリリース ブランチにバックポートします。

読み込み可能モジュールに関連するソースコードは GKI カーネルのソースツリーに投稿する必要はありませんが、すべてのドライバをアップストリームの Linux に送信することを強くおすすめします。

メインラインからのバックポートのリクエスト

一般に、GKI パートナーが必要とするメインライン パッチは GKI カーネルにバックポートできます。貢献に関するガイドラインに沿ってパッチを Gerrit にアップロードします。

パッチがアップストリームに投稿済みでも、まだ承認されていない場合は、承認されるまで待つことをおすすめします。スケジュールの関係上、パッチがアップストリームで受け入れられるまで待つことができない場合は、Buganizer で問題を報告します。

GKI 構成の追加と削除

arch/arm64/configs/gki_defconfig 内の構成の追加または削除をリクエストするには、リクエストとその理由を明記して、Buganizer で問題を報告します。アクセス可能な Buganizer プロジェクトがない場合は、構成変更を含むパッチを Gerrit に投稿し、構成が必要な理由をコミット メッセージに明記するようにします。

固定 KMI カーネルの構成変更は、KMI に影響を与えてはなりません。

コアカーネル コードの変更

AOSP 共通カーネルのコアカーネル コードは変更しないことをおすすめします。まず、アップストリームの Linux にパッチを送信してから、バックポートします。コアカーネルの変更に正当な理由がある場合は、リクエストとその理由を明記して、Buganizer で問題を報告します。Buganizer で問題を報告しなければ、Android 固有の機能は承認されません。Buganizer で問題を報告できない場合は、kernel-team@android.com にメールを送ってください。

EXPORT_SYMBOL_GPL() を使用したシンボルのエクスポート

シンボルのエクスポートのみを含むパッチをアップストリームに送信しないでください。アップストリームの Linux をふまえて、EXPORT_SYMBOL_GPL() の追加では、シンボルを使用するツリー内のモジュラー ドライバが必要です。そのため、新しいドライバ、または既存のドライバに対する変更を、エクスポートと同じパッチセットに含めます。

パッチをアップストリームに送信する場合、コミット メッセージには、パッチが必要でありコミュニティにとって有益であるという正当な理由を明記する必要があります。ツリー外のドライバまたは機能のためにエクスポートを有効にすることは、アップストリームの管理者にとって説得力のある理由にはなりません。

なんらかの理由でパッチをアップストリームに送信できない場合は、Buganizer で問題を報告し、パッチをアップストリームに送信できない理由を説明します。一般に、シンボルがカーネル サブシステムに対するインターフェースとしてサポートされている場合には、エクスポートとして受け入れられる可能性があります。ただし、ランダムな内部ヘルパー関数は受け入れられない可能性が高いため、使用しないようにコードをリファクタリングするよう求められます。

KMI シンボルリストにシンボルを追加する

KMI シンボルリストは、ベンダー モジュールで使用する GKI カーネル シンボルで更新する必要があります。KMI シンボルのみが安定版として維持されるため、GKI では、KMI 以外のシンボルに依存するモジュールは読み込めません。

extract_symbols スクリプトは、カーネル ビルドツリーから関連するシンボルを抽出し、KMI シンボルリストの更新に使用できます。詳細については、シンボルリストのドキュメントをご覧ください。