Google在android11-5.4分支上开始要求所有下游厂商使用Generic Kernel Image(GKI),需要将SoC和device相关的代码从核心内核剥离到可加载模块中(下文称之为GKI改造),从而解决内核碎片化问题。GKI为内核模块提供了稳定的内核模块接口(KMI),模块和内核可以独立更新。本文主要介绍了在GKI改造过程中需遵循的原则、遇到的问题和解决方法。
一、不能破坏KMI
冻结KMI后,分支在其整个生命周期中都保持冻结状态,原则上不会接受破坏KMI的修改(除非发现严重的安全问题,且不能在不影响KMI稳定的情况下得到缓解)。在冻结的分支中,只有不破坏KMI的bug修复和partner features才能被接收。在不影响现有KMI接口的前提下,可以使用新导出的符号扩展KMI,新接口被接收添加到KMI后,必须保持稳定,不能被将来的修改破坏。
1. 问题现象:
替换google boot.img后,开机串口有如下打印错误,
[ 1.669135] init: Loading module /lib/modules/foo.ko with args ""
[ 1.676281] foo: disagrees about version of symbol xxx
2. 原因分析:
出现这种错误可能的原因是ko中调用的符号与vmlinux中的符号crc值不匹配,如在KMI接口使用的结构体中增加了新字段,间接地修改了接口定义:
3. 推荐方法:
(1) 扩展内核原生结构体和接口
(2) 向google申请,在内核原生结构体中加入一些padding
Google在android11-5.4分支中新增了两个宏
ANDROID_VENDOR_DATA,在结构体中保留一些padding以备将来可能的使用,这些padding正常的都是位于结构体尾部,padding变量标识n从1开始递增。
ANDROID_VENDOR_DATA_ARRAY同ANDROID_VENDOR_DATA,分配一个数组,数组大小是s,元素是u64类型。
下面是使用ANDROID_VENDOR_DATA和ANDROID_VENDOR_DATA_ARRAY在内核结构体中增加新字段的示例:
4. 措施:
在编译脚本中增加检测,编译GKI kernel后,比较生成的Module.symvers和原生android/abi_gki_aarch64.xml文件中符号的crc值,如果crc值不匹配,编译报错,表示使用了非规则的方法修改原生接口或结构体。
二、内核模块只能使用已export并添加到白名单中的接口
android11-5.4分支build.config.gki.aarch64文件中有如下配置
表示模块只能使用abi_gki_aarch64文件中的符号。
1. 问题现象:
替换google boot.img后,串口有如下打印错误:
[ 1.735506] foo: Unknown symbol xxx(err -2)
2. 原因分析:
原生内核没有用EXPORT_SYMBOL_GPL把接口xxx export,或已经export的接口没有添加到白名单(该接口abi可能不稳定)
3. 推荐方法:
(1) Google建议向上游linux社区申请将要使用的内核接口export,并向Google申请,将接口添加到白名单android/abi_gki_aarch64_xxx
android/abi_gki_aarch64_xxx
(2)使用其他已在白名单中的接口替代。
三、vendor hook机制
考虑到SoC和OEM厂商可能要对原生内核做一些客制化修改和优化,Google提供了一套vendor hook机制,下游厂商在需要修改内核源码的地方添加hook,并向Google申请,将patch upstream到AOSP。