2. Hello World UEFI应用

任务目标

编写一个最小的UEFI应用程序,在UEFI Shell或启动环境中输出字符串 Hello World,并编译生成可加载的 .efi 可执行文件。本案例聚焦于EDK2环境下UEFI应用的标准入口点实现与编译流程。

环境与依赖

  • EDK2开发环境(已在 Conf/target.txt 中配置好工具链,如 GCC5 或 VS2019)
  • 已执行 edksetup.sh(Linux)或 edksetup.bat(Windows)完成环境初始化
  • 已创建或已有工作区(如 OvmfPkg 或自定义包目录)

实现步骤

在EDK2包目录下创建应用源码文件 HelloWorld.c,并在包级 .dsc 文件中注册该组件。核心实现如下:

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>

/**
  UEFI应用入口点。

  @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 World\n");
  return EFI_SUCCESS;
}

在包级 .dsc 文件的 [Components] 段添加以下条目以注册该模块(以 OvmfPkg 为例):

OvmfPkg/HelloWorld/HelloWorld.inf

同时创建模块描述文件 HelloWorld.inf:

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = HelloWorld
  FILE_GUID                      = 8D0E2C1A-2F5E-4B7A-9C3D-6E8F1A2B4C5D
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain

[Sources]
  HelloWorld.c

[Packages]
  MdePkg/MdePkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib

编译与运行

  1. 在EDK2根目录执行 build -p OvmfPkg/OvmfPkgX64.dsc -m OvmfPkg/HelloWorld/HelloWorld.inf(或对应包名)。
  2. 编译产物位于 Build/OvmfX64/DEBUG_GCC5/X64/HelloWorld.efi(路径随工具链与架构变化)。
  3. 将 HelloWorld.efi 复制到UEFI Shell可访问的FAT分区,或直接放入OVMF启动盘镜像。
  4. 在UEFI Shell中切换到文件所在文件系统(如 fs0:),执行 HelloWorld.efi。

验证方法

运行后屏幕输出 Hello World 并返回Shell提示符,即表示应用编译正确、入口点被固件正常调用、Print 库函数工作正常。若在OVMF(QEMU)中运行,Shell窗口出现上述输出即为通过。

常见问题

  • 编译报错“无法打开库文件”:检查 .inf 中 [LibraryClasses] 是否包含 UefiApplicationEntryPoint 和 UefiLib。
  • 运行后无输出:确认 .efi 文件确实从FAT分区加载,且Shell当前目录正确。
  • GUID冲突:每个模块的 FILE_GUID 必须唯一,可使用 uuidgen 生成新值。