edk2编译
edk2 quickstart for virtualization
Here is a quickstart for everyone who wants (or needs to) deal with edk2 firmware, with a focus on virtual machine firmware. The article assumes you are using a linux machine with gcc.
Building firmware for VMs
To build edk2 you need to have a bunch of tools installed. An compiler and the make
are required of course, but also iasl
, nasm
and libuuid
. So install them first (package names are for centos/fedora).
dnf install -y make gcc binutils iasl nasm libuuid-devel
If you want cross-build arm firmware on a x86 machine you also need cross compilers. While being at also set the environment variables needed to make the build system use the cross compilers:
dnf install -y gcc-aarch64-linux-gnu gcc-arm-linux-gnu
export GCC5_AARCH64_PREFIX="aarch64-linux-gnu-"
export GCC5_ARM_PREFIX="arm-linux-gnu-"
Next clone the tiaocore/edk2 repository and also fetch the git submodules.
git clone https://github.com/tianocore/edk2.git
cd edk2
git submodule update --init
The edksetup
script will prepare the build environment for you. The script must be sourced because it sets some environment variables (WORKSPACE
being the most important one). This must be done only once (as long as you keep the shell with the configured environment variables open).
source edksetup.sh
Next step is building the BaseTools (also needed only once):
make -C BaseTools
Note: Currently (April 2022) BaseTools are being rewritten in Python, so most likely this step will not be needed any more at some point in the future.
Finally the build (for x64 qemu) can be kicked off:
build -t GCC5 -a X64 -p OvmfPkg/OvmfPkgX64.dsc
The firmware volumes built can be found in Build/OvmfX64/DEBUG_GCC5/FV
.
Building the aarch64 firmware instead:
build -t GCC5 -a AARCH64 -p ArmVirtPkg/ArmVirtQemu.dsc
The build results land in Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV
.
Qemu expects the aarch64 firmware images being 64M im size. The firmware images can't be used as-is because of that, some padding is needed to create an image which can be used for pflash:
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
There are a bunch of compile time options, typically enabled using -D NAME
or -D NAME=TRUE
. Options which are enabled by default can be turned off using -D NAME=FALSE
. Available options are defined in the *.dsc
files referenced by the build
command. So a feature-complete build looks more like this:
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
Secure boot support (on x64) requires SMM mode. Well, it builds and works without SMM, but it's not secure then. Without SMM nothing prevents the guest OS writing directly to flash, bypassing the firmware, so protected UEFI variables are not actually protected.
Also suspend (S3) support works with enabled SMM only in case parts of the firmware (PEI specifically, see below for details) run in 32bit mode. So the secure boot variant must be compiled this way:
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 ... ]
The FD_SIZE_4MB
option creates a larger firmware image, being 4MB instead of 2MB (default) in size, offering more space for both code and vars. The RHEL/CentOS builds use that. The Fedora builds are 2MB in size, for historical reasons.
If you need 32-bit firmware builds for some reason, here is how to do it:
build -t GCC5 -a ARM -p ArmVirtPkg/ArmVirtQemu.dsc
build -t GCC5 -a IA32 -p OvmfPkg/OvmfPkgIa32.dsc
The build results will be in in Build/ArmVirtQemu-ARM/DEBUG_GCC5/FV
and Build/OvmfIa32/DEBUG_GCC5/FV
Booting fresh firmware builds
The x86 firmware builds create three different images:
OVMF_VARS.fd
- This is the firmware volume for persistent UEFI variables, i.e. where the firmware stores all configuration (boot entries and boot order, secure boot keys, ...). Typically this is used as template for an empty variable store and each VM gets its own private copy, libvirt for example stores them in
/var/lib/libvirt/qemu/nvram
. OVMF_CODE.fd
- This is the firmware volume with the code. Separating this from VARS does (a) allow for easy firmware updates, and (b) allows to map the code read-only into the guest.
OVMF.fd
- The all-in-one image with both
CODE
andVARS
. This can be loaded as ROM using-bios
, with two drawbacks: (a) UEFI variables are not persistent, and (b) it does not work forSMM_REQUIRE=TRUE
builds.
qemu handles pflash storage as block devices, so we have to create block devices for the firmware images:
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 \
[ ... ]
Here is the arm version of that (using the padded files created using dd
, see above):
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 \
[ ... ]
Source code structure
The core edk2 repo holds a number of packages, each package has its own toplevel directory. Here are the most interesting ones:
- OvmfPkg
- This holds both the x64-specific code (i.e. OVMF itself) and virtualization-specific code shared by all architectures (virtio drivers).
- ArmVirtPkg
- Arm specific virtual machine support code.
- MdePkg, MdeModulePkg
- Most core code is here (PCI support, USB support, generic services and drivers, ...).
- PcAtChipsetPkg
- Some Intel architecture drivers and libs.
- ArmPkg, ArmPlatformPkg
- Common Arm architecture support code.
- CryptoPkg, NetworkPkg, FatPkg, CpuPkg, ...
- As the names of the packages already suggest: Crypto support (using openssl), Network support (including network boot), FAT Filesystem driver, ...
Firmware boot phases
The firmware modules in the edk2 repo often named after the boot phase they are running in. Most drivers are named SomeThingDxe
for example.
- ResetVector
- This is where code execution starts after a machine reset. The code will do the bare minimum needed to enter SEC. On x64 the most important step is the transition from 16-bit real mode to 32-bit mode or 64bit long mode.
- SEC (Security)
- This code typically loads and uncompresses the code for PEI and SEC. On physical hardware SEC often lives in ROM memory and can not be updated. The PEI and DXE firmware volumes are loaded from (updateable) flash.
- With OVMF both SEC firmware volume and the compressed volume holding PXE and DXE code are part of the OVMF_CODE image and will simply be mapped into guest memory.
- PEI (Pre-EFI Initialization)
- Platform Initialization is done here. Initialize the chipset. Not much to do here in virtual machines, other than loading the x64 e820 memory map (via fw_cfg) from qemu, or get the memory map from the device tree (on aarch64). The virtual hardware is ready-to-go without much extra preaparation.
- PEIMs (PEI Modules) can implement functionality which must be executed before entering the DXE phase. This includes security-sensitive things like initializing SMM mode and locking down flash memory.
- DXE (Driver Execution Environment)
- When PEI is done it hands over control to the full EFI environment contained in the DXE firmware volume. Most code is here. All kinds of drivers. the firmware setup efi app, ...
- Strictly speaking this isn't only one phase. The code for all phases after PEI is part of the DXE firmware volume though.
- Open Virtual Machine Firmware(OMVF) 提供了虚拟机的UEFI支持,可以在虚拟机中实现硬件直通(CPU/PCI)
-
edk2
核心仓库包含一系列软件包,每个软件包都有自己的顶级目录,以下是一些重要目录:-
OvmfPkg
: x64相关代码以及特定的虚拟化代码,如virtio驱动 -
ArmVirtPkg
: ARM特定代码 -
MdePkg, MdeModulePkg
: 主要核心代码,如PCI支持,USB至此和,通用服务和驱动等等 -
PcAtChipsetPkg
: Intel架构的驱动和库 -
ArmPkg, ArmPlatformPkg
: ARM架构支持代码 -
CryptoPkg, NetworkPkg, FatPkg, CpuPkg, ...
: 加密支持(使用openssl,网络支持(包括网络启动),FAT文件系统驱动等等
-
- 转自:https://www.kraxel.org/blog/2022/05/edk2-virt-quickstart/
- 参考:https://cloud-atlas.readthedocs.io/zh_CN/latest/kvm/arm_kvm/build_qemu_ovmf.html
- https://blog.csdn.net/huang987246510/article/details/107869142
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2022-09-18 Windows自带管理工具