/** @file | |
Help functions used by PCD DXE driver. | |
Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR> | |
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
(C) Copyright 2016-2021 Hewlett Packard Enterprise Development LP<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "Service.h" | |
#include <Library/DxeServicesLib.h> | |
PCD_DATABASE mPcdDatabase; | |
UINT32 mPcdTotalTokenCount; | |
UINT32 mPeiLocalTokenCount; | |
UINT32 mDxeLocalTokenCount; | |
UINT32 mPeiNexTokenCount; | |
UINT32 mDxeNexTokenCount; | |
UINT32 mPeiExMapppingTableSize; | |
UINT32 mDxeExMapppingTableSize; | |
UINT32 mPeiGuidTableSize; | |
UINT32 mDxeGuidTableSize; | |
BOOLEAN mPeiExMapTableEmpty; | |
BOOLEAN mDxeExMapTableEmpty; | |
BOOLEAN mPeiDatabaseEmpty; | |
LIST_ENTRY *mCallbackFnTable; | |
EFI_GUID **TmpTokenSpaceBuffer; | |
UINTN TmpTokenSpaceBufferCount; | |
UINTN mPeiPcdDbSize = 0; | |
PEI_PCD_DATABASE *mPeiPcdDbBinary = NULL; | |
UINTN mDxePcdDbSize = 0; | |
DXE_PCD_DATABASE *mDxePcdDbBinary = NULL; | |
/** | |
Get Local Token Number by Token Number. | |
@param[in] IsPeiDb If TRUE, the pcd entry is initialized in PEI phase, | |
If FALSE, the pcd entry is initialized in DXE phase. | |
@param[in] TokenNumber The PCD token number. | |
@return Local Token Number. | |
**/ | |
UINT32 | |
GetLocalTokenNumber ( | |
IN BOOLEAN IsPeiDb, | |
IN UINTN TokenNumber | |
) | |
{ | |
UINT32 *LocalTokenNumberTable; | |
// | |
// TokenNumber Zero is reserved as PCD_INVALID_TOKEN_NUMBER. | |
// We have to decrement TokenNumber by 1 to make it usable | |
// as the array index. | |
// | |
TokenNumber--; | |
LocalTokenNumberTable = IsPeiDb ? (UINT32 *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->LocalTokenNumberTableOffset) : | |
(UINT32 *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->LocalTokenNumberTableOffset); | |
TokenNumber = IsPeiDb ? TokenNumber : TokenNumber - mPeiLocalTokenCount; | |
return LocalTokenNumberTable[TokenNumber]; | |
} | |
/** | |
Get PCD type by Local Token Number. | |
@param[in] LocalTokenNumber The PCD local token number. | |
@return PCD type. | |
**/ | |
EFI_PCD_TYPE | |
GetPcdType ( | |
IN UINT32 LocalTokenNumber | |
) | |
{ | |
switch (LocalTokenNumber & PCD_DATUM_TYPE_ALL_SET) { | |
case PCD_DATUM_TYPE_POINTER: | |
return EFI_PCD_TYPE_PTR; | |
case PCD_DATUM_TYPE_UINT8: | |
if ((LocalTokenNumber & PCD_DATUM_TYPE_UINT8_BOOLEAN) == PCD_DATUM_TYPE_UINT8_BOOLEAN) { | |
return EFI_PCD_TYPE_BOOL; | |
} else { | |
return EFI_PCD_TYPE_8; | |
} | |
case PCD_DATUM_TYPE_UINT16: | |
return EFI_PCD_TYPE_16; | |
case PCD_DATUM_TYPE_UINT32: | |
return EFI_PCD_TYPE_32; | |
case PCD_DATUM_TYPE_UINT64: | |
return EFI_PCD_TYPE_64; | |
default: | |
ASSERT (FALSE); | |
return EFI_PCD_TYPE_8; | |
} | |
} | |
/** | |
Get PCD name. | |
@param[in] OnlyTokenSpaceName If TRUE, only need to get the TokenSpaceCName. | |
If FALSE, need to get the full PCD name. | |
@param[in] IsPeiDb If TRUE, the pcd entry is initialized in PEI phase, | |
If FALSE, the pcd entry is initialized in DXE phase. | |
@param[in] TokenNumber The PCD token number. | |
@return The TokenSpaceCName or full PCD name. | |
**/ | |
CHAR8 * | |
GetPcdName ( | |
IN BOOLEAN OnlyTokenSpaceName, | |
IN BOOLEAN IsPeiDb, | |
IN UINTN TokenNumber | |
) | |
{ | |
PCD_DATABASE_INIT *Database; | |
UINT8 *StringTable; | |
UINTN NameSize; | |
PCD_NAME_INDEX *PcdNameIndex; | |
CHAR8 *TokenSpaceName; | |
CHAR8 *PcdName; | |
CHAR8 *Name; | |
// | |
// Return NULL when PCD name table is absent. | |
// | |
if (IsPeiDb) { | |
if (mPcdDatabase.PeiDb->PcdNameTableOffset == 0) { | |
return NULL; | |
} | |
} else { | |
if (mPcdDatabase.DxeDb->PcdNameTableOffset == 0) { | |
return NULL; | |
} | |
} | |
// | |
// TokenNumber Zero is reserved as PCD_INVALID_TOKEN_NUMBER. | |
// We have to decrement TokenNumber by 1 to make it usable | |
// as the array index. | |
// | |
TokenNumber--; | |
Database = IsPeiDb ? mPcdDatabase.PeiDb : mPcdDatabase.DxeDb; | |
TokenNumber = IsPeiDb ? TokenNumber : TokenNumber - mPeiLocalTokenCount; | |
StringTable = (UINT8 *)Database + Database->StringTableOffset; | |
// | |
// Get the PCD name index. | |
// | |
PcdNameIndex = (PCD_NAME_INDEX *)((UINT8 *)Database + Database->PcdNameTableOffset) + TokenNumber; | |
TokenSpaceName = (CHAR8 *)&StringTable[PcdNameIndex->TokenSpaceCNameIndex]; | |
PcdName = (CHAR8 *)&StringTable[PcdNameIndex->PcdCNameIndex]; | |
if (OnlyTokenSpaceName) { | |
// | |
// Only need to get the TokenSpaceCName. | |
// | |
Name = AllocateCopyPool (AsciiStrSize (TokenSpaceName), TokenSpaceName); | |
} else { | |
// | |
// Need to get the full PCD name. | |
// | |
NameSize = AsciiStrSize (TokenSpaceName) + AsciiStrSize (PcdName); | |
Name = AllocateZeroPool (NameSize); | |
ASSERT (Name != NULL); | |
// | |
// Catenate TokenSpaceCName and PcdCName with a '.' to form the full PCD name. | |
// | |
AsciiStrCatS (Name, NameSize, TokenSpaceName); | |
Name[AsciiStrSize (TokenSpaceName) - sizeof (CHAR8)] = '.'; | |
AsciiStrCatS (Name, NameSize, PcdName); | |
} | |
return Name; | |
} | |
/** | |
Retrieve additional information associated with a PCD token. | |
This includes information such as the type of value the TokenNumber is associated with as well as possible | |
human readable name that is associated with the token. | |
@param[in] IsPeiDb If TRUE, the pcd entry is initialized in PEI phase, | |
If FALSE, the pcd entry is initialized in DXE phase. | |
@param[in] Guid The 128-bit unique value that designates the namespace from which to extract the value. | |
@param[in] TokenNumber The PCD token number. | |
@param[out] PcdInfo The returned information associated with the requested TokenNumber. | |
The caller is responsible for freeing the buffer that is allocated by callee for PcdInfo->PcdName. | |
@retval EFI_SUCCESS The PCD information was returned successfully | |
@retval EFI_NOT_FOUND The PCD service could not find the requested token number. | |
**/ | |
EFI_STATUS | |
ExGetPcdInfo ( | |
IN BOOLEAN IsPeiDb, | |
IN CONST EFI_GUID *Guid, | |
IN UINTN TokenNumber, | |
OUT EFI_PCD_INFO *PcdInfo | |
) | |
{ | |
PCD_DATABASE_INIT *Database; | |
UINTN GuidTableIdx; | |
EFI_GUID *MatchGuid; | |
EFI_GUID *GuidTable; | |
DYNAMICEX_MAPPING *ExMapTable; | |
UINTN Index; | |
UINT32 LocalTokenNumber; | |
Database = IsPeiDb ? mPcdDatabase.PeiDb : mPcdDatabase.DxeDb; | |
GuidTable = (EFI_GUID *)((UINT8 *)Database + Database->GuidTableOffset); | |
MatchGuid = ScanGuid (GuidTable, Database->GuidTableCount * sizeof (EFI_GUID), Guid); | |
if (MatchGuid == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
GuidTableIdx = MatchGuid - GuidTable; | |
ExMapTable = (DYNAMICEX_MAPPING *)((UINT8 *)Database + Database->ExMapTableOffset); | |
// | |
// Find the PCD by GuidTableIdx and ExTokenNumber in ExMapTable. | |
// | |
for (Index = 0; Index < Database->ExTokenCount; Index++) { | |
if (ExMapTable[Index].ExGuidIndex == GuidTableIdx) { | |
if (TokenNumber == PCD_INVALID_TOKEN_NUMBER) { | |
// | |
// TokenNumber is 0, follow spec to set PcdType to EFI_PCD_TYPE_8, | |
// PcdSize to 0 and PcdName to the null-terminated ASCII string | |
// associated with the token's namespace Guid. | |
// | |
PcdInfo->PcdType = EFI_PCD_TYPE_8; | |
PcdInfo->PcdSize = 0; | |
// | |
// Here use one representative in the token space to get the TokenSpaceCName. | |
// | |
PcdInfo->PcdName = GetPcdName (TRUE, IsPeiDb, ExMapTable[Index].TokenNumber); | |
return EFI_SUCCESS; | |
} else if (ExMapTable[Index].ExTokenNumber == TokenNumber) { | |
PcdInfo->PcdSize = DxePcdGetSize (ExMapTable[Index].TokenNumber); | |
LocalTokenNumber = GetLocalTokenNumber (IsPeiDb, ExMapTable[Index].TokenNumber); | |
PcdInfo->PcdType = GetPcdType (LocalTokenNumber); | |
PcdInfo->PcdName = GetPcdName (FALSE, IsPeiDb, ExMapTable[Index].TokenNumber); | |
return EFI_SUCCESS; | |
} | |
} | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Retrieve additional information associated with a PCD token. | |
This includes information such as the type of value the TokenNumber is associated with as well as possible | |
human readable name that is associated with the token. | |
@param[in] Guid The 128-bit unique value that designates the namespace from which to extract the value. | |
@param[in] TokenNumber The PCD token number. | |
@param[out] PcdInfo The returned information associated with the requested TokenNumber. | |
The caller is responsible for freeing the buffer that is allocated by callee for PcdInfo->PcdName. | |
@retval EFI_SUCCESS The PCD information was returned successfully. | |
@retval EFI_NOT_FOUND The PCD service could not find the requested token number. | |
**/ | |
EFI_STATUS | |
DxeGetPcdInfo ( | |
IN CONST EFI_GUID *Guid, | |
IN UINTN TokenNumber, | |
OUT EFI_PCD_INFO *PcdInfo | |
) | |
{ | |
EFI_STATUS Status; | |
BOOLEAN PeiExMapTableEmpty; | |
BOOLEAN DxeExMapTableEmpty; | |
UINT32 LocalTokenNumber; | |
BOOLEAN IsPeiDb; | |
ASSERT (PcdInfo != NULL); | |
Status = EFI_NOT_FOUND; | |
PeiExMapTableEmpty = mPeiExMapTableEmpty; | |
DxeExMapTableEmpty = mDxeExMapTableEmpty; | |
if (Guid == NULL) { | |
if (((TokenNumber + 1 > mPeiNexTokenCount + 1) && (TokenNumber + 1 <= mPeiLocalTokenCount + 1)) || | |
((TokenNumber + 1 > (mPeiLocalTokenCount + mDxeNexTokenCount + 1)))) | |
{ | |
return EFI_NOT_FOUND; | |
} else if (TokenNumber == PCD_INVALID_TOKEN_NUMBER) { | |
// | |
// TokenNumber is 0, follow spec to set PcdType to EFI_PCD_TYPE_8, | |
// PcdSize to 0 and PcdName to NULL for default Token Space. | |
// | |
PcdInfo->PcdType = EFI_PCD_TYPE_8; | |
PcdInfo->PcdSize = 0; | |
PcdInfo->PcdName = NULL; | |
} else { | |
PcdInfo->PcdSize = DxePcdGetSize (TokenNumber); | |
IsPeiDb = FALSE; | |
if ((TokenNumber + 1 <= mPeiNexTokenCount + 1)) { | |
IsPeiDb = TRUE; | |
} | |
LocalTokenNumber = GetLocalTokenNumber (IsPeiDb, TokenNumber); | |
PcdInfo->PcdType = GetPcdType (LocalTokenNumber); | |
PcdInfo->PcdName = GetPcdName (FALSE, IsPeiDb, TokenNumber); | |
} | |
return EFI_SUCCESS; | |
} | |
if (PeiExMapTableEmpty && DxeExMapTableEmpty) { | |
return EFI_NOT_FOUND; | |
} | |
if (!PeiExMapTableEmpty) { | |
Status = ExGetPcdInfo ( | |
TRUE, | |
Guid, | |
TokenNumber, | |
PcdInfo | |
); | |
} | |
if (Status == EFI_SUCCESS) { | |
return Status; | |
} | |
if (!DxeExMapTableEmpty) { | |
Status = ExGetPcdInfo ( | |
FALSE, | |
Guid, | |
TokenNumber, | |
PcdInfo | |
); | |
} | |
return Status; | |
} | |
/** | |
Get the PCD entry pointer in PCD database. | |
This routine will visit PCD database to find the PCD entry according to given | |
token number. The given token number is autogened by build tools and it will be | |
translated to local token number. Local token number contains PCD's type and | |
offset of PCD entry in PCD database. | |
@param TokenNumber Token's number, it is autogened by build tools | |
@param GetSize The size of token's value | |
@return PCD entry pointer in PCD database | |
**/ | |
VOID * | |
GetWorker ( | |
IN UINTN TokenNumber, | |
IN UINTN GetSize | |
) | |
{ | |
EFI_GUID *GuidTable; | |
UINT8 *StringTable; | |
EFI_GUID *Guid; | |
UINT16 *Name; | |
VARIABLE_HEAD *VariableHead; | |
UINT8 *VaraiableDefaultBuffer; | |
UINT8 *Data; | |
VPD_HEAD *VpdHead; | |
UINT8 *PcdDb; | |
VOID *RetPtr; | |
UINTN TmpTokenNumber; | |
UINTN DataSize; | |
EFI_STATUS Status; | |
UINT32 LocalTokenNumber; | |
UINT32 Offset; | |
STRING_HEAD StringTableIdx; | |
BOOLEAN IsPeiDb; | |
// | |
// Aquire lock to prevent reentrance from TPL_CALLBACK level | |
// | |
EfiAcquireLock (&mPcdDatabaseLock); | |
RetPtr = NULL; | |
ASSERT (TokenNumber > 0); | |
// | |
// TokenNumber Zero is reserved as PCD_INVALID_TOKEN_NUMBER. | |
// We have to decrement TokenNumber by 1 to make it usable | |
// as the array index. | |
// | |
TokenNumber--; | |
TmpTokenNumber = TokenNumber; | |
// | |
// EBC compiler is very choosy. It may report warning about comparison | |
// between UINTN and 0 . So we add 1 in each size of the | |
// comparison. | |
// | |
ASSERT (TokenNumber + 1 < mPcdTotalTokenCount + 1); | |
ASSERT ((GetSize == DxePcdGetSize (TokenNumber + 1)) || (GetSize == 0)); | |
// EBC compiler is very choosy. It may report warning about comparison | |
// between UINTN and 0 . So we add 1 in each size of the | |
// comparison. | |
IsPeiDb = (BOOLEAN)((TokenNumber + 1 < mPeiLocalTokenCount + 1) ? TRUE : FALSE); | |
LocalTokenNumber = GetLocalTokenNumber (IsPeiDb, TokenNumber + 1); | |
PcdDb = IsPeiDb ? ((UINT8 *)mPcdDatabase.PeiDb) : ((UINT8 *)mPcdDatabase.DxeDb); | |
if (IsPeiDb) { | |
StringTable = (UINT8 *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->StringTableOffset); | |
} else { | |
StringTable = (UINT8 *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->StringTableOffset); | |
} | |
Offset = LocalTokenNumber & PCD_DATABASE_OFFSET_MASK; | |
switch (LocalTokenNumber & PCD_TYPE_ALL_SET) { | |
case PCD_TYPE_VPD: | |
VpdHead = (VPD_HEAD *)((UINT8 *)PcdDb + Offset); | |
ASSERT (mVpdBaseAddress != 0); | |
RetPtr = (VOID *)(mVpdBaseAddress + VpdHead->Offset); | |
break; | |
case PCD_TYPE_HII|PCD_TYPE_STRING: | |
case PCD_TYPE_HII: | |
if (IsPeiDb) { | |
GuidTable = (EFI_GUID *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->GuidTableOffset); | |
} else { | |
GuidTable = (EFI_GUID *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->GuidTableOffset); | |
} | |
VariableHead = (VARIABLE_HEAD *)(PcdDb + Offset); | |
Guid = GuidTable + VariableHead->GuidTableIndex; | |
Name = (UINT16 *)(StringTable + VariableHead->StringIndex); | |
if ((LocalTokenNumber & PCD_TYPE_ALL_SET) == (PCD_TYPE_HII|PCD_TYPE_STRING)) { | |
// | |
// If a HII type PCD's datum type is VOID*, the DefaultValueOffset is the index of | |
// string array in string table. | |
// | |
StringTableIdx = *(STRING_HEAD *)((UINT8 *)PcdDb + VariableHead->DefaultValueOffset); | |
VaraiableDefaultBuffer = (UINT8 *)(StringTable + StringTableIdx); | |
} else { | |
VaraiableDefaultBuffer = (UINT8 *)PcdDb + VariableHead->DefaultValueOffset; | |
} | |
Status = GetHiiVariable (Guid, Name, &Data, &DataSize); | |
if (Status == EFI_SUCCESS) { | |
if (DataSize >= (VariableHead->Offset + GetSize)) { | |
if (GetSize == 0) { | |
// | |
// It is a pointer type. So get the MaxSize reserved for | |
// this PCD entry. | |
// | |
GetPtrTypeSize (TmpTokenNumber, &GetSize); | |
if (GetSize > (DataSize - VariableHead->Offset)) { | |
// | |
// Use actual valid size. | |
// | |
GetSize = DataSize - VariableHead->Offset; | |
} | |
} | |
// | |
// If the operation is successful, we copy the data | |
// to the default value buffer in the PCD Database. | |
// So that we can free the Data allocated in GetHiiVariable. | |
// | |
CopyMem (VaraiableDefaultBuffer, Data + VariableHead->Offset, GetSize); | |
} | |
FreePool (Data); | |
} | |
RetPtr = (VOID *)VaraiableDefaultBuffer; | |
break; | |
case PCD_TYPE_STRING: | |
StringTableIdx = *(STRING_HEAD *)((UINT8 *)PcdDb + Offset); | |
RetPtr = (VOID *)(StringTable + StringTableIdx); | |
break; | |
case PCD_TYPE_DATA: | |
RetPtr = (VOID *)((UINT8 *)PcdDb + Offset); | |
break; | |
default: | |
ASSERT (FALSE); | |
break; | |
} | |
EfiReleaseLock (&mPcdDatabaseLock); | |
return RetPtr; | |
} | |
/** | |
Register the callback function for a PCD entry. | |
This routine will register a callback function to a PCD entry by given token number | |
and token space guid. | |
@param TokenNumber PCD token's number, it is autogened by build tools. | |
@param Guid PCD token space's guid, | |
if not NULL, this PCD is dynamicEx type PCD. | |
@param CallBackFunction Callback function pointer | |
@return EFI_SUCCESS Always success for registering callback function. | |
**/ | |
EFI_STATUS | |
DxeRegisterCallBackWorker ( | |
IN UINTN TokenNumber, | |
IN CONST EFI_GUID *Guid OPTIONAL, | |
IN PCD_PROTOCOL_CALLBACK CallBackFunction | |
) | |
{ | |
CALLBACK_FN_ENTRY *FnTableEntry; | |
LIST_ENTRY *ListHead; | |
LIST_ENTRY *ListNode; | |
if (Guid != NULL) { | |
TokenNumber = GetExPcdTokenNumber (Guid, (UINT32)TokenNumber); | |
} | |
// | |
// TokenNumber Zero is reserved as PCD_INVALID_TOKEN_NUMBER. | |
// We have to decrement TokenNumber by 1 to make it usable | |
// as the array index of mCallbackFnTable[]. | |
// | |
ListHead = &mCallbackFnTable[TokenNumber - 1]; | |
ListNode = GetFirstNode (ListHead); | |
while (ListNode != ListHead) { | |
FnTableEntry = CR_FNENTRY_FROM_LISTNODE (ListNode, CALLBACK_FN_ENTRY, Node); | |
if (FnTableEntry->CallbackFn == CallBackFunction) { | |
// | |
// We only allow a Callback function to be register once | |
// for a TokenNumber. So just return EFI_SUCCESS | |
// | |
return EFI_SUCCESS; | |
} | |
ListNode = GetNextNode (ListHead, ListNode); | |
} | |
FnTableEntry = AllocatePool (sizeof (CALLBACK_FN_ENTRY)); | |
ASSERT (FnTableEntry != NULL); | |
FnTableEntry->CallbackFn = CallBackFunction; | |
InsertTailList (ListHead, &FnTableEntry->Node); | |
return EFI_SUCCESS; | |
} | |
/** | |
UnRegister the callback function for a PCD entry. | |
This routine will unregister a callback function to a PCD entry by given token number | |
and token space guid. | |
@param TokenNumber PCD token's number, it is autogened by build tools. | |
@param Guid PCD token space's guid. | |
if not NULL, this PCD is dynamicEx type PCD. | |
@param CallBackFunction Callback function pointer | |
@retval EFI_SUCCESS Callback function is success to be unregister. | |
@retval EFI_INVALID_PARAMETER Can not find the PCD entry by given token number. | |
**/ | |
EFI_STATUS | |
DxeUnRegisterCallBackWorker ( | |
IN UINTN TokenNumber, | |
IN CONST EFI_GUID *Guid OPTIONAL, | |
IN PCD_PROTOCOL_CALLBACK CallBackFunction | |
) | |
{ | |
CALLBACK_FN_ENTRY *FnTableEntry; | |
LIST_ENTRY *ListHead; | |
LIST_ENTRY *ListNode; | |
if (Guid != NULL) { | |
TokenNumber = GetExPcdTokenNumber (Guid, (UINT32)TokenNumber); | |
} | |
// | |
// TokenNumber Zero is reserved as PCD_INVALID_TOKEN_NUMBER. | |
// We have to decrement TokenNumber by 1 to make it usable | |
// as the array index of mCallbackFnTable[]. | |
// | |
ListHead = &mCallbackFnTable[TokenNumber - 1]; | |
ListNode = GetFirstNode (ListHead); | |
while (ListNode != ListHead) { | |
FnTableEntry = CR_FNENTRY_FROM_LISTNODE (ListNode, CALLBACK_FN_ENTRY, Node); | |
if (FnTableEntry->CallbackFn == CallBackFunction) { | |
// | |
// We only allow a Callback function to be register once | |
// for a TokenNumber. So we can safely remove the Node from | |
// the Link List and return EFI_SUCCESS. | |
// | |
RemoveEntryList (ListNode); | |
FreePool (FnTableEntry); | |
return EFI_SUCCESS; | |
} | |
ListNode = GetNextNode (ListHead, ListNode); | |
} | |
return EFI_INVALID_PARAMETER; | |
} | |
/** | |
Get next token number in given token space. | |
This routine is used for dynamicEx type PCD. It will firstly scan token space | |
table to get token space according to given token space guid. Then scan given | |
token number in found token space, if found, then return next token number in | |
this token space. | |
@param Guid Token space guid. Next token number will be scaned in | |
this token space. | |
@param TokenNumber Token number. | |
If PCD_INVALID_TOKEN_NUMBER, return first token number in | |
token space table. | |
If not PCD_INVALID_TOKEN_NUMBER, return next token number | |
in token space table. | |
@param GuidTable Token space guid table. It will be used for scan token space | |
by given token space guid. | |
@param SizeOfGuidTable The size of guid table. | |
@param ExMapTable DynamicEx token number mapping table. | |
@param SizeOfExMapTable The size of dynamicEx token number mapping table. | |
@retval EFI_NOT_FOUND Can not given token space or token number. | |
@retval EFI_SUCCESS Success to get next token number. | |
**/ | |
EFI_STATUS | |
ExGetNextTokeNumber ( | |
IN CONST EFI_GUID *Guid, | |
IN OUT UINTN *TokenNumber, | |
IN EFI_GUID *GuidTable, | |
IN UINTN SizeOfGuidTable, | |
IN DYNAMICEX_MAPPING *ExMapTable, | |
IN UINTN SizeOfExMapTable | |
) | |
{ | |
EFI_GUID *MatchGuid; | |
UINTN Index; | |
UINTN GuidTableIdx; | |
BOOLEAN Found; | |
UINTN ExMapTableCount; | |
// | |
// Scan token space guid | |
// | |
MatchGuid = ScanGuid (GuidTable, SizeOfGuidTable, Guid); | |
if (MatchGuid == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Find the token space table in dynamicEx mapping table. | |
// | |
Found = FALSE; | |
GuidTableIdx = MatchGuid - GuidTable; | |
ExMapTableCount = SizeOfExMapTable / sizeof (ExMapTable[0]); | |
for (Index = 0; Index < ExMapTableCount; Index++) { | |
if (ExMapTable[Index].ExGuidIndex == GuidTableIdx) { | |
Found = TRUE; | |
break; | |
} | |
} | |
if (Found) { | |
// | |
// If given token number is PCD_INVALID_TOKEN_NUMBER, then return the first | |
// token number in found token space. | |
// | |
if (*TokenNumber == PCD_INVALID_TOKEN_NUMBER) { | |
*TokenNumber = ExMapTable[Index].ExTokenNumber; | |
return EFI_SUCCESS; | |
} | |
for ( ; Index < ExMapTableCount; Index++) { | |
if ((ExMapTable[Index].ExTokenNumber == *TokenNumber) && (ExMapTable[Index].ExGuidIndex == GuidTableIdx)) { | |
break; | |
} | |
} | |
while (Index < ExMapTableCount) { | |
Index++; | |
if (Index == ExMapTableCount) { | |
// | |
// Exceed the length of ExMap Table | |
// | |
*TokenNumber = PCD_INVALID_TOKEN_NUMBER; | |
return EFI_NOT_FOUND; | |
} else if (ExMapTable[Index].ExGuidIndex == GuidTableIdx) { | |
// | |
// Found the next match | |
// | |
*TokenNumber = ExMapTable[Index].ExTokenNumber; | |
return EFI_SUCCESS; | |
} | |
} | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Find the PCD database. | |
@retval The base address of external PCD database binary. | |
@retval NULL Return NULL if not find. | |
**/ | |
DXE_PCD_DATABASE * | |
LocateExPcdBinary ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Search the External Pcd database from one section of current FFS, | |
// and read it to memory | |
// | |
Status = GetSectionFromFfs ( | |
EFI_SECTION_RAW, | |
0, | |
(VOID **)&mDxePcdDbBinary, | |
&mDxePcdDbSize | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Check the first bytes (Header Signature Guid) and build version. | |
// | |
if (!CompareGuid ((VOID *)mDxePcdDbBinary, &gPcdDataBaseSignatureGuid) || | |
(mDxePcdDbBinary->BuildVersion != PCD_SERVICE_DXE_VERSION)) | |
{ | |
ASSERT (FALSE); | |
} | |
return mDxePcdDbBinary; | |
} | |
/** | |
Update PCD database base on current SkuId | |
@param SkuId Current SkuId | |
@param IsPeiDb Whether to update PEI PCD database. | |
@retval EFI_SUCCESS Update PCD database successfully. | |
@retval EFI_NOT_FOUND Not found PCD database for current SkuId. | |
**/ | |
EFI_STATUS | |
UpdatePcdDatabase ( | |
IN SKU_ID SkuId, | |
IN BOOLEAN IsPeiDb | |
) | |
{ | |
UINTN Index; | |
PCD_DATABASE_SKU_DELTA *SkuDelta; | |
PCD_DATA_DELTA *SkuDeltaData; | |
if (IsPeiDb && (mPeiPcdDbBinary != NULL)) { | |
// | |
// Find the delta data for PEI DB | |
// | |
Index = (mPcdDatabase.PeiDb->Length + 7) & (~7); | |
SkuDelta = NULL; | |
while (Index < mPeiPcdDbSize) { | |
SkuDelta = (PCD_DATABASE_SKU_DELTA *)((UINT8 *)mPeiPcdDbBinary + Index); | |
if ((SkuDelta->SkuId == SkuId) && (SkuDelta->SkuIdCompared == 0)) { | |
break; | |
} | |
Index = (Index + SkuDelta->Length + 7) & (~7); | |
} | |
// | |
// Patch the delta data into current PCD database | |
// | |
if ((Index < mPeiPcdDbSize) && (SkuDelta != NULL)) { | |
SkuDeltaData = (PCD_DATA_DELTA *)(SkuDelta + 1); | |
while ((UINT8 *)SkuDeltaData < (UINT8 *)SkuDelta + SkuDelta->Length) { | |
*((UINT8 *)mPcdDatabase.PeiDb + SkuDeltaData->Offset) = (UINT8)SkuDeltaData->Value; | |
SkuDeltaData++; | |
} | |
} else { | |
return EFI_NOT_FOUND; | |
} | |
} | |
// | |
// Find the delta data for DXE DB | |
// | |
Index = (mPcdDatabase.DxeDb->Length + 7) & (~7); | |
SkuDelta = NULL; | |
if (Index == mDxePcdDbSize) { | |
return EFI_SUCCESS; | |
} | |
while (Index < mDxePcdDbSize) { | |
SkuDelta = (PCD_DATABASE_SKU_DELTA *)((UINT8 *)mDxePcdDbBinary + Index); | |
if ((SkuDelta->SkuId == SkuId) && (SkuDelta->SkuIdCompared == 0)) { | |
break; | |
} | |
Index = (Index + SkuDelta->Length + 7) & (~7); | |
} | |
// | |
// Patch the delta data into current PCD database | |
// | |
if ((Index < mDxePcdDbSize) && (SkuDelta != NULL)) { | |
SkuDeltaData = (PCD_DATA_DELTA *)(SkuDelta + 1); | |
while ((UINT8 *)SkuDeltaData < (UINT8 *)SkuDelta + SkuDelta->Length) { | |
*((UINT8 *)mPcdDatabase.DxeDb + SkuDeltaData->Offset) = (UINT8)SkuDeltaData->Value; | |
SkuDeltaData++; | |
} | |
return EFI_SUCCESS; | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Initialize the PCD database in DXE phase. | |
PCD database in DXE phase also contains PCD database in PEI phase which is copied | |
from GUID Hob. | |
**/ | |
VOID | |
BuildPcdDxeDataBase ( | |
VOID | |
) | |
{ | |
PEI_PCD_DATABASE *PeiDatabase; | |
EFI_HOB_GUID_TYPE *GuidHob; | |
UINTN Index; | |
UINT32 PcdDxeDbLen; | |
VOID *PcdDxeDb; | |
EFI_STATUS Status; | |
// | |
// Assign PCD Entries with default value to PCD DATABASE | |
// | |
mPcdDatabase.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); | |
mPcdDatabase.DxeDb = PcdDxeDb; | |
GuidHob = GetFirstGuidHob (&gPcdDataBaseHobGuid); | |
if (GuidHob != NULL) { | |
// | |
// If no PEIMs use dynamic Pcd Entry, the Pcd Service PEIM | |
// should not be included at all. So the GuidHob could | |
// be NULL. If it is NULL, we just copy over the DXE Default | |
// Value to PCD Database. | |
// | |
PeiDatabase = (PEI_PCD_DATABASE *)GET_GUID_HOB_DATA (GuidHob); | |
// | |
// Get next one that stores full PEI data | |
// | |
GuidHob = GetNextGuidHob (&gPcdDataBaseHobGuid, GET_NEXT_HOB (GuidHob)); | |
if (GuidHob != NULL) { | |
mPeiPcdDbBinary = (PEI_PCD_DATABASE *)GET_GUID_HOB_DATA (GuidHob); | |
mPeiPcdDbSize = (UINTN)GET_GUID_HOB_DATA_SIZE (GuidHob); | |
} | |
// | |
// Assign PCD Entries refereneced in PEI phase to PCD DATABASE | |
// | |
mPcdDatabase.PeiDb = PeiDatabase; | |
// | |
// Inherit the SystemSkuId from PEI phase. | |
// | |
if (mPcdDatabase.PeiDb->SystemSkuId != 0) { | |
Status = UpdatePcdDatabase (mPcdDatabase.PeiDb->SystemSkuId, FALSE); | |
ASSERT_EFI_ERROR (Status); | |
} | |
mPcdDatabase.DxeDb->SystemSkuId = mPcdDatabase.PeiDb->SystemSkuId; | |
} else { | |
mPcdDatabase.PeiDb = AllocateZeroPool (sizeof (PEI_PCD_DATABASE)); | |
ASSERT (mPcdDatabase.PeiDb != NULL); | |
} | |
// | |
// Initialized the external PCD database local variables | |
// | |
mPeiLocalTokenCount = mPcdDatabase.PeiDb->LocalTokenCount; | |
mDxeLocalTokenCount = mPcdDatabase.DxeDb->LocalTokenCount; | |
mPeiExMapppingTableSize = mPcdDatabase.PeiDb->ExTokenCount * sizeof (DYNAMICEX_MAPPING); | |
mDxeExMapppingTableSize = mPcdDatabase.DxeDb->ExTokenCount * sizeof (DYNAMICEX_MAPPING); | |
mPeiGuidTableSize = mPcdDatabase.PeiDb->GuidTableCount * sizeof (GUID); | |
mDxeGuidTableSize = mPcdDatabase.DxeDb->GuidTableCount * sizeof (GUID); | |
mPcdTotalTokenCount = mPeiLocalTokenCount + mDxeLocalTokenCount; | |
mPeiNexTokenCount = mPeiLocalTokenCount - mPcdDatabase.PeiDb->ExTokenCount; | |
mDxeNexTokenCount = mDxeLocalTokenCount - mPcdDatabase.DxeDb->ExTokenCount; | |
mPeiExMapTableEmpty = (mPcdDatabase.PeiDb->ExTokenCount == 0) ? TRUE : FALSE; | |
mDxeExMapTableEmpty = (mPcdDatabase.DxeDb->ExTokenCount == 0) ? TRUE : FALSE; | |
mPeiDatabaseEmpty = (mPeiLocalTokenCount == 0) ? TRUE : FALSE; | |
TmpTokenSpaceBufferCount = mPcdDatabase.PeiDb->ExTokenCount + mPcdDatabase.DxeDb->ExTokenCount; | |
TmpTokenSpaceBuffer = (EFI_GUID **)AllocateZeroPool (TmpTokenSpaceBufferCount * sizeof (EFI_GUID *)); | |
// | |
// Initialized the Callback Function Table | |
// | |
mCallbackFnTable = AllocateZeroPool (mPcdTotalTokenCount * sizeof (LIST_ENTRY)); | |
ASSERT (mCallbackFnTable != NULL); | |
// | |
// EBC compiler is very choosy. It may report warning about comparison | |
// between UINTN and 0 . So we add 1 in each size of the | |
// comparison. | |
// | |
for (Index = 0; Index + 1 < mPcdTotalTokenCount + 1; Index++) { | |
InitializeListHead (&mCallbackFnTable[Index]); | |
} | |
} | |
/** | |
Get Variable which contains HII type PCD entry. | |
@param VariableGuid Variable's guid | |
@param VariableName Variable's unicode name string | |
@param VariableData Variable's data pointer, | |
@param VariableSize Variable's size. | |
@return the status of gRT->GetVariable | |
**/ | |
EFI_STATUS | |
GetHiiVariable ( | |
IN EFI_GUID *VariableGuid, | |
IN UINT16 *VariableName, | |
OUT UINT8 **VariableData, | |
OUT UINTN *VariableSize | |
) | |
{ | |
UINTN Size; | |
EFI_STATUS Status; | |
UINT8 *Buffer; | |
Size = 0; | |
Buffer = NULL; | |
// | |
// Firstly get the real size of HII variable | |
// | |
Status = gRT->GetVariable ( | |
(UINT16 *)VariableName, | |
VariableGuid, | |
NULL, | |
&Size, | |
Buffer | |
); | |
// | |
// Allocate buffer to hold whole variable data according to variable size. | |
// | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
Buffer = (UINT8 *)AllocatePool (Size); | |
ASSERT (Buffer != NULL); | |
Status = gRT->GetVariable ( | |
VariableName, | |
VariableGuid, | |
NULL, | |
&Size, | |
Buffer | |
); | |
ASSERT (Status == EFI_SUCCESS); | |
*VariableData = Buffer; | |
*VariableSize = Size; | |
} else { | |
// | |
// Use Default Data only when variable is not found. | |
// For other error status, correct data can't be got, and trig ASSERT(). | |
// | |
ASSERT (Status == EFI_NOT_FOUND); | |
} | |
return Status; | |
} | |
/** | |
Invoke the callback function when dynamic PCD entry was set, if this PCD entry | |
has registered callback function. | |
@param ExTokenNumber DynamicEx PCD's token number, if this PCD entry is dyanmicEx | |
type PCD. | |
@param Guid DynamicEx PCD's guid, if this PCD entry is dynamicEx type | |
PCD. | |
@param TokenNumber PCD token number generated by build tools. | |
@param Data Value want to be set for this PCD entry | |
@param Size The size of value | |
**/ | |
VOID | |
InvokeCallbackOnSet ( | |
UINT32 ExTokenNumber, | |
CONST EFI_GUID *Guid OPTIONAL, | |
UINTN TokenNumber, | |
VOID *Data, | |
UINTN Size | |
) | |
{ | |
CALLBACK_FN_ENTRY *FnTableEntry; | |
LIST_ENTRY *ListHead; | |
LIST_ENTRY *ListNode; | |
// | |
// TokenNumber Zero is reserved as PCD_INVALID_TOKEN_NUMBER. | |
// We have to decrement TokenNumber by 1 to make it usable | |
// as the array index of mCallbackFnTable[]. | |
// | |
ListHead = &mCallbackFnTable[TokenNumber - 1]; | |
ListNode = GetFirstNode (ListHead); | |
while (ListNode != ListHead) { | |
FnTableEntry = CR_FNENTRY_FROM_LISTNODE (ListNode, CALLBACK_FN_ENTRY, Node); | |
FnTableEntry->CallbackFn ( | |
Guid, | |
(Guid == NULL) ? TokenNumber : ExTokenNumber, | |
Data, | |
Size | |
); | |
ListNode = GetNextNode (ListHead, ListNode); | |
} | |
return; | |
} | |
/** | |
Wrapper function for setting non-pointer type value for a PCD entry. | |
@param TokenNumber Pcd token number autogenerated by build tools. | |
@param Data Value want to be set for PCD entry | |
@param Size Size of value. | |
@return status of SetWorker. | |
**/ | |
EFI_STATUS | |
SetValueWorker ( | |
IN UINTN TokenNumber, | |
IN VOID *Data, | |
IN UINTN Size | |
) | |
{ | |
return SetWorker (TokenNumber, Data, &Size, FALSE); | |
} | |
/** | |
Set value for an PCD entry | |
@param TokenNumber Pcd token number autogenerated by build tools. | |
@param Data Value want to be set for PCD entry | |
@param Size Size of value. | |
@param PtrType If TRUE, the type of PCD entry's value is Pointer. | |
If False, the type of PCD entry's value is not Pointer. | |
@retval EFI_INVALID_PARAMETER If this PCD type is VPD, VPD PCD can not be set. | |
@retval EFI_INVALID_PARAMETER If Size can not be set to size table. | |
@retval EFI_INVALID_PARAMETER If Size of non-Ptr type PCD does not match the size information in PCD database. | |
@retval EFI_NOT_FOUND If value type of PCD entry is intergrate, but not in | |
range of UINT8, UINT16, UINT32, UINT64 | |
@retval EFI_NOT_FOUND Can not find the PCD type according to token number. | |
**/ | |
EFI_STATUS | |
SetWorker ( | |
IN UINTN TokenNumber, | |
IN VOID *Data, | |
IN OUT UINTN *Size, | |
IN BOOLEAN PtrType | |
) | |
{ | |
BOOLEAN IsPeiDb; | |
UINT32 LocalTokenNumber; | |
EFI_GUID *GuidTable; | |
UINT8 *StringTable; | |
EFI_GUID *Guid; | |
UINT16 *Name; | |
UINTN VariableOffset; | |
UINT32 Attributes; | |
VOID *InternalData; | |
VARIABLE_HEAD *VariableHead; | |
UINTN Offset; | |
UINT8 *PcdDb; | |
EFI_STATUS Status; | |
UINTN MaxSize; | |
UINTN TmpTokenNumber; | |
// | |
// TokenNumber Zero is reserved as PCD_INVALID_TOKEN_NUMBER. | |
// We have to decrement TokenNumber by 1 to make it usable | |
// as the array index. | |
// | |
TokenNumber--; | |
TmpTokenNumber = TokenNumber; | |
// | |
// EBC compiler is very choosy. It may report warning about comparison | |
// between UINTN and 0 . So we add 1 in each size of the | |
// comparison. | |
// | |
ASSERT (TokenNumber + 1 < mPcdTotalTokenCount + 1); | |
if (PtrType) { | |
// | |
// Get MaxSize first, then check new size with max buffer size. | |
// | |
GetPtrTypeSize (TokenNumber, &MaxSize); | |
if (*Size > MaxSize) { | |
*Size = MaxSize; | |
return EFI_INVALID_PARAMETER; | |
} | |
} else { | |
if (*Size != DxePcdGetSize (TokenNumber + 1)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
// | |
// EBC compiler is very choosy. It may report warning about comparison | |
// between UINTN and 0 . So we add 1 in each size of the | |
// comparison. | |
// | |
if ((TokenNumber + 1 < mPeiNexTokenCount + 1) || | |
((TokenNumber + 1 >= mPeiLocalTokenCount + 1) && (TokenNumber + 1 < (mPeiLocalTokenCount + mDxeNexTokenCount + 1)))) | |
{ | |
InvokeCallbackOnSet (0, NULL, TokenNumber + 1, Data, *Size); | |
} | |
// | |
// Aquire lock to prevent reentrance from TPL_CALLBACK level | |
// | |
EfiAcquireLock (&mPcdDatabaseLock); | |
// | |
// EBC compiler is very choosy. It may report warning about comparison | |
// between UINTN and 0 . So we add 1 in each size of the | |
// comparison. | |
// | |
IsPeiDb = (BOOLEAN)((TokenNumber + 1 < mPeiLocalTokenCount + 1) ? TRUE : FALSE); | |
LocalTokenNumber = GetLocalTokenNumber (IsPeiDb, TokenNumber + 1); | |
Offset = LocalTokenNumber & PCD_DATABASE_OFFSET_MASK; | |
PcdDb = IsPeiDb ? ((UINT8 *)mPcdDatabase.PeiDb) : ((UINT8 *)mPcdDatabase.DxeDb); | |
if (IsPeiDb) { | |
StringTable = (UINT8 *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->StringTableOffset); | |
} else { | |
StringTable = (UINT8 *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->StringTableOffset); | |
} | |
InternalData = PcdDb + Offset; | |
switch (LocalTokenNumber & PCD_TYPE_ALL_SET) { | |
case PCD_TYPE_VPD: | |
ASSERT (FALSE); | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
case PCD_TYPE_STRING: | |
if (SetPtrTypeSize (TmpTokenNumber, Size)) { | |
CopyMem (StringTable + *((STRING_HEAD *)InternalData), Data, *Size); | |
Status = EFI_SUCCESS; | |
} else { | |
Status = EFI_INVALID_PARAMETER; | |
} | |
break; | |
case PCD_TYPE_HII|PCD_TYPE_STRING: | |
case PCD_TYPE_HII: | |
if (PtrType) { | |
if (!SetPtrTypeSize (TmpTokenNumber, Size)) { | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
} | |
if (IsPeiDb) { | |
GuidTable = (EFI_GUID *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->GuidTableOffset); | |
} else { | |
GuidTable = (EFI_GUID *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->GuidTableOffset); | |
} | |
VariableHead = (VARIABLE_HEAD *)(PcdDb + Offset); | |
Guid = GuidTable + VariableHead->GuidTableIndex; | |
Name = (UINT16 *)(StringTable + VariableHead->StringIndex); | |
VariableOffset = VariableHead->Offset; | |
Attributes = VariableHead->Attributes; | |
Status = SetHiiVariable (Guid, Name, Attributes, Data, *Size, VariableOffset); | |
break; | |
case PCD_TYPE_DATA: | |
if (PtrType) { | |
if (SetPtrTypeSize (TmpTokenNumber, Size)) { | |
CopyMem (InternalData, Data, *Size); | |
Status = EFI_SUCCESS; | |
} else { | |
Status = EFI_INVALID_PARAMETER; | |
} | |
break; | |
} | |
Status = EFI_SUCCESS; | |
switch (*Size) { | |
case sizeof (UINT8): | |
*((UINT8 *)InternalData) = *((UINT8 *)Data); | |
break; | |
case sizeof (UINT16): | |
*((UINT16 *)InternalData) = *((UINT16 *)Data); | |
break; | |
case sizeof (UINT32): | |
*((UINT32 *)InternalData) = *((UINT32 *)Data); | |
break; | |
case sizeof (UINT64): | |
*((UINT64 *)InternalData) = *((UINT64 *)Data); | |
break; | |
default: | |
ASSERT (FALSE); | |
Status = EFI_NOT_FOUND; | |
break; | |
} | |
break; | |
default: | |
ASSERT (FALSE); | |
Status = EFI_NOT_FOUND; | |
break; | |
} | |
EfiReleaseLock (&mPcdDatabaseLock); | |
return Status; | |
} | |
/** | |
Wrapper function for get PCD value for dynamic-ex PCD. | |
@param Guid Token space guid for dynamic-ex PCD. | |
@param ExTokenNumber Token number for dynamic-ex PCD. | |
@param GetSize The size of dynamic-ex PCD value. | |
@return PCD entry in PCD database. | |
**/ | |
VOID * | |
ExGetWorker ( | |
IN CONST EFI_GUID *Guid, | |
IN UINTN ExTokenNumber, | |
IN UINTN GetSize | |
) | |
{ | |
return GetWorker (GetExPcdTokenNumber (Guid, (UINT32)ExTokenNumber), GetSize); | |
} | |
/** | |
Wrapper function for set PCD value for non-Pointer type dynamic-ex PCD. | |
@param ExTokenNumber Token number for dynamic-ex PCD. | |
@param Guid Token space guid for dynamic-ex PCD. | |
@param Data Value want to be set. | |
@param SetSize The size of value. | |
@return status of ExSetWorker(). | |
**/ | |
EFI_STATUS | |
ExSetValueWorker ( | |
IN UINTN ExTokenNumber, | |
IN CONST EFI_GUID *Guid, | |
IN VOID *Data, | |
IN UINTN SetSize | |
) | |
{ | |
return ExSetWorker (ExTokenNumber, Guid, Data, &SetSize, FALSE); | |
} | |
/** | |
Set value for a dynamic-ex PCD entry. | |
This routine find the local token number according to dynamic-ex PCD's token | |
space guid and token number firstly, and invoke callback function if this PCD | |
entry registered callback function. Finally, invoken general SetWorker to set | |
PCD value. | |
@param ExTokenNumber Dynamic-ex PCD token number. | |
@param Guid Token space guid for dynamic-ex PCD. | |
@param Data PCD value want to be set | |
@param SetSize Size of value. | |
@param PtrType If TRUE, this PCD entry is pointer type. | |
If FALSE, this PCD entry is not pointer type. | |
@return status of SetWorker(). | |
**/ | |
EFI_STATUS | |
ExSetWorker ( | |
IN UINTN ExTokenNumber, | |
IN CONST EFI_GUID *Guid, | |
IN VOID *Data, | |
IN OUT UINTN *SetSize, | |
IN BOOLEAN PtrType | |
) | |
{ | |
UINTN TokenNumber; | |
TokenNumber = GetExPcdTokenNumber (Guid, (UINT32)ExTokenNumber); | |
InvokeCallbackOnSet ((UINT32)ExTokenNumber, Guid, TokenNumber, Data, *SetSize); | |
return SetWorker (TokenNumber, Data, SetSize, PtrType); | |
} | |
/** | |
Get variable size and data from HII-type PCDs. | |
@param[in] VariableGuid Guid of variable which stored value of a HII-type PCD. | |
@param[in] VariableName Unicode name of variable which stored value of a HII-type PCD. | |
@param[out] VariableSize Pointer to variable size got from HII-type PCDs. | |
@param[out] VariableData Pointer to variable data got from HII-type PCDs. | |
**/ | |
VOID | |
GetVariableSizeAndDataFromHiiPcd ( | |
IN EFI_GUID *VariableGuid, | |
IN UINT16 *VariableName, | |
OUT UINTN *VariableSize, | |
OUT VOID *VariableData OPTIONAL | |
) | |
{ | |
BOOLEAN IsPeiDb; | |
PCD_DATABASE_INIT *Database; | |
UINTN TokenNumber; | |
UINT32 LocalTokenNumber; | |
UINTN Offset; | |
EFI_GUID *GuidTable; | |
UINT8 *StringTable; | |
VARIABLE_HEAD *VariableHead; | |
EFI_GUID *Guid; | |
UINT16 *Name; | |
UINTN PcdDataSize; | |
UINTN Size; | |
UINT8 *VaraiableDefaultBuffer; | |
STRING_HEAD StringTableIdx; | |
*VariableSize = 0; | |
// | |
// Go through PCD database to find out DynamicHii PCDs. | |
// | |
for (TokenNumber = 1; TokenNumber <= mPcdTotalTokenCount; TokenNumber++) { | |
IsPeiDb = (BOOLEAN)((TokenNumber + 1 < mPeiLocalTokenCount + 1) ? TRUE : FALSE); | |
Database = IsPeiDb ? mPcdDatabase.PeiDb : mPcdDatabase.DxeDb; | |
LocalTokenNumber = GetLocalTokenNumber (IsPeiDb, TokenNumber); | |
if ((LocalTokenNumber & PCD_TYPE_HII) != 0) { | |
// | |
// Get the Variable Guid and Name pointer. | |
// | |
Offset = LocalTokenNumber & PCD_DATABASE_OFFSET_MASK; | |
VariableHead = (VARIABLE_HEAD *)((UINT8 *)Database + Offset); | |
StringTable = (UINT8 *)((UINT8 *)Database + Database->StringTableOffset); | |
GuidTable = (EFI_GUID *)((UINT8 *)Database + Database->GuidTableOffset); | |
Guid = GuidTable + VariableHead->GuidTableIndex; | |
Name = (UINT16 *)(StringTable + VariableHead->StringIndex); | |
if (CompareGuid (VariableGuid, Guid) && (StrCmp (VariableName, Name) == 0)) { | |
// | |
// It is the matched DynamicHii PCD. | |
// | |
PcdDataSize = DxePcdGetSize (TokenNumber); | |
Size = VariableHead->Offset + PcdDataSize; | |
if (Size > *VariableSize) { | |
*VariableSize = Size; | |
} | |
if (VariableData != NULL) { | |
if ((LocalTokenNumber & PCD_TYPE_ALL_SET) == (PCD_TYPE_HII|PCD_TYPE_STRING)) { | |
// | |
// If a HII type PCD's datum type is VOID*, the DefaultValueOffset is the index of | |
// string array in string table. | |
// | |
StringTableIdx = *(STRING_HEAD *)((UINT8 *)Database + VariableHead->DefaultValueOffset); | |
VaraiableDefaultBuffer = (UINT8 *)(StringTable + StringTableIdx); | |
} else { | |
VaraiableDefaultBuffer = (UINT8 *)Database + VariableHead->DefaultValueOffset; | |
} | |
CopyMem ((UINT8 *)VariableData + VariableHead->Offset, VaraiableDefaultBuffer, PcdDataSize); | |
} | |
} | |
} | |
} | |
} | |
/** | |
Set value for HII-type PCD. | |
A HII-type PCD's value is stored in a variable. Setting/Getting the value of | |
HII-type PCD is to visit this variable. | |
@param VariableGuid Guid of variable which stored value of a HII-type PCD. | |
@param VariableName Unicode name of variable which stored value of a HII-type PCD. | |
@param SetAttributes Attributes bitmask to set for the variable. | |
@param Data Value want to be set. | |
@param DataSize Size of value | |
@param Offset Value offset of HII-type PCD in variable. | |
@return status of GetVariable()/SetVariable(). | |
**/ | |
EFI_STATUS | |
SetHiiVariable ( | |
IN EFI_GUID *VariableGuid, | |
IN UINT16 *VariableName, | |
IN UINT32 SetAttributes, | |
IN CONST VOID *Data, | |
IN UINTN DataSize, | |
IN UINTN Offset | |
) | |
{ | |
UINTN Size; | |
VOID *Buffer; | |
EFI_STATUS Status; | |
UINT32 Attribute; | |
UINTN SetSize; | |
Size = 0; | |
SetSize = 0; | |
// | |
// Try to get original variable size information. | |
// | |
Status = gRT->GetVariable ( | |
(UINT16 *)VariableName, | |
VariableGuid, | |
NULL, | |
&Size, | |
NULL | |
); | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
// | |
// Patch new PCD's value to offset in given HII variable. | |
// | |
if (Size >= (DataSize + Offset)) { | |
SetSize = Size; | |
} else { | |
SetSize = DataSize + Offset; | |
} | |
Buffer = AllocatePool (SetSize); | |
ASSERT (Buffer != NULL); | |
Status = gRT->GetVariable ( | |
VariableName, | |
VariableGuid, | |
&Attribute, | |
&Size, | |
Buffer | |
); | |
ASSERT_EFI_ERROR (Status); | |
CopyMem ((UINT8 *)Buffer + Offset, Data, DataSize); | |
if (SetAttributes == 0) { | |
SetAttributes = Attribute; | |
} | |
Status = gRT->SetVariable ( | |
VariableName, | |
VariableGuid, | |
SetAttributes, | |
SetSize, | |
Buffer | |
); | |
FreePool (Buffer); | |
return Status; | |
} else if (Status == EFI_NOT_FOUND) { | |
// | |
// If variable does not exist, a new variable need to be created. | |
// | |
// | |
// Get size, allocate buffer and get data. | |
// | |
GetVariableSizeAndDataFromHiiPcd (VariableGuid, VariableName, &Size, NULL); | |
Buffer = AllocateZeroPool (Size); | |
ASSERT (Buffer != NULL); | |
GetVariableSizeAndDataFromHiiPcd (VariableGuid, VariableName, &Size, Buffer); | |
// | |
// Update buffer. | |
// | |
CopyMem ((UINT8 *)Buffer + Offset, Data, DataSize); | |
if (SetAttributes == 0) { | |
SetAttributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE; | |
} | |
Status = gRT->SetVariable ( | |
VariableName, | |
VariableGuid, | |
SetAttributes, | |
Size, | |
Buffer | |
); | |
FreePool (Buffer); | |
return Status; | |
} | |
// | |
// If we drop to here, the value is failed to be written in to variable area. | |
// | |
return Status; | |
} | |
/** | |
Get Token Number according to dynamic-ex PCD's {token space guid:token number} | |
A dynamic-ex type PCD, developer must provide pair of token space guid: token number | |
in DEC file. PCD database maintain a mapping table that translate pair of {token | |
space guid: token number} to Token Number. | |
@param Guid Token space guid for dynamic-ex PCD entry. | |
@param ExTokenNumber Dynamic-ex PCD token number. | |
@return Token Number for dynamic-ex PCD. | |
**/ | |
UINTN | |
GetExPcdTokenNumber ( | |
IN CONST EFI_GUID *Guid, | |
IN UINT32 ExTokenNumber | |
) | |
{ | |
UINT32 Index; | |
DYNAMICEX_MAPPING *ExMap; | |
EFI_GUID *GuidTable; | |
EFI_GUID *MatchGuid; | |
UINTN MatchGuidIdx; | |
if (!mPeiDatabaseEmpty) { | |
ExMap = (DYNAMICEX_MAPPING *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->ExMapTableOffset); | |
GuidTable = (EFI_GUID *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->GuidTableOffset); | |
MatchGuid = ScanGuid (GuidTable, mPeiGuidTableSize, Guid); | |
if (MatchGuid != NULL) { | |
MatchGuidIdx = MatchGuid - GuidTable; | |
for (Index = 0; Index < mPcdDatabase.PeiDb->ExTokenCount; Index++) { | |
if ((ExTokenNumber == ExMap[Index].ExTokenNumber) && | |
(MatchGuidIdx == ExMap[Index].ExGuidIndex)) | |
{ | |
return ExMap[Index].TokenNumber; | |
} | |
} | |
} | |
} | |
ExMap = (DYNAMICEX_MAPPING *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->ExMapTableOffset); | |
GuidTable = (EFI_GUID *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->GuidTableOffset); | |
MatchGuid = ScanGuid (GuidTable, mDxeGuidTableSize, Guid); | |
// | |
// We need to ASSERT here. If GUID can't be found in GuidTable, this is a | |
// error in the BUILD system. | |
// | |
ASSERT (MatchGuid != NULL); | |
MatchGuidIdx = MatchGuid - GuidTable; | |
for (Index = 0; Index < mPcdDatabase.DxeDb->ExTokenCount; Index++) { | |
if ((ExTokenNumber == ExMap[Index].ExTokenNumber) && | |
(MatchGuidIdx == ExMap[Index].ExGuidIndex)) | |
{ | |
return ExMap[Index].TokenNumber; | |
} | |
} | |
DEBUG ((DEBUG_ERROR, "%a: Failed to find PCD with GUID: %g and token number: %d\n", __func__, Guid, ExTokenNumber)); | |
ASSERT (FALSE); | |
return 0; | |
} | |
/** | |
Wrapper function of getting index of PCD entry in size table. | |
@param LocalTokenNumberTableIdx Index of this PCD in local token number table. | |
@param IsPeiDb If TRUE, the pcd entry is initialized in PEI phase, | |
If FALSE, the pcd entry is initialized in DXE phase. | |
@return index of PCD entry in size table. | |
**/ | |
UINTN | |
GetSizeTableIndex ( | |
IN UINTN LocalTokenNumberTableIdx, | |
IN BOOLEAN IsPeiDb | |
) | |
{ | |
UINT32 *LocalTokenNumberTable; | |
UINTN LocalTokenNumber; | |
UINTN Index; | |
UINTN SizeTableIdx; | |
if (IsPeiDb) { | |
LocalTokenNumberTable = (UINT32 *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->LocalTokenNumberTableOffset); | |
} else { | |
LocalTokenNumberTable = (UINT32 *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->LocalTokenNumberTableOffset); | |
} | |
SizeTableIdx = 0; | |
for (Index = 0; Index < LocalTokenNumberTableIdx; Index++) { | |
LocalTokenNumber = LocalTokenNumberTable[Index]; | |
if ((LocalTokenNumber & PCD_DATUM_TYPE_ALL_SET) == PCD_DATUM_TYPE_POINTER) { | |
// | |
// SizeTable only contain record for PCD_DATUM_TYPE_POINTER type | |
// PCD entry. | |
// | |
if ((LocalTokenNumber & PCD_TYPE_VPD) != 0) { | |
// | |
// We have only two entry for VPD enabled PCD entry: | |
// 1) MAX Size. | |
// 2) Current Size | |
// Current size is equal to MAX size. | |
// | |
SizeTableIdx += 2; | |
} else { | |
// | |
// We have only two entry for Non-Sku enabled PCD entry: | |
// 1) MAX SIZE | |
// 2) Current Size | |
// | |
SizeTableIdx += 2; | |
} | |
} | |
} | |
return SizeTableIdx; | |
} | |
/** | |
Get size of POINTER type PCD value. | |
@param LocalTokenNumberTableIdx Index of local token number in local token number table. | |
@param MaxSize Maxmium size of POINTER type PCD value. | |
@return size of POINTER type PCD value. | |
**/ | |
UINTN | |
GetPtrTypeSize ( | |
IN UINTN LocalTokenNumberTableIdx, | |
OUT UINTN *MaxSize | |
) | |
{ | |
INTN SizeTableIdx; | |
UINTN LocalTokenNumber; | |
SIZE_INFO *SizeTable; | |
BOOLEAN IsPeiDb; | |
UINT32 *LocalTokenNumberTable; | |
// EBC compiler is very choosy. It may report warning about comparison | |
// between UINTN and 0 . So we add 1 in each size of the | |
// comparison. | |
IsPeiDb = (BOOLEAN)(LocalTokenNumberTableIdx + 1 < mPeiLocalTokenCount + 1); | |
if (IsPeiDb) { | |
LocalTokenNumberTable = (UINT32 *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->LocalTokenNumberTableOffset); | |
SizeTable = (SIZE_INFO *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->SizeTableOffset); | |
} else { | |
LocalTokenNumberTableIdx -= mPeiLocalTokenCount; | |
LocalTokenNumberTable = (UINT32 *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->LocalTokenNumberTableOffset); | |
SizeTable = (SIZE_INFO *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->SizeTableOffset); | |
} | |
LocalTokenNumber = LocalTokenNumberTable[LocalTokenNumberTableIdx]; | |
ASSERT ((LocalTokenNumber & PCD_DATUM_TYPE_ALL_SET) == PCD_DATUM_TYPE_POINTER); | |
SizeTableIdx = GetSizeTableIndex (LocalTokenNumberTableIdx, IsPeiDb); | |
*MaxSize = SizeTable[SizeTableIdx]; | |
// | |
// SizeTable only contain record for PCD_DATUM_TYPE_POINTER type | |
// PCD entry. | |
// | |
if ((LocalTokenNumber & PCD_TYPE_VPD) != 0) { | |
// | |
// We have only two entry for VPD enabled PCD entry: | |
// 1) MAX Size. | |
// 2) Current Size | |
// We consider current size is equal to MAX size. | |
// | |
return *MaxSize; | |
} else { | |
// | |
// We have only two entry for Non-Sku enabled PCD entry: | |
// 1) MAX SIZE | |
// 2) Current Size | |
// | |
return SizeTable[SizeTableIdx + 1]; | |
} | |
} | |
/** | |
Set size of POINTER type PCD value. The size should not exceed the maximum size | |
of this PCD value. | |
@param LocalTokenNumberTableIdx Index of local token number in local token number table. | |
@param CurrentSize Size of POINTER type PCD value. | |
@retval TRUE Success to set size of PCD value. | |
@retval FALSE Fail to set size of PCD value. | |
**/ | |
BOOLEAN | |
SetPtrTypeSize ( | |
IN UINTN LocalTokenNumberTableIdx, | |
IN OUT UINTN *CurrentSize | |
) | |
{ | |
INTN SizeTableIdx; | |
UINTN LocalTokenNumber; | |
SIZE_INFO *SizeTable; | |
UINTN MaxSize; | |
BOOLEAN IsPeiDb; | |
UINT32 *LocalTokenNumberTable; | |
// | |
// EBC compiler is very choosy. It may report warning about comparison | |
// between UINTN and 0 . So we add 1 in each size of the | |
// comparison. | |
// | |
IsPeiDb = (BOOLEAN)(LocalTokenNumberTableIdx + 1 < mPeiLocalTokenCount + 1); | |
if (IsPeiDb) { | |
LocalTokenNumberTable = (UINT32 *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->LocalTokenNumberTableOffset); | |
SizeTable = (SIZE_INFO *)((UINT8 *)mPcdDatabase.PeiDb + mPcdDatabase.PeiDb->SizeTableOffset); | |
} else { | |
LocalTokenNumberTableIdx -= mPeiLocalTokenCount; | |
LocalTokenNumberTable = (UINT32 *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->LocalTokenNumberTableOffset); | |
SizeTable = (SIZE_INFO *)((UINT8 *)mPcdDatabase.DxeDb + mPcdDatabase.DxeDb->SizeTableOffset); | |
} | |
LocalTokenNumber = LocalTokenNumberTable[LocalTokenNumberTableIdx]; | |
ASSERT ((LocalTokenNumber & PCD_DATUM_TYPE_ALL_SET) == PCD_DATUM_TYPE_POINTER); | |
SizeTableIdx = GetSizeTableIndex (LocalTokenNumberTableIdx, IsPeiDb); | |
MaxSize = SizeTable[SizeTableIdx]; | |
// | |
// SizeTable only contain record for PCD_DATUM_TYPE_POINTER type | |
// PCD entry. | |
// | |
if ((LocalTokenNumber & PCD_TYPE_VPD) != 0) { | |
// | |
// We shouldn't come here as we don't support SET for VPD | |
// | |
ASSERT (FALSE); | |
return FALSE; | |
} else { | |
if ((*CurrentSize > MaxSize) || | |
(*CurrentSize == MAX_ADDRESS)) | |
{ | |
*CurrentSize = MaxSize; | |
return FALSE; | |
} | |
// | |
// We have only two entry for Non-Sku enabled PCD entry: | |
// 1) MAX SIZE | |
// 2) Current Size | |
// | |
SizeTable[SizeTableIdx + 1] = (SIZE_INFO)*CurrentSize; | |
return TRUE; | |
} | |
} | |
/** | |
VariableLock DynamicHiiPcd. | |
@param[in] IsPeiDb If TRUE, the pcd entry is initialized in PEI phase, | |
If FALSE, the pcd entry is initialized in DXE phase. | |
@param[in] VariableLock Pointer to VariableLockProtocol. | |
**/ | |
VOID | |
VariableLockDynamicHiiPcd ( | |
IN BOOLEAN IsPeiDb, | |
IN EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock | |
) | |
{ | |
EFI_STATUS Status; | |
PCD_DATABASE_INIT *Database; | |
UINT32 LocalTokenCount; | |
UINTN TokenNumber; | |
UINT32 LocalTokenNumber; | |
UINTN Offset; | |
EFI_GUID *GuidTable; | |
UINT8 *StringTable; | |
VARIABLE_HEAD *VariableHead; | |
EFI_GUID *Guid; | |
UINT16 *Name; | |
Database = IsPeiDb ? mPcdDatabase.PeiDb : mPcdDatabase.DxeDb; | |
LocalTokenCount = IsPeiDb ? mPeiLocalTokenCount : mDxeLocalTokenCount; | |
// | |
// Go through PCD database to find out DynamicHii PCDs. | |
// | |
for (TokenNumber = 1; TokenNumber <= LocalTokenCount; TokenNumber++) { | |
if (IsPeiDb) { | |
LocalTokenNumber = GetLocalTokenNumber (TRUE, TokenNumber); | |
} else { | |
LocalTokenNumber = GetLocalTokenNumber (FALSE, TokenNumber + mPeiLocalTokenCount); | |
} | |
if ((LocalTokenNumber & PCD_TYPE_HII) != 0) { | |
Offset = LocalTokenNumber & PCD_DATABASE_OFFSET_MASK; | |
VariableHead = (VARIABLE_HEAD *)((UINT8 *)Database + Offset); | |
// | |
// Why not to set property by VarCheckProtocol with Attributes and Property directly here? | |
// It is because that set property by VarCheckProtocol will indicate the variable to | |
// be a system variable, but the unknown max size of the variable is dangerous to | |
// the system variable region. | |
// | |
if ((VariableHead->Property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) != 0) { | |
// | |
// DynamicHii PCD with RO property set in *.dsc. | |
// | |
StringTable = (UINT8 *)((UINT8 *)Database + Database->StringTableOffset); | |
GuidTable = (EFI_GUID *)((UINT8 *)Database + Database->GuidTableOffset); | |
Guid = GuidTable + VariableHead->GuidTableIndex; | |
Name = (UINT16 *)(StringTable + VariableHead->StringIndex); | |
Status = VariableLock->RequestToLock (VariableLock, Name, Guid); | |
ASSERT_EFI_ERROR (Status); | |
} | |
} | |
} | |
} | |
/** | |
VariableLockProtocol callback | |
to lock the variables referenced by DynamicHii PCDs with RO property set in *.dsc. | |
@param[in] Event Event whose notification function is being invoked. | |
@param[in] Context Pointer to the notification function's context. | |
**/ | |
VOID | |
EFIAPI | |
VariableLockCallBack ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; | |
Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock); | |
if (!EFI_ERROR (Status)) { | |
VariableLockDynamicHiiPcd (TRUE, VariableLock); | |
VariableLockDynamicHiiPcd (FALSE, VariableLock); | |
} | |
} |