AutoGen.h
typedef struct {
DYNAMICEX_MAPPING ExMapTable[PEI_EXMAPPING_TABLE_SIZE];
UINT32 LocalTokenNumberTable[PEI_LOCAL_TOKEN_NUMBER_TABLE_SIZE];
GUID GuidTable[PEI_GUID_TABLE_SIZE];
UINT8 StringTable[1]; /* _ */
SIZE_INFO SizeTable[PEI_SIZE_TABLE_SIZE];
UINT8 SkuIdTable[PEI_SKUID_TABLE_SIZE];
SKU_ID SystemSkuId;
} PEI_PCD_DATABASE_INIT;
typedef struct {
UINT64 PcdS3BootScriptTablePrivateDataPtr_a1aff049_fdeb_442a_b320_13ab4cb72bbc[1];
UINT32 PcdFlashNvStorageFtwWorkingBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc[1];
UINT32 PcdFlashNvStorageFtwSpareBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc[1];
UINT32 PcdFlashNvStorageVariableBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc[1];
} PEI_PCD_DATABASE_UNINIT;
typedef struct {
PEI_PCD_DATABASE_INIT Init;
PEI_PCD_DATABASE_UNINIT Uninit;
} PEI_PCD_DATABASE;
AutoGen.c
PEI_PCD_DATABASE_INIT gPEIPcdDbInit = {
/* VPD */
/* ExMapTable */
{
{ 0U, 0U, 0U },
},
/* LocalTokenNumberTable */
{
offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageFtwWorkingBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageFtwSpareBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageVariableBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
offsetof(PEI_PCD_DATABASE, Uninit.PcdS3BootScriptTablePrivateDataPtr_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_DATUM_TYPE_UINT64 | PCD_TYPE_DATA,
},
/* GuidTable */
{
{0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
},
/* StringTable */
{ 0 }, /* _ */
/* SizeTable */
{
0U, 0U, /* _ */
},
/* SkuIdTable */
{ 0U, },
0U
};
以上是在Compile過程中,Build Tool為PCD PEIM所生成的檔案。
可以注意到這邊只有少數幾組PCD,原因是PCD PEIM中不會包含只在DXE被使用的PCD。
而這個PEI_PCD_DATABASE,最後會透過HOB傳給PCD DXE。
建立HOB的過程可參考BuildPcdDatabase。
整個Structure分為Init與Uninit的部分,兩個差別是在於,如果有定義好預設值的Dynamic PCD,其預設值會被放在Init裡面,但如果在DSC裡定義的值為0或NULL的話,會被視為無預設值,且會放置於Uninit中。
/* LocalTokenNumberTable */
{
offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageFtwWorkingBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageFtwSpareBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageVariableBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
offsetof(PEI_PCD_DATABASE, Uninit.PcdS3BootScriptTablePrivateDataPtr_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_DATUM_TYPE_UINT64 | PCD_TYPE_DATA,
},
PEI_PCD_DATABASE_INIT中的LocalTokenNumberTable會在Compile過程中,會決定好每個PCD中的Data在PEI_PCD_DATABASE中的Offset以及Data Type與PCD Type。
#define offsetof(s,m) (UINT32) (UINTN) &(((s *)0)->m)
代入一個Structure的原型與其Member,會算出其Member的Offset為多少。
PCD Type Mask:
PCD_TYPE_DATA
PCD_TYPE_HII
PCD_TYPE_VPD
PCD_TYPE_SKU_ENABLED
PCD_TYPE_STRING
Datum Type:
PCD_DATUM_TYPE_POINTER
PCD_DATUM_TYPE_UINT8
PCD_DATUM_TYPE_UINT16
PCD_DATUM_TYPE_UINT32
PCD_DATUM_TYPE_UINT64
在上面的LocalTokenNumberTable中,共有四個PCD,如果去搜尋整套編譯過的Source Code,可以在某些AutoGen.h看到這些Dynamic PCD的Token Number,並且可以注意到第一個PCD的Token為1,那為何不是從0開始?0是給PCD_INVALID_TOKEN_NUMBER使用(Source Code中有註解),所以第一個PCD是從1開始,只是後面會將Token Number減1,再代入Table取得其相關資料。
#define _PCD_TOKEN_PcdFlashNvStorageFtwWorkingBase 1U
#define _PCD_GET_MODE_32_... LibPcdGet32(_PCD_TOKEN_PcdFlashNvStorageFtwWorkingBase)
#define _PCD_SET_MODE_32_...(Value) LibPcdSet32(_PCD_TOKEN_PcdFlashNvStorageFtwWorkingBase, (Value))
#define _PCD_TOKEN_PcdFlashNvStorageFtwSpareBase 2U
#define _PCD_GET_MODE_32_... LibPcdGet32(_PCD_TOKEN_PcdFlashNvStorageFtwSpareBase)
#define _PCD_SET_MODE_32_...(Value) LibPcdSet32(_PCD_TOKEN_PcdFlashNvStorageFtwSpareBase, (Value))
#define _PCD_TOKEN_PcdFlashNvStorageVariableBase 3U
#define _PCD_GET_MODE_32_... LibPcdGet32(_PCD_TOKEN_PcdFlashNvStorageVariableBase)
#define _PCD_SET_MODE_32_...(Value) LibPcdSet32(_PCD_TOKEN_PcdFlashNvStorageVariableBase, (Value))
#define _PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr 4U
#define _PCD_GET_MODE_64_... LibPcdGet64(_PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr)
#define _PCD_SET_MODE_64_...(Value) LibPcdSet64(_PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr, (Value))
在PPI或Protocol的底層會透過呼叫GetWorker或SetWorker來存取PCD。
GetWorker:
VOID *
GetWorker (
IN UINTN TokenNumber,
IN UINTN GetSize
)
透過Token Number取得該PCD的LocalTokenNumber (Offset、PCD Type、Data Type)。
LocalTokenNumber = PeiPcdDb->Init.LocalTokenNumberTable[TokenNumber];
透過Mask單獨取得Offset。
Offset = LocalTokenNumber & PCD_DATABASE_OFFSET_MASK;
接下來會依照各種不同PCD Type做不同的處理:
PCD_TYPE_VPD:
傳回VPD (Vital Product Data)中特定的Offset的資料。
VPD_HEAD *VpdHead;
VpdHead = (VPD_HEAD *) ((UINT8 *)PeiPcdDb + Offset);
return (VOID *) (UINTN) (PcdGet32 (PcdVpdBaseAddress) + VpdHead->Offset);
PCD_TYPE_HII|PCD_TYPE_STRING:
PCD_TYPE_HII:
透過Offset取得Variable Head,裡面有著GUID與Name的Index。
VariableHead = (VARIABLE_HEAD *) ((UINT8 *)PeiPcdDb + Offset);
Guid = &(PeiPcdDb->Init.GuidTable[VariableHead->GuidTableIndex]);
Name = (UINT16*)&StringTable[VariableHead->StringIndex];
透過GetVariable取得Variable Data。
Status = GetHiiVariable (Guid, Name, &Data, &DataSize);
如有錯誤則去取得PCD設定的預設值(如:NOT_FOUND)。
if (Status == EFI_SUCCESS) {
return (VOID *) ((UINT8 *) Data + VariableHead->Offset);
} else {
//
// Return the default value specified by Platform Integrator
//
if ((LocalTokenNumber & PCD_TYPE_ALL_SET) == (PCD_TYPE_HII|PCD_TYPE_STRING)) {
return (VOID*)&StringTable[*(STRING_HEAD*)((UINT8*)PeiPcdDb + VariableHead->DefaultValueOffset)];
} else {
return (VOID *) ((UINT8 *) PeiPcdDb + VariableHead->DefaultValueOffset);
}
}
PCD_TYPE_DATA:
直接透過Offset取得Data。
return (VOID *) ((UINT8 *)PeiPcdDb + Offset);
PCD_TYPE_STRING:
先取得該PCD的STRING_HEAD (StringTableIdx),裡面存放著對應StringTable的Offset。
StringTableIdx = * (STRING_HEAD*) ((UINT8 *) PeiPcdDb + Offset);
return (VOID *) (&StringTable[StringTableIdx]);
SetWorker:
EFI_STATUS
SetWorker (
IN UINTN TokenNumber,
IN VOID *Data,
IN OUT UINTN *Size,
IN BOOLEAN PtrType
)
透過Token Number取得該PCD的LocalTokenNumber (Offset、PCD Type、Data Type)。
LocalTokenNumber = PeiPcdDb->Init.LocalTokenNumberTable[TokenNumber];
會先判斷Data Type是不是Pointer,通常像String之類的,會被歸類為Pointer。
要注意到在Build Tool會在PCD Database中,先宣告好固定長度的陣列來存放String等其它Data。
如果PtrType為True,而傳入的Size又大於原先預留好的最大值的話,就會造成問題,這邊就先擋掉並回傳MaxSize。
if (PtrType) {
//
// Get MaxSize first, then check new size with max buffer size.
//
GetPtrTypeSize (TokenNumber, &MaxSize, PeiPcdDb);
if (*Size > MaxSize) {
*Size = MaxSize;
return EFI_INVALID_PARAMETER;
}
} else {
if (*Size != PeiPcdGetSize (TokenNumber + 1)) {
return EFI_INVALID_PARAMETER;
}
}
當某個PCD被修改時會去呼叫,為該PCD註冊Notify的Function。
可以透過PCD Library的LibPcdCallbackOnSet來進行註冊會比較方便。
if (TokenNumber + 1 < PEI_NEX_TOKEN_NUMBER + 1) {
InvokeCallbackOnSet (0, NULL, TokenNumber + 1, Data, *Size);
}
透過Mask單獨取得Offset,並取得Data。
Offset = LocalTokenNumber & PCD_DATABASE_OFFSET_MASK;
InternalData = (VOID *) ((UINT8 *) PeiPcdDb + Offset);
接下來會依照各種不同PCD Type做不同的處理:
PCD_TYPE_VPD:
PCD_TYPE_HII:
PCD_TYPE_HII|PCD_TYPE_STRING:
可能是上述幾個Type都不支援在PEI修改NVS,所以這邊就直接return回EFI_INVALID_PARAMETER。
尤其PCD_TYPE_HII實際上就是個Variable。
return EFI_INVALID_PARAMETER;
PCD_TYPE_STRING:
傳入的資料為一串Array,資料長度可能會變動,所以先更改Current Size。
SizeTable中有兩個欄位,一個是Max Size,一個是Current Size。
之後再透過CopyMem,將傳入的Data Copy到StringTable中。
if (SetPtrTypeSize (TokenNumber, Size, PeiPcdDb)) {
StringTableIdx = *((STRING_HEAD *)InternalData);
CopyMem (&PeiPcdDb->Init.StringTable[StringTableIdx], Data, *Size);
return EFI_SUCCESS;
} else {
return EFI_INVALID_PARAMETER;
}
PCD_TYPE_DATA:
PtrType的處理方式跟PCD_TYPE_STRING很像。
if (PtrType) {
if (SetPtrTypeSize (TokenNumber, Size, PeiPcdDb)) {
CopyMem (InternalData, Data, *Size);
return EFI_SUCCESS;
} else {
return EFI_INVALID_PARAMETER;
}
}
其餘基本型態只是透過轉型來處理。
switch (*Size) {
case sizeof(UINT8):
*((UINT8 *) InternalData) = *((UINT8 *) Data);
return EFI_SUCCESS;
case sizeof(UINT16):
*((UINT16 *) InternalData) = *((UINT16 *) Data);
return EFI_SUCCESS;
case sizeof(UINT32):
*((UINT32 *) InternalData) = *((UINT32 *) Data);
return EFI_SUCCESS;
case sizeof(UINT64):
*((UINT64 *) InternalData) = *((UINT64 *) Data);
return EFI_SUCCESS;
default:
ASSERT (FALSE);
return EFI_NOT_FOUND;
}
沒有留言:
張貼留言