依照PCD類型的不同,我們的Module可以讀取在Compiler前就已設定好的Macro,或者是使用Dynamic PCD來讓各個Module相互存取數值。
在EDK時BIOS內的不同Module要互相傳遞數值,最常使用的可能是CMOS、Memory或Variable。
但CMOS可使用的位置並不算多(雖然還算滿多的...),也要顧慮到該位址是否已被占用。
而Memory也要先配置好空間,然後別支Module也要知道該位置,說真的也有點麻煩...
再來就是Variable,Variable說方便是方便,但使用時都要做一些繁瑣動作,例如訂好Name、GUID還有Data Type(雖然PCD也要訂定好GUID與各資料型別等部分,但Compiler會做掉很多地方),對於一般不需保存於NVS的資料,個人比較偏好使用PCD,但其生命周期只有Boot-time而已。
PCD分為五種型態:
- PcdsFeatureFlag
- PcdsFixedAtBuild
- PcdsPatchableInModule
- PcdsDynamic
- PcdsDynamicEx
其它PCD則是可以在Boot-time時期做存取的動作。
可在Package中的DEC檔中宣告每個不同的PCD。
以MdePkg.dec為例:
[PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx] gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress|0xE0000000|UINT64|0x0000000a gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLangCodes|"engfraengfra"|VOID*|0x0000001c gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang|"eng"|VOID*|0x0000001d
在DEC中的PCD宣告,可以有多種型態,以上面的例子來說,這些PCDs就宣告了四種型態,並且分別給予它們初始值、資料型別與Token值。
然後可在DSC中定義它實際的PCD型態與初始值。
以MdePkg.dsc為例:
[PcdsFixedAtBuild]
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x0f
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000
gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress|0xE0000000
========================================
那如果在DSC定義一個與DEC中所宣告的型態不同會如何?
MdePkg.dec
[PcdsFixedAtBuild,PcdsPatchableInModule]
gEfiMdePkg...Guid.PcdDebugPrintErrorLevel|0x80000000|UINT32|0x00000006
Nt32Pkg.dsc
[PcdsDynamicDefault]
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000040
將會Compile error...
Type [Dynamic] of PCD [gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel] in DSC file doesn't match the type [FixedAtBuild] defined in DEC file.
底下這句話是Spec中所提到的:
Some DEC files define multiple PCD types, but only one type can be used and will be chosen by the platform DSC file.
========================================
在編譯的時候Compiler會為每一支Module自動產生AutoGen.h與AutoGen.c。
關於PCD的部分,它會將使用者在inf中所加入的PCD轉換到上述的兩個檔案,對於不同型態的PCD,則會有不同的處理方式。
以下將會介紹各種不同的PCD:
PcdsFeatureFlag
FeatureFlag顧名思義可以用它來表示一個Feature是On或Off,所以它是一個Boolean值TRUE或FALSE。可從下面的例子看出,PcdsFeatureFlag型態的PCD,將會被轉換成const的常數,也意味的它只能被唯讀。
Example:
DSC
[PcdsFeatureFlag]
gEfiMdePkgTokenSpaceGuid.PcdUgaConsumeSupport|TRUE
AutoGen.h
#define _PCD_TOKEN_PcdUgaConsumeSupport 11U
#define _PCD_VALUE_PcdUgaConsumeSupport ((BOOLEAN)1U)
extern const BOOLEAN _gPcd_FixedAtBuild_PcdUgaConsumeSupport;
#define _PCD_GET_MODE_BOOL_PcdUgaConsumeSupport _gPcd_FixedAtBuild_PcdUgaConsumeSupport
//#define _PCD_SET_MODE_BOOL_PcdUgaConsumeSupport ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
AutoGen.c
GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdUgaConsumeSupport = _PCD_VALUE_PcdUgaConsumeSupport;
========================================
PcdsFixedAtBuild
FixedAtBuild與FeatureFlag挺相似的,最大的不同則是在於它可以宣告多種不同的變數型別,而不是只有局限於Boolean而已。Example:
DSC
[PcdsFixedAtBuild]
gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress|0xE0000000
AutoGen.h
#define _PCD_TOKEN_PcdPciExpressBaseAddress 16U
#define _PCD_VALUE_PcdPciExpressBaseAddress 0xE0000000ULL
extern const UINT64 _gPcd_FixedAtBuild_PcdPciExpressBaseAddress;
#define _PCD_GET_MODE_64_PcdPciExpressBaseAddress _gPcd_FixedAtBuild_PcdPciExpressBaseAddress
//#define _PCD_SET_MODE_64_PcdPciExpressBaseAddress ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
AutoGen.c
GLOBAL_REMOVE_IF_UNREFERENCED const UINT64 _gPcd_FixedAtBuild_PcdPciExpressBaseAddress = _PCD_VALUE_PcdPciExpressBaseAddress;
========================================
PcdsPatchableInModule
宣告成PatchableInModule與上面兩種型態的PCD的最大不同是,它可以在Runtime時去變動它的數值。然後它還有一個特質,通過下面的範例可以注意到,它被宣告成一個全域變數(Global Variable),在PE/COFF的檔案格式中,而全域變數將會被初始化並放置於Data Section之中。並有機會透過外部工具直接修改。
記得Compile完所產生的.efi檔案都是PE(Portable Executable)格式,有興趣的人可以用7z將.efi打開看看。
由於它只被宣告一個全域變數,所以只有同一個Module能夠取存,不能透過它與各Module相互傳遞數值。
Example:
DSC
[PcdsPatchableInModule]
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000
AutoGen.h
#define _PCD_TOKEN_PcdDebugPrintErrorLevel 17U
#define _PCD_PATCHABLE_VALUE_PcdDebugPrintErrorLevel ((UINT32)0x80000040U)
extern volatile UINT32 _gPcd_BinaryPatch_PcdDebugPrintErrorLevel;
#define _PCD_GET_MODE_32_PcdDebugPrintErrorLevel _gPcd_BinaryPatch_PcdDebugPrintErrorLevel
#define _PCD_SET_MODE_32_PcdDebugPrintErrorLevel(Value) (_gPcd_BinaryPatch_PcdDebugPrintErrorLevel = (Value))
AutoGen.c
volatile UINT32 _gPcd_BinaryPatch_PcdDebugPrintErrorLevel = _PCD_PATCHABLE_VALUE_PcdDebugPrintErrorLevel;
========================================
PcdsDynamic v.s. PcdsDynamicEx
這兩種型態的PCD都是集中於PCD Database作集中管理,有PEI與DXE兩個部分。型態又可細分為以下幾種:
- PcdsDynamicDefault and PcdsDynamicExDefault
- PcdsDynamicHii and PcdsDynamicExHii
- PcdsDynamicVpd and PcdsDynamicExVpd
在DEC所宣告的Section需要為PcdsDynamic或PcdsDynamicEx,而在DSC中則是它們的子分類。
PcdsDynamicDefault
DynamicDefault型態的PCD的最大優勢是在於,各個Module之間可以藉由這個PCD來互相傳遞數值。所以它有著多樣化的應用方式,例如預先把某個不會變動的值從HW讀出來,並存到某個PCD裡面,這樣接下來其它的Module就可以不用再經過繁複的動作去存取HW上的值,直接透過PCD即可。
可以看到下面的例子,它會去透過一個Library去存取數值,而不是宣告一個常數或全域變數。
還可注意到一點,在DEC中所定的Token與AutoGen.h的數值並不相同!
其實Spec有提到,不管DEC中如何訂,Compiler都會自己產生一組,這應該是為了避免人為因素而有重複的狀況。
Although the token is auto-generated. A value is required for the Token field; if it is not defined, then the the build will fail.
Example:
DEC
[PcdsDynamic, PcdsDynamicEx]
gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptTablePrivateDataPtr|0x0|UINT64|0x00030000
DSC
[PcdsDynamicDefault]
gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptTablePrivateDataPtr|0x0
AutoGen.h
#define _PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr 1U
#define _PCD_GET_MODE_64_PcdS3BootScriptTablePrivateDataPtr LibPcdGet64(_PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr)
#define _PCD_SET_MODE_64_PcdS3BootScriptTablePrivateDataPtr(Value) LibPcdSet64(_PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr, (Value))
========================================
PcdsDynamicExDefault
多了Ex的DynamicExDefault與DynamicDefault很相似,但如果一個Module不是同時間與整個Platform一起Build的,而是一個外部的Binary,Platform與Binary又需要互相存取某個PCD的數值的話,這時候一開始該PCD就需要宣告成DynamicEx型態的PCD。但外部的Binary在使用DynamicEx PCD時,必須先知道該PCD的GUID與Token值,才能透過LibPcdGetExXX或LibPcdSetExXX來存取。
Example:
DEC
[PcdsDynamic, PcdsDynamicEx]
gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptTablePrivateDataPtr|0x0|UINT64|0x00030000
DSC
[PcdsDynamicExDefault]
gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptTablePrivateDataPtr|0x0
AutoGen.h
#define _PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr 196608U
#define _PCD_GET_MODE_64_PcdS3BootScriptTablePrivateDataPtr LibPcdGetEx64(&gEfiMdeModulePkgTokenSpaceGuid, _PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr)
#define _PCD_SET_MODE_64_PcdS3BootScriptTablePrivateDataPtr(Value) LibPcdSetEx64(&gEfiMdeModulePkgTokenSpaceGuid, _PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr, (Value))
========================================
PcdsDynamicHii
HII型態的PCD在DSC需定好它所對應的Variable Name、GUID、Offset(位於Variable Data的哪個Offset)還有Default Value。如果系統中不存在著該Variable,那麼就會從PCD取得DSC所定好的預設值,否則就會回傳該Variable的Data加上Offset。
Example:
DEC
[PcdsDynamic, PcdsDynamicEx]
gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut|0xffff|UINT16|0x40000001
DSC
[PcdsDynamicHii]
gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut|L"Timeout"|gEfiGlobalVariableGuid|0x0|10
AutoGen.h
#define _PCD_TOKEN_PcdPlatformBootTimeOut 15U
#define _PCD_GET_MODE_16_PcdPlatformBootTimeOut LibPcdGet16(_PCD_TOKEN_PcdPlatformBootTimeOut)
#define _PCD_SET_MODE_16_PcdPlatformBootTimeOut(Value) LibPcdSet16(_PCD_TOKEN_PcdPlatformBootTimeOut, (Value))
========================================
PcdsDynamicExHii
多了Ex的DynamicExHii則意思跟DynamicEx很相似。Example:
DEC
[PcdsDynamic, PcdsDynamicEx]
gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut|0xffff|UINT16|0x40000001
DSC
[PcdsDynamicExHii]
gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut|L"Timeout"|gEfiGlobalVariableGuid|0x0|10
AutoGen.h
#define _PCD_TOKEN_PcdPlatformBootTimeOut 1073741825U
#define _PCD_GET_MODE_16_PcdPlatformBootTimeOut LibPcdGetEx16(&gEfiIntelFrameworkModulePkgTokenSpaceGuid, _PCD_TOKEN_PcdPlatformBootTimeOut)
#define _PCD_SET_MODE_16_PcdPlatformBootTimeOut(Value) LibPcdSetEx16(&gEfiIntelFrameworkModulePkgTokenSpaceGuid, _PCD_TOKEN_PcdPlatformBootTimeOut, (Value))
========================================
PcdsDynamicVpd and PcdsDynamicExVpd
VPD (Vital Product Data),在NVS會有一塊區域被用來儲存VPD的資料,與其它Dynamic PCD不同,VPD只能唯讀,無法在執行期間去修改它的數值。關於VPD的部分,目前我還沒有機會用到。
以上就先介紹的這個部分,下一篇再來介紹應用與原理。
哈囉 想請教一下 PCD生命週期是只有boot time
回覆刪除也就是說,進入SHELL PCD就沒了嗎
也就是說在SHELL下面寫TOOL也沒辦法去SET PCD嗎
Hi~
回覆刪除在Shell底下還是可以存取Dynamic PCD,等到了要進OS時,呼叫了gBS->ExitBootServices後,一般的非Runtime driver都會被unload掉,此時才無法使用。
普通的Fixed PCD不受影響,因為它只是一個#define~~
Thanks.