UEFI——PCD介绍

一、概念

PCD(Platform Configuration Database,平台配置数据库)就是抽取出代码中可配置的数据而不修改源代码。类似C语言中的宏,降低了代码维护的工作量,增加了可复用性。不同于宏的是,有些PCD(动态PCD)可以在代码运行的时候修改。

EDK II PCD’s Purpose

  • Establishes platform common definitions
  • Build-time/Run-time aspects
  • Binary Editing Capabilities

EDK II PCD’s Goals

  • Simplify porting
  • Easy to associate with a module or platform

MdeModulePkg/Universal/PCD/Dxe/Pcd.inf文件中有完整介绍。

二、PCD类型

PCD的主要类型有:FixedAtBuild、FeatureFlag、PatchableInModule、Dynamic、DynamicEx、DynamicHii、DynamicVpd。

语法示例:

[PcdsFeatureFlag.common]
[PcdsFixedAtBuild.IA32]
[PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]

FixedAtBuild:值是在build time 时候就定好了的,在binary阶段是不可以改的,整个boot level也是不可以改的,可以认为就是一个宏,const 全局变量。这个PCD有一个好处,是一个module level的,非系统级别的,即是说不同模块可以配置不同PCD的值,典型的例子就是DEBUG Message 是否要输出Message的问题,这是由一个PCD来control的,由于是模块级别的,所以可以控制一个模块输出一个模块不输出。

FeatureFlag(control feature Boolean类型):和FixAtBuild是一类,可以认为是BOOLEAN类型的FixedAtBuild,只是支持静态build time的设置,而且是module level的。

PatchableInModule:module level,它可以patch(binary level 的patch),在boot time也可以修改,但是能影响的也只是一个模块,一个Driver,在这个模块的修改不会影响到其他模块,相当于一个inf文件的source范围,但是和FixAtBuild不同的是,这只是一个全局变量,并不是const类型的,binary level可以修改的。

Dynamic/DynamicEx:动态的PCD,在整个boot过程中,有人set有人read,是可以一直修改的,作用域是system,system level,意思就是在整个BIOS,所有的模块访问Dynamic的PCD,都是共享的同一个值,某模块的修改会导致其他模块获取到的值也发生变化。Dynamic 又有三个子类:DynamicDefault、DynamicHII、DynamicVpd。注意在DEC文件中定义了[PcdsDynamic]类型的PCD后,若在DSC文件修改,DSC文件中PCD块的名字为[PcdsDynamicDefault]、[PcdsDynamicHii]、[PcdsDynamicVpd]。

Dynamic与DynamicEx的区别:如果只是做source 的build就是说platform是从源码build出来的,没有binary在里面的时候,PCD用的都是Dynamic这种类型,如果在BIOS里面有一些模块是binary方式集成进来的而这些binary又需要用到PCD,那么这些Binary集成的要用到的PCD就必须要设置为DynamicEx类型,这就是Dynamic与DynamicEx之间的区别。

DynamicDefault:PCD的值可以认为存在于hob或者memory中,会导致在下次启动时丢失上一次的值,获取到的是新define的值。

DynamicHII的关于PCD的值存在于EFI Variable中,可能对应的是一个NV的,non-volatile 的,即在某次PCD的值发生set之后,下次启动仍然是本次set的值。

DynamicVpd是属于另一种,ReadOnly,这个PCD的值存在VPD空间的,VPD是放在FLASH上的只读的,通常来说是一些系统的Default Section,工厂出厂的一些配置存放在这里,属于必不会被修改的。

Special PCDS

  • Multi-Structure PCD:C data structure and assign the value to each sub-field directly.
  • Multi-Sku PCD: Multiple configurations generated at build time & set at run time, (PI Spec Vol 3 chap. 8)
  • DefaultStores PCD: Support the default stores concept in UEFI specification, (UEFI, HII Chap.32)

Multiple “C” Data Structure as PCDs
这个PCD解决的问题是当一个PCD和一个EFI Variable进行关联的时候,通常一个EFI Variable里面是一个C的Structure,就有可能关联到这个C Structure的某一个field,这时候就需要知道这个Struct的offset在哪里,这个对于维护和开发会带来难度,由于并不知道Struct的offset是多少,且当Struct改动之后关于PCD 的dsc也要进行修改,倘若有很多field需要使用,则需要定义更多的PCD,加大code的复杂度,故而引入MUlti—Structure PCD。本身而言,只是一个VOID * 的PCD,这个PCD mapping 到一个Structure,以后访问该PCD就相当于访问Structure,访问Structure中的每个field就相当于访问PCD然后通过PCD引用相应的field。

为了更容易使用Structure PCD,需要DSC能够支持对每个field的赋值,在C code能直接引用PCD,然后再把PCD mapping成一个structure,按照structure layout去访问每个field,总的来说,为了使单个域在dsc里面能够赋值,而且整个PCD在DSC里面也能赋值,这样就把所有的Structure赋值挪到DSC里面,就不需要在C source里面做。同时即便Structure修改了 DSC也不需要做任何改动

Example: edk2-platforms/ Platform/ Intel/ AdvancedFeaturePkg.dec/

SMBIOS type 0 data structure

gAdvancedFeaturePkgTokenSpaceGuid.PcdSmbiosType0BiosInformation| \
{0x0}|SMBIOS_TABLE_TYPE0|0x80010000 {
<HeaderFiles>
IndustryStandard/SmBios.h
<Packages>
MdePkg/MdePkg.dec
AdvancedFeaturePkg/AdvancedFeaturePkg.dec
}
gAdvancedFeaturePkgTokenSpaceGuid.PcdSmbiosType0BiosInformation.Vendor|0x1
gAdvancedFeaturePkgTokenSpaceGuid.PcdSmbiosType0BiosInformation.BiosVersion|0x2
gAdvancedFeaturePkgTokenSpaceGuid.PcdSmbiosType0BiosInformation.BiosSegment|0xF000
gAdvancedFeaturePkgTokenSpaceGuid.PcdSmbiosType0BiosInformation.BiosReleaseDate|0x3
gAdvancedFeaturePkgTokenSpaceGuid.PcdSmbiosType0BiosInformation.BiosSize|0xFF
gAdvancedFeaturePkgTokenSpaceGuid.PcdSmbiosType0BiosInformation.BiosCharacteristics.PciIsSupported|1
gAdvancedFeaturePkgTokenSpaceGuid.PcdSmbiosType0BiosInformation.BiosCharacteristics.PlugAndPlayIsSupported|1

Multi-SKU PCD
Multiple configurations generated at build time & set at run time, (PI Spec Vol 3 chap. 8)

倘若有一个很大的Structure,如Platform 的setup的structure,这个Structure可能在一个平台有不同版本的board,每个board里面可能有一些细微的差别,可能会导致这个Setup的structure可能有些不同,这种情况下按照原来的配置方法可能要创建好几个DSC,每个DSC里面对PCD设置不同的值,相对来说比较复杂。那么这个PCD相当于有一个base,根据board的差异继承base数据然后只改差异的地方,支撑整个Structure。
UEFI——PCD介绍_第1张图片
Default Stores PCD
Support the default stores concept in UEFI specification, (UEFI, HII Chap. 32)

HII在启动起来之后有Reset to default等选项,用户可能在对setup选项进行修改之后,发现系统出现了各种各样的问题,这时候可能需要恢复到初始状态,选择reset to default标准的,相当于重置设置,另外一种manufaction default,相当于恢复出厂设置,那么这个值用StructPCD描述出来,根据对象为标准default还是manu default写入DSC里面,倘若需要使用,则进行配置使用即可。
UEFI——PCD介绍_第2张图片

三、PCD Library

PEI

  • PCD PEIM produces PCD database
  • Two PCD PPIs:PCD_PPI and EFI_PEI_PCD_PPI

DXE

  • DXE Driver Manages PCDs
  • Two PCD Protocols:PCD_PROTOCOL and EFI_PCD_PROTOCOL

为了方便code的编写,对PCD的使用引入PCD lib,这里面包含了常用的PCD接口,故而不用去可以学习内部实现是调用PPI、Protocol或是直接映射,这些都被Lib隐藏了,所以使用过程中,只需要关注PCD Protocol和PCD PPI function:PcdGetXX()、PcdSetXX()、 PcdGetExXX()、 PcdSetEx()、 PcdToken()、 PcdSetSku()、 PcdGetNextToken() 、PcdGetNextTokenSpace()、 CallBackOnSet() 、CancelCallBack() 。

Note:“XX" = 8 16 32 Size Ptr Boolean

PCDgetXX()和PCDSetXX()是用得最多的,属于统一化的API接口,无论哪一种Type都可以使用这两个,使用时会根据你具体Type类型,把这个Function mapping到具体的PCDType所对应的值的上面。

四、PCD语法

UEFI——PCD介绍_第3张图片

PCD语法示例

Define:
PCD defined in the DEC file from any package
PCD 在.DEC文件中进行声明,就涉及到了Package,所以说PCD是一个Package的interface,就是说PCD是Package对外提供的接口之一。在定义过程中有两个需要注意的information,TokenSpaceGuid(PCD GUID说明到底在哪个Space 空间里)和TokenName(PCD 名字),这两个决定了PCD的唯一性,不可以重复,也不可以轻易改变。

Value为Default Value,Datum Type描述的是数据类型 UINT 8 16 等,倘若是VOID * 还得在[MaxSize]中填入这块Buffer的最大值128 256等,最后一个为Token number UINT32的。

[Guids.common]
PcdTokenSpaceGuidName={ 0xXXXXXXXX, 0xXXXX, 0xXXXX, { 0xXX, . . .}}
. . .
[Pcds...]
PcdTokenSpaceGuidName.PcdTokenName|Value[|DatumType[|MaxSize]]|Token

Reference:
PCD usage listed in INF file for module
PCD在.inf文件中进行使用。在inf里一般来说有个Pcd Section,只要把这个PCD列上去就行了,通常也不会写值。

[…Pcd…]
PcdTokenSpaceGuidName.PcdTokenName|[Value]

Modify:
Value of PCD set in Platform DSC
PCD在.DSC文件中进行配置,在这里会决定PCD的值和类型,若PCD在.DEC文件中进行了定义,在inf文件中使用,但在DSC中并没有重新set,这时候PCD就会使用默认值(.DEC的Value),类型则是支持的最初的类型,类型也是有一个优先级,优先考虑FixAtBuild,其次PatchInModule,然后Dynamic,DynamicEx。

[Pcds...]
PcdTokenSpaceGuidName.PcdTokenName|Value[|DatumType[|MaximumDatumSize]]

PCD变量用法示例

这是一个PcdsFixedAtBuild类型的示例

  1. 在DEC文件中定义
[PcdsFixedAtBuild, PcdsPatchableInModule]
gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize|0x400|UINT32|0x30000003
  1. 在INF文件中引用
    MdeModulePkg\Universal\Variable\RuntimeDxe\VariableRuntimeDxe.inf
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize ## CONSUMES
  1. 在DSC文件中修改
    将PcdMaxVariableSize的值从0x400改为0x008400
[PcdsFixedAtBuild]
gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize|0x008400
  1. 在C文件中使用
    MdeModulePkg\Universal\Variable\RuntimeDxe\Variable.c // max NV variable size
mVariableModuleGlobal->MaxVariableSize = PcdGet32 (PcdMaxVariableSize);

五、PCD作用机制解析

Fixed PCD

PCD起作用并不是简简单单通过上述步骤就可以实现的, 在文件中描述完毕后通过Parsing Tool(实际就是Build Tool)进行parse,生成对应的AutoGen code,在code里面可以以PCD类型生成不同的PCD原型,然后被PCD Lib里的PCDGet32()、PCDGet16()等不同的function进行引用,而Driver Source Code里面,调用函数所用到的就是AutoGen中通过Build Tool生成的值,或是访问的方法。
UEFI——PCD介绍_第4张图片

Fixed PCD AutoGen Code

Example:MdeModulePkg\Universal\Variable\RuntimeDxe\VariableRuntimeDxe
UEFI——PCD介绍_第5张图片
这里再提一点,PcdLib里有PcdGetXX()和FixedPcdGetXX(),实际上这两者是不同的,以上面PcdMaxVariableSize为例,FixedPcdGetXX()是映射到一个宏:_PCD_VALUE_PcdMaxVariableSize,而PcdGetXX()是映射到一个变量_gPcd_FixedAtBuild_PcdMaxVariableSize,FixedPcdGetXX()用于ASL、VFR文件,因为这类文件不是标准的C的源码,用PcdGetXX()是会报错的。

Dynamic PCD

关于动态的PCD,通过 build后parse,拿到所有的动态PCD,然后生成到Database里面,Compiler到一起,最后生成Driver Binary。存储在PE32 space,通过隔离Database,使得PCD的值可以进行修改替换,达到动态目的。
UEFI——PCD介绍_第6张图片

Dynamic PCD特点

  • Only can be set and changed during Boot time.
  • PCD can be set with the library Set:LibPcdSet…
  • PCD can be retrieved with the library Get:LibPcdGet…

Example: Use the variable PcdPlatformBootTimeOut defined for the platform time in seconds before booting, modified for a value of 03 seconds

Dynamic PCD示例

Dynamic PCD AutoGen Code

UEFI——PCD介绍_第7张图片

参考:
1.BIOS知识枝桠——PCD
2.【BIOS/UEFI】PCD配置和使用

你可能感兴趣的:(UEFI,BIOS,UEFI)