2.2 VMM为Guest准备物理内存
计算机系统启动后,主板的BIOS ROM将会被映射到内存地址空间,以x86架构的32位系统为例,映射的地址空间为0x000F0000~0x000FFFFF,然后CPU跳转到0xF000:0xFFF0处,开始运行BIOS代码。BIOS会检查内存信息,记录下来,并对外提供内存信息查询功能。BIOS将中断向量表(IVT)的第0x15个表项中的地址设置为查询内存信息函数的地址,后续的boot loader或者OS就可以通过发起中断号为0x15的软中断,调用BIOS中的这个函数,获取系统内存信息。
因此,为了给Guest提供这个获取内存系统信息的功能,VMM需要模拟BIOS的这个功能。简单来讲,VMM需要完成如下几件事:
1)主板的BIOS ROM是被系统映射到内存地址空间0x000F0000~0x000FFFFF。对于软件模拟而言,就不需要这个映射过程了,VMM可以将自己实现的模拟BIOS中的查询系统内存信息的中断处理函数,直接置于内存0x000F0000~0x000FFFFF中。
2)在建立IVT时,设置第0x15个表项中的中断函数地址指向模拟的BIOS中的内存查询处理函数的地址。
3)对于真实的物理系统,BIOS会查询真实的物理内存情况,建立内存信息。而对于VMM而言,则需要根据用户配置的虚拟机的内存信息,在BIOS数据区中自己制造内存信息表。
中断号为0x15的BIOS中断处理函数,依据传入的参数,即寄存器eax、ah中的值,将返回不同的内存信息。比如将ah寄存器设置为0x8A时,中断将返回扩展内存大小,即地址在1MB以上的内存的尺寸;将ah寄存器设置为0x88时,最多检测出64MB内存,实际内存超过64MB时也返回64MB。功能最强的是将eax寄存器的值设置为0xE820,0x15号中断处理函数将返回主机的完整的内存信息。
由于技术的演进与迭代,以及不同的地址空间被划分为不同的用途,为了向后兼容,内存地址空间被分为了许多不连续的段。对于使用0xE820方式获取内存而言,0x15号中断处理函数使用结构体e820_entry描述每个段,包括内存段的起始地址、内存段的大小以及内存段的类型:
commit 2f3976eeee4e0421c136c3431990a55cbf0f2bbf kvm: BIOS E820 memory map emulation kvmtool.git/include/kvm/e820.h struct e820_entry { uint64_t addr; /* start of memory segment */ uint64_t size; /* size of memory segment */ uint32_t type; /* type of memory segment */ } __attribute__((packed));
综上,VMM需要模拟的BIOS与内存相关部分如图2-7所示。
图2-7 VMM模拟的BIOS与内存相关部分