通用內核映像

Android的通用內核(ACK的)對於所有Android產品內核的基礎。供應商和設備內核位於 ACK 的下游。供應商通過修改內核源代碼和添加設備驅動程序來添加對 SoC 和外圍設備的支持。這些修飾可以是廣泛的,給點,作為在設備上運行的代碼的多達50%是樹外的代碼(未從上游Linux或從AOSP共同內核)。

因此,設備內核包括:

  • 上游:來自 kernel.org 的 Linux 內核
  • AOSP:來自 AOSP 通用內核的其他特定於 Android 的補丁
  • 供應商:供應商提供的 SoC 和外設啟用和優化補丁
  • OEM/設備:其他設備驅動程序和自定義

幾乎每個設備都有一個自定義內核。這是內核碎片。

Android內核層次結構導致碎片化

圖1. Android的內核層次結構導致了分裂

碎片化成本

內核碎片對 Android 社​​區有幾個負面影響。

安全更新是勞動密集型的

安全補丁報告列舉了Android的安全公告(ASB)必須被移植入每個器件的內核。但是,由於內核碎片化,在現場將安全修復程序傳播到 Android 設備的成本高得令人望而卻步。

難以合併長期支持的更新

長期支持(LTS)版本包括安全修補程序和其他重要的bug修復。與 LTS 版本保持同步已被證明是提供安全修復的最有效方法。在 Pixel 設備上,發現 ASB 中報告的 90% 的內核安全問題已經針對保持最新的設備進行了修復。

然而,由於設備內核中的所有自定義修改,很難將 LTS 修復合併到設備內核中。

禁止Android平台版本升級

碎片化使得需要在現場將內核更改添加到設備的新 Android 功能變得困難。 Android Framework 代碼必須假設支持多達五個內核版本,並且沒有為新平台版本進行內核更改(Android 10 支持 3.18、4.4、4.9、4.14 和 4.19 內核,在某些情況下還沒有增強了自 2017 年 Android 8 以來的新功能)。

難以將內核更改貢獻回上游 Linux

隨著對內核的所有更改,大多數旗艦設備附帶的內核版本已經至少有 18 個月的歷史了。例如,4.14內核被釋放kernel.org在2017年十一月和使用4.14內核出貨量2019年春季第一Android手機。

上游內核發布和產品之間的這種長時間延遲使得 Android 社​​區難以將所需的功能和驅動程序提供給上游內核,因此修復碎片問題具有挑戰性。

修復碎片:通用內核映像

通用內核映像(GKI)項目地址內核碎片通過統一核心內核和移動SoC和板級支持了核心內核成可加載模塊。所述GKI內核禮物內核模塊穩定的內核模塊接口(KMI),所以模塊和內核可以獨立地更新。

GKI:

  • ACK資源建成。
  • 一款單內核二進制文件加上相關每個架構可加載模塊,每LTS版本(目前只針對arm64 android11-5.4android12-5.4 )。
  • 所支持的相關ACK所有的Android平台版本進行測試。在 GKI 內核版本的生命週期內不會棄用任何功能
  • 暴露了穩定KMI給定LTS內的驅動程序。
  • 不含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兼容性手段VTS和CTS-上-GSI + GKI測試與通用系統映像(GSI)和由GKI引導映像閃爍到安裝GKI內核boot在分區和GSI系統映像system分區。設備可以與不同的產品內核船,可以使用可加載模塊GKI不提供。然而,無論是產品和GKI內核必須加載從相同的模塊vendor_bootvendor分區。因此,所有產品內核都需要具有相同的二進制內核模塊接口 (KMI)。供應商可以擴展產品內核的 KMI,只要它與 GKI KMI 保持兼容即可。在 GKI 1.0 中不要求供應商模塊可卸載。

GKI 1.0 目標

  • 當產品內核被 GKI 內核替換時,不要在 VTS 或 CTS 中引入回歸。
  • 減輕 OEM 和供應商的內核維護負擔,以便與 AOSP 通用內核保持同步。
  • 無論設備是升級到新的 Android 平台版本還是新發布的,都在內核中包含核心 Android 更改。
  • 永遠不要破壞 Android 用戶空間。
  • 將特定於硬件的組件與核心內核分離為可加載模塊。

GKI 2.0 - GKI 產品

有些設備,使用推出了Android 12(2021)平台發布的內核版本V5.10或更高要求船舶與GKI內核。經過認證的啟動映像將隨 LTS 和關鍵錯誤修復一起提供並定期更新。由於 KMI 將保持二進制穩定性,因此可以在不更改供應商映像的情況下安裝這些引導映像。

GKI 2.0 目標

  • 不要在 GKI 中引入顯著的性能或功耗回歸。
  • 使 OEM 能夠在沒有供應商參與的情況下提供內核安全修復和錯誤修復 (LTS)。
  • 降低更新設備主要內核版本的成本(例如,從 v5.10 到 2021 LTS 內核)。
  • 通過使用清晰的升級過程更新內核版本,每個架構僅維護一個 GKI 內核二進製文件。

GKI 2.0 boot.img 集成

設置以下變量板納入認證GKI boot.img到你的代碼。下面顯示的設置只是一個示例,可根據設備進行調整。

# Uses a prebuilt boot.img
TARGET_NO_KERNEL := true
BOARD_PREBUILT_BOOTIMAGE := device/${company}/${board}/boot.img

# Enables chained vbmeta for the boot.img so it can be updated independently,
# without updating the vbmeta.img. The following configs are optional.
# When they're absent, the hash of the boot.img will be stored then signed in
# the vbmeta.img.
BOARD_AVB_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem
BOARD_AVB_BOOT_ALGORITHM := SHA256_RSA4096
BOARD_AVB_BOOT_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
BOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION := 2

GKI設計

KMI 內核分支

GKI內核從ACK KMI的內核分支,其詳細描述於內置的Android普通內核。該KMI唯一地由內核版本和Android平台版本識別,從而分支命名為<androidRelease>-<kernel version> 。例如,對於Android的11 5.4 KMI內核分支被命名為android11-5.4.它的預期,為Android 12(未提交,僅顯示演示如何在新的分支模型將會在今後的進展)將有兩個額外的KMI仁, android12-5.4並基於5.10 LTS內核第二內核,12月份發布2020。

KMI 內核層次結構

圖 4 顯示了 5.10 KMI 內核的分支層次結構。 android12-5.10是對應於Android的12和KMI內核android13-5.10對應於Android的T(AOSP實驗)(未提交,僅顯示演示如何在新的分支模型將在未來的進展)。

5.10 的 KMI 內核層次結構

圖4. KMI內核層次為5.10

如圖4所示,KMI分支循環通過三個階段:開發(DEV),穩定(刺),並冷凍。這些階段中詳細描述的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 穩定性。例如,對於KMI android11-5.4是完全獨立於KMI用於android12-5.4

一般情況下,Linux社區一直皺著眉頭在內核ABI穩定性的概念的主線內核。面對不同的工具鏈、配置和不斷發展的 Linux 主線內核,在主線中保持穩定的 KMI 是不可行的。但是,這在高度受限的 GKI 環境中是可能的。約束條件是:

  • 該KMI是相同的LTS內核中唯一的穩定(例如, android11-5.4 )。
    • 沒有KMI穩定性保持android-mainline
  • 只有在AOSP供給和對應的分支定義的特定工具鏈被用於建立內核和模塊。
  • 只有在符號列表中指定的模塊使用的已知符號才會被監視穩定性並被視為 KMI 符號。
    • 推論是模塊必須僅使用 KMI 符號。如果需要非 KMI 符號,則通過失敗的模塊加載來強制執行此操作。
  • KMI 分支凍結後,任何更改都不能破壞 KMI,包括:
    • 配置更改
    • 內核代碼更改
    • 工具鏈更改(包括更新)

KMI監控

ABI監測工具監測presubmit測試期間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.comkernel_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 內核必須配置為在不同的設備集上運行。因此,它必須內置支持所有這些設備所需的所有子系統和選項,不包括用於啟用硬件的可加載模塊。合作夥伴應要求所需的配置變化來開發內核。

啟動更改

為了便於GKI和供應商部件之間的完全分離,在boot分區僅包括一般的組件,包括內核和與GKI模塊內存盤。定義了新版本的引導頭 (v3) 以指示符合 GKI 架構。啟動映像的 GKI 版本由 Google 提供,並在測試 GKI 兼容性時替換供應商的啟動映像版本。

為第一階段的虛擬盤initrecovery ,和fastbootdinitramfs包含由自舉程序級聯2 CPIO檔案圖像。第一cpio歸檔來自於新vendor_boot分區。第二個來自於boot分區。

此處提供了更改的摘要。見供應商的引導分區的詳細信息。

啟動分區

boot分區包括報頭,內核和引導虛擬盤的通用部分的CPIO歸檔。

了引導頭v3的boot分區,現有的這些部分boot分區不再存在:

  • 第二階段引導加載程序:如果設備具有第二階段引導加載程序,則必須將其存儲在其自己的分區中。
  • DTB:該DTB存儲在供應商引導分區

boot分區包含與GKI組件cpio歸檔:

  • 位於GKI內核模塊/lib/modules/
  • first_stage_init ,它依賴於庫
  • fastbootdrecovery (在A / B和虛擬A中使用/ B設備)

該GKI引導映像是由谷歌提供,必須用於GKI兼容性測試

最新的arm64 android11-5.4 boot.img是下載ci.android.comaosp_arm64中的構建工件aosp-master分支。

最新的arm64 android11-5.4內核映像( Image.gz )可被下載ci.android.comkernel_aarch64中的構建工件aosp_kernel-common-android11-5.4分支。

供應商引導分區

vendor_boot分區引入了GKI。它與虛擬 A/B 進行 A/B 連接,並由一個標頭、供應商 ramdisk 和設備樹 blob 組成。供應商 ramdisk 是一個 CPIO 存檔,其中包含設備啟動所需的供應商模塊。這包括啟用關鍵 SoC 功能的模塊以及啟動設備和顯示閃屏所需的存儲和顯示驅動程序。

CPIO 檔案包含:

  • 第一級init廠商內核模塊位於/lib/modules/
  • modprobe配置文件位於/lib/modules
  • modules.load指示第一階段期間要裝載的模塊文件init

引導加載程序要求

引導加載程序必須(從加載通用虛擬盤CPIO圖像boot分區)到存儲器中立即供應商虛擬盤CPIO圖像(從以下vendor_boot分區)。解壓後,結果是覆蓋在供應商 ramdisk 文件結構之上的通用 ramdisk。

GKI 兼容性測試

對於 Android 11 平台版本,使用 v5.4 內核啟動的設備必須使用 Google 提供的 GKI 啟動映像運行 VTS 和 CTS-on-GSI 測試。

為 GKI 內核做貢獻

該GKI內核從AOSP通用內核開始內置android11-5.4 。所有提交的補丁必須遵守這些準則的貢獻,該文獻兩個策略:

  1. 最佳:使所有的更改上游的Linux。如果合適,向後移植到穩定版本。這些補丁會自動合併到相應的 AOSP 通用內核中。如果補丁已經在上游 Linux 中,請發布符合以下補丁要求的補丁的向後移植。
  2. 少好:開發你的補丁了樹(從上游的Linux的觀點)。除非補丁修復了Android的具體錯誤,他們不太可能,除非他們已經協調與被接受kernel-team@android.com

對於實施 GKI 的合作夥伴,可能存在需要樹外補丁的正當理由(尤其是在必須滿足芯片計劃的情況下)。對於這些情況,提交的Buganizer問題。

通過格里特提交GKI更改android-mainline分支需要先回遷到其他的版本分支。

與可加載模塊相關的源代碼不需要貢獻給 GKI 內核源代碼樹,但是強烈建議將所有驅動程序提交給上游 Linux。

從主線請求向後移植

通常,GKI 合作夥伴所需的主線補丁可以向後移植到 GKI 內核。上傳後的補丁,以格里特貢獻的指導方針

如果補丁已經發佈到上游,但還沒有被接受,建議等到它被接受。如果時間表不允許等待補丁被上游接受,請提交 Buganizer 問題。

添加和刪除 GKI 配置

請求在CONFIGS的添加或刪除arch/arm64/configs/gki_defconfig ,提交的Buganizer問題寫明請求和理由。如果沒有可訪問的 Buganizer 項目,則將帶有配置更改的補丁發佈到 Gerrit,並確保提交消息明確說明需要配置的原因。

凍結的 KMI 內核中的配置更改不得影響 KMI。

修改核心內核代碼

不鼓勵修改 AOSP 通用內核中的核心內核代碼。首先將補丁發送到上游 Linux,然後向後移植它們。如果核心內核更改有充分的理由,請提交 Buganizer 問題,明確說明請求和理由。如果沒有 Buganizer 問題,則不接受任何 Android 特定功能。發送電子郵件至kernel-team@android.com如果不能創建的Buganizer問題。

使用通過 EXPORT_SYMBOL_GPL() 導出符號

不要向上游發送僅包含符號導出的補丁。要被認為是上游的Linux,的相加EXPORT_SYMBOL_GPL()要求使用符號的在樹模塊化驅動程序,因此包括新的驅動程序或改變在相同的補丁集作為導出現有的驅動程序。

當向上游發送補丁時,提交消息必須包含一個明確的理由,說明為什麼需要補丁並對社區有益。啟用導出以使樹外驅動程序或功能受益對於上游維護者來說並不是一個有說服力的論點。

如果由於某種原因無法將補丁發送到上游,請提交 Buganizer 問題並解釋無法將補丁發送到上游的原因。通常,作為內核子系統支持接口的符號很可能被接受為導出。但是,隨機的內部輔助函數不太可能被接受,您將被要求重構代碼以避免使用它們。

將符號添加到 KMI 符號列表

KMI 符號列表必須使用供應商模塊使用的 GKI 內核符號進行更新。因為只有 KMI 符號保持穩定,所以 GKI 不允許加載依賴於非 KMI 符號的模塊。

extract_symbols腳本從內核構建樹提取相關的符號,並且可以用於更新KMI符號列表。參考符號列表文件的更多細節。