GKI改造原则、机制和方法

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

(2)使用其他已在白名单中的接口替代。

 

三、vendor hook机制

考虑到SoC和OEM厂商可能要对原生内核做一些客制化修改和优化,Google提供了一套vendor hook机制,下游厂商在需要修改内核源码的地方添加hook,并向Google申请,将patch upstream到AOSP。

1. vendor hook实现步骤如下:

(1) 在include/trace/hooks/目录下创建一个新的头文件xxx.h,定义一组hook接口register_trace_android_vh_xxx、trace_android_vh_xxx和全局变量__tracepoint_android_vh_xxx

(2) 在drivers/android/vendor_hooks.c文件中包含hook头文件xxx.h,export hook变量__tracepoint_android_vh_xxx给模块使用

(3)在内核模块中增加register代码将回调函数绑定到hook变量__tracepoint_android_vh_xxx

(4) 在内核xxx.c文件中包含hook头文件xxx.h,调用hook接口trace_android_vh_xxx(即hook变量__tracepoint_android_vh_xxx绑定的callback函数)

2. 问题现象:

测试偶现dump “BUG: scheduling while atomic:”

 

3. 原因分析:

vendor hook变量有两种,都是基于tracepoints的:

正常的:使用DECLARE_HOOK宏创建tracepoint函数trace_<name>,要求name在trace中是独一无二的,callback函数的调用是在关抢占的场景中使用的

受限制的:受限制的hook在scheduler hook类的场景中使用,绑定的callback函数可以在cpu offline或非原子上下文中调用(调用前没有关抢占),受限制的vendor hook不能被解绑定,所以绑定的模块不能卸载,只允许有一个绑定(任何其他绑定将会返回-EBUSY错误)。

 

4. 推荐方法:

根据使用场景选择适合的vendor hook变量,在可能会调度的场景需要使用受限制的vendor hook

 

四、vendor hook延伸

SoC和OEM feature都要从内核剥离出来编译成内核模块,内核源码中相互调用export接口是没有问题的,那么模块之间相互调用export接口呢?

1. 问题现象:

编译报错 depmod: ERROR: Found 2 modules in dependency cycles!

 

2. 原因分析:

模块之间相互调用export接口,导致编译时报错。

3. 推荐方法:

借鉴Google的vendor hook机制,在A模块中定义并export全局变量,B模块初始化函数中将callback函数注册绑定到该全局变量,这样只有B模块调用A模块中的变量和接口,A模块通过hook变量回调B模块中接口,解决编译调用问题。

五、通过内核已有的事件注册接口替代vendor hook

是不是只能通过vendor hook实现修改内核呢?

android11-5.4分支log中有这么一笔关于vendor hook的提交:

为按键组合增加vendor hook

我们注意到内核中有一个接口input_register_handle,它的注释是注册一个新的input handle,添加到设备和handle列表中,只要使用input_open_device()接口打开设备,输入事件触发后会轮询到该handle,input_register_handle接口应该在handler的connect方法中调用,因此我们可以使用input_register_handler、input_register_handle来实现上述的按键组合功能,不需要在内核中增加vendor hook。

通过这个改造示例,启发我们思考是否只能使用vendor hook机制,是否可以使用其他内核已有的机制、事件注册接口将原先嵌入在内核中的feature剥离出来。

 

参考文章

[1]  https://source.android.com/devices/architecture/kernel/generic-kernel-image

[2]  https://blog.csdn.net/geshifei/article/details/94360470

[3]  https://blog.csdn.net/qq_39937242/article/details/82631165

长按关注

内核工匠微信

Linux 内核黑科技 | 技术文章 | 精选教程

  • 23
    点赞
  • 117
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
Linux GKI(Generic Kernel Image)开发指南是一份详细介绍如何在Linux内核中使用GKI功能的指南。GKI是一种通用内核映像格式,可以在不同设备上共享和运行。以下是关于如何使用GKI开发Linux内核的一些要点。 首先,在开始使用GKI开发之前,需要了解GKI的基本概念和原理。GKI的主要目标是将内核的硬件相关部分和设备树分开,并将其打包成独立的内核映像。这样做的好处是可以将通用的内核代码和设备树与具体的硬件相关代码分开,实现内核的共享和升级更加简便。 其次,GKI开发中的一个关键步骤是配置内核选项。在配置内核选项时,需要根据目标设备的具体要求选择相应的配置选项,包括硬件支持、功能模块和驱动程序等。此外,还需要配置GKI相关选项,如将设备树与内核映像分开打包、启用GKI功能等。 然后,需要进行内核源代码的编译。按照GKI的要求,需要使用传统的内核编译过程,但在编译选项和命令上有一些差异。编译过程中需要指定设备树文件路径、生成GKI映像等。 最后,需要将编译生成的GKI内核映像刷写到目标设备上进行测试和使用。刷写过程可能因设备类型和平台有所差异,需要参考具体设备的刷写方法和工具。 总之,Linux GKI开发指南提供了一种使用通用内核映像的方法,可以简化内核的开发和升级过程。通过了解GKI的基本原理和步骤,并按照指南进行开发,可以更好地在不同设备上共享和运行Linux内核

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

内核工匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值