移植firefly core-1126-jd4官方sdk源码到其他rv1126板卡时 kernel启动中失去响应问题解决
问题背景
在项目中采用firefly core-1126-jd4的sdk适配其他rv1126板卡遇到kernel启动中无响应。串口能看到运行到usb、mmc等模块驱动流程,但之后就打印,通过追加打印确认usb、mmc模块的init已经执行完,怀疑是执行其他某个静态编译进kernel的模块init中失去响应,但该模块失去响应前并未有任何打印,所以不确定具体是哪个模块。茫茫kernel数百上千个模块难以分析。
问题定位
kernel源码的init/main.c文件中有个二维函数地址表initcall_levels,定义如下:
extern initcall_entry_t __initcall_start[];
extern initcall_entry_t __initcall0_start[];
extern initcall_entry_t __initcall1_start[];
extern initcall_entry_t __initcall2_start[];
extern initcall_entry_t __initcall3_start[];
extern initcall_entry_t __initcall4_start[];
extern initcall_entry_t __initcall5_start[];
extern initcall_entry_t __initcall6_start[];
extern initcall_entry_t __initcall7_start[];
extern initcall_entry_t __initcall_end[];
static initcall_entry_t *initcall_levels[] __initdata = {__initcall0_start,__initcall1_start,__initcall2_start,__initcall3_start,__initcall4_start,__initcall5_start,__initcall6_start,__initcall7_start,__initcall_end,
};
static char *initcall_level_names[] __initdata = {"pure","core","postcore","arch","subsys","fs","device","late",
};
一级索引表示等级,二级地址表示各个等级内模块的数组,二级数组中每一项都是链接脚本中的一个内存段,各个数组与内存段的对应关系如下:
__initcall_start --> .initcallearly.init
__initcall0_start --> .initcall0.init
__initcall1_start --> .initcall1.init
__initcall2_start --> .initcall2.init
__initcall3_start --> .initcall3.init
__initcall4_start --> .initcall4.init
__initcall5_start --> .initcall5.init
__initcall6_start --> .initcall6.init
__initcall7_start --> .initcall7.init
__initcall_end --> .con_initcall.init
各个段都有一系列module的init函数。
kernel在kernel_init中会根据该二维表逐个调用各层级各模块的init函数以加载各模块。
有个do_initcalls会do_initcall_level函数
kernel_init
---->kernel_init_freeable
-------->do_pre_smp_initcalls (遍历加载.initcallearly.init中的模块)
-------->do_basic_setup
---------------->do_initcalls (遍历加载.initcall0.init~.initcall7.init各内存段中的各模块)
-------------------------------->do_initcall_level (遍历加载具体内存段中的各模块)xs
其中do_initcall_level 实现如下(加入追踪打印):
static void __init do_initcall_level(int level)
{// 忽略无关逻辑for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) {printk("aaaa %pF\n",initcall_from_entry(fn));do_one_initcall(initcall_from_entry(fn));printk("bbbb\n");}
}
内核中除了极个别早期模块会在do_pre_smp_initcalls中加载,大多数模块在do_initcall_level中加载。
根据打印在aaaa mdev_misc_init之后并没有bbbb打印,确认内核在mdev_misc模块中失去响应。
然而grep搜遍内核并未见mdev_misc模块在哪里实现,反而是在vmlinux及mmc_blk_data两个二进制文件中有mdev_misc_init字符串,通过file确认mmc_blk_data是firefly通过ar打包多个.o文件的集合静态库。
kernel/drivers/mmc/core/Makefile中有如下片段:
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
mmc_block-objs := block.o queue.o
mmc_block-objs += mmc_blk_data
其中mmc_block.o及mmc_block-objs并非真实的编译单元文件及编译目标规则,只是一种目标依赖传导规则,表示通过obj-$(CONFIG_MMC_BLOCK)加入mmc_block.o目标,而mmc_block-objs表示mmc_block.o并不从mmc_block.c文件编译而来,而是关联到block.o queue.o mmc_blk_data三个文件,其中block.o queue.o分别是各自c源码编译而来,mmc_blk_data为预置静态库,不提供源码。
追踪代码确定block.c中调用的mmc_blk_data中实现的mmc_blk_data_init与mmc_blk_data_deinit两个函数。
修改如下:
// #include "block_data.h" 取消该头文件引用
// 忽略无关逻辑
if (card->host->restrict_caps & RESTRICT_CARD_TYPE_EMMC) {// mmc_blk_data_init(md); 注掉对mmc_blk_data_init调用
}
// 忽略无关逻辑
if (card->host->restrict_caps & RESTRICT_CARD_TYPE_EMMC) {// mmc_blk_data_deinit(md); 注掉对mmc_blk_data_deinit调用
}
再次编译kernel烧写即可运行通过。