QEMU 代码分析:BIOS 的加载过程
http://www.ibm.com/developerworks/cn/linux/1410_qiaoly_qemubios/
QEMU 中使用 BIOS 简介
BIOS 提供主板或者显卡的固件信息以及基本输入输出功能,QEMU 使用的是一些开源的项目,如 Bochs、openBIOS 等。QEMU 中使用到的 BIOS 以及固件一部分以二进制文件的形式保存在源码树的 pc-bios 目录下。pc-bios 目录里包含了 QEMU 使用到的固件,还有一些 BIOS 以 git 源代码子模块的形式保存在 QEMU 的源码仓库中,当编译 QEMU 程序的时候,也同时编译出这些 BIOS 或者固件的二进制文件。QEMU 支持多种启动方式,比如说 efi、pxe 等, 都包含在该目录下,这些都需要特定 BIOS 的支持。
清单 1. QEMU 源码树中的 BIOS 文件
$ ls pc-bios/
acpi-dsdt.aml efi-rtl8139.rom openbios-ppc pxe-e1000.rom qemu_logo_no_text.svg slof.bin bamboo.dtb
efi-virtio.rom openbios-sparc32 pxe-eepro100.rom qemu-nsis.bmp spapr-rtas bamboo.dts keymaps
openbios-sparc64 pxe-ne2k_pci.rom qemu-nsis.ico spapr-rtas.bin bios.bin kvmvapic.bin optionrom
pxe-pcnet.rom vgabios.bin efi-e1000.rom linuxboot.bin palcode-clipper pxe-rtl8139.rom
s390-ccwvgabios-cirrus.bin efi-eepro100.rom petalogix-ml605.dtb pxe-virtio.rom s390-ccw.img
vgabios-qxl.bin efi-ne2k_pci.rom multiboot.bin petalogix-s3adsp1800.dtb q35-acpi-dsdt.aml
s390-zipl.rom vgabios-stdvga.bin efi-pcnet.rom ohw.diff ppc_rom.bin qemu-icon.bmp sgabios.bin
vgabios-vmware.bin
清单 2. QEMU 源码树以子模块方式保存的 BIOS 代码
-bash-4.1$ cat .gitmodules
[submodule "roms/vgabios"]
path = roms/vgabios
url = git://git.qemu.org/vgabios.git/
[submodule "roms/seabios"]
path = roms/seabios
url = git://git.qemu.org/seabios.git/
[submodule "roms/SLOF"]
path = roms/SLOF
url = git://git.qemu.org/SLOF.git
[submodule "roms/ipxe"]
path = roms/ipxe
url = git://git.qemu.org/ipxe.git
[submodule "roms/openbios"]
path = roms/openbios
url = git://git.qemu.org/openbios.git
[submodule "roms/qemu-palcode"]
path = roms/qemu-palcode
url = git://github.com/rth7680/qemu-palcode.git
[submodule "roms/sgabios"]
path = roms/sgabios
url = git://git.qemu.org/sgabios.git
[submodule "pixman"]
path = pixman
url = git://anongit.freedesktop.org/pixman
[submodule "dtc"]
path = dtc
url = git://git.qemu.org/dtc.git 当我们从源代码编译 QEMU 时候,QEMU 的 Makefile 会将这些二进制文件拷贝到 QEMU 的数据文件目录中。
清单 3. QEMU 的 Makefile 中关于 BIOS 的拷贝操作:
ifneq ($(BLOBS),)
set -e; for x in $(BLOBS); do \
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
done
QEMU 加载 BIOS 过程分析
当 QEMU 用户空间进程开始启动时,QEMU 进程会根据所传递的参数以及当前宿主机平台类型,自动加载适当的 BIOS 固件。 QEMU 进程启动初始阶段,会通过 module_call_init 函数调用 qemu_register_machine 注册该平台支持的全部机器类型,接着调用 find_default_machine 选择一个默认的机型进行初始化。 以最新的 QEMU 代码(1.7.0)的 x86_64 平台为例,支持的机器类型有:
清单 4. 1.7.0 版本 x86_64 QEMU 中支持的类型
pc-q35-1.7 pc-q35-1.6 pc-q35-1.5 pc-q35-1.4 pc-i440fx-1.7 pc-i440fx-1.6 pc-i440fx-1.5
pc-i440fx-1.4 pc-1.3 pc-1.2 pc-1.1 pc-1.0 pc-0.15 pc-0.14
pc-0.13 pc-0.12 pc-0.11 pc-0.10 isapc
最新代码中使用的默认机型为 pc-i440fx-1.7,使用的 BIOS 文件为:
pc-bios/bios.bin
Default machine name : pc-i440fx-1.7
bios_name = bios.bin
pc-i440fx-1.7 解释为 QEMU 模拟的是 INTEL 的 i440fx 硬件芯片组,1.7 为 QEMU 的版本号。找到默认机器之后,为其初始化物理内存,QEMU 首先申请一块内存空间用于模拟虚拟机的物理内存空间,申请完好内存之后,根据不同平台或者启动 QEMU 进程的参数,为虚拟机的物理内存初始化。具体函数调用过程见图 1。
图 1. QEMU 硬件初始化函数调用流程图:
在 QEMU 中,整个物理内存以一个结构体 struct MemoryRegion 表示,具体定义见清单 5。
清单 5. QEMU 中 MemoryRegion 结构体
struct MemoryRegion {
/* All fields are private - violators will be prosecuted */
const MemoryRegionOps *ops;
const MemoryRegionIOMMUOps *iommu_ops;
void *opaque;
struct Object *owner;
MemoryRegion *parent;
Int128 size;
hwaddr addr;
void (*destructor)(MemoryRegion *mr);
ram_addr_t ram_addr;
bool subpage;
bool terminates;
bool romd_mode;
bool ram;
bool readonly; /* For RAM regions */
bool enabled;
bool rom_device;
bool warning_printed; /* For reservations */
bool flush_coalesced_mmio;
MemoryRegion *alias;
hwaddr alias_offset;
unsigned priority;
bool may_overlap;
QTAILQ_HEAD(subregions, MemoryRegion) subregions;
QTAILQ_ENTRY(MemoryRegion) subregions_link;
QTAILQ_HEAD(coalesced_ranges, CoalescedMemoryRange) subregions_link;
const char *name;
uint8_t dirty_log_mask;
unsigned ioeventfd_nb;
MemoryRegionIoeventfd *ioeventfds;
NotifierList iommu_notify;
};
每一个 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 创建的虚拟机中的内存的过程。
QEMU 代码分析:BIOS 的加载过程的更多相关文章
- 第42天学习打卡(Class类 Class类的常用方法 内存分析 类的加载过程 类加载器 反射操作泛型 反射操作注解)
Class类 对象照镜子后得到的信息:某个类的属性.方法和构造器.某个类到底实现了哪些接口.对于每个类而言,JRE都为其保留一个不变的Class类型的对象.一个Class对象包含了特定某个结构(cla ...
- Dubbo源码分析之ExtensionLoader加载过程解析
ExtensionLoader加载机制阅读: Dubbo的类加载机制是模仿jdk的spi加载机制: Jdk的SPI扩展加载机制:约定是当服务的提供者每增加一个接口的实现类时,需要在jar包的META ...
- Linux gadget驱动分析1------驱动加载过程
为了解决一个问题,简单看了一遍linux gadget驱动的加载流程.做一下记录. 使用的内核为linux 2.6.35 硬件为芯唐NUC950. gadget是在UDC驱动上面的一层,如果要编写ga ...
- 分析ELF的加载过程
http://blog.chinaunix.net/uid-72446-id-2060538.html 对于可执行文件来说,段的加载位置是固定的,程序段表中如实反映了段的加载地址.对于共享库来?段的加 ...
- 重温.NET下Assembly的加载过程
最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现,并没能解决我的问题,有些点写的不是特别详 ...
- 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线
重温.NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...
- NET下Assembly的加载过程
NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现,并没 ...
- insmod模块加载过程代码分析1【转】
转自:http://blog.chinaunix.net/uid-27717694-id-3966290.html 一.概述模块是作为ELF对象文件存放在文件系统中的,并通过执行insmod程序链接到 ...
- springmvc源码分析——入门看springmvc的加载过程
本文将分析springmvc是如何在容器启动的时候将各个模块加载完成容器的创建的. 我知道在web.xml文件中我们是这样配置springmvc的: 可以看到,springmvc的核心控制器就是Dis ...
随机推荐
- MacBook下java环境的搭建
在Mac下搭建JAVA环境: 1.下载并安装JDK: 下载最新的JDK,傻瓜式安装,一直下一步就OK了. 2.配置环境变量: 在终端中输入 sudo vim ~/.bash_profile ,打开 . ...
- 【idea】idea的常规设置
[一]在输入框输入字符,自动提示代码 File->Power Save Mode 去掉“对号” [二]自动代码提示的快捷键设置 (1)不区分大小写提示 (2)修改快捷提示快捷键.将basic= ...
- CH1808 Milking Grid
题意 POJ2185 数据加强版 描述 Every morning when they are milked, the Farmer John's cows form a rectangular gr ...
- Oracle Database express 11g 第 2 版安装和配置
官方Oracle Database 快捷版 11g 第 2 版的下载地址: http://www.oracle.com/technetwork/cn/products/express-edition/ ...
- eclipse adt logcat过滤用法
点击Save Filters的加号,by Log Tag如下就可以只看2dx程序出来的debug信息了
- 【转】让开发变得简单一点- Visual Studio 2010几个让人印象深刻的新功能
原文网址:http://xhinker.blog.51cto.com/640011/313055/ 引言 "我们的目标,不仅仅是做出几个新功能,而是要回答一个问题:'如何让现在的开发人员生活 ...
- maven库 mvn依赖
http://maven.outofmemory.cn/ http://mvnrepository.com/ 先执行 mvn clean 然后执行 mvn 命令 如:mvn compile . ...
- [C++ Primer] 第2章: 变量
初始化与赋值 当对象在创建时获得了一个特定的值, 我们就说这个对象被初始化了. 在使用=号时, 如果创建了新的对象, 则为初始化而非赋值. 初始化不是赋值, 初始化的含义是创建变量时赋予其一个初始值, ...
- [DP题]采药
1775:采药 总时间限制:1000ms内存限制:65536kB 描述 辰辰是个很有潜能.天资聪颖的孩子,他的梦想是称为世界上最伟大的医师.为此,他想拜附近最有威望的医师为师.医师为了判断他的资质,给 ...
- 花瓶使用笔记 (抓数据时,记得添加host,不然抓不了包的)
情况一: 有时候抓不了app的数据,那么把app的host 添加一下就可以了 proxy > SSL Proxying Settings 情况二: 开了 翻 墙 是抓不了包的! (掉了一次坑)