.. _build_qemu_ovmf: ======================= 编译QEMU+OVMF(ARM架构) ======================= 源代码编译QEMU ====================== - 安装编译依赖:: pacman -S ninja - 执行以下方法下载编译:: tar xvJf qemu-7.1.0.tar.xz cd qemu-7.1.0 ./configure make -j8 #按照主机CPU核心数设置编译并发 源代码编译OVMF ===================== :ref:`ovmf` 提供了虚拟机的UEFI支持,可以在虚拟机中实现硬件直通(CPU/PCI),对于ARM架构的服务器支持有重要意义。在 :ref:`asahi_linux` 的arch linux for ARM仓库没有提供 UEFI/EDK2 软件包 ``ovmf-aarch64`` ,所以这里也从源代码编译 .. note:: `EDK2 Nightly Build `_ 提供了编译好的EDK2,非官方的OVMF(各种平台) ``ovmf-aarch6`` 软件包提供BIOS是 ``/usr/share/ovmf/AARCH64/QEMU_EFI.fd`` - 安装编译工具:: pacman -S make gcc binutils acpica nasm .. note:: 安装 ``iasl`` 实际安装是 ``acpica`` (替代了iasl),这个软件包是ACPI工具 在aarch64架构上没有提供 ``nasm`` ,这个软件包是 80x86 assembler 详情参考 `edk2-aarch64 `_ - 下载源代码:: git clone git@github.com:tianocore/edk2.git cd edk2 git submodule update --init - ``edksetup`` 脚本会准备编译环境:: source edksetup.sh 这里似乎有个报错:: bash: /home/huatai/docs/github.com/tianocore/edk2-master/BaseTools/BuildEnv: No such file or directory 我采用以下方法绕过:: cd .. ln -s edk2 edk2-master cd edk2 source edksetup.sh 此时在 ``Conf`` 目录下会生成 ``target.txt`` 可以编辑你需要编译的UEIF镜像目标平台 举例: X64平台: .. literalinclude:: build_qemu_ovmf/target.txt_x64 :language: bash :caption: 编译x64的UEFI镜像,用于OVMF,使用GCC5 AARCH64平台: .. literalinclude:: build_qemu_ovmf/target.txt_aarch64 :language: bash :caption: 编译ARM 64位的UEFI镜像,用于OVMF,使用GCC5 - 编译 BaseTools (只需要执行一次):: make -C BaseTools - 如果已经配置过 ``Conf/target.txt`` ,则直接执行:: build 如果没有配置 ``Conf/target.txt`` 则可以命令行传递编译参数: 对于x64 qemu,使用:: build -t GCC5 -a X64 -p OvmfPkg/OvmfPkgX64.dsc 则编译后firmware卷位于 ``Build/OvmfX64/DEBUG_GCC5/FV`` 对于aarch64 firmware,使用:: build -t GCC5 -a AARCH64 -p ArmVirtPkg/ArmVirtQemu.dsc 则编译后firmware位于 ``Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV`` - Qemu希望 aarch64 的 firmware 是 64M ,所以firmware镜像不能直接使用,需要一些填充来创建可以用于 pflash 的镜像:: cd Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV dd of="QEMU_EFI-pflash.raw" if="/dev/zero" bs=1M count=64 dd of="QEMU_EFI-pflash.raw" if="QEMU_EFI.fd" conv=notrunc dd of="QEMU_VARS-pflash.raw" if="/dev/zero" bs=1M count=64 dd of="QEMU_VARS-pflash.raw" if="QEMU_VARS.fd" conv=notrunc 编译选项 ---------- .. note:: 这段比较复杂,对于深入研究安全启动有必要了解。我实际没有实践 有许多编译时选项,通常使用 ``-D NAME`` 或 ``-D NAME=TRUE`` 启用。 可以使用 ``-D NAME=FALSE`` 关闭默认启用的选项。 可用选项在构建命令引用的 ``*.dsc`` 文件中定义,所以一个功能完整的镜像构建可能类似如下:: build -t GCC5 -a X64 -p OvmfPkg/OvmfPkgX64.dsc \ -D FD_SIZE_4MB \ -D NETWORK_IP6_ENABLE \ -D NETWORK_HTTP_BOOT_ENABLE \ -D NETWORK_TLS_ENABLE \ -D TPM2_ENABLE 安全启动支持(在x64上)需要SMM模式。在没有SMM的情况下,没有什么可以阻止用户操作系统绕过固件直接写入闪存,因此受保护的 UEFI 变量实际上并未受到保护。此外,挂起(S3)支持仅在固件的某些部分(特别是PEI,详见下文)以32位模式运行时才与启用的SMM 一起使用。 所以安全启动变体必须这样编译:: build -t GCC5 -a IA32 -a X64 -p OvmfPkg/OvmfPkgIa32X64.dsc \ -D FD_SIZE_4MB \ -D SECURE_BOOT_ENABLE \ -D SMM_REQUIRE \ [ ... add network + tpm + other options as needed ... ] ``D_SIZE_4MB`` 参数选项创建更大的固件映像,大小为 4MB 而不是 2MB(默认),为代码和变量提供更多空间。 RHEL/CentOS 构建使用它。由于历史原因,Fedora 构建的大小为 2MB。 如果需要32位firmware,采用以下方法:: build -t GCC5 -a ARM -p ArmVirtPkg/ArmVirtQemu.dsc build -t GCC5 -a IA32 -p OvmfPkg/OvmfPkgIa32.dsc 编译后的输出结果位于 ``Build/ArmVirtQemu-ARM/DEBUG_GCC5/FV`` 和 ``Build/OvmfIa32/DEBUG_GCC5/FV`` 引导新的firmware构建 ===================== x86 firmware build会创建3各不同镜像: - ``OVMF_VARS.fd`` : 这是持久化的UEFI变量的firmware卷,即firmware存储所有配置(引导条目和引导顺序、安全引导密钥等)。通常这个文件用作空变量存储的模板,每个VM都有自己的私有副本。例如 :ref:`libvirt` 将文件存储在 ``/var/lib/libvirt/qemu/nvram`` 中。 - ``OVMF_CODE.fd`` : 带有代码的firmware卷。将它和 ``VARS`` 分开可以: - 确保轻松更新固件 - 允许将只读代码映射到guest操作系统 = ``OVMF.fd`` : 是包含 ``CODE`` 和 ``VARS`` 的一体化镜像。这样就可以使用 ``-bios`` 参数直接作为ROM加载。但是有2个缺点: - ``UEFI`` 变量不是持久的 - 不适用于 ``SMM_REQUIRE=TRUE`` 构建 ``qemu`` 将 pflash 存储作为快设备处理,所以必须创建用于firmware镜像的块设备文件文件:: CODE=${WORKSPACE}/Build/OvmfX64/DEBUG_GCC5/FV/OVMF_CODE.fd VARS=${WORKSPACE}/Build/OvmfX64/DEBUG_GCC5/FV/OVMF_VARS.fd qemu-system-x86_64 \ -blockdev node-name=code,driver=file,filename=${CODE},read-only=on \ -blockdev node-name=vars,driver=file,filename=${VARS},snapshot=on \ -machine q35,pflash0=code,pflash1=vars \ [ ... ] 以下是ARM版本(也就是上文使用 ``dd`` 创建的填充文件:: CODE=${WORKSPACE}/Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_EFI-pflash.raw VARS=${WORKSPACE}/Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_VARS-pflash.raw qemu-system-aarch64 \ -blockdev node-name=code,driver=file,filename=${CODE},read-only=on \ -blockdev node-name=vars,driver=file,filename=${VARS},snapshot=on \ -machine virt,pflash0=code,pflash1=vars \ [ ... ] 源代码结构 ============= ``edk2`` 核心仓库包含一系列软件包,每个软件包都有自己的顶级目录,以下是一些重要目录: - ``OvmfPkg`` : x64相关代码以及特定的虚拟化代码,如virtio驱动 - ``ArmVirtPkg`` : ARM特定代码 - ``MdePkg, MdeModulePkg`` : 主要核心代码,如PCI支持,USB至此和,通用服务和驱动等等 - ``PcAtChipsetPkg`` : Intel架构的驱动和库 - ``ArmPkg, ArmPlatformPkg`` : ARM架构支持代码 - ``CryptoPkg, NetworkPkg, FatPkg, CpuPkg, ...`` : 加密支持(使用openssl,网络支持(包括网络启动),FAT文件系统驱动等等 使用OVMF ============== 在 :ref:`libvirt` 启动VM时传递参数 ``--boot uefi`` 可启动OVMF,举例:: virt-install --name centos8 --ram=2048 --vcpus=1 --cpu host --hvm --disk path=/var/lib/libvirt/images/centos8-vm1,size=10 --location /home/ostechnix/centos8.iso --network bridge=br0 --graphics vnc --boot uefi 参考 `edk2-aarch64 `_ 软件包文件目录,应该有:: usr/share/edk2/aarch64/QEMU_CODE.fd usr/share/edk2/aarch64/QEMU_EFI.fd usr/share/edk2/aarch64/QEMU_VARS.fd 所以使用如下复制方法:: mkdir -p /usr/share/edk2/aarch64 cp QEMU_EFI-pflash.raw /usr/share/edk2/aarch64/QEMU_CODE.fd cp QEMU_VARS-pflash.raw /usr/share/edk2/aarch64/QEMU_VARS.fd 不过,要让 ``libvirt`` 能够找到nvram文件,请修改 ``/etc/libvirt/qemu.conf`` : .. literalinclude:: build_qemu_ovmf/qemu.conf :language: bash :caption: /etc/libvirt/qemu.conf 配置 nvram 路径 然后重启一次 libvirt 参考 ====== - `edk2 quickstart for virtualization `_ 这篇文档非常详细,特别是编译的步骤,包括ARM64。本文的编译OVMF参考该文档 - `ubuntu wiki: OVMF `_ - `How to run OVMF `_ - `QEMU Build instructions `_ - `Setup: Linux host, QEMU vm, arm64 kernel `_ 手工启动QEMU vm(ARM) - `arch linux arm aarch64 + ovmf uefi + qemu `_ - `How to build OVMF `_ 不过这个文档没有很详细的关于ARM64架构的说明