UEFI BIOS之PCD

本文详细介绍了UEFI下的PlatformConfigurationDatabase(PCD)的各种类型,包括FeatureFlagPCD、PatchableInModulePCD、FixedAtBuildPCD、DynamicPCD和DynamicExPCD,以及它们的访问方式、实现机制和存储位置。着重讲解了FixedAtBuildPCD和DynamicPCD的区别,以及如何通过工具修改PatchableInModulePCD的值。
摘要由CSDN通过智能技术生成

PCD的全称是Platform Configuration Database,它是一个存放了UEFI下可访问数据的数据库。

它的特点是可以在UEFI存在的大部分时间内访问,在【UEFI实战】在库中使用全局变量中就曾经介绍过通过PCD来传递全局变量。这里说是大部分时间内,那是因为在诸如SEC阶段,以及PEI、DXE阶段的早期,某些PCD相关的模块还没有加载起来之前,这些类型的PCD还是不能访问的。

TokenSpaceGuidCName.PcdCName

TokenSpaceGuidCName是一个GUID,PcdCName是一个变量名,两者合起来构成了唯一的PCD变量。

PCD的类型

FeatureFlag PCD:它最终返回的是一个TRUE或者FALSE,用于判断条件中;FeatureFlag PCD跟FixedAtBuild是一样的,它相当于类型是BOOELAN的FixedAtBuild PC

PatchableInModule PCD:这种变量的值可以在编译的时候确定,这个不算特别,特别的是它可以在编译完成的二进制文件上通过工具来修改值;

FixedAtBuild PCD:静态值,在编译的时候确定,整个UEFI阶段不可变;

Dynamic PCD:前面的三种类型可以认为是静态的PCD,而这里以及之后的是动态的PCD;它的特点是可以在UEFI运行的过程中通过Set宏来修改值;在《edk-ii-build-specification.pdf》中有说明该种类型的PCD必须在DSC中在列一遍,但是实际使用似乎并不是必须的;

DynamicEx PCD:跟Dynamic PCD类似,算是加强版,使用宏PcdGetEx/PcdSetEx来访问变量;

需要注意的是上面的类型并不是在一个SPEC中定义的,前面的4中是满足EDKII规范,而最后一个满足的是PI规范,这个对使用的影响还不是很确定。

实现说明

下面说明各种PCD的实现。

FixedAtBuild PCD

不同的PCD类型实现方式有差异,这里首先介绍最简单的FixedAtBuild PCD。.然后在一个模块中使用

3. 编译后查看该模块编译目录中的AutoGen.c文件,可以看到如下的内容

这里的_PCD_VALUE_PcdTestVarx的值在AutoGen.h中声明:

也就是说,对应FixedAtBuild PCD来说,它就是在编译的时候通过宏的方式生成的。

也因此它是固定不变的一个值。

Dynamic PCD

下面介绍Dynamic PCD,首先看一个例子:

dec定义,

inf添加

C函数调用

Log打印:

1)AutoGen.c中没有了PCD变量的定义;

2)PCD访问方式变了,Dynamic对应的是函数;

3)Set函数出现了;

AutoGen.c

AutoGen.h

也就是说这里要依赖于Protocol(DXE阶段是Protocol,而PEI阶段是PPI)来完成操作。

这Protocol在Pcd.info模块中安装。

Pcd.inf模块分为PEI和DXE两个版本,分别放在PEI阶段和DXE阶段的最前面,只有整个模块初始化完成之后,才能够开始正常使用PCD宏来访问Dynamic PCD变量。

以DXE阶段的Pcd.inf模块为例,它主要做了两件事情:

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

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

/ PCD protocol need to be installed before the module access Dynamic type PCD.

    // But dynamic type PCD is not required in PI 1.2 specification.

v

///

/// This service abstracts the ability to set/get Platform Configuration Database (PCD).

///

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;

在上述GetX和SetX的函数实现中,最重要的是如下的函数:

GetWorker():

SetValueWorker

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

到这里为止,最需要了解的就是PCD数据库是如何建立的?

从BuildPcdDxeDataBase()这个函数中可以找到答案。

这里主要进行了PCD数据库的创建,它分为两个步骤:

1. 通过LocateExPcdBinary()从FFS中获取PCD已初始化变量;

2. 为未初始化的变量(默认全0)分配空间(这样做的目的应该是为了减少生成二进制的大小);

接下来需要关注的就是这个放在FFS中的PCD数据是从何而来的?

查看LocateExPcdBinary()函数里面的代码:

 gPcdDataBaseSignatureGuid

在rom中查找Guid

3C7D193C-682C-4C14-A68F-552DEA4F437E

因此Dynamic PCD的数据是在编译的时候初始化并存放在BIOS二进制中的,然后在BIOS运行过程中会获取这些数据,并存放到内存中,后续就可以修改了。

DynamicEx类型的基本上一致,这里不再说明。

PatchableInModule PCD

最后剩下的是PatchableInModule PCD,下面是一个例子:

这里定义了PCD相关的两个变量,一个是值,注意它的类型修饰符是volatile,另一个是PCD的大小。

它的访问方式与FixedAtBuild PCD没有什么两样。

对于PatchableInModule PCD来说,唯一的差别就在于,变量是volatile的,但是这个volatile具体体现在哪里呢?我们如何对它进行Patch?

因为该PCD是可以通过外部工具来修改的,那么有一点是可以肯定的,即它是可见的,通过查看BIOS二进制就可以找到。

首先找FV_RECOVERY.fd(这个是最终生成的BIOS二进制)中寻找,但是没有找到,再在FAT.efi中寻找,发现了它的踪迹: 在Fat.map中

因此理论上可以通过工具来修改这个值(在生成最终的RECOVERY.fd之前)。

而实际上,在EDK的源代码中,提供了操作PatchableInModule PCD的工具

在Fat.inf模块中加入Pcd 并且Pcd会在C文件中使用

將Efi和map文件放到工具路径

因此可以利用这两个工具来修改PCD。

这里将这两个工具(以及依赖文件)放到下述的编译中间目录:

打开Fat.efi后0x13090, 0x13110找到对应PCD的值,和工具解析对应

然后通过PatchPcdValue.exe来修改PCD:

这里将PcdPatchableVar的值修改成了0x88888888,比较修改前后的值:

但是如果在编译的时候自动完成这个动作,或者在整个二进制生成之后修改PatchableInModule PCD呢?

目前这还是个疑问。

  • }
  • returnGetPcdProtocol()->Get32(TokenNumber);
  • LibPcdGet32(
  •   IN UINTN             TokenNumber
  •   )
  • {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小菜鸟-BIOS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值