PCD相关知识

2023-11-21 04:40
文章标签 知识 相关 pcd

本文主要是介绍PCD相关知识,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目标:PCD的工作原理,以及在编译过程中对PCD的处理流程。

输出:PCD的学习笔记

1:UEFI中PCD基础知识

        要明白PCD的工作原理,首先要对PCD有一个全面的了解,接下来讲解PCD相关内容,如有不对的地方,请及时指正,谢谢!!!

        1.1 PCD的定义和作用PCD(Platform Configuration Database),是一个UEFI下可访问的数据库,EDK2用来进行全局配置,PCD是把代码里面的可配置选项抽取出来,platform需要修改的时候,可以不用去修改源代码。其参数的配置,可以在编译过程中、运行时中都可以进行,甚至在二进制文件中也可以配置。降低了代码维护的工作量,增加了可复用性。

        使用时机:PCD可以使用于UEFI存在的大部分时间,除了在SEC阶段、早期的PEI和DXE阶段,基本都可以访问。在使用前,我们需要搞清楚PCD的结构和类型。

        1.2 PCD变量的格式:定义如下代码所示

TokenSpaceGuidCname.PcdCname|DefaultValue|DatumType|Token

        PcdCname为变量名,DefaultValue为其默认值,DatumType是PCD的数据类型,Token是一个32位的整型,在DEC中每个PCD都有一个独有的Token。DatumType可以是BOOLEAN、UINT8、UINT16、UINT32、UINT64或VOID *型。使用流程见1.6

        1.3 PCD变量的类型:PCD类型和描述如图1、按照模块级别和Boot阶段是否可修改分类如图2所示。

        图1 PCD类型和描述

分类是否可修改备注
FixedAtBuild类型
在运行阶段或二进制形态下都不可改。编译阶段确定,是静态值
FeatureFlag类型
不可改它实际上和FixedAtBuild是同一类型,它的值只能是Boolean的TRUE或者FALSE
PatchableInModule类型
在编译的时候确定,它在编译后的二进制文件上使用工具修改。与FixedAtBuild不同,它只能影响一个模块(作用域在一个模块)。
Dynamic类型
可以在UEFI运行过程中修改。作用域是整个系统,Dynamic类型可以认为是存在于Memory中,再加载是会失去原始设置的
DynamicHii类型存在Efi variable中的(NVRAM中)其修改时非易失性的。
DynamicVpd类型只读的,不可写的一般出厂确定。
DynamicEx类型
可修改与Dynamic类型类似,相当于加强版,其与Dynamic类型的区别,在于是否使用二进制文件中的PCD

图2 按照模块级别和Boot阶段是否可修改分类

        如图2所示,所谓模块级别就是在不同的模块中可以有不同的值。在Boot阶段不可修改类似于宏定义或者用const修饰的变量;

        PatchableInModule类型的PCD值在Boot阶段可以修改,可以用于以二进制形式发布的模块中,PatchableInModule PCD在编译完成后的二进制文件上是可以用特殊的办法更的;                

        Dynamic :动态的PCD它的特点是可以在UEFI运行的过程中通过Set宏来修改值;DynamicDynamicEx是系统级的、动态的,在整个boot过程中可以被修改,同时在整个系统中都能生效。DynamicEx类的PCD主要用在二进制文件中使用到PCD的情况,可以实现跨平台访问和修改以二进制发布的驱动中的PCD的值,只知道DynamicExDynamic的加强版,如果在代码中没有二进制形式,或者二进制文件没有用到PCD,那么这里使用的PCD 类型就是Dynamic的,但是如果是用binary方式集成进来了,要使用二进制中的PCD就必须要用DynamicEx类型的PCD,用PcdGetEx/PcdSetEx来访问变量;需要注意的是上面的类型并不是在一个SPEC中定义的,前面的4中是满足EDKII规范,而最后一个满足的是PI规范。

        Dynamic 有三个子类DynamicHIIDynamicVpdDynamicExDynamicHII是放在EFI variable(NVRAM中)的,修改是非易失性的,就是set了以后下次加载还是这次set的值。DynamicVpd ReadOnly,不可写。存放系统的default section,厂商的出厂设置就放在这里,是必不会也不可被修改的。

        PCD根据存储方式分类:

                a)默认存储:PcdsDynamicDefault和PcdsDynamicExDefault

                b)变量存储:PcdsDynamicHii和PcdsDynamicExHii

                c)OEM指定的存储区域:PcdsDynamicVpd和PcdsDynamicExVpd

        1.4 PCD的数据类型:PCD的数据类型有BOOLEAN、UINT8、UINT16、UINT32、UINT64或VOID *型。C结构体或者数组

        1.5 PCD使用周期:在诸如SEC阶段,以及PEI、DXE阶段的早期,某些PCD相关的模块还没有加载起来之前,这些类型的PCD不能访问。

        1.6 PCD使用流程:PCD在Dec文件中声名、INF文件引用、Dsc文件中修改、在C中修改和访问。使用PCD的流程如图3所示。步骤如下所示。

图3 使用流程  

        1.6.1 PCD在Dec文件中声名如图4所示。


        图4 Dec文件中声名

        1:PcdTokenSpaceGuidName可以使用Dec文件中现有的,也可以自己声明一个。在 [Guids] 块下声明,大括号中的内容是唯一不重复的Guid;

        2:PCD 由 :TokenSpaceGuid 和 TokenNumber (就是上面标红的)唯一确定,与TokenName无关;

        3:TokenName (与PcdTokenSpaceGuidName一起)在获取PCD的值的时候使用;

        4:图中的[Pcds...] 指出了该PCD支持的类型,可以用FixedAtBuild,FeatureFlag,PatchableInModule,Dynamic 以及 DynamicEx 中的任意一种代替,也可以是除了 FeatureFlag 之外的多个组合在一起并使用 ’,’ 进行分隔;

        5:DatumType 是PCD数据类型,在上述1.4小节已经讲述过。

        6:声明后,只有Value是可以在DSC中更改的,其他的都不可以;

        7:PcdTokenSpaceGuidName 类似于C++ 中的命名空间,在不同的 PcdTokenSpaceGuidName 下,PcdTokenName 可以相同。

       

        1.6.2 INF文件引用: 

PCD类型:INF文件中块名称:
PcdsFeatureFlagFeaturePcd
PcdsFixedAtBuildFixedPcd
PcdsPatchableInModulePatchPcd
PcdsDynamicPcd
PcdsDynamicExPcdEx

图5 INF文件引用

         只需要列出PCD变量名就可以了,其他信息不用列出。

        注意:不同类型的 PCD 在 文件中对应的块名称不同,使用多种类型的 PCD 要分别在 INF 文件中对应的块中引用。不同类型的 PCD 如图6所示。

图6  不同类型的 PCD

        例如在MdeModulepkg/Application/HelloWorld/HelloWorld.inf中为Pcd块,如图7所示。

图7 Pcd块

        此时可以设置为PcdsDynamicDefault等类型

        用DEBUG ((EFI_D_ERROR, "[beni]Version: 0x%x.\n", PcdGet32(PcdOemVersion)));要使用PcdGet32这种的宏,需要包含PcdLib这个库。还需要在inf中包含该PCD。

        注意: 如果一个PCD被声明多种类型且在INF文件中引用时都放 [Pcd] 块中,编译工具会根据优先级决定PCD的类型:PcdsFixedAtBuild > PcdsPatchableInModule > PcdsDynamicDefault > PcdsDynamicExDefault。

        

        1.6.3 Dsc文件中修改。如图8所示。例子EmulatorPkg/EmulatorPkg.dsc如图9所示

图8 Dsc文件中修改

        图9 例子

        在 DSC 文件中对 PCD做初始化,如果没有初始化则使用 DEC 文件中默认的 PCD 值。

        1.6.4 在C中修改和访问。 PcdLib 提供了接口用于 PC的 的读取和修改。PcdGetXX() 和 PcdSetXX() 可以用于任何 PCD 类型(XX:8、16、32、Size、Ptr、Boolean)。PcdGetXX() 用于根据 PCD Name 获取 PCD 值,就是 TokenNumber;PcdSetXX() 用于根据 PCD 的名称重新设置 PCD 的。

        注意:

2:PCD工作原理总结

        根据1中关于PCD的介绍,现在总结一下,

        在UEFI (统一的可扩展固件接口)中,PCD (平台配置数据)是一种机制,用于管理和访问系统固件中的配置信息。PCD包含了多个键值对,用于记录各种硬件和软件的配置数据。

        PCD的工作原理如下:

        1. 定义PCD:在系统固件中,通过编写配置文件,定义所需的PCD。配置文件中包含每个PCD的标识符、类型、默认值等信息。(Dec中)

        2. 加载PCD:在系统启动过程中,固件读取配置文件,并加载PCD到内存中。加载时,固件会根据配置文件的定义,为每个PCD分配内存空间,并将默认值写入。

        3. 访问PCD:在运行时,操作系统和驱动程序可以通过固件提供的接口来访问PCD。接口可以提供读取和写入PCD的功能。(上述的PcdGetXX() 或者PcdSetXX()

        4. 修改PCD:操作系统和驱动程序可以通过接口修改PCD的值。这样可以动态地调整配置信息,以满足系统需求。(Dsc中)

        5. 运用PCD:操作系统和驱动程序可以使用PCD中保存的配置信息来进行各种操作。例如,根据PCD中的硬件配置信息,决定设备的初始化方式。

3:编译过程中对PCD的处理流程总结

        此部分讲解主要针对四个例子,分别是FixedAtBuild PCD、FeatureFlag PCD、Dynamic PCD、PatchableInModule PCD。

        3.1:FixedAtBuild PCD,声名如图10、使用这个FixedAtBuild PCD,在源文件中DEBUG打印,如图11,再查看该模块编译目录中的AutoGen.c文件如图12,AutoGen.h分析如图13。

图10 声名

图11 DEBUG打印

图12 AutoGen.c

图13 AutoGen.h

        如图11,我们使用的是PcdGet8 (PcdTestVar1));将它展开后的结果就是_PCD_GET_MODE_8_PcdTestVar1,它在上述的如图13的AutoGen.h就已经定义了,最终的值就是0xA5(图10中的)。也就是说,对应FixedAtBuild PCD来说,它就是在编译的时候通过宏的方式生成的。也因此它是固定不变的一个值。

        3.2 FeatureFlag PCD:前面已经讲述了这个,这个相当于类型是BOOELAN的FixedAtBuild PCD。注意访问这个变量可以使用两种方式,如下所示。

#define FeaturePcdGet(TokenName)            _PCD_GET_MODE_BOOL_##TokenName
#define PcdGetBool(TokenName)               _PCD_GET_MODE_BOOL_##TokenName

        3.3 Dynamic PCD:还是和3.1的分析流程一样,先创建PCD->DEBUG使用->编译后查看AutoGen.c->再看AutoGen.h文件,依次分析。创建文件如图14,使用如图15,查看AutoGen.c(无相关内容),AutoGen.h如图16。

        图14

        图15

          图16

        从图16中可以看出这个明显和3.1小节的图13明显不同,3.1小节的图13里面都是固定值,但是图16里面宏定义的都是相关库函数,如LibPcdGet32、LibPcdGetSize、LibPcdSet32、LibPcdSet32S。我们以LibPcdGet32为例进行分析,LibPcdGet32实现如下图17所示。

图17 

        可以看到此时函数中出现Protocol字样,我们想到这个可能和Protocol产生关系。其实不同阶段依赖对象不同,DXE阶段是Protocol,而PEI阶段是PPI。Protocol在Pcd.inf模块中安装。Pcd.inf模块分为PEI和DXE两个版本,分别放在PEI阶段和DXE阶段的最前面,只有整个模块初始化完成之后,才能够开始正常使用PCD宏来访问Dynamic PCD变量。以DXE阶段的Pcd.inf模块为例,它主要做了两件事情:

        1. 初始化该阶段使用的PCD数据库;

        2. 安装各种处理PCD需要的Protocol;

        此时有两个问题出现了:PCD数据库怎么建立的?处理PCD的Protocol有哪些?先看第一个问题,我们从源码中分析,

****************BuildPcdDxeDataBase()****************// Assign PCD Entries with default value to PCD DATABASEmPcdDatabase.DxeDb = LocateExPcdBinary ();ASSERT(mPcdDatabase.DxeDb != NULL);PcdDxeDbLen = mPcdDatabase.DxeDb->Length + mPcdDatabase.DxeDb->UninitDataBaseSize;PcdDxeDb = AllocateZeroPool (PcdDxeDbLen);ASSERT (PcdDxeDb != NULL);CopyMem (PcdDxeDb, mPcdDatabase.DxeDb, mPcdDatabase.DxeDb->Length);FreePool (mPcdDatabase.DxeDb);mPcdDatabase.DxeDb = PcdDxeDb;

        反正肯定是从哪里拿了一坨数据,然后分配点空间,这不就成了。拿的一坨数据肯定是从LocateExPcdBinary ()搞的,分配空间肯定是AllocateZeroPool (PcdDxeDbLen),这PCD数据库不久成了。但是重点是LocateExPcdBinary ()是从哪里搞的一坨数据?那只能看源码了,LocateExPcdBinary ()里面部分内容如下。

Status = GetSectionFromFfs (EFI_SECTION_RAW,0,(VOID **) &DxePcdDbBinary,&DxePcdDbSize);ASSERT_EFI_ERROR (Status);

        网上说PCD数据是存放在当前FFS的第一个Section开始的数据。FFS是啥我还不清楚,但是看图知道最后一个参数是大小,倒数第二个应该是传入的地址。PCD数据的查看需要参考(编译生成的二进制的结构相关内容)。这里我们只需知道,Dynamic PCD的数据是在编译的时候初始化并存放在UEFI二进制中的,然后在UEFI运行过程中会获取这些数据,并存放到内存中,后续就可以修改了。这是第一个问题的答案。

        接着解决第二个问题:安装的Protocol如下:

typedef struct {PCD_PROTOCOL_SET_SKU              SetSku;PCD_PROTOCOL_GET8                 Get8;PCD_PROTOCOL_GET16                Get16;PCD_PROTOCOL_GET32                Get32;PCD_PROTOCOL_GET64                Get64;PCD_PROTOCOL_GET_POINTER          GetPtr;PCD_PROTOCOL_GET_BOOLEAN          GetBool;PCD_PROTOCOL_GET_SIZE             GetSize;PCD_PROTOCOL_GET_EX_8             Get8Ex;PCD_PROTOCOL_GET_EX_16            Get16Ex;PCD_PROTOCOL_GET_EX_32            Get32Ex;PCD_PROTOCOL_GET_EX_64            Get64Ex;PCD_PROTOCOL_GET_EX_POINTER       GetPtrEx;PCD_PROTOCOL_GET_EX_BOOLEAN       GetBoolEx;PCD_PROTOCOL_GET_EX_SIZE          GetSizeEx;PCD_PROTOCOL_SET8                 Set8;PCD_PROTOCOL_SET16                Set16;PCD_PROTOCOL_SET32                Set32;PCD_PROTOCOL_SET64                Set64;PCD_PROTOCOL_SET_POINTER          SetPtr;PCD_PROTOCOL_SET_BOOLEAN          SetBool;PCD_PROTOCOL_SET_EX_8             Set8Ex;PCD_PROTOCOL_SET_EX_16            Set16Ex;PCD_PROTOCOL_SET_EX_32            Set32Ex;PCD_PROTOCOL_SET_EX_64            Set64Ex;PCD_PROTOCOL_SET_EX_POINTER       SetPtrEx;PCD_PROTOCOL_SET_EX_BOOLEAN       SetBoolEx;PCD_PROTOCOL_CALLBACK_ONSET       CallbackOnSet;PCD_PROTOCOL_CANCEL_CALLBACK      CancelCallback;PCD_PROTOCOL_GET_NEXT_TOKEN       GetNextToken;PCD_PROTOCOL_GET_NEXT_TOKENSPACE  GetNextTokenSpace;
} PCD_PROTOCOL;

        这里就可以看到上文中LibPcdGet32()里面调用第四行的Get32()函数。观察发现上述代码中最多的就是Getxxx和Setxxx,我们进一步看这些种类库函数的实现,Getxxx这类库函数实现过程中,最重要的代码为

VOID *
GetWorker (IN UINTN             TokenNumber,IN UINTN             GetSize)

        Setxxx这类库函数实现过程中,最重要的代码为

EFI_STATUS
SetWorker (IN          UINTN                   TokenNumber,IN          VOID                    *Data,IN OUT      UINTN                   *Size,IN          BOOLEAN                 PtrType)

        GetWorker()的作用就是在PCD数据库里面找到对应的PCD的指针,而SetWork()的作用就是找到指针然后赋值。

        3.4 PatchableInModule PCD:DEC声名、模块中使用、查看AutoGen.c、查看AutoGen.h、分别如图18、图19、图20、图21所示。

图18

图19

图20

图21

        由上述四个图所示,与之前不同的是,这个是由volatile修饰的(用这个修饰的变量是容易发生改变的,所以读取的时候一定要从原始的寄存器中读取,不能用编译器中缓冲的数据代替。具体的用法查询volatile。)理论上可以通过工具(GenPatchPcdTable.exe和PatchPcdValue.exe)来修改这个值(在生成最终的OVMF.fd之前)。

这篇关于PCD相关知识的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/399868

相关文章

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

log4j2相关配置说明以及${sys:catalina.home}应用

${sys:catalina.home} 等价于 System.getProperty("catalina.home") 就是Tomcat的根目录:  C:\apache-tomcat-7.0.77 <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" /> 2017-08-10

Node Linux相关安装

下载经编译好的文件cd /optwget https://nodejs.org/dist/v10.15.3/node-v10.15.3-linux-x64.tar.gztar -xvf node-v10.15.3-linux-x64.tar.gzln -s /opt/node-v10.15.3-linux-x64/bin/npm /usr/local/bin/ln -s /opt/nod

git ssh key相关

step1、进入.ssh文件夹   (windows下 下载git客户端)   cd ~/.ssh(windows mkdir ~/.ssh) step2、配置name和email git config --global user.name "你的名称"git config --global user.email "你的邮箱" step3、生成key ssh-keygen

zookeeper相关面试题

zk的数据同步原理?zk的集群会出现脑裂的问题吗?zk的watch机制实现原理?zk是如何保证一致性的?zk的快速选举leader原理?zk的典型应用场景zk中一个客户端修改了数据之后,其他客户端能够马上获取到最新的数据吗?zk对事物的支持? 1. zk的数据同步原理? zk的数据同步过程中,通过以下三个参数来选择对应的数据同步方式 peerLastZxid:Learner服务器(Follo

rtmp流媒体编程相关整理2013(crtmpserver,rtmpdump,x264,faac)

转自:http://blog.163.com/zhujiatc@126/blog/static/1834638201392335213119/ 相关资料在线版(不定时更新,其实也不会很多,也许一两个月也不会改) http://www.zhujiatc.esy.es/crtmpserver/index.htm 去年在这进行rtmp相关整理,其实内容早有了,只是整理一下看着方

枚举相关知识点

1.是用户定义的数据类型,为一组相关的常量赋予有意义的名字。 2.enum常量本身带有类型信息,即Weekday.SUN类型是Weekday,编译器会自动检查出类型错误,在编译期间可检查错误。 3.enum定义的枚举类有什么特点。         a.定义的enum类型总是继承自java.lang.Enum,且不能被继承,因为enum被编译器编译为final修饰的类。         b.只能定义