GKI是什么? Google为什么要推行GKI?
GKI全称General Kernel Image。GKI在framework和kernel之间提供了标准接口,使得android OS能够轻松适配/维护/兼容不同的设备和linux kernel。
Google引入GKI的目的是将Framework和Kernel进一步的解耦。因为,android一直高度依赖linux kernel, linux kernel一更新,android系统就要跟着适配更新。google发步的安全patch,也会因为kernel的版本而掣肘。
通用内核映像 (GKI) 项目通过统一核心内核并将 SoC 和板级支持从核心内核移至可加载模块中,解决了内核碎片化问题。GKI 内核为内核模块提供了稳定的内核模块接口 (KMI),因此模块和内核可以独立进行更新。
内核碎片化带来的问题:(市面上的内核版本太多,严重阻碍了Android的迭代更新),具体描述如下:
1. 安全更新需要耗费大量人力
Android 安全公告 (ASB) 中引用的安全补丁程序必须向后移植(旧的android 系统)到每个设备内核中。但是,由于存在内核碎片化问题,向正常使用的 Android 设备传播安全修复的代价非常之高。
2. 很难合并长期支持的更新
长期支持 (LTS) 版本包含安全修复和其他重大问题修复。事实证明,使用最新的 LTS 版本是提供安全修复的最有效方式。我们发现,ASB 报告的内核安全问题中有 90% 都已在保持最新状态的 Pixel 设备上得到修复。(简单一句话,打补丁不如升级OS)
不过,由于设备内核中所有的自定义修改,很难仅将 LTS 修复合并到设备内核中。
3. 妨碍 Android 平台进行版本升级
由于碎片化问题,很难向正常使用的设备添加需要更改内核的 Android 新功能。Android 框架代码必须假设支持的内核版本多达 5 个,并且没有针对新的平台版本进行任何内核更改(Android 10 支持内核版本 3.18、4.4、4.9、4.14 和 4.19;在某些情况下,这些版本自 2017 年 Android 8 发布以来还未添加新功能)。
4. 很难将内核更改贡献回上游 Linux
对内核进行完所有更改后,大多数旗舰设备附带的内核版本已经至少存在 18 个月了。例如,kernel.org 于 2017 年 11 月发布了 4.14 版内核,而首批使用 4.14 版内核的 Android 手机于 2019 年春季才发布(新特性上推linux main branch就太晚了)。
上游内核发布与产品发布之间的这种长时间延迟导致 Android 社区很难将所需的功能和驱动程序馈送到上游内核中,因此解决碎片化问题并非易事。
于是就有了GKI项目!!!!!大家还记得从Android8引入的Trible 机制吗?也是Android将framwork和linux kernel解耦的一个项目。
总而言之,无论是Android还是Apple OS 都是希望能够将自己的最新OS推送到尽可能多的设备上,从而抢占市场分额!
核心特性和目标如下:
1. 基于 ACK 来源构建而成。(ACK , Android/Aosp common kernel,Android最上游代码)
2. 是每个架构和每个 LTS 版本的单内核二进制文件以及关联的可加载模块(目前只有适用于 android11-5.4 和 android12-5.4 的 arm64)。
3.已经过关联 ACK 支持的所有 Android 平台版本的测试。在 GKI 内核版本的生命周期内不会发生功能弃用。
4. 为给定 LTS 中的驱动程序提供了稳定版 KMI。
5. 不包含 SoC 专用代码或板卡专用代码。(这个很好理解,不包含e.g specifc Qualcomm and Board code)。
Key features and goals of Android GKI include:
Stability(稳定性): The GKI aims to maintain a stable ABI(Application Binary Interface//应用程序二进制接口), ensuring that the Android framework can work with different kernel versions without requiring modifications.
Compatibility(兼容性): GKI enables Android to run on a wide range of devices with different kernel configurations, reducing fragmentation and enabling faster updates.
Portability(可移植性): By abstracting Android from the underlying kernel, GKI simplifies the process of porting Android to new devices and adapting it to different kernel versions.
Security(安全性): GKI facilitates the timely delivery of security patches(通常是Google的安全性补丁) and updates by decoupling the Android framework from the kernel, allowing for more efficient patching processes.
GKI可以更改吗?
通常在开发中我们不修改,而且其GKI镜像中的内容配置文件,我们也不可以修改,例如:
../kernel_platform/msm-kernel/arch/arm64/configs/gki_defconfig
但是Android的官网也提供了修改的方法,如下说明,分别有三个方面(再次提醒,通常作为ODM是不改的,(Android(ACK) / Qualcomm(供应商内核) / ODM(设备内核));
1. 将新功能配置为 GKI 模块。
2. 将内置内核功能配置为 GKI 模块。
3. 将受保护的 GKI 模块转换为不受保护的模块。
以上三个类型的修改,后面需要的时候可以进一步细看。有一个提醒就是,无论哪个修改,都必需需要提交更改以供审核。GKI 模块是 Android 专用内核功能,因此无需在上游提交模块转换补丁。不过,您必须按照其他准则提交 Android 通用内核 (ACK) 补丁。
什么是ACK,简称AOSP Common Kernel,应该是和LKML几乎一样的分支内容。官网的解释是
AOSP 通用内核(也称为 Android 通用内核或ACK https://android.googlesource.com/kernel/common/ )是kernel.org内核的下游,包含 Android 社区感兴趣的尚未合并到主线或长期支持 (LTS) 内核中的补丁。
官网对android-mainlin做简单介绍:
android-mainline
是 Android 功能的主要开发分支。每当 Linus Torvalds 发布版本或候选版本时,Linux 主线就会合并到android-mainline
中。 2019 年之前,Android 通用内核是通过克隆最近声明的 LTS(Long Time Support) 内核并添加 Android 特定补丁来构建的。这个过程在 2019 年发生了变化,从android-mainline
分支出新的 Android 通用内核。这种新模型通过逐步实现相同的结果,避免了转发端口和测试 Android 补丁的大量工作。 android-mainline
经过重要的持续测试,该模型从发布之日起就确保了高质量的内核。
当上游声明一个新的 LTS 时,相应的公共内核将从android-mainline
分支出来。这允许合作伙伴在声明 LTS 版本之前通过从android-mainline
合并来开始项目。创建新的通用内核分支后,合作伙伴可以无缝地将合并源更改为新分支。
其他常见的内核分支从其关联的LTS 内核接收定期合并。这些合并通常在 LTS 版本发布后立即完成。例如,当 Linux 4.19.64 发布时,它被合并到 4.19 通用内核中(例如android-4.19-q
)。强烈鼓励合作伙伴(Qualcomm ? MTK?) 定期将通用内核(GKI)合并到其产品内核中,以及时了解 LTS 和 Android 特定错误修复的最新情况(Qualcomm 会不定期的去合并Android的通用内核补丁)。
所以,想给ACK 提交补丁一定是要经过严格省查的。所以,作为ODM开发中,通常不会这么做。对处理器/个别电子器件精通的内核维护者通常能够给linux内核提交patch,那也绝对是行业大牛;通常ODM开发内核中的驱动,我们通常需要定义在自己的config文件中,并且是一=m的形式进行编译。开机过程中,需要进行动态加载的。这个是目前GKI2.0的基本要求。
官网对GKI KMI内核分支介绍
GKI 内核具有稳定的内核模块接口。 KMI (内核模块接口)由内核版本和 Android 平台版本唯一标识,因此分支被命名为<androidRelease>-<kernel version>
。例如,Android 11 的 5.4 GKI 内核名为android11-5.4.
对于 Android 12,还有两个额外的 GKI 内核: android12-5.4
和android12-5.10
。kernel_platform/msm-kernel/Makefile. 我的高通平台是从Android 12 launch的。
也就是说,Android发布时候会绑定对应的linux kernel版本,目的是提供一个固定KMI。后续开发者可以根据KMI中的接口,进行对应的内核开发。
从 Android 12 开始,功能内核将少于启动内核,以限制功能内核的数量必须支持的稳定 KMI。
简单看下KMI的时间线:
每个 ACK KMI 分支都会循环经历三个阶段: 例如android12-5.10
开发阶段:
创建后,ACK KMI (我理解这是一个kernel分支,GKI则是对应的内核镜像)分支进入开发阶段(图 2 中的dev ),并开放为下一个 Android 平台版本提供功能贡献。当 5.10 被声明为新的上游 LTS 内核时,创建了android12-5.10
。内核版本的第二个 ACK KMI 分支可能会提前创建,以允许后续版本的开发。android13-5.10
是在android12-5.10
过渡出开发阶段时创建的。
稳定阶段:
当 ACK KMI 分支声明功能完成时,它会进入稳定阶段,在图 2 中标记为稳定阶段。合作伙伴功能和错误修复仍然被接受(合作伙伴:e.g Qualcomm),但启用了 KMI 跟踪以检测影响接口的任何更改。在此阶段,会接受破坏 KMI 的更改,但必须根据需要更新 KMI 定义。有关 KMI 监控的详细信息,请参阅GKI 概述。
冷冻阶段:
在将新平台版本推送到 AOSP 之前,ACK KMI 分支会被冻结,并在分支的生命周期内保持冻结状态。这意味着,除非发现严重的安全问题,且无法在不影响稳定 KMI 的情况下缓解,否则不会接受任何破坏 KMI 的更改。为了避免 KMI 损坏,如果 Android 设备不需要修复,则可能会修改或删除从 LTS 合并的一些补丁。
GKI1.0的情况,这里就不多解释,有个核心的要求是:GKI 1.0 不要求供应商模块可卸载,可以=y。
我们看看GKI2.0的情况下,
GKI 2.0 - GKI 产品
搭载 Android S (2021) 平台版本且使用内核版本 v5.x(5.x 是 2020 年年底被选为 LTS 的内核版本)或更高版本的设备必须附带 GKI 内核(将提供已签名的启动映像,并通过 LTS 和重大问题修复定期对其进行更新。由于 KMI 将保持二进制稳定性,因此无需对供应商映像进行任何更改,即可安装这些启动映像。
GKI 2.0 的目标
1. 不为 GKI 引入明显的性能或能效降低问题。
2. 使 OEM 无需供应商参与即可提供内核安全修复和问题修复 (LTS)。(是说OEM直接从Google 获取补丁?而不用从e.g Qualcomm获取?)
3. 降低更新设备主要内核版本(例如,将 v5.x 更新为 v5.y)所需的费用。(通常我们的升级直接从Qualcomm拉代码)
4. 通过按照清晰的升级过程更新内核版本,只为每个架构(例如Qualcomm6490 Armv7 架构)维护一个 GKI 内核二进制文件。
开发有关:
GKI2.0 下的内核开发,首先要注意,需要遵循GKI2.0中要求的ABI接口检查,目的是,你不能更改KMI中的接口。这个部分,可以在编译中进行检查。
详细的说明如下ABI检查如下:
ABI 工具使用 KMI 符号列表来限制必须监控哪些接口以实现稳定性。主符号列表包含 GKI 内核模块所需的符号。供应商应提交并更新其他符号列表,以确保其依赖的接口保持 ABI 兼容性。
符号列表包含报告的特定供应商或设备所需的符号。工具使用的完整列表是所有 KMI 符号列表文件的并集。ABI 工具会确定每个符号的详细信息,包括函数签名和嵌套数据结构。
当 KMI 被冻结时,不允许对现有 KMI 接口进行任何更改;它们是稳定的。
当有补丁改动了KMI 接口或者数据结构(例如include/linux/mmxx.h),应用此补丁后再次运行 build ABI 时,该工具会退出,同时显示非零错误代码并报告ABI错误。例如:
Leaf changes summary: 1 artifact changed
Changed leaf types summary: 1 leaf type changed
Removed/Changed/Added functions summary: 0 Removed, 0 Changed, 0 Added function
Removed/Changed/Added variables summary: 0 Removed, 0 Changed, 0 Added variable
'struct mm_struct at mm_types.h:372:1' changed:
type size changed from 6848 to 6912 (in bits)
there are data member changes:
[...]
最常见的错误原因是,驱动程序使用了内核中不在符号列表中的新符号。目前,我使用的符号表是:hon4490-android14-vendor/kernel_platform/common/android/abi_gki_aarch64_qcom,当出现问题时,第一时间检查该接口是否在符号列表中。
最后,新的设备驱动是需要单独配置到kernel configure中,而且必需以=m的形式进行,这是GKI2.0的标准要求。
举个例子:
例如,需要往内核中添加一个新的设备驱动,Touch的驱动。
1. 修改上一层目录中的Kconfig/Makefile,添加驱动编译规则,例如
obj-$(CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS) += xxx.o
那么,同样需要将CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS编译选项添加到整个内核的编译配置中,才能够在编译内核的时候进行编译。
这里使用到的就是 configs/vendor/xxx_GKI.config , 添加CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=m的编译配置。其实同样可以添加到configs/vendor/xxx_consolidate.config。这里的区别是什么?原来,xxx_consolidate.config是编译userdebug时候被包含的配置文件。
那关于GKI2.0的大概历史就是这样,相关的开发注意事项需要结合开发过程进行深化理解。
实干出真知!