Google is committed to advancing racial equity for Black communities. See how.
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

通用內核映像

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 Framework代碼必須假定最多支持五個內核版本,並且未對新平台版本進行任何內核更改(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社區很難將所需的功能和驅動程序提供給上游內核,因此解決碎片化問題具有挑戰性。

修復碎片:通用內核映像

通用內核映像Generic Kernel Image ,GKI)項目通過統一核心內核並將SoC和闆卡支持從核心內核轉移到可加載模塊中,從而解決了內核碎片問題。 GKI內核為內核模塊提供了穩定的內核模塊接口(KMI),因此可以獨立更新模塊和內核。

GKI:

  • ACK資源建成。
  • 每個架構,每個LTS版本的單內核二進製文件以及相關的可加載模塊(當前僅適用於android11-5.4android12-5.4 arm64)。
  • 所支持的相關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產品

GKI內核需要使用在Android S (2021)平台版本中使用內核版本v5.10或更高版本啟動的某些設備。簽名的啟動映像將可用,並通過LTS和嚴重的錯誤修復定期進行更新。因為將為KMI保持二進制穩定性,所以可以安裝這些引導映像,而無需更改供應商映像。

GKI 2.0目標

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

GKI設計

KMI內核分支

GKI內核是從ACK KMI內核分支構建的,在Android Common Kernels中對其進行了詳細描述。 KMI由內核版本和Android平台版本唯一標識,因此分支名為<androidRelease>-<kernel version> 。例如, Android 11的5.4 KMI內核分支名為android11-5.4.預計對於Android S (未提交並僅用於演示新分支模型的未來發展),將有兩個附加的KMI內核android12-5.4和一個基於新LTS內核的第二個內核,應該是於2020年底宣布。

KMI內核層次結構

圖4顯示了5.10 KMI內核的分支層次結構。 android12-5.10是對應於Android S的KMI內核,而android13-5.10對應於Android T的KMI內核(未提交,僅用於演示新的分支模型將如何發展)。

5.10的KMI內核層次結構

圖4. 5.10的KMI內核層次結構

如圖4所示,KMI分支通過三個階段循環:開發(dev),穩定(stab)和凍結。這些階段在Android Common Kernels中進行了詳細描述。

凍結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,並確保它們兼容。

可以從ci.android.comkernel_aarch64構建)下載最新的android11-5.4二進制GKI內核。

單一編譯器

編譯器的更改可能會更改影響ABI的內部內核數據結構佈局。由於保持KMI穩定至關重要,因此用於構建GKI內核的工具鏈必須與用於構建供應商模塊的工具鏈完全兼容。 GKI內核是使用AOSP中包含的LLVM工具鏈構建的。

從Android 10開始,所有Android內核都必須使用LLVM工具鏈構建。使用GKI,用於構建產品內核和供應商模塊的LLVM工具鏈必須生成與AOSP的LLVM工具鏈相同的ABI,並且合作夥伴必須確保KMI與GKI內核兼容。

Android內核構建文檔描述瞭如何構建參考GKI內核。特別是,記錄在案的過程可確保使用正確的工具鍊和配置來構建內核。鼓勵下游合作夥伴使用相同的工具來生成最終內核,以避免由工具鍊或其他構建時依賴性導致的不兼容性。

內核配置

arch/arm64/configs/gki_defconfig內核是使用arch/arm64/configs/gki_defconfig 。因為這些配置影響KMI,所以對GKI配置的管理非常謹慎。對於凍結的KMI內核,只有在不影響KMI的情況下,才可以添加或刪除配置。

必須將GKI內核配置為在各種設備上運行。因此,它必須對所有這些設備所需的所有子系統和選件具有內置支持,不包括用於啟用硬件的可加載模塊。合作夥伴應請求對開發內核進行必要的配置更改

開機變更

為了促進GKI和供應商組件之間的明確分離, boot分區僅包含通用組件,包括內核和帶有GKI模塊的ramdisk。定義了新版本的引導標頭(v3),以指示符合GKI體系結構。引導映像的GKI版本由Google提供,並在測試GKI兼容性時替換了引導映像的供應商版本。

用於第一階段的initrecoveryfastbootd的ramdisk是一個initramfs映像,它包含兩個由引導加載程序連接的CPIO歸檔文件。第一個CPIO存檔來自新的vendor_boot分區。第二個來自boot分區。

此處提供了更改摘要。有關詳細信息,請參見供應商引導分區

引導分區

boot分區包括引導ramdisk通用部分的標頭,內核和CPIO存檔。

使用引導頭v3 boot分區時,先前的boot分區的以下部分不再存在:

  • 第二階段引導程序:如果設備具有第二階段引導程序,則必須將其存儲在其自己的分區中。
  • 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可從ci.android.com下載,位於aosp-master分支的aosp_arm64構建構件中。

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

供應商引導分區

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

CPIO存檔包含:

  • 位於/lib/modules/第一階段init供應商內核模塊
  • 位於/lib/modules modprobe配置文件
  • modules.load文件,指示在第一階段init期間要加載的模塊

引導程序要求

引導加載程序必須緊隨供應商ramdisk CPIO映像(來自vendor_boot分區)將通用ramdisk CPIO映像(來自boot分區)加載到內存中。解壓縮後,結果是通用虛擬磁盤覆蓋在供應商虛擬磁盤的文件結構之上。

GKI兼容性測試

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

為GKI內核做貢獻

GKI內核是從android11-5.4開始的AOSP通用內核構建的。所有提交的補丁程序必須符合這些貢獻準則,其中記錄了兩種策略:

  1. 最好:對上游Linux進行所有更改。如果合適,請向後移植到穩定版本。這些補丁會自動合併到相應的AOSP通用內核中。如果該修補程序已經在上游Linux中,請發布符合以下修補程序要求的修補程序的反向端口。
  2. 不太好:從樹上開發補丁程序(從上游Linux的角度來看)。除非這些補丁修復了一個Android特定的錯誤,否則除非與kernel-team@android.com協調,否則它們不太可能被接受。

對於實施GKI的合作夥伴來說,可能有充分的理由為什麼需要樹外補丁(特別是如果必須滿足芯片計劃的話)。對於這些情況,請提交Buganizer問題。

首先通過Gerrit將GKI更改提交到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符號列表

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

extract_symbols腳本從內核構建樹中提取相關符號,並可用於更新KMI符號列表。有關更多詳細信息,請參考符號列表文檔