参考博客
linux4.15 arm qemu @ubuntu18.04环境搭建_papaofdoudou的博客-CSDN博客
问题1:针对启动命令qemu-system-arm -M vexpress-a9 -m 1024M -kernel arch/arm/boot/zImage -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic
的启动流程是怎样的?
回答:命令行启动的是zImage,而zImage的来源是zImage,而zImage包含压缩的内和和解压程序
通过makefile可以知道zImage的来源是vmlinux经过objcopy得到:
要注意这个vmlinux并非是顶层目录那个内和ELF文件的vmlinux,两个名字文件相同,但是差别巨大,简单来说,内通过编译zImage产生了两个vmlinux,这里的vmlinux包含了内和顶层的vmliunx压缩文件和解压程序,虽然它们都是ELF格式,但是前者相当于一级火箭,首先点火,解压内部的vmlinux后,后者作为二级火箭完成内核启动。
关于linux vmlinux,zImage vmlinux, Image以及zImage的关系,可以表示如下:
至此,可执行程序的结构解释的通了。
qemu启动流程,有没有灌入bootrom?答案是肯定的,我们通过如下命令启动调试的时候:
qemu-system-arm -M vexpress-a9 -m 1024M -kernel arch/arm/boot/zImage -append "rdinit=/linuxrc earlyprintk console=ttyAMA0" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -s -S
发现执行的第一条指令,虽然是vexpress-a9板子的物理内存初始地址,但是这个运行地址却并不在任何一个vmlinux中。
这就说明,这里执行的指令是qemu准备的,我们试着单步执行一下:
可以看到,开始0x60000000执行的四条指令,其中最重要的是0x6000000c处的指令,就是他将PC指针引导向一个新的位置0x60010000,这个位置还会使QEMU安插的指令吗?经过对比,我们得出结论,它不属于QEMU安插的启动指令,而是属于一级火箭助推器压缩的vmlinux,证据是可以通过反编译vmlinux看出来,指令码完全一样:
所以,真正QEMU安插的指令只有0x60000000到0x6000000C安插的几条,后面就开始执行Linux环境生成的代码了。
一个问题解决了,但是又由上面的问题因出来一个新的疑问,我们看到所谓的的一级火箭助推器的第一条指令是从0x60010000开始的。
但是,上上图中展示的同样的指令确是从0地址开始的,这种运行地址和加载地址不一致的现象不是不可能,只是需要编译的时候做一个特殊处理,就是将代码编译为地址无关指令,GCC中就需要在编译的是时候加入-fpic选项才行,打开一级火箭助推器vmlinux的构建,我们看一下是这样吗?
我们的猜测是对的,确实如此:
QEMU是在那里灌入BOOTROM启动指令的?这就要从QEMU中查找原因了,下载一份QEMU代码,我们在qemu-7.0.0/hw/arm/boot.c中找到了线索:
可能是QEMU版本的原因,我们先忽略前面两行指令,后面的指令不恰好就是0x60000000开始的指令么?
FIXUP_BOARDID,FIXUP_ARGPTR_LO,FIXUP_ENTRYPOINT_LO,FIXUP_TERMINATOR分别表示如下:
BOARDID是0x000008e0,跳转地址是0x60010000,还有一个比较重要的是,devicetree blob文件,是0x68000000,也即是说,启动命令中的DTB文件将会被加载到物理地址的0x68000000。
这一点,可以在内核调试中得到印证,传入内核的DTB,物理地址是0x68000000,虚拟地址是0xC8000000
关于BROM后的启动入口为什么是0x60010000,可以从下面代码得到解释,KERNEL_LOAD_ADDR为0x10000.
所以启动指令这块也得到了圆满解释。
对比RT-THREAD的 QEMU启动命令,为何RTT可以启动 ELF格式的RTT固件,而当我将启动命令该为qemu-system-arm -M vexpress-a9 -m 1024M -kernel vmlinux -append "rdinit=/linuxrc earlyprintk console=ttyAMA0" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic时,启动却失败?
qemu-system-arm -M vexpress-a9 -m 1024M -kernel vmlinux -append "rdinit=/linuxrc earlyprintk console=ttyAMA0" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic
回答:RTT的启动命令为:
qemu-system-arm -M vexpress-a9 -kernel rtthread.elf -serial vc -serial vc -sd sd.bin -S -s
对比可以看到,和我们调试LINUX的启动命令非常相似,唯一的区别是-kernel选项,在RTT情况下直接指定了RTT可启动固件。
为何Linux做不到呢?线索来自于对Linux的调试,调试如下命令发现,第一条指令执行的地址是0xc0008000,他是VMLINUX的entry地址,而不再是正常情况下的0x60000000。而0xc0008000这个时候还没有映射呢!
qemu-system-arm -M vexpress-a9 -m 1024M -kernel vmlinux -append "rdinit=/linuxrc earlyprintk console=ttyAMA0" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -s -S
实际的映射在VMLINUX的启动程序中:
所以当然不能正常运行。
看RTT的表现,RTT的连接运行地址直接是物理地址,无需做这样的翻译:
总结
QEMU虚拟机本身对系统行为的模拟是完备的,不需要修改GUEST内核,所以基本上,QEMU启动命令的每个选项,背后都有文章,主要分成两个方面进行模拟,第一是对CPU的模拟,这是靠TCG翻译完成的,对于算力密集型程序,TCG会很忙很忙。第二个是对IO行为的模拟,而这个是依赖对各类IO支持完成的。
结束



