3、EDK2开发环境搭建:源码获取、编译配置、模拟器运行与第一个UEFI应用
好,咱们进入实战环节。这一章我带你亲手把EDK2环境搭起来,跑通模拟器,再写个Hello World级别的UEFI应用。说实话,环境搭建这一步卡住过不少人,我自己早期也被Python版本和工具链路径折腾过。别急,跟着我的节奏来,一步步走稳。
3.1 源码获取:从GitHub拉取EDK2
EDK2的源码托管在GitHub上,项目名叫tianocore/edk2。我建议你直接克隆主分支,别用那些过时的稳定版——UEFI规范更新快,主分支反而更靠谱。
git clone https://github.com/tianocore/edk2.git
cd edk2
git submodule update --init --recursive
这里有个坑:子模块一定要更新。EDK2依赖了MdeModulePkg、BaseTools等子仓库,不拉全的话编译会报一堆莫名其妙的错误。我当年第一次编译时偷懒没执行submodule update,结果卡了半小时查错。
git status确认子模块状态。如果看到modified: xxx (new commits),说明子模块没对齐,重新执行git submodule update --remote即可。
3.2 编译环境配置:Python、工具链与EDK2设置
EDK2的编译系统基于Python 3,所以Python环境是必须的。我推荐Python 3.8以上版本,太老的版本有些脚本跑不动。
接下来配置工具链。Linux下我习惯用GCC5,Windows下用VS2019或VS2022。这里以Linux为例:
export WORKSPACE=$PWD
export PACKAGES_PATH=$WORKSPACE/edk2
export EDK_TOOLS_PATH=$WORKSPACE/edk2/BaseTools
source edk2/edksetup.sh
执行edksetup.sh后,脚本会自动检测工具链。如果报错说找不到Python,检查一下python3命令是否在PATH里。我个人习惯把Python路径写进.bashrc,省得每次重设。
edksetup.bat,否则某些符号链接创建会失败。我曾经因为这个折腾了一下午,最后发现是权限问题。
编译BaseTools也很关键:
make -C BaseTools
这一步会生成GenFw、GenFds等工具,没有它们后续编译会卡住。编译完成后,可以用build -v测试一下环境是否正常。
3.3 模拟器运行:用QEMU启动UEFI Shell
环境搭好了,咱们先跑个模拟器验证一下。EDK2自带了一个EmulatorPkg,但说实话,那个在Linux下不太好用。我更推荐用QEMU + OVMF(UEFI固件模拟器)。
先安装QEMU:
sudo apt-get install qemu-system-x86
然后编译OVMF:
build -a X64 -t GCC5 -p OvmfPkg/OvmfPkgX64.dsc -b DEBUG
编译完成后,在Build/OvmfX64/DEBUG_GCC5/FV目录下会生成OVMF.fd。用QEMU加载它:
qemu-system-x86_64 -bios Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd -m 1024
你会看到一个UEFI Shell界面。输入map -r可以列出所有设备,输入help查看命令列表。嗯,看到这个界面,说明你的环境已经跑通了。
-m参数是否给了足够内存。我建议至少512MB,否则UEFI固件可能加载失败。
3.4 第一个UEFI应用程序:HelloWorld
好,重头戏来了。咱们写一个最简单的UEFI应用,打印"Hello, UEFI!"。
首先,在edk2目录下创建一个MyHelloPkg文件夹,里面放两个文件:
MyHelloPkg/MyHelloPkg.dsc
[Defines]
PLATFORM_NAME = MyHelloPkg
PLATFORM_GUID = 12345678-1234-1234-1234-123456789abc
PLATFORM_VERSION = 0.1
DSC_SPECIFICATION = 0x00010005
OUTPUT_DIRECTORY = Build/MyHelloPkg
SUPPORTED_ARCHITECTURES = X64
BUILD_TARGETS = DEBUG|RELEASE
SKUID_IDENTIFIER = DEFAULT
[Components]
MyHelloPkg/HelloWorld.inf
MyHelloPkg/HelloWorld.inf
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = HelloWorld
FILE_GUID = abcdef01-2345-6789-abcd-ef0123456789
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
[Sources]
HelloWorld.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
MyHelloPkg/HelloWorld.c
#include <Uefi.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiLib.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
Print (L"Hello, UEFI!\n");
Print (L"Welcome to EDK2 development!\n");
return EFI_SUCCESS;
}
编译命令:
build -a X64 -t GCC5 -p MyHelloPkg/MyHelloPkg.dsc -b DEBUG
编译成功后,在Build/MyHelloPkg/DEBUG_GCC5/X64目录下找到HelloWorld.efi。把这个文件放到QEMU的虚拟磁盘里,或者用-drive参数挂载一个FAT分区,然后在UEFI Shell中运行它:
fs0:\> HelloWorld.efi
Hello, UEFI!
Welcome to EDK2 development!
-fw_cfg参数直接把EFI文件传给QEMU。命令如下:qemu-system-x86_64 -bios OVMF.fd -fw_cfg name=opt/HelloWorld.efi,file=HelloWorld.efi然后在Shell里用
load命令加载它。
3.5 知识体系总览
下面这张图帮你理清本章的核心逻辑:从源码获取到应用运行的全流程。
这张图把整个流程串起来了。你想想看,从拉代码到跑通应用,其实就七个步骤。每一步都有对应的命令和文件,照着做就行。
3.6 常见问题与避坑
- 编译报错"Python not found":检查
python3 --version,如果没装就sudo apt install python3。我建议用Python 3.8+。 - QEMU启动后卡在"Press ESC for boot menu":按ESC进入菜单,选择"Boot Manager"然后选"UEFI Shell"。如果没看到Shell,检查OVMF是否编译正确。
- HelloWorld.efi运行时报"Image not found":大概率是文件路径不对。用
map -r确认磁盘映射,然后cd fs0:进入正确分区。 - Windows下编译失败:确保VS的
nmake在PATH里。我习惯用"Developer Command Prompt for VS"启动终端。
好,这一章的内容就到这儿。你跟着步骤走,应该能顺利跑出"Hello, UEFI!"。下一章咱们会深入UEFI驱动的开发模型,到时候见。