通用的内核映像-GKI

通用内核映像  |  Android 开源项目  |  Android Open Source Project

前提摘要:

Android 通用内核(ACK) 是所有Android产品内核的基础,供应商内核和设备内核位于供应商内核和设备内核位于 ACK 的下游。供应商通过修改内核源代码并添加设备驱动程序,添加了对 SoC 和外围设备的支持。这些修改内容可能很多,以至于设备上运行的代码中有多达 50% 是树外代码(并非来自上游 Linux 和 AOSP 通用内核)。

因此,设备内核由以下部分组成:

  • 上游LTS:就是linux内核
  • AOSP:AOSP 通用内核的其他 Android 专用补丁程序
  • 供应商:供应商提供的 SoC 和外围设备支持以及优化补丁程序
  • 原始设备制造商 (OEM)/设备:其他设备驱动程序和自定义项

几乎所有设备都具有自定义内核。这就导致了内核碎片化问题。

android glide 使用说明 android gki_android

 

碎片化的代价

难于更新,linux内核的更新难于合并到设备内核里,因为你不能只更新内核,驱动程序可能也需要更新;Android 安全公告(ASB) 中引用的安全补丁程序也难于移植合并到设备内核;

解决碎片化的问题:通用内核映像-GKI

GKI,就是把内核核心部分挑出来,把SoC和板级支持的部分移至可加载的模块里去而成的内核;

GKI 内核为内核模块提供了稳定的内核模块接口(KMI),支持内核和模块的独立更新,让这俩关联性降低了

GKI具有如下特点,

android glide 使用说明 android gki_工具链_02

下图是实现了GKI的Android设备,

android glide 使用说明 android gki_Android_03

GKI 设计

KMI 内核分支

GKI 内核基于 ACK KMI 内核分支构建而来。KMI 由内核版本和 Android 平台版本唯一标识,因此分支的命名方式为 

-。例如,Android 11 的 5.4 KMI 内核分支名为 

android11-5.4.

KMI 分支会经历三个阶段:开发阶段(dev)、稳定阶段(stab)和冻结阶段;

KMI 就是一个文件,里面好多函数名,表示你可以用哪些文件(abi_gki_aarch64_qcom);

冻结一般就不会大更了,你可以扩展KMI通过导出新的符号,它们添加到KMI后会立刻进入稳定状态;但是你不能去改现有的KMI,比如不可以对KMI接口所用的数据结构添加字段,但是可以添加函数,也就是底层数据结构别动,但是你可以用它写你自己的函数,

android glide 使用说明 android gki_android_04

KMI 稳定性

KMI 稳定性就是要保证生成的 GKI 内核和模块必须正常运行,就如同它们是一起构建的一样;因为它们构建不太一样,GKI 内核以二进制文件的形式构建和推出,但供应商可加载模块在单独的树中构建;

GKI 兼容性测试,大概就是要替换为通用的内核映像GKI和通用的系统映像GSI然后刷机,但是无论使用的哪种内核,都应该保证供应商镜像里的可加载模块可以正常运行,这个是关键;

KMI 并非包含内核中的所有符号,甚至并非包含完整的 3 万多个导出符号。相反,可供模块使用的符号都明确列在一组符号列表文件中,这些文件在内核树的根目录中公开维护。所有符号列表文件中所有符号的并集定义了一组维持为稳定版的 KMI 符号。abi_gki_aarch64_db845c 就是符号列表文件的一个示例,该文件声明了 DragonBoard 845c 必需的符号。

只有符号列表中列出的符号及其相关结构和定义才会被视为 KMI 的一部分。如果符号列表中没有你需要的符号,可以对其发布更改。当新接口加入符号列表,并因此成为 KMI 描述的一部分后,它们会维持为稳定版;在分支被冻结后,不得将其从符号列表中移除,也不得进行修改。

每个 KMI 内核树都有自己的一组符号列表。系统不会尝试在不同的 KMI 内核分支之间提供 ABI 稳定性。例如,android11-5.4 的 KMI 完全独立于 android12-5.4 的 KMI,就是ABI只检查一个KMI;

面对不同的工具链、配置和不断发展的 Linux Mainline 内核,在 Mainline 中维持稳定版 KMI 并不可行。但是,这在十分受限的 GKI 环境中还是有可能的。限制条件如下:

  • KMI 仅在相同的 LTS 内核(例如 android11-5.4)内保持稳定。
  • 不对 android-mainline 维持 KMI 稳定性。
  • 只使用 AOSP 中提供的并且为相应分支指定的特定 Clang 工具链来构建内核和模块。
  • 只有在符号列表中指定的已知由模块使用的符号才会受到稳定性监控,并被视为 KMI 符号。
  • 最后的结果就是,模块只能使用 KMI 符号。这一点通过在需要非 KMI 符号时使模块加载失败来强制执行。
  • KMI 分支被冻结后,将不能进行任何会破坏 KMI 的更改,这些包括:
  • 配置更改
  • 内核代码更改
  • 工具链更改(包括更新)

KMI 监控

ABI 监控工具可在 预提交测试期间 监控 KMI 稳定性。破坏 KMI 的更改 无法通过预提交测试,必须重新处理才能实现兼容性。合作伙伴和公众可以将这些工具集成到其构建流程中比如ci。在开发过程中以及合并 LTS 版本时,Android 内核团队使用这些工具来查找 KMI 遭到破坏的情况。如果在 LTS 合并期间检测到 KMI 遭到破坏的情况,则可以移除违规补丁程序,或重构补丁程序以实现兼容性,从而保留该 KMI。

如果以不兼容的方式修改了现有 KMI 符号,相应 KMI 会被视为已遭到破坏。示例:

  • 向 KMI 函数添加了新参数
  • 向 KMI 函数所用的结构添加了新字段
  • 添加了新的枚举值,进而更改了 KMI 函数所用的枚举的值
  • 更改了配置,进而更改了影响 KMI 的数据成员的存在状态

添加新符号不一定会破坏 KMI,但必须将所用的新符号添加到符号列表和 ABI 表示形式中。否则,系统便无法识别将来对 KMI 这一部分的更改。

单编译器

更改编译器可能会改变影响 ABI 的内部内核数据结构布局。由于保持 KMI 稳定性非常重要,因此用于构建 GKI 内核的工具链必须与用于构建供应商模块的工具链完全兼容。GKI 内核是使用 AOSP 中包含的 LLVM 工具链构建的。

从 Android 10 开始,所有 Android 内核都必须使用 LLVM 工具链构建。使用 GKI 后,用于构建产品内核和供应商模块的 LLVM 工具链必须生成与 AOSP 中的 LLVM 工具链相同的 ABI,并且合作伙伴必须确保 KMI 与 GKI 内核兼容。

Android 内核 build 文档描述了参考 GKI 内核的构建方式。具体来说,文档中介绍的过程可确保使用正确的工具链和配置来构建内核。我们建议下游合作伙伴使用相同的工具来构建最终内核,以避免工具链或其他构建时依赖项导致的不兼容问题。

内核配置

GKI 内核是使用 arch/arm64/configs/gki_defconfig 构建的。由于这些配置会影响 KMI,因此对 GKI 配置的管理非常谨慎。对于冻结的 KMI 内核,只有在不影响 KMI 的情况下,才能添加或移除配置。

GKI 内核必须配置为可在各种设备上运行。因此,该内核必须内置对所有这些设备所需的所有子系统和选项的支持,其中不包括用于启用硬件的可加载模块。合作伙伴应请求对开发内核进行所需的配置更改

GKI 兼容性测试

对于 Android 11 平台版本,搭载 v5.4 内核的设备必须使用 Google 提供的 GKI 启动映像运行 VTS 和 CTS-on-GSI 测试。

GSI = 我们的user版本 + google原生system.img + google原生boot-5.4.img

VTS = 我们的user版本 + 我们自己的vendor-debug-boot.img + google原生system.img + google原生boot-5.4.img 

android glide 使用说明 android gki_android glide 使用说明_05

 

向 KMI 符号列表添加符号

必须使用供应商模块所用的 GKI 内核符号来更新 KMI 符号列表。由于只有 KMI 符号才会被维持为稳定版,因此 GKI 不允许加载依赖于非 KMI 符号的模块。

extract_symbols 脚本可从内核 build 树中提取相关符号,还可用于更新 KMI 符号列表。