模块

模块与包概念
模块与包

  • 包:是一组模块平台描述文件(.dsc文件),包声明文件(.dec文件)组成的集合。
  • 模块可执行文件,即.efi文件)像插件一样可以动态地加载到UEFI内核中。
  • 每个成功模块由元数据文件(.inf)和源文件(有些情况包含.efi文件)组成。.inf类似与Linux下得Makefile.dsc文件则相当于VS项目中的.sln文件;模块相当于VS项目中的工程,.inf文件则相当于VS工程中的.proj文件

UEFI主要模块

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

DSC文件变量
DSC文件

示例

# 定义的全局宏,
[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-

语法格式

BuildOptions语法格式

FAMILY是指编译时使用的编译器

语法字段解释

语法字段

标准引用程序工程模块

工程模块的基础,

每个工程模块至少需要分为

  • 工程文件 : .inf文件
  • 源文件C/C++文件,.asm汇编文件,也包含.uni(字符串资源文件)和.vfr(窗口资源文件)

INF文件

INF是模块的工程文件,描述了模块的属性,包含模块由那些代码组成提供什么 ,依赖什么,支持什么CPU架构等信息。对ODM厂商(第三方开发者而言),可以针对自家设备发布二进制形式的模块,不必提供源代码。

一般来说,如果是提供库的模块,则其位于包的Library子目录下,并且会针对不同的架构再创建子目录划分;如果是UEFI_APPLICATION一般位于子目录Applications下。

INF文件的节标志

[Defines]

Define变量 Define变量

如果编译的模块为库模块,则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,X64IPF,EBC,ARMAARCH64中任何一个。如果需要对所有架构适用,可以使用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]

定义ProtocolGUID,其规则与[Guids]是一样的。

[Protocols.(Arch)]  # common,IA32,X64,IPF,EBC或者不指定,可添加Private
    ProtocolName = GUID

其余节

PPIPI阶段PEIM和PEIM之间的沟通桥梁,类似Dxe阶段Protocol 。一个PEIMInstall一个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的大小,单位为字节
  • BlockSizeFlash中一个Block的大小
  • ErasePolarity : 表示用1或者0擦除Flash,一般为1
  • NumBlocks : 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)所指明的内容,其中offsetSize表示其后的内容处于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.

资源文件

IDFUNI文件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: 复选框,可通过空格键或者回车键进行选择
  • 关键字formsetendformset成对出现,他们之间所包含的内容定义了整个formset
    • guid : 标志本formsetGUID值
    • 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 app
  • OvmfPkg: OVMF是用来给虚拟机提供UEFI支持的包,可以使用QEMUKVM来引导OVMF固件,并进一步引导HLOS
  • PcAtChipsetPkg符合PcAt标准器件的接口和实现
  • StdLib标准库UDK实现,StdLibPrivateInternalFiles包时用来给StdLib使用的,不能用作其他引用
  • UefiCpuPkg兼容UEFICPU模版

参考资料