一、PCD的定义及概念
在UEFI固件接口中,PCD(Platform Configuration Database)是一个用于存储和访问平台特定配置信息的机制。PCD允许UEFI驱动程序和应用程序在运行时获取和设置平台相关的参数,而无需硬编码这些值。PCD变量可以被动态的读取和修改。(类似于C语言中的宏而区别于宏)
PCD的种类可以分为两大类:
(1)在编译过程中起作用,包含PcdsFeatureFlag , PcdsFixedAtBuild 以及 PatchableInModule;其中FeatureFlag 类型PCD只能定义为Bool值,FixedAtBuild可以支持多种数据类型UINIT32、UINT8、VOID* 等,PcdsPatchableInModule类型PCD在编译阶段可以被GenPatchPcdTable修改其值,并且在运行时也可以改变其值(PatchableInModule 本质上就讲PCD存放在EFI module的data 段 )。
(2)在运行过程中起作用,这类PCD包括PcdsDynamicDefault、PcdsDynamicExDefault、 PcdsDynamicHii、PcdsDynamicExHii、PcdsDynamicVpd、PcdsDynamicExVpd、PcdsDynamic 、PcdsDynamicEx;
其中, PcdsDynamicDefault与PcdsDynamicExDefault在Runtime阶段可以被改变, 但是当内存掉电后change值将会丢失,格式如下:
不同类型的PCD在文件中对应的块名称不同,使用多种类型的PCD要分别在INF文件中对应的块中引用。
如果一个PCD被声明多种类型且在INF文件中引用时都放 [Pcd] 块中,编译工具会根据优先级决定PCD的类型:PcdsFixedAtBuild > PcdsPatchableInModule > PcdsDynamicDefault >
【PCD与宏的区别】
PCD用于存储和访问平台特定的配置信息。这些配置信息可以在固件映像构建时设置,也可以在固件运行时动态地读取和修改。PCD变量用于控制固件的行为,如硬件设置、性能选项、调试级别等。宏定义用于在编译时替换代码中的文本。宏定义通常用于代码重用、代码简化、避免重复代码编写等,一旦定义是不可变的。
二、PCD的简单使用
1、编写MyHelloWorldPCD.c
FeaturePcdGet宏是一个获取FeaturePcd变量的值的宏定义,使用了预处理器宏_PCD_GET_MODE_BOOL_,它将TokenName替换为FeaturePcd变量的Token Space和名称,其代码原型为:
/**Retrieves a Boolean PCD feature flag based on a token name.根据token名称检索布尔PCD特征标识Returns the Boolean value for the PCD feature flag specified by TokenName.If TokenName is not a valid token in the token space, then the module will not build.If TokenName is not a feature flag PCD, then the module will not build.@param TokenName The name of the PCD token to retrieve a current value for.@return Boolean value for the PCD feature flag.**/
#define FeaturePcdGet(TokenName) _PCD_GET_MODE_BOOL_##TokenName
利用PcdGet32(TokenName)获取32位PCD值,代码原型为
/**Retrieves a 32-bit PCD token value based on a token name.根据Token名称检索一个32位的PCD token值Returns the 32-bit value for the token specified by TokenName.If TokenName is not a valid token in the token space, then the module will not build.@param TokenName The name of the PCD token to retrieve a current value for.@return 32-bit value for the token specified by TokenName.**/
PcdGetPtr(TokenName)检索指向PCD token缓冲区的指针,代码原型为:
/**Retrieves a pointer to a PCD token buffer based on a token name.根据token名称检索PCD token缓冲区的指针Returns a pointer to the buffer for the token specified by TokenName.If TokenName is not a valid token in the token space, then the module will not build.@param TokenName The name of the PCD token to retrieve a current value for.@return A pointer to the buffer. //返回一个指向缓冲区的指针**/
#define PcdGetPtr(TokenName) _PCD_GET_MODE_PTR_##TokenName
完整代码为:
#include <uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/PcdLib.h>EFI_STATUS
EFIAPI
MyHelloWorldPCDEntry(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable
)
{ EFI_STATUS Status = EFI_SUCCESS;UINT32 PrintTimes ;UINT32 i ;CONST CHAR16 *PrintStr; // DEBUG ((EFI_D_ERROR , "[MyHelloWorldPCD] MyHelloWorldPCDEntry Start..\n"));Print(L"[MyHelloWorldPCD] MyHelloWorldPCDEntry Start..\n");if (!FeaturePcdGet(PcdMyHelloWorldPrintEnable)){ //PcdMyHelloWorldPrintEnable是一个token 名称,返回一个Boolean值Print (L"[MyHelloWorldPCD] PcdHelloWorldPrintEnable ..\n");PrintTimes = PcdGet32(PcdMyHelloWorldPrintTimes);for (i = 0; i < PrintTimes; i++){PrintStr = PcdGetPtr(PcdHelloWorldPrintString);Print (L"[MyHelloWorldPCD] Pcd Str = %s\n",PrintStr);}}// DEBUG ((EFI_D_ERROR , "[MyHelloWorldPCD] MyHelloWorldPCDEntry End..\n"));Print(L"[MyHelloWorldPCD] MyHelloWorldPCDEntry End..\n");return Status;
}
使用PCD要在INF文件中引用
#use to operate bool value
[FeaturePcd]gEfiMdeModulePkgTokenSpaceGuid.PcdMyHelloWorldPrintEnable ## CONSUMES[Pcd]gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintString ## CONSUMESgEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintTimes ## SOMETIMES_CONSUMES
使用PCD要在DEC文件中声明
...[PcdsFeatureFlag]gEfiMdeModulePkgTokenSpaceGuid.PcdMyHelloWorldPrintEnable|FALSE|BOOLEAN|0x0001200d//PcdMyHelloWorldPrintEnable是PCD变量的名称;FALSE是PCD变量的默认值;BOOLEAN是PCD变量的数据类型;0x0001200d是PCD token的值,用于在PCD数据库中表示和引用特定的PCD变量。[PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]# @Prompt HellowWorld print times.gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintTimes|3|UINT32|0x40000005# @Prompt HelloWorld print string.gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintString|L"UEFI Hello World!\n"|VOID*|0x40000004...
在DSC文件中修改(如果没有修改会使用DEC中默认的PCD值)
[...Pcd...]PcdTokenSpaceGuidName.PcdTokenName | Value [ | DatumType[ |MaximumDatumSize ] ]
运行代码生成EFI文件,并将efi文件拷贝到虚拟盘HDD_BOOT.img中运行,运行结果如下:
参考文章
EFI 基础教程 (八)- PCD 简单使用
PCD配置和使用