linux 尾队列,QEMU 代码分析:BIOS 的加载过程[转]

本文详细介绍了QEMU如何使用开源BIOS固件,以及BIOS在QEMU虚拟机内存中的加载过程。QEMU通过MemoryRegion结构体表示物理内存,并将BIOS二进制文件加载到内存的高端地址。在启动时,QEMU根据参数选择合适的BIOS,并通过一系列函数调用将BIOS映射到内存中,最终使得虚拟机可以从BIOS入口点开始执行。
摘要由CSDN通过智能技术生成

QEMU

是一个广泛使用的开源计算机仿真器和虚拟机,它提供了虚拟机硬件的虚拟化功能,其使用的某些特定硬件的固件则由一些开源项目提供。本文将介绍

QEMU 代码中使用到的 BIOS,通过分析 QEMU 代码,讲解 BIOS 是如何加载到虚拟机的物理内存。

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)"; \

doneQEMU 加载 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.binpc-i440fx-1.7 解释为 QEMU 模拟的是 INTEL 的 i440fx

硬件芯片组,1.7 为 QEMU 的版本号。找到默认机器之后,为其初始化物理内存,QEMU

首先申请一块内存空间用于模拟虚拟机的物理内存空间,申请完好内存之后,根据不同平台或者启动 QEMU

进程的参数,为虚拟机的物理内存初始化。具体函数调用过程见图 1。

图 1. QEMU 硬件初始化函数调用流程图:

在 QEMU 中,整个物理内存以一个结构体 struct MemoryRegion 表示,具体定义见清单 5。

清单 5. QEMU 中 MemoryRegion 结构体

struct MemoryRegion {

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;

bool

enabled;

bool

rom_device;

bool

warning_printed;

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 创建的虚拟机中的内存的过程。

参考 git://git.qemu.org/qemu.git 中 QEMU 的源代码。

QEMU 的详细介绍:请点这里

QEMU 的下载地址:请点这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值