標籤:des http io os ar 使用 for sp 檔案
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.5pc-i440fx-1.4 pc-1.3 pc-1.2 pc-1.1 pc-1.0 pc-0.15 pc-0.14pc-0.13 pc-0.12 pc-0.11 pc-0.10 isapc
最新代碼中使用的預設機型為 pc-i440fx-1.7,使用的 BIOS 檔案為:
pc-bios/bios.binDefault machine name : pc-i440fx-1.7bios_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 的載入過程