转载自前同事zhouyuanjie。
- 问题环境
project:ubox
openwrt:v18.06.4
kernel:4.14.98
uboot:2018.03
- 问题现象
在openwrt编译ubox镜像后,使用镜像启动偶现镜像卡在“Starting kernel ...”阶段。
ubox镜像是通过mkimage工具做的FIT镜像,配置如下:
- 问题分析
分析一:
因未打包的源文件在uboot中使用的是booti命令启动,FIT镜像需要使用bootm命令启动,所以将有问题的FIT镜像采用booti的命令启动,发现能够正常启动,判断FIT打包前的源文件是没有问题的,排除因去掉了不该去掉的配置造成不能启动的情况。
分析二:
因问题是属于uboot->kernel的交付阶段,在此阶段没有可用调试工具和设备,则采用黑盒和代码分析的方法,首先分析bootm的启动流程:
do_bootm | bootm命令 |
do_bootm_states | 完成image和fdt的查找、解压、装载等动作 |
boot_selected_os | 选择从什么启动系统并设置回调函数 |
do_bootm_linux | 为启动image做准备,如关闭dcache等 |
armv8_switch_to_el2 | 选择启动模式(32 or 64)和级别(el2为系统级别) |
armv8_switch_to_el2_m | 实际启动内核 |
经上述流程分析发现,uboot在实际启动前做了级别切换,怀疑CPU在进入kernel前进入了某种模式,导致CPU卡死在uboot中?然后分析kernel启动代码,并在kernel中添加点亮led的方式确定CPU是否成功进入内核,代码如下:
经上述内核代码调试分析后发现,在上图(1)的位置CPU会抛出(synchronoud abort异常,led实则未点亮,看抛出的这个异常大致确定是寻址或译码上的问题,此处并未深究,但能够抛出异常也能确定是进入kernel中),而去掉图(1)在图(2)出则会卡死,分析问题点代码{
adr_l x0, __hyp_stub_vectors
msr vbar_el2, x0
},异常点代码是在设置异常向量表后卡死。经objdump分析判断CPU在uboot交付kernel后产生异常,进入异常处理(死循环),详见下图:
经上述分析,启动过程中是卡死在kernel阶段,并且是在设置异常向量表后出现的异常,所以排除是因uboot模式切换错误导致的卡死。因无法确定CPU是产生的哪种异常,所以启动流程暂停分析。
分析三:
结合分析一、分析二,在有问题的FIT的镜像使用FIT源文件用booti命令能够成功启动。则尝试在do_bootm_states镜像装载完成后,调用booti去启动内核,代码如下:
经尝试,在bootm镜像装载完成后,使用booti命令启动问题依然。继续分析booti和bootm启动流程的差异点,发现booti和bootm的启动流程基本相同,所不同的是,在booti在调用do_bootm_states前对装载的镜像做了偏移操作,如下:
经偏移操作后,kernel的FIT镜像中的Load地址为0x4380000,经偏移后实际使用地址为0x43880000,怀疑问题在kernel或fdt的load地址上。
分析四:
经分析三结论,尝试修改FIT镜像中的kernel和fdt的load地址,如下:
经多次尝试发现一个共同点,就是kernel的地址后面必须是0x80000(bootm并不会做对齐操作),并且fdt的load地址必须对齐(fdt的load地址在openwrt中默认是没有的,则bootm解析FIT后fdt的load地址为bootm装载的地址),结合分析三和linux-4.14.98/Documentation/arm64/booting.txt,以及查看kernel头部信息确认,头部信息中确实设置了相关位置,并且load的地址需要做偏移,如下:
- 结论
经问题分析测试后,确定问题有两点。
- kernel的load地址必须偏移0x80000,如基址是0x43800000则实际的load地址必须是0x43880000;
- 固定fdt的load地址。
因上述两点则会导致,进入kernel后CPU出现异常被挂起。