4. UEFI 工程结构解析

UEFI 固件开发的核心是 EDK2(EFI Development Kit II)框架。理解其工程结构是进行模块开发与平台移植的基础。本章将深入剖析 EDK2 的目录布局、三大核心描述文件(.dsc、.dec、.fdf)以及 Package 与 Module 的层次关系。

4.1 EDK2 顶层目录结构

一个标准的 EDK2 工作区(Workspace)通常包含以下顶层目录。开发者需熟悉每个目录的职责,以便快速定位代码与配置文件。

目录/文件说明典型内容
BaseTools/构建工具集,包含编译、链接、生成固件镜像的工具链GNUmakefile, Python 脚本, C 源码
Conf/全局构建配置文件,如 target.txt、tools_def.txt工具链路径、目标架构、编译选项
MdePkg/核心基础包,定义 UEFI 规范中的数据类型、协议、PPI 等UEFI 规范头文件、库函数
MdeModulePkg/通用模块包,提供大量可复用的驱动与库磁盘驱动、控制台驱动、BDS 阶段代码
IntelFrameworkPkg/Intel 框架包,包含传统 BIOS 遗留接口Framework 协议、SMM 相关代码
NetworkPkg/网络协议栈实现TCP/IP、HTTP Boot、iSCSI 驱动
ShellPkg/UEFI Shell 实现Shell 命令、Shell 应用程序
OvmfPkg/QEMU 虚拟平台包,用于模拟器调试OVMF 固件、虚拟平台描述文件
Platform/厂商或社区提供的平台定制包板级初始化代码、平台描述文件
Build/编译输出目录(自动生成)中间文件、最终固件镜像
提示: 初学者应重点关注 MdePkg 和 MdeModulePkg,它们构成了 EDK2 的“标准库”与“通用驱动库”,是学习 UEFI 编程的起点。

4.2 Package 与 Module 的概念

EDK2 采用 Package(包) 和 Module(模块) 两层组织架构。Package 是逻辑上的功能集合,Module 是具体的可编译单元。

  • Package(.dec 文件定义):包含一组相关的 Module、Library、Protocol、PPI 和 GUID 声明。例如 MdePkg 是一个 Package,它定义了所有 UEFI 规范中的基础类型。
  • Module(.inf 文件定义):一个独立的可编译单元,可以是驱动(DXE/SMM/UEFI Driver)、应用程序(Shell App)或库(Library Instance)。每个 Module 必须有一个 .inf 文件描述其源文件、依赖和编译选项。
关键关系: Package 提供接口声明(.dec),Module 实现具体功能(.inf + .c/.h)。一个 Package 可以包含多个 Module,而一个 Module 可以依赖多个 Package 提供的接口。
Package 与 Module 层次关系 MdePkg (Package) MdeModulePkg (Package) NetworkPkg (Package) BaseLib (Library) UefiLib (Library) DiskIoDxe (Driver) ConSplitterDxe (Driver) TcpDxe (Driver) HttpBootDxe (Driver) 每个 Package 包含多个 Module(Driver/Library/Application) Module 通过 .inf 文件声明自身,Package 通过 .dec 文件声明接口

4.3 三大核心描述文件详解

EDK2 的构建系统依赖三个关键描述文件:.dsc(平台描述)、.dec(包声明)、.fdf(固件描述)。它们共同决定了固件的组成、布局和编译方式。

4.3.1 .dec 文件(Package Declaration File)

.dec 文件定义了一个 Package 对外提供的接口,包括 GUID、Protocol、PPI、PCD 和 Library Class。它是 Package 的“头文件索引”。

# MdePkg/MdePkg.dec 示例片段
[Defines]
  DEC_SPECIFICATION              = 0x00010005
  PACKAGE_NAME                   = MdePkg
  PACKAGE_GUID                   = 1E73767F-8A52-4b2c-BF0C-DB83E8E2B7B1
  PACKAGE_VERSION                = 1.08

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

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

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

[Protocols]
  gEfiSimpleTextInProtocolGuid   = {0x387477C1, 0x69C7, 0x11d2, {0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B}}

4.3.2 .inf 文件(Module Information File)

每个 Module 必须有一个 .inf 文件,描述其源文件、依赖的 Library、定义的 GUID 以及编译选项。

# MdeModulePkg/Universal/DiskIoDxe/DiskIoDxe.inf 示例
[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = DiskIoDxe
  FILE_GUID                      = 6B38F7B4-AD98-4e3f-BD30-5DEEC0B2E5C0
  MODULE_TYPE                    = UEFI_DRIVER
  ENTRY_POINT                    = DiskIoDriverEntryPoint

[Sources]
  DiskIo.c
  DiskIo.h

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

[LibraryClasses]
  UefiDriverEntryPoint
  UefiLib
  MemoryAllocationLib
  BaseLib

[Protocols]
  gEfiDiskIoProtocolGuid         ## PRODUCES
  gEfiBlockIoProtocolGuid        ## CONSUMES

4.3.3 .dsc 文件(Platform Description File)

.dsc 文件是整个平台固件的“构建清单”。它指定了要包含哪些模块、使用哪些库实例、PCD 默认值以及全局编译选项。

# OvmfPkg/OvmfPkgX64.dsc 示例片段
[Defines]
  PLATFORM_NAME                  = Ovmf
  PLATFORM_GUID                  = 5a9e7754-8270-4b8e-9b0f-9f3d5f7b9c1a
  PLATFORM_VERSION               = 0.1
  DSC_SPECIFICATION              = 0x00010005
  OUTPUT_DIRECTORY               = Build/OvmfX64
  SUPPORTED_ARCHITECTURES        = X64
  BUILD_TARGETS                  = DEBUG|RELEASE
  SKUID_IDENTIFIER               = DEFAULT

[LibraryClasses]
  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
  # 平台特定库覆盖
  SerialPortLib|OvmfPkg/Library/SerialPortLib/SerialPortLib.inf

[Components]
  # 包含的模块列表
  MdeModulePkg/Universal/DiskIoDxe/DiskIoDxe.inf
  MdeModulePkg/Universal/ConSplitterDxe/ConSplitterDxe.inf
  NetworkPkg/TcpDxe/TcpDxe.inf
  OvmfPkg/PlatformDxe/PlatformDxe.inf

[PcdsFixedAtBuild]
  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000
  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase|0xFFE00000

4.3.4 .fdf 文件(Flash Description File)

.fdf 文件定义了固件镜像在 Flash 中的物理布局,包括 FV(Firmware Volume)、FFS(Firmware File System)以及各个模块在镜像中的偏移地址。

# OvmfPkg/OvmfPkgX64.fdf 示例片段
[FD.BFV]
BaseAddress   = 0xFFE00000
Size          = 0x00200000
ErasePolarity = 1

[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
WRITE_DISABLED_CAP = FALSE
WRITE_ENABLED      = TRUE
WRITE_STATUS       = TRUE
WRITE_LOCK_CAP     = TRUE
WRITE_LOCK_STATUS  = TRUE

  INF  OvmfPkg/Sec/SecMain.inf

[FV.DXEFV]
FvAlignment        = 16
ERASE_POLARITY     = 1

  INF  MdeModulePkg/Universal/DiskIoDxe/DiskIoDxe.inf
  INF  MdeModulePkg/Universal/ConSplitterDxe/ConSplitterDxe.inf
  INF  NetworkPkg/TcpDxe/TcpDxe.inf
注意: .dsc 决定“编译什么”,.fdf 决定“如何摆放”。两者必须保持一致:.dsc 中列出的模块,必须在 .fdf 的某个 FV 中有对应的 INF 条目,否则模块不会被包含进最终固件镜像。
EDK2 构建流程与文件关系 .dsc 平台描述 模块列表 / PCD / 库 .dec 包声明 GUID / 协议 / 库类 .inf 模块信息 源文件 / 依赖 / 入口 .fdf 固件布局 FV / FFS / 地址映射 EDK2 Build 引擎 BaseTools / make / Python 固件镜像 (.fd/.rom) 包含所有 FV 和 FFS

4.4 构建系统工作流程

EDK2 的构建过程可以概括为以下步骤:

  1. 解析 .dsc:读取平台描述文件,确定要编译的模块列表、库实例和 PCD 配置。
  2. 解析 .dec:加载所有依赖 Package 的接口声明,建立 GUID 和 Protocol 的映射表。
  3. 解析 .inf:对每个模块,读取其源文件列表、依赖库和编译选项。
  4. 编译:调用编译器(如 GCC、MSVC、ICC)编译每个模块的源文件,生成 .efi 文件。
  5. 链接与打包:根据 .fdf 定义的布局,将 .efi 文件打包成 FFS,再组合成 FV,最终生成完整的固件镜像 .fd 文件。
实践要点: 修改固件时,通常只需编辑 .dsc 添加新模块,并在 .fdf 的对应 FV 中添加 INF 引用。如果模块使用了新的 Protocol 或 PCD,还需确保对应的 .dec 文件已被包含。

4.5 常见问题与避坑指南

问题原因解决方案
编译报错:Module not found.dsc 中引用了不存在的模块路径检查模块路径是否正确,确认 .inf 文件存在
链接错误:Unresolved external symbol模块依赖的 Library 未在 .dsc 的 [LibraryClasses] 中声明在 .dsc 中添加对应的 Library 实例映射
固件无法启动:No FV found.fdf 中 FV 的 BaseAddress 或 Size 与硬件不匹配核对 Flash 数据手册,修正 .fdf 中的地址参数
Protocol 未安装模块的 .inf 中未正确声明 [Protocols] 依赖检查 .inf 的 [Protocols] 段,确保 ## PRODUCES / ## CONSUMES 标注正确