首页 > 基础资料 博客日记
如何使用 UEFI Shell 执行 Hello World 程序
2026-04-01 17:30:03基础资料围观1次
如何创建一个 UEFI 应用程序
在之前的文章中曾详细介绍了 EDKII 开发环境的搭建以及 OVMF 固件的编译过程。并且使用 QEMU 虚拟机来执行编译好的 OVMF 固件。我们知道在 Linux 终端中可以在命令行中执行编译好的应用程序,UEFI 也有 shell,如下图所示。我们能够在 shell 中执行编译好的 UEFI Application。本文以简单的 Hello World 程序为例来介绍 UEFI 应用程序的编译执行过程和各个文件的作用。
1. 编译并执行一个 Hello World 程序
-
在 EDKII 目录下创建文件 HelloWorldPkg

-
创建文件 HelloWorld.c
#include <Uefi.h> #include <Library/UefiLib.h> EFI_STATUS EFIAPI UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { Print(L"Hello, World!\n"); return EFI_SUCCESS; } -
创建文件 HelloWorld.inf
GUID 可通过网站产生:https://guidgen.com/
[Defines] INF_VERSION = 0x00010006 BASE_NAME = HelloWorld FILE_GUID = 69ea2943-dbdd-404c-a3bf-6ef3fdfdf0a1 MODULE_TYPE = UEFI_APPLICATION VERSION_STRING = 1.0 ENTRY_POINT = UefiMain [Sources] HelloWorld.c [Packages] MdePkg/MdePkg.dec [LibraryClasses] UefiApplicationEntryPoint UefiLib -
创建文件 HelloWorldPkg.dsc
[Defines] PLATFORM_NAME = HelloWorldPkg PLATFORM_GUID = 0adf0da5-100e-49a9-9f87-76215486216d PLATFORM_VERSION = 0.1 DSC_SPECIFICATION = 0x00010005 SUPPORTED_ARCHITECTURES = X64 BUILD_TARGETS = DEBUG|RELEASE [LibraryClasses] UefiLib|MdePkg/Library/UefiLib/UefiLib.inf UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf BaseLib|MdePkg/Library/BaseLib/BaseLib.inf UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf [Components] HelloWorldPkg/HelloWorld.inf -
编译为
.efi文件打开终端
cd /home/ayuan/src/edk2 source edksetup.sh编译 HelloWorldPkg
打开文件
./Conf/target.txt,修改如下项ACTIVE_PLATFORM = HelloWorldPkg/HelloWorldPkg.dsc TARGET = DEBUG TARGET_ARCH = X64 TOOL_CHAIN_TAG = GCC5回到终端执行命令
build,生成的 efi 文件的路径如下:/home/ayuan/src/edk2/Build/HelloWorldPkg/DEBUG_GCC5/X64/HelloWorld.efi -
在 QEMU 中打开 OVMF 固件,然后在 UEFI Shell 中执行刚才编译的 HelloWorld.efi 文件
qemu-system-x86_64 -bios /home/ayuan/run-ovmf/OVMF.fd -drive format=raw,file=fat:rw:/home/ayuan/run-ovmf/hda-contents -m 512M # 或者 qemu-system-x86_64 -m 512M -drive if=pflash,format=raw,readonly=on,file=/home/ayuan/run-ovmf/OVMF.fd -drive if=pflash,format=raw,file=fat:rw:/home/ayuan/run-ovmf/hda-contents
除了 .c 源文件之外,我们还涉及到 INF, DSC, DEC 三个重要的文件。三个文件分别用于描述“模块”,“平台”,和“包”。他们的关系如下所示:
平台 (Platform)
└── 由多个 模块 (Module) 组成
├── 来自 包A (Package A)
├── 来自 包B (Package B)
└── 来自 包C (Package C)
1. 包(Package)是“资源提供者” 一个包就是一个功能或主题相关的“大仓库”。 例如:
- MdePkg:最基础的库、头文件、通用协议
- MdeModulePkg:通用驱动(如控制台、文件系统、USB 等)
- OvmfPkg:专用于 QEMU/Ovmf 虚拟机的平台包
- ShellPkg:UEFI Shell 相关模块
包通过 .DEC 文件对外声明:我提供了哪些头文件、哪些库、哪些 GUID、哪些 PCD。
2. 模块(Module)是“可构建单元” 模块是真正会被编译的东西(.efi、.lib)。 每个模块 必须属于某个包,它的 .INF 文件第一件事就是通过 [Packages] 节声明自己属于哪些包,从而获得头文件和定义。 一个 ConOutDxe.inf(控制台输出驱动)属于 MdeModulePkg 这个包。
3. 平台(Platform)是“最终产品组装者” 平台负责决定:“我这个主板/产品要用哪些模块?” 它通过 .DSC 文件的 [Components] 节,把来自不同包的各种模块“挑选”进来,并配置 PCD 值、库映射关系等。 最终通过构建命令生成完整的固件映像。
三个文件的相互引用关系如下:
在 .INF 文件中必须说明引用包,就是当前模块的源码使用了哪些包定义的函数或者接口:
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
在 .DSC 文件中需要引用模块,就是需要将那些模块编译进该平台,如:
[Components]
MdeModulePkg/Universal/Console/ConOutDxe/ConOutDxe.inf
OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf
读者可能注意到我们在 HelloWorldPkg 中并没有创建 DEC 文件,这是因为没有其他模块使用到我们自定义的这个包,所以不创建也没什么问题。
2. INF 文件说明
INF 文件是单个模块(Module)的“身份证”。一个模块可以是驱动(Driver)、库(Library)、应用(Application)或 PEI/DXE 模块等。它告诉构建系统这个模块由哪些源文件组成,依赖哪些包、库、协议、GUID,模块的类型、入口点、输出文件名是什么,编译时需要哪些特殊选项等。没有 INF 文件,模块就无法被构建。每个 .inf 文件对应一个独立的、可独立构建的单元(最终生成 .efi 或 .lib)。INF 通常放在模块目录下(如 MdeModulePkg/Universal/Console/ConOutDxe/ConOutDxe.inf)。
INF 文件的常用组成:
- [Defines]:模块基本信息(版本、GUID、类型、入口点等)。
- [Packages]:依赖哪些包(提供头文件和 PCD)。
- [Sources]:源代码文件列表。
- [LibraryClasses]:需要链接哪些库类。
- [Protocols] / [Guids] / [Ppis]:使用的协议/GUID/PPI 及使用方式(BY_START、PRODUCES 等)。
- [BuildOptions]:特定编译器选项。
- [Depex](可选):DXE 依赖表达式。
例如:
[Defines]
INF_VERSION = 1.27
BASE_NAME = HelloWorld
FILE_GUID = 12345678-ABCD-1234-ABCD-123456789ABC
MODULE_TYPE = UEFI_APPLICATION # 或 DXE_DRIVER、BASE 等
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain # 入口函数名
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[Sources]
HelloWorld.c
HelloWorld.h
[LibraryClasses]
UefiLib
UefiApplicationEntryPoint
DebugLib
[Protocols]
gEfiShellProtocolGuid ## CONSUMES
[Guids]
gEfiMdeModulePkgTokenSpaceGuid ## SOMETIMES_PRODUCES
3. DSC 文件说明
DSC 为平台描述文件,是整个平台(Platform)的“构建蓝图”。它定义了这个平台要包含哪些模块(INF 文件),库类(LibraryClass)如何映射到具体实现,PCD(Platform Configuration Database)值如何覆盖,平台整体的架构、构建目标、输出目录等。一个平台通常只有一个主 DSC 文件(如 OvmfPkg/OvmfPkgX64.dsc 或 PlatformPkg/Platform.dsc)。DSC 不负责包的内容声明(那是 DEC),也不负责 Flash 布局(那是 FDF),但会引用 FDF 来生成最终固件映像。
DSC 文件常用组成:
[Defines]:平台名称、GUID、支持架构、构建目标等。
[LibraryClasses]:库类 → 具体 INF 的映射(全局生效)。
[Pcds]:覆盖包中声明的 PCD 默认值(FixedAtBuild、Dynamic 等)。
[Components]:列出所有要构建的模块 INF 文件(支持条件编译)。
[Components.IA32] / [Components.X64] 等架构特定节。
例如:
[Defines]
PLATFORM_NAME = MyPlatform
PLATFORM_GUID = 87654321-ABCD-1234-ABCD-123456789ABC
PLATFORM_VERSION = 1.0
DSC_SPECIFICATION = 1.28
OUTPUT_DIRECTORY = Build/MyPlatform
SUPPORTED_ARCHITECTURES = IA32|X64
BUILD_TARGETS = DEBUG|RELEASE
SKUID_IDENTIFIER = DEFAULT
[LibraryClasses]
DebugLib| MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
UefiLib| MdePkg/Library/UefiLib/UefiLib.inf
# ... 其他库映射
[PcdsFixedAtBuild]
gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintTimes| 5 | UINT32 | 0x40000005
[Components]
# 核心模块
MdeModulePkg/Universal/Console/ConOutDxe/ConOutDxe.inf
MyPkg/HelloWorld/HelloWorld.inf # 引用上面的 INF
[Components.X64]
# 只在 X64 下构建的模块
OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf
4. DEC 文件说明
DEC 是包(Package)的“目录索引”。一个包是一组相关模块、库、头文件、GUID、协议、PCD 的集合(如 MdePkg、MdeModulePkg、OvmfPkg)。DEC 文件的作用是声明包对外提供什么(GUID、Protocol、PPI、LibraryClass、PCD),指定头文件包含路径([Includes]),让其他模块的 INF 文件可以通过 [Packages] 引用这个包,从而获得头文件和 PCD 定义。
没有 DEC,模块就无法知道这个包里有哪些可用的接口和配置。
DEC 文件常用组成:
[Defines]:包名称、GUID、版本。
[Includes]:头文件目录(支持架构特定)。
[LibraryClasses]:包提供的库类及其头文件路径。
[Guids] / [Protocols] / [Ppis]:声明 GUID/协议/PPI(带注释说明用途)。
[Pcds]:声明所有 PCD(FeatureFlag、FixedAtBuild、Dynamic 等)及其默认值、类型、Token。
例如:
[Defines]
DEC_SPECIFICATION = 1.27
PACKAGE_NAME = MdePkg
PACKAGE_GUID = 1E0A9C1A-5A9C-4C9A-9B7A-5A9C1E0A9C1A
PACKAGE_VERSION = 1.05
[Includes]
Include
Include/Ia32 # 架构特定
[LibraryClasses]
## @libraryclass 基础内存操作库
BaseMemoryLib| Include/Library/BaseMemoryLib.h
[Guids]
## Include/Guid/MdePkgTokenSpace.h
gEfiMdePkgTokenSpaceGuid = { 0x1E0A9C1A, 0x5A9C, 0x4C9A, {0x9B, 0x7A, 0x5A, 0x9C, 0x1E, 0x0A, 0x9C, 0x1A} }
[PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]
## 此 PCD 定义 HelloWorld 打印次数
# @Prompt HelloWorld print times.
gEfiMdePkgTokenSpaceGuid.PcdHelloWorldPrintTimes|1|UINT32|0x40000005
*部分示例代码由 grok 生成
本文参考:https://www.bilibili.com/video/BV17h411x7Wr?spm_id_from=333.788.videopod.sections&vd_source=2ee7caa81fced5c94d0d863e82c6acae
Steady Progress!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签:
相关文章
最新发布
- 关于列式存储(Column-base Storage)的几个要点解读
- 同样都是九年义务教育,他知道的AI算力科普好像比我多耶
- Slickflow 与 OpenClaw 结合实践:技术原理、集成方式与 Skill 说明
- 如何使用 UEFI Shell 执行 Hello World 程序
- Agent构建:声明式优于硬编码
- AI 可以取代运维了吗?
- 测试人必备的4个AI Skills(附下载地址和详细用法)
- 记一次Webshell流量分析2 | 添柴不加火
- 直击政企AI落地“深水区”,华为混合云推出OpenClaw本地部署方案
- FastAPI里玩转Redis和数据库的正确姿势,别让异步任务把你坑哭了!

