4. GKI 版本迁移场景

GKI(Generic Kernel Image)迁移并不是单一动作,而是一组围绕内核接口、模块边界和启动链路的系统性改造。实际项目中,最常见的迁移需求可以归纳为三条主线:旧版 GKI 升级到新版 GKI、非 GKI 内核改造为 GKI、以及跨 Android 版本迁移。三者难度、风险点和验证重点各不相同,但都遵循“先解耦、再对齐、后验证”的基本逻辑。

一、核心原理:GKI 迁移的本质是接口契约迁移

GKI 的核心思想是把内核拆分为通用内核镜像(Generic Kernel Image)与供应商模块(Vendor Modules)两部分。通用部分由 Google 统一维护,供应商差异通过 KMI(Kernel Module Interface)和 GKI 模块机制承载。因此,迁移的本质不是“换一个内核版本”,而是把原来直接编译进内核的私有代码,迁移到符合 KMI 约束的模块或受控接口上。

KMI 由符号列表、结构体布局、函数签名和模块版本信息共同定义。只要 KMI 保持稳定,供应商模块就可以在不同 GKI 版本之间复用;一旦 KMI 发生破坏性变更,就必须同步修改模块并重新验证。理解这一点,就能明白为什么迁移工作往往集中在符号导出、结构体访问、模块加载顺序和启动参数上。

GKI 三类迁移场景与关键动作 场景一:旧 GKI → 新 GKI • 核对 KMI 符号列表差异 • 更新 vendor 模块 .ko • 检查 module_layout 版本 • 验证启动与模块加载 • 回归外设与电源管理 场景二:非 GKI → GKI • 拆分通用与厂商代码 • 私有驱动模块化 • 适配 KMI 导出符号 • 改造 dts / 启动参数 • 建立模块签名与校验 场景三:跨 Android 版本 • 对齐目标 GKI 分支 • 适配 SELinux 策略 • 更新 vendor boot 镜像 • 处理 ABI / API 变更 • CTS / VTS 全量验证 公共迁移流程:解耦 → 对齐 KMI → 模块化 → 启动验证 → 回归测试 所有场景最终都收敛到 KMI 兼容性与模块加载正确性 KMI 符号一致性 符号缺失 / 签名变更 模块加载成功率 insmod / modprobe 日志 启动与 CTS 通过率 开机、外设、VTS 用例

二、方法流程:三类场景的差异化操作

旧版 GKI 到新版 GKI通常是最“轻”的迁移。因为双方都遵循 GKI 框架,主要工作是核对 KMI 差异并重新编译供应商模块。典型命令如下:

# 查看当前内核 KMI 符号
adb shell cat /proc/kallsyms | grep -i "kmi"

# 检查模块版本与内核是否匹配
modinfo vendor_display.ko | grep vermagic

# 加载模块并观察日志
adb shell dmesg | grep -i "module_layout\|Unknown symbol"

如果出现 Unknown symbol 或 disagrees about version of symbol,说明 KMI 不匹配,需要更新模块或调整符号导出。

非 GKI 到 GKI是改造量最大的场景。原有代码往往把大量厂商驱动直接编译进 vmlinux,迁移时必须把这些代码拆分为独立模块,并只通过 KMI 允许的接口访问内核。常见动作包括:把 arch/arm64 下的板级代码迁移到设备树;把私有驱动改为 ko;把直接调用未导出符号改为使用 GKI 提供的稳定接口。

跨 Android 版本迁移则叠加了 Android 平台侧变化。除了内核本身,还要同步 vendor_boot、init_boot、SELinux 策略、HAL 接口和启动参数。例如 Android 13 到 Android 14 可能引入新的 bootconfig 字段或模块加载策略,必须逐项对齐。

三、典型场景与验证清单

下面这张清单可以作为迁移前的自检表,覆盖三类场景的共性检查点:

检查项 旧 GKI → 新 GKI 非 GKI → GKI 跨 Android 版本
KMI 符号列表 必须逐项比对 需重新建立导出 随目标分支对齐
模块化改造 通常已完成 核心工作量 可能需二次拆分
设备树 / bootconfig 小幅调整 需重构 需同步平台变更
SELinux 策略 一般不变 需新增规则 重点适配
验证重点 模块加载与回归 启动与驱动功能 CTS / VTS 全量

四、误区与复盘

常见误区一:只换内核版本,不改模块。很多团队认为升级 GKI 只是替换 Image,结果模块因 module_layout 校验失败而无法加载。正确做法是先确认 KMI 是否兼容,再决定模块是否需要重编。

常见误区二:把非 GKI 代码直接“塞进”模块。模块只能使用 KMI 导出的符号,直接引用未导出符号会导致编译或加载失败。必须通过 GKI 提供的稳定接口或 EXPORT_SYMBOL_GPL 白名单机制解决。

复盘建议:迁移完成后,保留一份 KMI 符号差异报告和模块加载日志。后续每次 GKI 升级都可以复用这份基线,快速判断哪些模块需要重新编译,哪些接口需要适配,从而把迁移从“一次性大工程”变成“可重复的例行操作”。

总体来看,三类迁移场景的差异在于改造深度,但共同的成功标准是一致的:KMI 对齐、模块可加载、系统可启动、功能可回归。抓住这条主线,就能在复杂的版本迁移中保持清晰的判断和可控的节奏。