2 如何使用PCD
PCD可以使用于UEFI存在的大部分时间,除了在SEC阶段、早期的PEI和DXE阶段,基本都可以访问。在使用前,我们需要搞清楚PCD的结构和类型。
2.1 PCD的类型
PCD变量的格式有点像结构体:
TokenSpaceGuidCName.PcdCName
其中,TokenSpaceGuidCName是GUID,而PcdCName是变量名,两者组合构成了一个PCD变量。
PCD有如下的类型。
FixedAtBuild类型
它在编译阶段确定,是静态值,在运行阶段或二进制形态下都不可改。可以认为它就是一个宏了。
FeatureFlag类型
它实际上和FixedAtBuild是同一类型,返回一个Bool类型(True或False),可用于判断条件。
PatchableInModule类型
此类型的变量值在编译的时候确定,它在编译后的二进制文件上使用工具修改。与FixedAtBuild不同,它只能影响一个模块(作用域在一个模块)。
Dynamic类型、DynamicHii类型和DynamicVpd类型
Dynamic类型变量的作用域是整个系统,它是动态的PCD,可以在UEFI运行过程中修改。
DynamicHii类型与Dynamic类型存储的位置不同,Dynamic类型可以认为是存在于Memory中,再加载是会失去原始设置的;而DynamicHii类型是存在Efi variable中的(NVRAM中),其修改时非易失性的。
而DynamicVpd类型变量是只读的,不可写的,一般出厂确定。
DynamicEx类型
与Dynamic类型类似,相当于加强版。其与Dynamic类型的区别,在于是否使用二进制文件中的PCD。比如FSP,如果要使用其中的PCD变量,则FSP中的PCD类型必须设置为### DynamicEx类型。
2.2 访问PCD变量
为管理PCD变量,PEI提供了PCD_PPI和EFI_PEI_PCD_PPI;DXE提供了PCD_PROTOCOL和EFI_PCD_PROTOCOL。
不过,为了方便使用,EDK2中引入了PCD Library,把这些访问细节隐藏了起来。(MdePkg\Include\Library\PcdLib.h)
库中包含如下函数:
PcdGetXX()
PcdSetXX()
PcdGetExXX()
PcdSetExXX()
PcdToken()
PCDSetSku()
PcdGetNextToken()
PcdGetNextTokenSpace()
CallBackOnSet()
CancelCallBack()
其中,XX可以为8、16、32、Size、Ptr或者Boolean。
2.3 PCD的声明和使用
PCD的使用,基本可以按照如下流程进行。
DEC文件中声明PCD变量的基本信息,比如:
[PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]
gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintTimes|1|UINT32|0x40000005
gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintString|L"UEFI Hello World!\n"|VOID*|0x40000004
其格式为:
TokenSpaceGuidCname.PcdCname|DefaultValue|DatumType|Token
如前所述,PcdCname为变量名,DefaultValue为其默认值,DatumType是PCD的数据类型,Token是一个32位的整型,在DEC中每个PCD都有一个独有的Token。
DatumType可以是BOOLEAN、UINT8、UINT16、UINT32、UINT64或VOID *型。
DSC文件中设置PCD变量的值
可以在DSC文件中设置相应PCD变量的值,比如:
[PcdsFixedAtBuild]
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x0f
此设置过程不是必须的,如果没有设置,则使用DEC文件中的默认值。
INF文件中声明
在模块的INF文件中,需要声明PCD变量,才可以在源码中使用。比如:
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintString
gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintTimes
只需要列出PCD变量名就可以了,其他信息不用列出。
完成上述工作后,就可以在源代码中,使用PCD库函数访问PCD变量了。示例如下:(摘自MdeModulePkg\Application\HelloWorld\HelloWorld.c)
if (FeaturePcdGet (PcdHelloWorldPrintEnable)) {
for (Index = 0; Index < PcdGet32 (PcdHelloWorldPrintTimes); Index ++) {
//
// Use UefiLib Print API to print string to UEFI console
//
Print ((CHAR16*)PcdGetPtr (PcdHelloWorldPrintString));
}
}