3. EDK2 工程结构:目录、描述文件与固件布局

EDK2 是 UEFI 驱动开发的标准参考实现框架。理解其工程结构是编写可移植、可维护驱动的基础。本章将解析 EDK2 的顶层目录布局,并深入说明 .dsc、.fdf、.dec 三种核心元数据文件的作用与协作关系。

3.1 EDK2 顶层目录结构

一个标准的 EDK2 工作空间(通常称为 edk2 或 WORKSPACE)包含以下关键目录与文件:

目录/文件作用
MdePkg/基础定义包:包含 UEFI 规范定义的数据类型、协议头、PI 规范定义、库类声明。
MdeModulePkg/基础模块包:提供通用的 UEFI 驱动、库实例(如磁盘 I/O、控制台、BDS 等)。
UefiCpuPkg/CPU 架构相关包:包含 CPU 驱动、MP 服务、SMM 基础库。
NetworkPkg/网络协议栈:SNP、ARP、IP4/IP6、TCP/UDP、TLS 等。
ShellPkg/UEFI Shell 实现:命令行解释器及内置命令。
OvmfPkg/OVMF(开源虚拟机固件):用于 QEMU/KVM 的 UEFI 固件示例。
ArmPkg/ARM/AArch64 架构支持:包含 ARM 特定库、异常处理、GIC 驱动。
BaseTools/构建工具集:包含 GenFw、GenFds、Trim、Ecc 等编译与固件生成工具。
Conf/构建配置文件:target.txt、tools_def.txt 定义工具链与目标平台。
edk2.bat / edksetup.sh环境初始化脚本:设置 WORKSPACE、PACKAGES_PATH 等环境变量。
包(Package)概念:EDK2 以 Pkg 为组织单元。每个包是一个逻辑模块集合,包含头文件、库、驱动、协议定义。包名通常以 Pkg 结尾,如 MyDriverPkg。

3.2 三种核心描述文件:.dsc / .fdf / .dec

EDK2 构建系统通过三个描述文件定义“构建什么”、“如何组合”、“依赖什么”。它们分别对应平台级、固件布局级、模块接口级。

3.2.1 .dec 文件 — 包声明(Declaration)

.dec 文件位于每个包的根目录,声明该包对外提供的接口、协议 GUID、PPI GUID、PCD 条目、库类。它是模块间依赖的契约。

# MdePkg/MdePkg.dec 片段
[Defines]
  DEC_SPECIFICATION              = 0x00010005
  PACKAGE_NAME                   = MdePkg
  PACKAGE_GUID                   = 1E73767F-8A52-4b2c-BF3E-7B5B3B6F5B2A
  PACKAGE_VERSION                = 1.08

[Includes]
  Include                        # 公共头文件路径

[LibraryClasses]
  UefiLib|Include/Library/UefiLib.h
  BaseLib|Include/Library/BaseLib.h

[Guids]
  gEfiGlobalVariableGuid         = {0x8BE4DF61, 0x93CA, 0x11d2, {0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C}}

[PcdsFixedAtBuild]
  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xFF|UINT8|0x00000001

3.2.2 .dsc 文件 — 平台描述(Platform Description)

.dsc 文件定义整个固件映像的构建范围:包含哪些模块、使用哪些库实例、PCD 值、编译选项。它是构建系统的入口。

# OvmfPkg/OvmfPkgX64.dsc 片段
[Defines]
  PLATFORM_NAME                  = Ovmf
  PLATFORM_GUID                  = 5a9e7754-8270-4b2c-bf3e-7b5b3b6f5b2a
  PLATFORM_VERSION               = 0.1
  DSC_SPECIFICATION              = 0x00010005
  OUTPUT_DIRECTORY               = Build/OvmfX64
  SUPPORTED_ARCHITECTURES        = X64
  BUILD_TARGETS                  = DEBUG|RELEASE|NOOPT
  SKUID_IDENTIFIER               = DEFAULT

[LibraryClasses]
  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf

[Components]
  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
  OvmfPkg/PlatformDxe/PlatformDxe.inf
  # 用户自定义驱动
  MyDriverPkg/MyDriver/MyDriver.inf

3.2.3 .fdf 文件 — 固件描述(Flash Description)

.fdf 文件定义固件在 Flash 中的物理布局:FD(Flash Device)区域划分、FVs(Firmware Volumes)、FFS(Firmware File System)文件放置顺序。它决定最终生成的 .fd 二进制映像的二进制布局。

# OvmfPkg/OvmfPkgX64.fdf 片段
[FD.BFV]
BaseAddress   = 0x00000000
Size          = 0x00200000
ErasePolarity = 1
BlockSize     = 0x00001000
NumBlocks     = 0x200

[FV.SECFV]
FvAlignment        = 16
ERASE_POLARITY     = 1
MEMORY_MAPPED      = TRUE
STICKY_WRITE       = TRUE
LOCK_CAP           = TRUE
LOCK_STATUS        = TRUE
READ_DISABLED_CAP  = FALSE
READ_ENABLED       = TRUE
READ_STATUS        = TRUE
READ_LOCK_CAP      = TRUE
READ_LOCK_STATUS   = TRUE

  INF  UefiCpuPkg/SecCore/SecCore.inf
  INF  OvmfPkg/Sec/SecMain.inf

[FV.DXEFV]
  INF  MdeModulePkg/Core/Dxe/DxeMain.inf
  INF  MyDriverPkg/MyDriver/MyDriver.inf
三文件协作流程:.dsc 告诉构建系统“编译哪些模块”,.fdf 告诉构建系统“把这些模块的 FFS 文件按什么顺序放入哪个 FV”,.dec 提供模块编译所需的头文件路径、GUID 和 PCD 声明。三者缺一不可。

3.3 三文件关系图

下图展示了 .dsc、.fdf、.dec 在 EDK2 构建流程中的角色与数据流:

EDK2 三文件协作关系 .dec 文件 包声明(接口契约) .dsc 文件 平台描述(构建入口) .fdf 文件 固件布局(Flash 映像) 提供 GUID / PCD / 库类 传递模块列表与 PCD Build 工具链 .fd 固件映像 构建流程简述: 1. Build 工具解析 .dsc,确定模块列表与库绑定。 2. 根据 .dec 解析模块依赖的 GUID、PCD、头文件路径。 3. 编译模块生成 .efi 文件,再按 .fdf 布局打包为 .fd 固件映像。

3.4 文件作用对比表

维度.dec.dsc.fdf
所属层级包(Package)平台(Platform)平台(Platform)
核心职责声明接口与依赖选择模块与库实例定义 Flash 布局
包含内容GUID、PPI、协议、PCD、库类、头文件路径模块 INF 列表、库绑定、PCD 赋值、编译选项FD 区域、FV 划分、FFS 文件放置顺序
是否必须每个包必须有每个平台必须有每个平台必须有
生成产物无直接产物,供编译时解析控制编译过程最终 .fd 映像
修改影响影响所有依赖该包的模块影响当前平台所有模块影响固件二进制布局

3.5 模块 INF 文件 — 模块描述

除了上述三个平台级文件,每个模块(驱动或库)还包含一个 .inf 文件,用于描述该模块自身的源文件、依赖库、PCD 需求、架构支持等。INF 文件是 .dsc 中 [Components] 引用的基本单元。

# MyDriverPkg/MyDriver/MyDriver.inf 示例
[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = MyDriver
  FILE_GUID                      = 1B2F3C4D-5E6F-7890-ABCD-EF1234567890
  MODULE_TYPE                    = UEFI_DRIVER
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = MyDriverEntryPoint

[Sources]
  MyDriver.c
  MyDriver.h

[Packages]
  MdePkg/MdePkg.dec
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  UefiDriverEntryPoint
  UefiLib
  BaseLib
  MemoryAllocationLib

[Protocols]
  gEfiSimpleTextOutProtocolGuid
  gEfiBlockIoProtocolGuid

[Depex]
  TRUE
常见错误:初学者常忘记在 .dsc 的 [LibraryClasses] 中为模块使用的库类绑定具体库实例,导致构建报“未解析的外部符号”。务必确保每个 .inf 中声明的 [LibraryClasses] 在 .dsc 中都有对应的库实例映射。

3.6 构建流程总结

  1. 解析 .dsc:读取平台定义,确定目标架构、编译类型(DEBUG/RELEASE)、模块列表。
  2. 解析模块 .inf:对每个模块,读取其源文件、依赖包、库类、协议 GUID。
  3. 解析 .dec:根据 [Packages] 找到对应 .dec,解析 GUID、PCD、头文件路径。
  4. 编译:调用编译器(如 GCC、MSVC、ICC)生成 .efi 文件。
  5. 解析 .fdf:读取 Flash 布局规则,将 .efi 文件打包为 FFS,按 FV 组织,最终生成 .fd 固件映像。