模块
- 包:是一组
模块及平台描述文件(.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.decDEC文件
每个
包只有一个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 = VALUESET 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.pngUNI文件
同一个字符串变量,可针对不同的语言定义不同的内容
#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和依赖库MdePkgModule 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编程实战》