# 模块
- 包:是一组
模块及平台描述文件(.dsc文件),包声明文件 (.dec文件) 组成的集合。 模块(可执行文件,即.efi文件) 像插件一样可以动态地加载到UEFI内核中。- 每个成功模块由
元数据文件(.inf) 和源文件(有些情况包含.efi文件) 组成。.inf类似与Linux下得Makefile,.dsc文件则相当于 VS 项目中的.sln文件;模块相当于 VS 项目中的工程,.inf文件则相当于 VS 工程中的.proj文件。
# UEFI 主要模块
# DSC 文件
DSC文件描述了模块,库和组件如何编译,其中还包含很多的节(section)标志,包含必要的[Defines],[Components], 和可选的[LibraryClasses],[Libraries],[SkuIds],[BuildOptions],[PCD],[UserExtensions],[DefaultStores]。所有节标志,都内置与中括号中,且大小写敏感。在同一个括号中,可以包含复数的节标志字符串,他们之间使用逗号隔开,注释使用#
# 使用逗号隔开 | |
[Library.X64,LibraryClasses.IPF] | |
# 其制定CPU架构的节,通常的节(如[LibraryClasses]有更高的优先级优先使用高优先级编辑) | |
# 在DSC文件中,使用!include来包含其他的文件,!include可以在任何节中出现 | |
[components] | |
...... | |
!include StdLib/StdLib.inc #包含StdLib库 |
# [Defines]
此节定义
各种变量,以供后续编译使用,必须在DEC中第一个定义,通过
DEFINE定义的宏都是全局的,都可以使用$(MACRO)来访问语法如下
[Defines]中可配置PCD信息,PCD:Platform Configuration Database, 数据库,类似window的注册表。PCD 除了SEC早期,PEI阶段,DXE早期阶段外,都可以访问。
[Defines] | |
Name = Value | |
DEFINE MACRO = Value |
# 示例
# 定义的全局宏, | |
[Defines] | |
PLATFORM_NAME = AppPkg | |
PLATFORM_GUID = 0458dade-8b6e-4e45-b773-1b27cbda3e06 | |
PLATFORM_VERSION = 0.01 | |
DSC_SPECIFICATION = 0x00010006 | |
OUTPUT_DIRECTORY = Build/AppPkg | |
SUPPORTED_ARCHITECTURES = IA32|IPF|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 | |
DEFINE UEFI_BOOK_DIR = uefi\book |
# [ LibraryClasses ]
用来
提供模块所使用的库入口,而且它允许将模块编译成库,这些库可以被 [Components] 中的模块使用,当DSC文件中的模块不需要使用库时,这个节也可以不设置,可选的。
# 语法格式
$(Arch)和$(MODULE_TYPE)是可选的,节内的库对指定的结构和模块都有效。
[LibraryClasses.$(Arch).$(MODULE_TYPE),LiBraryClasses.$(Arch).$(MODULE_TYPE)] | |
LibraryName | Path/LibraryName.inf |
LibraryClasses有六种表示方法,按照模块优先搜索的顺序,从高到低
<LibraryClasses> | |
[LibraryClasses.$(Arch).$(MODULE_TYPE),LibraryClasses.$(Arch).$(MoDULE_TYPE)] | |
[LibraryClasses.$(Arch).$(MODULE_TYPE)] | |
[LibraryClasses.common.$(MODULE_TYPE)] | |
[LibraryClasses.$(Arch)] | |
[LibraryClasses.common] |
MdeModulePkg/Universal/PCD/Pei/Pcd.inf { | |
<LibraryClasses> | |
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf | |
} | |
# 直接制定目前编译模块所需要的库,此方法优先级最高 |
[
Components] 中的模块在寻找所需要的库时,将按照上面优先级依次寻找
# [ Components ]
用来定义模块编译的
节,通过制定模块的INF文件所在的位置,Build工具可以编译生成.efi文件。
[Components.$(Arch)] | |
Path/and/Filename.inf | |
# 语法格式 | |
[Components.$(Arch)]{ | |
Path/and/Filename.inf{ | |
<LibraryClasses> # 嵌套节 | |
LibraryName | Path/LibraryName.inf | |
# 还可以嵌套<Defines>,<PCD*>和<BuildOptions> | |
} | |
} |
# [ BuildOptions ]
给出编译器和相关的
编译参数,它会覆盖为编译模块准备的默认参数。如果是为了替换编译参数,则可以使用==。如果是为了添加编译参数,则可以使用=
# 语法格式
[BuildOptions] | |
${FAMILY}:${TARGET}_${TAGNAME}_{ARCH}_${TOOLCODE}——FLAGS[= | == ] 编译参数 |
# 示例
编译参数定义宏
DISABLE_NEW_DEPRECATED_INTERFACES, 作为源代码的编译开关。两个参数分别禁用优化和启用框架指针省略功能。
[BuildOptions] | |
*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTEREACES | |
MSFT:DEBUG_*_*_CC_FLAGS = /Od / Oy- |
# 语法格式
FAMILY是指编译时使用的编译器。
# 标准引用程序工程模块
工程模块的基础,
每个工程模块至少需要分为
工程文件 :.inf文件源文件:C/C++文件,.asm汇编文件,也包含.uni(字符串资源文件) 和.vfr(窗口资源文件)
# INF 文件
INF是模块的工程文件,描述了模块的属性,包含模块由那些代码组成,提供了什么,依赖什么库,支持什么CPU架构等信息。对 ODM 厂商 (第三方开发者而言),可以针对自家设备发布二进制形式的模块,不必提供源代码。一般来说,如果是提供
库的模块,则其位于包的Library子目录下,并且会针对不同的架构再创建子目录划分;如果是UEFI_APPLICATION一般位于子目录Applications下。
# [Defines]
如果编译的模块为
库模块,则LIBRARY_CLASS变量必须制定,生成的库模块在指定运行哪些类型的模块时使用
LIBRARY_CLASS = FOO | PEI_CORE PEIM | |
LIBRARY_CLASS = BAR | DXE_CORE DXE_DRIVER DXE_SMM_DRIVER | |
# 如果只想在UEFI应用中使用,则可以如下设置 | |
LIBRARY_CLASS = FOO | UEFI_APPLICATION |
# [Sources]
列出模块中所有的
源文件和资源文件,这些文件位于INF文件所在的目录或者子目录。这个节可以针对不同的架构指定文件.
$(Arch)可以是COMMON,IA32,X64,IPF,EBC,ARM或AARCH64中任何一个。如果需要对所有架构适用,可以使用COMMON或者不指定任何架构。
[Sources.$(Arch)] | |
SourceCode.c |
同时可以
对源文件制定编译的工具链,即只有在使用指定的工具链时,此源文件才会被编译,目前常用的4种工具分别是MSFT(微软的Visual Studio编译器),GCC(GNU GCC编译器),INTEL(Intel C 编译器和ntel EFI字节码编译器) 和RVCT(ARM RealVIew工具链)
[Sources.ARM] | |
GicV3/Arm/ArmGicV3.S | GCC | |
GicV3/Arm/ArmGicV3.asm | RVCT |
# [ BuildOptions ]
INF中的与DSC文件中的语法格式基本相同,区别在与INF文件只对本模块有效。而DSC对所有模块都有效。日常开发中,通过 INF 文件修改编译选项以解决一些
特别问题。比如在解决汉字字符串显示问题时,强制要求编译器把源文件按UTF-8编码进行识别
[BuildOptions] | |
MSFT:*_*_*_CC_FLAGS = /utf-8 |
# [Protocols]
列出
模块使用的协议,在INF文件中列出的是协议的GUID,通过EDK2的分析工具,GUID被输出到模块的AUtoGen.c,如果模块没有使用任何协议,则这个节为空
[Protocols.$(Arch)]#COMMON,IA32,X64,IPF,EBC或者不指定 | |
gEfiProtocolGuid [ | FeatureFlagExpression ] | |
# 当FeatureFlagExpression为true时所添加的Protocol Guid是有效的,当为FALSE是,会忽略Protocol Guid |
# [ LibraryClasses ]
列出
本模块需要链接的库
[LibraryClasses.$(Arch)] #COMMON,IA32,X64,IPF,EBC或者不指定
LibraryClassNamel [ | FeatureFlagExpression ]
在日常开发中,模块如果要
添加库,一般需要进行两个步骤,
- 在
INF文件下的[LibraryClasses]中添加库名- 在
DSC文件的[LibraryClasses]中寻找词库,如果有,则需要添加编译此库的INF文件
[LibraryClasses] | |
UefiApplicationEntryPoint | |
UefiLib |
# [Packages]
列出
本模块引用的所有包DEC文件
[Packages.$(Arch)] # COMMON,IA32,X64,IPF,EBC或者不指定,针对不同的平台架构 | |
MdePkg/MdePkg.dec |
EDC文件的目录使用的是相对路径,其根位置($(WORKSPACE)(通过edksetup.bat/edksetup.sh指定的工作目录))。DEC文件的指定是有顺序的,比如MdePkg/MdePkg.dec必须在MdeModulePkg/MdeModulePkg.dec之前.
[Packages] | |
MdePkg/MdePkg.dec | |
MdeModulePkg/MdeModulePkg.dec | |
[Package.IA32] | |
DEFINE CPUS = IA32FamilyCpuPkg | |
$(CPUS)/DualCore/DualCore.dec |
# DEC 文件
每个
包只有一个DEC文件,DEC文件用来配合DSC文件,描述了包的公开数据和接口
# [Defines]
必须的节,用来提供包的GUID,版本和名称等信息与DSC相同
# [Includes]
列出本包提供的
头文件所在目录,此节DSC文件中没有对应语法结构
[Includes.$(Arch)] # COMMON,IA32,X64,IPF,EBC或者不指定,可添加Private | |
Path |
头文件的路径是
相对路径,其根目录为DEC所在的目录指定框架的时候,可以添加
Private限定符,用来规定所包含的头文件只能在本包中的模块中使用
[Include.common] | |
[Include.common.Private] | |
[Include.IA32.Private] |
不带Private标志的项,严禁与带Private标志的项结合同一文件目录,不能同时指定带Private标志的项和不带Private标志的项
# [ LibraryClasses ]
对外提供的库中都会提供头文件,这些头文件位于包下的 ``Include\Library目录下,用来明确库 和头文件的对应关系
[LibraryClasses.$(Arch)] # COMMON,IA32,X64,IPF,EBC或者不指定 | |
LibraryClassesName | PAth/LibraryHeader.h |
# [ Guids ]
这个
节用于定义Guid变量,对于Private标志的要求[Includes]相同
[Guids.$(Arch)] # COMMON,IA32,X64,IPF,EBC或者不指定,可添加Private | |
GUIDName = GUID |
# 举例
## Include/Guid/GlobalVariable.h | |
gEfiGlobalVariableGuid = { 0x8BE4DF61, 0x93CA, 0x11D2, { 0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C }} | |
## Include/Guid/PcAnsi.h | |
gEfiVT100PlusGuid = { 0x7BAEC70B, 0x57E0, 0x4C76, { 0x8E, 0x87, 0x2F, 0x9E, 0x28, 0x08, 0x83, 0x43 }} |
# [Protocols]
定义 Protocol 的 GUID ,其规则与 [Guids] 是一样的。
[Protocols.(Arch)] # common,IA32,X64,IPF,EBC或者不指定,可添加Private | |
ProtocolName = GUID |
# 其余节
PPI是PI阶段PEIM和PEIM之间的沟通桥梁,类似Dxe阶段的Protocol。一个PEIM中Install 一个PPI后,另一个PEIM通过Locate获取该PPI。
[Ppis]: 用于源文件用到的PPI, 语法与[Guids]类似[PCD]:是对DSC文件[PCD]的补充[UserExtensions]:可以定制用户的命令
# FDF 文件
Flash Description File用于描述固件在Flash中的布局和位置,这些固件是与UEFI/PI兼容的二进制镜像。一般生成固件的源码只有一个FDF文件,其作用是规定把那些包编入Flash中,并确定编入的位置
FDF文件用于生成Option ROM镜像,固件镜像和可启动镜像,它与DSC文件和二进制文件配合,在GenFW工具的协助下生成镜像。
# [Defines]
可选的节,用来个
跟踪FDF文件的版本,定义全局宏以及设定PCD的值
[Defines] | |
Name=Value | |
DEFINE MACRO = Value |
其中
set语句专门用来对PCD变量进行赋值的
# [FD]
FD(Firmware Device) 即固件设备,一个BIOS ROM就是一个FD, 这个节在开发平台 Flash 时是必须得,开发Option ROM时不需要此项。FD 由各类声明和 FD 区域布局组成,它可以构成一个完整的Flash设备镜像。Flash 设备可以是移动式可启动镜像(比如可启动的 U 盘),系统Flash镜像(如 BIOS ROM) 或更新镜像(UEFI 中称为 Capsule 镜像),用来升级系统Flash
# 令牌声明 (TOKEN Statements) :
Token = Value [| PcdName] |
BaseAddress: FD 的基址,设备开机后BIOS被加载到系统中的位置Size:FD的大小,单位为字节BlockSize:Flash中一个Block的大小ErasePolarity: 表示用1或者0擦除 Flash, 一般为1NumBlocks: Flash 中 Block 的个数
# 定义声明
DEFINE Statements: 用来定义宏的声明,所定义的宏在整个FDF文件中都有效,使用的时候,可以通过$(MACRO)来引用
DEFINE MACRO = PATH |
# 设置声明
设置
声明用来设置PCD变量的值语法格式
SET PcdName = VALUE |
SET gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase = $(MEMFD_BASE_ADDRESS) + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfWorkAreaBase + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfConfidentialComputingWorkAreaHeader | |
SET gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize = gUefiOvmfPkgTokenSpaceGuid.PcdOvmfWorkAreaSize - gUefiOvmfPkgTokenSpaceGuid.PcdOvmfConfidentialComputingWorkAreaHeader |
除
类声明外,[FD]的另外一个组成部分是区域布局(Region Layout),它用来指明各种区域类型的数据在FD中的布局
offset|Size | |
[TokenSpaceGuidCName.PcdOffsetCname | TokenSpaceGuidCName.PcdSizeCName] ? | |
[RegopmType] ? |
上诉代码的含义为
FD开辟一段空间,用来放置区域类型 (RegionType) 所指明的内容,其中offset和Size表示其后的内容处于FD中的偏移和内容的大小,RegionType可以是FV(Firmware Volume,固件区块),DATA(数据),FILE(文件),INF(INF 文件) 和CAPSULE(更新固件的镜像) 也可以不指定
0x000000|0x006000 | |
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize | |
0x006000|0x001000 | |
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize |
# [ FV ]
FV(Firmware Volume) 是固件的逻辑区块,相当于FD上的分区,此节定义了镜像包含的组件和模块,对于平台镜像是必要的,但Option ROM镜像不需要此节
[FV.UiFvName] |
# [ OptionRom ]
此节
用来编译独立的Legacy PCI Option ROM或者UEFI PCI Option ROM.
# 资源文件
IDF和UNI文件VFR文件可算做资源文件,用于描述图像,文字和框架等资源,在Build工具的协助下,生成二进制文件 (如.efi文件,.lib文件)
IDF(Image Description File,图像描述文件),UNI(Unicode String File,宽字节字符串文件) 和VFR(Visual Forms Representation,可视化窗体描述) 都是资源文件,同属于用户接口组件。
IDF文件用来描述图像资源,UNI文件用来描述字符串资源,VFR文件用来描述窗体资源,类似与 Windows 操作系统下的窗口。
# IDF 文件
在
IDF文件中,可通过#image标识符指定图像文件,所给出的资源文件一般与IDF文件在同一个目录下。使用
TRANSPARENT用来指定是否使用透明显示。
#image IMG_LOGO TRANSPARENT Logo.bmp | |
#image IMG_FULL_LOGO Logo.jpg | |
#image IMG_OEM_LOGO Logo.png |
# UNI 文件
同一个字符串变量,可针对不同的语言定义不同的内容
#langdef en-US "English" | |
#langdef zh-Hans "简体中文" | |
#string STR_LANGUAGE_SELECT #language en-US "Select Language" | |
#language zh-Hans "选择语言" |
在 UNI 文件中,可以使用的标识符有
#langdef,#string,#language。
#langdef用于声明本字符串资源文件所支持的语言#string用于定义字符串#language用于标注所用的语言
#langdef和#language标识符在使用时,需要指定语言代码 (Language Code),en-US代表的是英文
# VFR 文件
VFR文件用来描述窗体的框架文件,它可以使用#define和#include来定义变量和包含的头文件
formset(窗体集合):用来标志整个窗体的结构
form:窗体,标志整个窗体的结构checkbox:复选框,可通过空格键或者回车键进行选择
- 关键字
formset和endformset成对出现,他们之间所包含的内容定义了整个formset。guid: 标志本formset的GUID值title: 在界面中标志本formset的字符串标题help: 在界面上显示本formset的帮助信息classguid: 本formset所挂载界面的GUID值varstore: 变量所用数据结构类型form:窗体关键字,与endform成对出现,定义窗体的结构checkbox:复选框关键字,与endcheckbox成对出现,定义复选框供用户使用。
///** @file | |
// | |
// File Explorer Formset | |
// | |
// Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR> | |
// SPDX-License-Identifier: BSD-2-Clause-Patent | |
// | |
//**/ | |
#include "FormGuid.h" | |
formset | |
guid = EFI_FILE_EXPLORE_FORMSET_GUID, | |
title = STRING_TOKEN(STR_FILE_EXPLORER_TITLE), | |
help = STRING_TOKEN(STR_NULL_STRING), | |
classguid = EFI_FILE_EXPLORE_FORMSET_GUID, | |
form formid = FORM_FILE_EXPLORER_ID, | |
title = STRING_TOKEN(STR_FILE_EXPLORER_TITLE); | |
label FORM_FILE_EXPLORER_ID; | |
label LABEL_END; | |
endform; | |
form formid = FORM_ADD_NEW_FILE_ID, | |
title = STRING_TOKEN(STR_ADD_NEW_FILE_TITLE); | |
string | |
prompt = STRING_TOKEN(STR_NEW_FILE_NAME_PROMPT), | |
help = STRING_TOKEN(STR_NEW_FILE_NAME_HELP), | |
flags = INTERACTIVE, | |
key = NEW_FILE_NAME_ID, | |
minsize = 2, | |
maxsize = 20, | |
endstring; | |
subtitle text = STRING_TOKEN(STR_NULL_STRING); | |
text | |
help = STRING_TOKEN(STR_CREATE_FILE_AND_EXIT), | |
text = STRING_TOKEN(STR_CREATE_FILE_AND_EXIT), | |
flags = INTERACTIVE, | |
key = KEY_VALUE_CREATE_FILE_AND_EXIT; | |
text | |
help = STRING_TOKEN(STR_NO_CREATE_FILE_AND_EXIT), | |
text = STRING_TOKEN(STR_NO_CREATE_FILE_AND_EXIT), | |
flags = INTERACTIVE, | |
key = KEY_VALUE_NO_CREATE_FILE_AND_EXIT; | |
endform; | |
form formid = FORM_ADD_NEW_FOLDER_ID, | |
title = STRING_TOKEN(STR_ADD_NEW_FOLDER_TITLE); | |
string | |
prompt = STRING_TOKEN(STR_NEW_FOLDER_NAME_PROMPT), | |
help = STRING_TOKEN(STR_NEW_FOLDER_NAME_HELP), | |
flags = INTERACTIVE, | |
key = NEW_FOLDER_NAME_ID, | |
minsize = 2, | |
maxsize = 20, | |
endstring; | |
subtitle text = STRING_TOKEN(STR_NULL_STRING); | |
text | |
help = STRING_TOKEN(STR_CREATE_FOLDER_AND_EXIT), | |
text = STRING_TOKEN(STR_CREATE_FOLDER_AND_EXIT), | |
flags = INTERACTIVE, | |
key = KEY_VALUE_CREATE_FOLDER_AND_EXIT; | |
text | |
help = STRING_TOKEN(STR_NO_CREATE_FOLDER_AND_EXIT), | |
text = STRING_TOKEN(STR_NO_CREATE_FOLDER_AND_EXIT), | |
flags = INTERACTIVE, | |
key = KEY_VALUE_NO_CREATE_FOLDER_AND_EXIT; | |
endform; | |
endformset; |
# EDK2 目录介绍
AppPkg:UEFI Application Development Kit是一系列用来开发UEFI APP开发的套件,标准依赖库,工具以及demo,目标是降低UEFI app的开发框架ArmPkg: 提供ARM框架相关的 Protocols, 属于ARM平台上的通用代码ArmPlatformPkg:ARM开发板相关的UEFI代码,包含ARM平台上通用的一些组件,重复利用这些组件会令 ARM 平台不用的版型之间的移植变得更加容易BaseTools: 提供了编译EDK2的相关工具:AutoGen,Build,GenSec,GenFV,GenFW,GenRds 工具。Conf:CryptoPkg:UEFI定义了HLOS(high level OS)和平台固件之间的接口,多个安全特性也再去其中,用来提供加密支持EmbeddedPkg: 为memory mapped controllers提供protocol实现,是一个简单的 EFI shell(EBL)EmulatorPkg:Emulator虚拟环境,用来替代 Nt32Pkg 和 UnixPkg,k 可以跨平台编译FatPkg:FAT支持包MdeModulePkg: 此包提供符合UEEI/PI工业标准的模版,也提供标准相关的开发环境,PPIs/PROTOCOLs/GUIDS和依赖库MdePkg: 全程Module Development Environment Package, 此为特殊的Package,包含了用于开发module所需要的最小环境。一个module可能也会依赖于其他的Package,但是所有 modules 必须依赖于MdePkg.NetworkPkg: 提供网络支持的包,比如:IPV6 网络协议栈 / IPsec 驱动 / ISCSI 驱动 / 网络配置先关的shell appOvmfPkg:OVMF 是用来给虚拟机提供UEFI支持的包,可以使用QEMU和KVM来引导OVMF固件,并进一步引导HLOSPcAtChipsetPkg: 此包提供了符合PcAt标准器件的接口和实现StdLib: 提供了标准库UDK实现,StdLibPrivateInternalFiles包时用来给StdLib使用的,不能用作其他引用UefiCpuPkg: 提供兼容UEFI的CPU模版和库
# 参考资料
- UEFI 原理与编程 - 第三章节内容整理
- 《UEFI 编程实战》