在 EDK2(UEFI 开发环境) 中,编译自己的包(Package)和 HelloWorld
程序主要是为了 学习 UEFI 开发流程 和 避免污染 EDK2 主代码库。
EDK2 是一个复杂的 UEFI 开发框架,直接修改核心代码(如 MdeModulePkg
或 OvmfPkg
)可能会引入错误,影响整个编译环境。
通过创建自己的包(如 MyPkg
)和 HelloWorld
程序,可以:
- 理解 UEFI 模块的编译过程(
.inf
,.dec
,.dsc
,.fdf
文件的作用)。 - 掌握 UEFI 应用程序、驱动程序的开发方式(
EFI_APPLICATION
,UEFI_DRIVER
)。 - 熟悉 UEFI Shell 下的调试方法(如
load
、unload
、dmpstore
等命令)。
EDK2 的核心包(如 MdePkg
、MdeModulePkg
、OvmfPkg
)是 UEFI 固件的基础,随意修改可能导致:
- 编译失败(依赖关系复杂,容易破坏其他模块)。
- 难以升级 EDK2(如果修改了官方代码,更新 EDK2 时可能冲突)。
- 难以维护(调试问题时难以区分是官方代码的问题还是自己的改动导致的)。
因此需要编译自己的Package包。
文章参考:UEFI学习(二)-- 搭建属于自己的Package和Library_orin uefi学习-CSDN博客
1. 创建自定义包的目录结构
MyPkg/
├── MyApplications
│ └── MyHelloWorld
│ ├── MyHelloWorld.c
│ └── MyHelloWorld.inf
├── MyDrivers
│ └── MyDriver
├── MyPkg.dec
└── MyPkg.dsc
1.1 文件描述
文件 | 描述 |
---|---|
Applications | 放置不同的应用程序 |
Drivers | 放置不同的驱动测序 |
MyPkg.dec | 定义包的依赖和接口(类似头文件集合) |
MyPkg.dsc | 指定编译哪些模块和库依赖。 |
2. 编写自己的HelloWorld程序
为了方便Build,可以使用vscode 的task功能,对应的task.json代码为这个,为了方便联想,还可以添加c_cpp_properties.json,这个自动生成就好。
{
"version": "2.0.0",
"tasks": [
{
"label": "Build EDK2",
"type": "shell",
"command": "bash",
"args": [
"-c",
"source ../myexport.sh && build -a X64 -p MyPkg/MyPkg.dsc"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "dedicated"
},
"problemMatcher": []
}
]
}
2.1 MyHelloWorld.c
#include <Uefi.h>
#include <Library/UefiLib.h>
EFI_STATUS
EFIAPI UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{
Print(L"Hello,This is my first UEFI Application\n");
return EFI_SUCCESS;
}
在UEFI中,函数的返回值都是返回状态,对应的返回值都是通过指针形参进行返回,使用IN和OUT,尽量不要使用中文的标点符号,不然在 qemu 终端里面显示不出来。
2.2 MyHelloWorld.inf
对应的GUID可以在线生成。
[Defines]
INF_VERSION = 0x00010006 # INF 文件格式版本 (EDK2 v1.6)
BASE_NAME = MyHelloWorld # 输出文件名 (生成 MyHelloWorld.efi)
FILE_GUID = 92C5A2F5-7CFC-1C83-EADA-0F9A5BBB8AE7 # 模块唯一标识符 (必须全局唯一)
MODULE_TYPE = UEFI_APPLICATION # 模块类型 (UEFI应用程序)
VERSION_STRING = 1.0 # 模块版本号
ENTRY_POINT = UefiMain # 程序入口函数名
[Sources]
MyHelloWorld.c # 源代码文件列表
[Packages]
MdePkg/MdePkg.dec # 依赖的包声明文件
[LibraryClasses]
UefiApplicationEntryPoint # 应用入口点库
UefiLib # UEFI基础功能库
2.3 MyPkg.dec
[Defines]
DEC_SPECIFICATION = 0x00010005
PACKAGE_NAME = MyPkg
PACKAGE_GUID = 263EC29A-EAB4-4504-FFFD-970978EA3246
PACKAGE_VERSION =0.01
2.4 MyPkg.dsc
在LibraryClasses添加库的时候,会出现 error 4000: Instance of library class [PrintLib] is not found for module,这样的错误,可以直接在vscode下按住ctrl+p然后搜索PrintLib.inf这样的文件,然后可以进行快速查找,如果是其他的库文件也是类似的查找办法,这样感觉edk2有点坑。
[Defines]
PLATFORM_NAME = MyPkg
PLATFORM_GUID = 0D4E362C-6CBC-F881-7625-84B95963D6C8
PLATFORM_VERSION = 0.1
DSC_SPECIFICATION = 0X00010005
OUTPUT_DIRECTORY = Build/MyPkg
SUPPORTED_ARCHITECTURES = X64
BUILD_TARGETS = DEBUG|RELEASE
SKUID_IDENTIFIER = DEFAULT
#
# Debug output control
#
DEFINE DEBUG_ENABLE_OUTPUT = FALSE # Set to TRUE to enable debug output
DEFINE DEBUG_PRINT_ERROR_LEVEL = 0x80000040 # Flags to control amount of debug output
DEFINE DEBUG_PROPERTY_MASK = 0
[LibraryClasses]
UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.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
StackCheckLib|MdePkg/Library/StackCheckLib/StackCheckLib.inf
StackCheckFailureHookLib|MdePkg/Library/StackCheckFailureHookLibNull/StackCheckFailureHookLibNull.inf
[Components]
MyPkg/MyApplications/MyHelloWorld/MyHelloWorld.inf
3. 编译自己的包
因为已经添加了task.json文件,可以直接按住ctrl+shift+B进行自动化任务编译,如果想要编译其他的包,可以更改task.json里面的"source ../myexport.sh && build -a X64 -p MyPkg/MyPkg.dsc"这一行代码,然后把-p MyPkg/MyPkg.dsc改为对应的路径就好。
编译成功就会出现Done关键字,然后就可以放到QEMU虚拟机里面进行运行。类似于上一章,可以把Build/MyPkg/DEBUG_GCC5/X64/MyHelloWorld.efi拷贝到ESP目录下,运行.bat文件,结果如图所示: