【UEFI】--- 关于UEFI&PCD的总结介绍
1个人理解
个人理解PCD基本等同于Token,应当算是一种描述性语言,按照规定书写好PCD的配置档,在编译的过程中,会根据你的配置生成同等含义的C文档,而在C文档中对应会出现相应的define或者变量声明定义。下面通过具体的例子来说明PCD档的基本含义和使用方式以及注意事项:首先需要准备EDK的环境,具体到某个案子的代码也可以,因为UEFI的架构下,基本上都是使用EDK编译的。
2 实例介绍
先用一个实际的例子,让大家对于PCD有个直观的感受(以EDKII/MdeModulePkg/Application/HelloWorld为例)
2.1 MdeModulePkg/Application/HelloWorld.c的代码如下所示:
2.2 PCD在MdeModulePkg/MdeModulePkg.dec中的定义如下图所示:
/* PCD的TOKEN值*/
在上图的PCD的定义中,最后都有一些0x0001200a这样的值,这些都是TOKEN的值,它的主要作用是用来区分每个Token,给每个Token一个独立的编号,必须赋值,否则会编译失败。用户在DEC中定义PCD时会指定每个PCD的TOKEN值,编译器编译之后,在 AutoGen.h文件中可以发现TOKEN的值会被宏定义成下列的值,与用户指定的不太一样,这是因为编译器会自动为每一个PCD再次生成一个TOKEN值,以免由于用户方面操作不当,而导致TOKEN的值重复。
2.3 MdeModulePkg/Application/HelloWorld.inf配置档如下图所示:
一般看到的PCD定义如2.2中所示,PCD的定义一般是在*.dec的文件里进行第一次默认声明,比如:MdeModulePkg/Application/HelloWorld.c文档中所用到的这三个PCD就是在MdeMoulePkg/MdeMoulePkg.dec中进行声明。在PCD的定义中,一般需要关注的就是变量名,默认值,变量类型。
*.inf文档配置后可以在*.c文档中使用,之所以能使用,是因为在编译过程中,编译器会帮助生成宏定义或者变量声明的东西。在EDK的环境下直接编译HelloWorld例子,在build/MdeModulePkg/Application/HelloWorld/DEBUG文件夹下有AutoGen.h和AutoGen.c这两个文件,这两个文件中括了PCD的声明和定义,个人理解是编译器解析DEC和INF档中有关PCD的说明,接着首先生成中间文件AutoGen.h和AutoGen.c,接着HelloWorld.c负责去调用这两个文件中的内容。从而达到使用PCD的目的(个人理解,仅供参考)。
我将AutoGen.h和AutoGen.c文件中的内容做了一些截取,筛选出来了一些重要部分show给大家看。根据PCD最开始的描述,在相应的C文档中,都有其对应的宏定义和说明,我们的HelloWorld.c中最终所使用的PCD应该是也是来源于这两个中间文件,从这两个文件怎么与HelloWorld.c联系在一起,如果实现调用生成.efi目标程序,还需要再研究一下(未完待续)
3 PCD基本类型介绍总结
基本可以将PCD当作一个描述性语言,编译工具(例如AutoGen工具)在编译过程中会根据相应DEC文件或这DSC文件中的描述,进行编译然后在生成的AutoGen.h和AutoGen.c或者其他的.h与.C文件中,对每个PCD所表述的变量进行定义或者宏定义。
3.1 PCD涉及的基本文件类型
*.inf是编译某个模块的指导文件。
*.dsc是编译一个Package的指导文件。
*.dec中定义了公开的数据和接口,供其他Package中的某个Moudle使用。
3.2 PCD类别
EDK II中PCD根据其作用的时间,分为两大类:
3.1.1 在编译过程中起作用,
这类PCD等同C语言中的全局静态变量,包含FeatureFlag PCD, FixedAtBuild PCD以及 PatchableInModule PCD三种:
1)PcdsFeatureFlag:
表示一个Feature是On还是Off,Value一般是TRUE或者FALSE,该类型的PCD在编译时会赋予Const类型,定义在AutoGen.h文件中,多用来描述一些BOOL型的变量值。
如1.2实例所示:
在HelloWorld.inf文档中定义了如下的PCD
[FeaturePcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable
在最终生成的AutoGen.c和AutoGen.h中可以看到PcdHelloWorldPrintEnable的宏定义以及最终的赋值是赋了一个BOOL型的真值1,如下:
#define _PCD_VALUE_PcdHelloWorldPrintEnable ((BOOLEAN)1U)
2)FixedAtBuild:
与PcdsFeatureFlag类似,最大的不同在于它可以宣告多种不同的变量类型,不仅限于Bool型。可以是UINT32型的变量等.例如,我在MdeModulePkg/MdeModulePkg.dec中增添如下定义:
在其HelloWorld.inf中增添如下声明:
build过之后可以看到在AutoGen.c和AutoGen.h中有如下定义和声明:
3)PatchableInModule:
该种变量与上述最大的不同是在Runtime时可以去变动它的数值,除此之外,它将被宣告成一个Global变量,被宣告成该种变量同一时刻,仅有同一个Module可以存取,不能透过它与各Module相互传递数值。
3.1.2 在平台初始化过程中起作用
包含: DynamicDefaultPCD, DynamicHIIPCD, 和DynamicVpdPCD等几种:
1)DynamicDefault:
DynamicDefault形态的PCD的最大又是在于,各个Module之间可以借由这个PCD来互相传递数值。典型的例子:比如预先把某个不会变动的值从HW中读出来,并且存储到一个DynamicDefault类型的PCD中,这样其他的Module就可以不用再经过繁杂的动作去存取HW上的值,而是可以直接透过PCD读取就好。
PcdsDynamicExDefault:
多了Ex的PCD与上述DynamicDefault很相似,但如果一个Module不是同时间与整个Platform一起Build的,而是一个外部的Binary,Platform与Binary又需要互相存取某个PCD的数值的话,这时候一开始就应该把该PCD宣告成PcdsDynamicExDefault的形态,但是外部的Binary在使用该PCD的时候,必须先知道该PCD的TOKEN值才可通过LibPcdGetExXX或者LibPcdSetExXX来进行存取。
2)DynamicHIIPCD:
HII型態的PCD在DSC需定好它所對應的Variable Name、GUID、Offset(位於Variable Data的哪個Offset)還有Default Value。如果系統中不存在著該Variable,那麼就會從PCD取得DSC所定好的預設值,否則就會回傳該Variable的Data加上Offset。
DynamicExHIIPCD:
多了Ex的DynamicExHii則意思跟DynamicEx很相似。
3)PcdsDynamicVpd and PcdsDynamicExVpd:
VPD (Vital Product Data),在NVS會有一塊區域被用來儲存VPD的資料,與其它Dynamic PCD不同,VPD只能唯讀,無法在執行期間去修改它的數值。關於VPD的部分,目前我還沒有機會用到,故不多说
4 关于PCD默认值和重新赋值
PCD最开始声明定义的地方在*.dec文件中,如果直接使用的该PCD的话,那么编译器从*.dec取出该值作为默认值供使用,多数情况下,用户可能会不使用*.dec中所提供的默认值,那么就需要对默认值进行修改。如何修改呢?仍旧以HelloWorld为例进行说明。
如上图是在MdeModule.dec文件中对HelloWorld所用的PCD的默认定义,在进行编译之后,在AutoGen.h中生成的字符串数组如下如所示:假如你仍旧想使用这个PCD但是不想使用这个UEFI HELLO……的字符串,该如何做呢?
一般是在MdeModule.dsc文件中做如下修改,然后编译:
编译出来的Autogen.c文件中,可以看到原来的PCD所定义的字符串已经改为了你想要的,如下图:
以上是关于PCD的一些基本了解,以上内容为本人自己学习总结,仅供参考如有错误,请及时指出,谢谢!
目前还需要深入了解的是关于Dynamic类型PCD的定义,使用,传递,以后看代码的时候需多加注意。