qemu u-boot跳转linux kernel错误gdb调试

        前段时间,按照网上教程操作,在qume环境下vexpress开发板,通过u-boot启动 kernel,内核始终起不来。qume环境单独启动u-boot和Linux都可以。本人环境:ubuntu20.4 + linux5.15.131+u-boot-2023.10。

找了很多教程,比如:

QEMU 实验(二): 通过 u-boot 启动 kernel - 简书

使用QEMU启动uboot引导linux内核-CSDN博客

在 qemu 上模拟运行 uboot + linux 内核 + NFS(实验环境 ubuntu 18.04 qemu 4.0.0)_boofuzz qemu固件-CSDN博客

        始终是同样的现象,u-boot启动Linux打印“Starting kernel ...”后,就没有任何输出了,也不知道停在了哪里。最近有时间了,网上找不到解决方法,就自己分析了。

        接下来,本文先演示问题现象,并重点演示该问题的分析方法,如果对分析方法不感兴趣可以直接跳到结论处。

问题现象

        先演示问题现象:u-boot启动Linux打印“Starting kernel ...”后,就没有任何输出了,也不知道停在了哪里。

        参考上面的文章,使用zImage和uImage都有是同样的现象。Kernel Image和dtb都被正确加载了,但跳转到内核后就没反应了。

       

问题调试

        首先我们通过gdb查看u-boot,跳转Linux过程是否出错。我们知道u-boot启动过程中,会自己做重定位,也就是把自己从低地址搬移到高地址。gdb调试时,直接通过b boot_jump_linux 在函数boot_jump_linux处增加断点是不起效的,应为u-boot重定位后函数boot_jump_linux运行地址变了。先在relocate_code和copy_loop处增加断点,通过断点处打印寄存器,可以看到u-boot重定位后的地址,之后通过函数boot_jump_linu的偏移,加上重定位后的u-boot起始地址,就可以确定重定位后函数boot_jump_linux的地址,然后在此处增加断点。 

        可以看到,u-boot重定位到了地址0x7ff61000的位置。那么函数boot_jump_linux重定位后的地址为:0x6080169c (boot_jump_linux函数重定位前的地址,通过u-boot.map或者反汇编可以找到) - 0x60800000 + 0x7ff61000 = 0x7FF6269C       

        接着调试操作错误,重新启动了gdb,在boot_jump_linux和执行kernel_entry处增加断点。

断点0x7ff6269c boot_jump_linux入口,处打印zImage和dtb数据都正确,并且kernel_entry的入口地址为0x60008000,和tftp 0x60008000 uImage;bootm 0x60008000 - 0x61000000是一致的。之后si单步执行,跳转到0x60008000后执行也正常。继续执行程序停留在内核dump_machine_table函数中。

该函数在setup_arch中被调用,查看setup_arch代码,推测应该是变量__atags_pointer出错了。

如下图,通过gdb打印__atags_pointer值为0,上面的代码中setup_machine_fdt没有执行。

打印__machine_arch_type值为0x8e0,对应十进制2272,对应arch/arm/include/generated/asm/mach-types.h中刚好是MACH_TYPE_VEXPRESS。

变量__atags_pointer应该指向dtb文件的物理地址,由u-boot传递给内核,u-boot通过r1和r2向内核传递参数,r1为Machine ID,r2为dtb地址,内核head-common.s中会检查r2为dtb地址和内容,检查正确,之后会保存的变量__atags_pointer。相关代码如下:

所以,需要检查u-boot向内核传递给参数的过程。

        重新启动qemu + gdb,在0x60008000处增加断点,打印zImage和dtb内容。如下图,可以看到u-boot跳转到zImage后,zImage和dtb内容正确,u-boot通过r1和r2向内核传递参数,r1为Machine ID、r2为dtb的地址也正确(这里有大小端的区别)。

        接下来是zImage的解压,解压代码没什么可以分析的,编译时自动生成的,本人也分析不了。所以直接在解压后的内核入口地址处增加断点,刚好也是0x60008000(vexpress开发板,物理地址从0x60000000,内核代码偏移0x8000,对应虚拟地址0x80008000,此时还没有开启mmu,所以断点要采用物理地址。具体这些地址怎么来的文章Linux内核启动建立临时页表与开启MMU过程分析(GDB+QEMU跟踪调试)_linux mmu配置-CSDN博客有分析),所以不用增加,直接c继续。

        如上图,继续执行,再次停留在断点0x60008000处,打印0x60008000内容,可以看到和vmlinux反汇编指令一致,也就是zImage解压没有问题(这要有问题就麻烦了,本人水平不够,分析不了)。但是打印dtb地址0x61000000,数据变了,也就是解压后dtb不对了,这里应该容易想到,解压后的kernel镜像大于16M,覆盖了地址0x61000000,也就把dtb破坏了。

        所以,重新启动qemu,修改dtb加载地址到0x68000000,内核正常启动。

结论

        linux5.15.131内核比较新,镜像比较大,dtb被加载到0x61000000,zImage解压后覆盖掉了dtb文件,将dtb加载地址改为0x68000000就可以了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值