上QQ阅读APP看书,第一时间看更新
2.2.2 建立内存段信息
BIOS探测完内存信息后,会将内存信息保存在BIOS的数据区BDA/EBDA中,当bootloader或者OS通过软中断的方式向BIOS询问内存信息时,BIOS中的中断处理函数将从BIOS的数据区中复制内存信息到调用者指定的位置。同样的道理,既然VMM是为操作系统提供一个透明的环境,那么就需要在虚拟BIOS使用的数据区,按照e820记录的格式,准备好内存段的信息。
物理机的内存由多个内存段组成,每个段使用一个e820记录描述,典型的x86架构的32位系统的内存映射如表2-3所示。
表2-3 典型的x86架构的32位系统的内存映射
以kvmtool为例,其组织的内存段如下:
commit 2f3976eeee4e0421c136c3431990a55cbf0f2bbf kvm: BIOS E820 memory map emulation kvmtool.git/kvm.c void kvm__setup_mem(struct kvm *self) { struct e820_entry *mem_map; unsigned char *size; size = guest_flat_to_host(self, E820_MAP_SIZE); mem_map = guest_flat_to_host(self, E820_MAP_START); *size = 4; mem_map[0] = (struct e820_entry) { .addr = REAL_MODE_IVT_BEGIN, .size = BDA_END - REAL_MODE_IVT_BEGIN, .type = E820_MEM_RESERVED, }; mem_map[1] = (struct e820_entry) { .addr = BDA_END, .size = EBDA_END - BDA_END, .type = E820_MEM_USABLE, }; mem_map[2] = (struct e820_entry) { .addr = EBDA_END, .size = BZ_KERNEL_START - EBDA_END, .type = E820_MEM_RESERVED, }; mem_map[3] = (struct e820_entry) { .addr = BZ_KERNEL_START, .size = self->ram_size - BZ_KERNEL_START, .type = E820_MEM_USABLE, }; } #define BZ_KERNEL_START 0x100000UL kvmtool.git/include/kvm/bios.h #define E820_MAP_SIZE EBDA_START #define E820_MAP_START (EBDA_START + 0x01)
函数kvm__setup_mem建立了4个内存段:第1个内存段用于存储实模式的IVT;第2个内存段是传统的低端内存;第3个是BIOS,包括主板BIOS和显卡BIOS,以及用于显卡相关的内存;第4个是扩展内存区,主要的可用内存都在这个段了。关注第4个内存段的起始位置BZ_KERNEL_START,从名字就可以看出,这是bootloader加载内核时存放内核的内存地址。根据宏BZ_KERNEL_START的值可见,就是在扩内存的起始位置,即物理内存1MB处。
另外,kvmtool将内存段数设置为4,存储在了内存地址E820_MAP_SIZE处,根据宏E820_MAP_SIZE的值可见,就是存储在了EBDA区的第1个字节。内核可以从这个位置读取内存段数,即e820记录的总数。从EBDA区的第2个字节开始,开始存放内存段信息。