2. 创建第一个UEFI应用程序
任务目标
编写一个最小的UEFI应用程序,在UEFI Shell或启动管理器加载该应用时,向控制台输出字符串 Hello, UEFI World!,并返回状态码 EFI_SUCCESS。本案例聚焦于理解UEFI应用程序的入口点签名、必要的头文件以及编译链接的基本流程。
环境与依赖
- 操作系统:Linux(本文以Ubuntu 22.04为例)或Windows(需安装对应工具链)
- EDK II 开发环境:已安装并配置好
edk2源码树(推荐使用edk2-stable202311或更新版本) - 编译工具链:GCC(Linux)或 Visual Studio(Windows),EDK II 构建系统会自动检测
- 模拟器(可选):
QEMU+OVMF固件,用于在无真实硬件时验证运行
实现步骤
在 EDK II 源码树中,应用程序通常放在 AppPkg/Applications/ 或自定义包目录下。本案例使用一个独立包 HelloWorldPkg 来保持最小化。
1. 创建包目录结构
edk2/
└── HelloWorldPkg/
├── HelloWorldPkg.dsc # 包描述文件
└── Applications/
└── HelloWorld/
├── HelloWorld.c # 主程序源码
└── HelloWorld.inf # 模块描述文件
2. 编写主程序源码(HelloWorld.c)
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
/**
应用程序入口点。
@param[in] ImageHandle 加载的固件映像句柄。
@param[in] SystemTable 指向系统服务表的指针。
@return 返回 EFI_SUCCESS 表示成功。
**/
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
Print (L"Hello, UEFI World!\n");
return EFI_SUCCESS;
}
3. 编写模块描述文件(HelloWorld.inf)
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = HelloWorld
FILE_GUID = 8D0E8C3A-6F2A-4A6B-9B5F-3D7C1A2B4E5F
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
[Sources]
HelloWorld.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
4. 编写包描述文件(HelloWorldPkg.dsc)
[Defines]
PLATFORM_NAME = HelloWorldPkg
PLATFORM_GUID = 9E4F2A1C-3B5D-4E6F-8A7B-2C3D4E5F6A7B
PLATFORM_VERSION = 1.0
DSC_SPECIFICATION = 0x00010005
OUTPUT_DIRECTORY = Build/HelloWorldPkg
SUPPORTED_ARCHITECTURES = IA32|X64
BUILD_TARGETS = DEBUG|RELEASE
SKUID_IDENTIFIER = DEFAULT
[LibraryClasses.common]
UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
[Components]
HelloWorldPkg/Applications/HelloWorld/HelloWorld.inf
编译与运行
编译
cd edk2
source edksetup.sh # Linux 下初始化环境
build -p HelloWorldPkg/HelloWorldPkg.dsc -m HelloWorldPkg/Applications/HelloWorld/HelloWorld.inf -a X64 -t GCC5
编译成功后,生成的 HelloWorld.efi 文件位于 Build/HelloWorldPkg/DEBUG_GCC5/X64/ 目录下。
运行(QEMU + OVMF 模拟)
# 准备包含 HelloWorld.efi 的 FAT 镜像(假设已挂载到 /mnt)
cp Build/HelloWorldPkg/DEBUG_GCC5/X64/HelloWorld.efi /mnt/
qemu-system-x86_64 -bios /path/to/OVMF.fd -hda fat:rw:/mnt -nographic
在 QEMU 的 UEFI Shell 中,切换到 FS0: 并执行:
FS0:\> HelloWorld.efi
验证方法与判定标准
- 编译验证:执行
build命令后,终端输出Build... done且无错误信息,即编译通过。 - 运行验证:在 UEFI Shell 中运行
HelloWorld.efi后,屏幕显示Hello, UEFI World!,随后 Shell 提示符重新出现,说明程序正常返回。 - 状态码验证:在 Shell 中执行
echo %lasterror%,若输出Success,则证明返回值为EFI_SUCCESS。
常见问题
- 编译报错找不到头文件:确认
MdePkg.dec路径正确,且edksetup.sh已成功执行。 - 运行后无输出:检查
HelloWorld.efi是否被正确复制到 FAT 分区,且文件系统可读。 - GUID 冲突:每个模块的
FILE_GUID必须唯一,可使用uuidgen命令生成新 GUID。