每一个 MemoryRegion 代表一块内存区域。仔细观察 MemoryRegion 的成员函数,它包含一个 Object 的成员函数用于指向它的所有者,以及一个 MemoryRegion 成员用于指向他的父节点(有点类似链表)。另外还有三个尾队列(QTAILQ) subregions, subregions_link, subregions_link。 也就是说,一个 MemoryRegion 可以包含多个内存区,根据不同的参数区分该内存域的功能。 在使用 MemoryRegion 之前要先为其分配内存空间并调用 memory_region_init 做必要的初始化。BIOS 也是通过一个 MemoryRegion 结构指示的。它的 MemoryRegion.name 被设置为"pc.bios", size 设置为 BIOS 文件的大小(65536 的整数倍)。接着掉用 rom_add_file_fixed 将其 BIOS 文件加载到一个全局的 rom 队列中。
最后,回到 old_pc_system_rom_init 函数中,将 BIOS 映射到内存的最上方的地址空间。
清单 6. old_pc_system_rom_init 函数中将 BIOS 映射到物理内存空间的代码:hw/i386/pc_sysfw.c : memory_region_add_subregion(rom_memory, (uint32_t)(-bios_size) bios);
(uint32_t)(-bios_size) 是一个 32 位无符号数字,所以-bios_size 对应的地址就是 FFFFFFFF 减掉 bios_size 的大小。 bios size 大小为 ./pc-bios/bios.bin = 131072 (128KB)字节,十六进制表示为 0x20000,所以 bios 在内存中的位置为 bios position = fffe0000,bios 在内存中的位置就是 0xfffdffff~0xffffffff 现在 BIOS 已经加在到虚拟机的物理内存地址空间中了。
最后 QEMU 调用 CPU 重置函数重置 VCPU 的寄存器值 IP=0x0000fff0, CS=0xf000, CS.BASE= 0xffff0000,CS.LIMIT=0xffff. 指令从 0xfffffff0 开始执行,正好是 ROM 程序的开始位置。虚拟机就找到了 BIOS 的入口。
小结作者通过阅读 QEMU 程序的源代码,详细介绍了 QEMU 中使用到的 BIOS 文件,QEMU 中物理内存的表示方法,以及 QEMU 是如何一步步将 BIOS 的二进制载入到通过 QEMU 创建的虚拟机中的内存的过程。
参考 git://git.qemu.org/qemu.git 中 QEMU 的源代码。
Ubuntu 12.04之找不到Qemu命令
Arch Linux上安装QEMU+EFI BIOS