/*++
*
* PcdDxe.c - Platform Configuration Database DXE Driver
*
* Source: MdeModulePkg/Universal/PCD/Dxe/
* - Service.c (database logic, get/set operations)
* - Pcd.c (protocol entry points)
*
* This module implements the DXE-phase PCD (Platform Configuration
* Database) infrastructure. It provides get/set for PCD values,
* NV storage backing, dynamic SKU variant support, and notification
* callbacks for PCD value changes.
*
* Portions derived from edk2 MdeModulePkg.
*
*--*/
#include "PcdDxe.h"
/*=============================================================================
* Global State Variables
*============================================================================*/
//
// Saved UEFI handles from ModuleEntryPoint
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gST = NULL;
EFI_BOOT_SERVICES *gBS = NULL;
EFI_RUNTIME_SERVICES *gRT = NULL;
//
// Debug console protocol handle, lazily initialized by GetDebugConOut()
//
VOID *gDebugConProtocol = NULL;
//
// HOB list pointer, retrieved from SystemTable configuration table
// by GetHobListInternal()
//
VOID *mHobList = NULL;
//
// Callback function table. Array of LIST_ENTRY (16 bytes each),
// one entry per PCD token. Initialized during PcdDxeInitDatabase().
//
LIST_ENTRY *mCallbackFnTable = NULL;
//
// PEI-phase PCD database pointer (from GUID HOB)
//
UINT8 *mPcdDatabasePeiDb = NULL;
//
// DXE-phase PCD database pointer (from FV section, owned copy)
//
UINT8 *mPcdDatabaseDxeDb = NULL;
//
// Pointer to pre-computed size info table for GetPcdInfo protocol
//
UINTN *mPcdInfoSizeTable = NULL;
//
// Number of entries in mPcdInfoSizeTable
//
UINTN mPcdInfoCount = 0;
//
// Total number of PCD tokens (PEI + DXE combined)
//
UINT32 mPcdTotalTokenCount = 0;
//
// Number of PCD tokens from the PEI phase
//
UINT32 mPeiTokenCount = 0;
//
// Number of DXE EX (extra / extension) tokens
//
UINT32 mDxeExTokenCount = 0;
//
// Bit counts for token size fields (for bit-field extraction)
//
UINT32 mPeiTokenSizeBitCount = 0;
UINT32 mDxeTokenSizeBitCount = 0;
//
// Size of the GUID tables in bytes (16 bytes per entry)
//
UINT32 mPeiGuidTableSize = 0;
UINT32 mDxeGuidTableSize = 0;
//
// Offset from end of PEI DB to start of DXE token range
//
UINT32 mDxeTokenOffset = 0;
//
// Boolean flags indicating empty databases / tables
//
BOOLEAN mIsPeiDbEmpty = FALSE;
BOOLEAN mIsDxeDbEmpty = FALSE;
BOOLEAN mIsPeiSizeTableEmpty = FALSE;
//
// Lock state variable (1 = released, 2 = acquired)
//
UINT32 gLockState = EFI_LOCK_RELEASED;
/*=============================================================================
* PCD Database Layout
*
* Offset from PcdDbBase:
* +0x00: Self-reference / base address marker
* +0x18: Self-marker (pointer to +0x18 in the same database)
* +0x1C: Variable GUID table section offset
* +0x20: String table section offset
* +0x24: (unused / reserved)
* +0x28: Token-to-size mapping section offset
* +0x2C: Size values section offset
* +0x30: SkuId offset table offset
* +0x34: GUID table entries offset
* +0x38: String table offset
* +0x3C: SkuId table entries offset
* +0x40: DxeExIndex table entries offset
* +0x42: Number of DXE tokens
* +0x44: Number of GUID table entries
*
* Local token number encoding (UINT32):
* Bits [31:28] - Token type (see PCD_TYPE_* defines)
* Bit 29 - SkuId variants present
* Bit 30 - NV (non-volatile) storage backing
* Bit 28 - VPD (vendor platform device) backing
* Bits [23:0] - Index into the database section
*
*============================================================================*/
/*=============================================================================
* Debug Support
*============================================================================*/
/*
* GetDebugConOut - Lazily retrieve and cache the DebugCon protocol.
*
* Checks current TPL; if <= TPL_APPLICATION (0x10), tries to locate
* gEfiDebugConProtocolGuid via gBS->LocateProtocol().
*
* Address: 0x2EF0
*/
VOID *
GetDebugConOut (
VOID
)
{
if (gDebugConProtocol == NULL) {
UINTN Tpl;
EFI_STATUS Status;
Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
gBS->RestoreTPL (Tpl);
if (Tpl <= 0x10) {
Status = gBS->LocateProtocol (
&gEfiDebugConProtocolGuid,
NULL,
&gDebugConProtocol
);
if (EFI_ERROR (Status)) {
gDebugConProtocol = NULL;
}
}
}
return gDebugConProtocol;
}
/*
* DebugPrint - Print a DEBUG / ASSERT message.
*
* Gets the DebugCon protocol (lazily) and calls its OutputString
* method with the file, line, and expression information.
*
* Address: 0x2FF8
*/
VOID
DebugPrint (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Expression
)
{
VOID *ConOut;
ConOut = GetDebugConOut ();
if (ConOut != NULL) {
(*(EFI_STATUS (EFIAPI **)(VOID *, CONST CHAR8 *, CONST CHAR8 *, ...)))(UINTN)ConOut + 8)(
FileName,
LineNumber,
Expression
);
}
}
/*
* DebugAssert - Print an ASSERT failure message and enter dead loop.
*
* Reads CMOS diagnostic register 0x4B to determine the assertion
* reporting mode. Checks the Status mask bits to decide whether to
* actually output the assertion message via the DebugCon protocol.
*
* Address: 0x2F70
*/
VOID
DebugAssert (
IN EFI_STATUS Status,
IN CONST CHAR8 *Format,
...
)
{
VOID *ConOut;
UINT64 AssertMask;
UINT8 CmosData;
UINT8 CmosData2;
ConOut = GetDebugConOut ();
AssertMask = 0;
//
// Read CMOS diagnostic status (register 0x4B)
//
__outbyte (0x70, (__inbyte (0x70) & 0x80) | 0x4B);
CmosData = __inbyte (0x71);
if (CmosData > 3) {
//
// Check NVRAM boot flag from memory-mapped IO at 0xFDAF0490
//
if (CmosData == 0) {
CmosData = ((*(UINT8 *)0xFDAF0490 & 2) | 1);
}
}
if ((CmosData - 1) <= 0xFD) {
AssertMask = 4;
if (CmosData == 1) {
AssertMask = 0x80000004ULL;
}
}
//
// Output the assertion message if the mask allows
//
if ((AssertMask & Status) != 0) {
if (ConOut != NULL) {
(*(VOID (EFIAPI **)(VOID *, CONST CHAR8 *, ...)))(UINTN)ConOut + 8)(
Format,
Status & 0x7FFFFFFF
);
}
}
}
/*=============================================================================
* Module Entry Point
*============================================================================*/
/*
* ModuleEntryPoint - Standard UEFI DXE driver entry point.
*
* Saves ImageHandle, SystemTable, BootServices, and RuntimeServices
* to global variables (gImageHandle, gST, gBS, gRT). Then calls
* GetHobListInternal() and PcdDxeDriverEntry().
*
* Address: 0x384
*/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
gImageHandle = ImageHandle;
ASSERT (gImageHandle != NULL);
gST = SystemTable;
ASSERT (gST != NULL);
gBS = SystemTable->BootServices;
ASSERT (gBS != NULL);
gRT = SystemTable->RuntimeServices;
ASSERT (gRT != NULL);
//
// Locate the HOB list (needed for PCD database init)
//
GetHobListInternal ();
//
// Call the module-specific driver initialization
//
return PcdDxeDriverEntry (ImageHandle, SystemTable);
}
/*=============================================================================
* Main Driver Entry
*============================================================================*/
/*
* PcdDxeDriverEntry - Main driver initialization entry point.
*
* 1. Verify gPcdProtocolGuid is NOT already installed (assert otherwise).
* 2. Call PcdDxeInitDatabase() to set up the PCD DB.
* 3. Install EFI_PCD_PROTOCOL and EFI_PCD_DXE_PROTOCOL via
* InstallMultipleProtocolInterfaces.
* 4. Register a ReadyToBoot notification for PCD variable-set callbacks.
* 5. Re-install the PCD protocol on the located handle.
*
* Address: 0x430
*/
EFI_STATUS
PcdDxeDriverEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE NewHandle;
VOID *ExistingPcd;
//
// Step 1: verify gPcdProtocolGuid is NOT already installed
//
ExistingPcd = NULL;
Status = gBS->LocateProtocol (&gPcdProtocolGuid, NULL, &ExistingPcd);
ASSERT (Status != EFI_SUCCESS);
//
// Step 2: initialize the PCD database
//
PcdDxeInitDatabase ();
//
// Step 3: install PCD protocols
//
NewHandle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&NewHandle,
&gPcdProtocolGuid,
gPcdProtocolInterface,
&gPcdDxeProtocolGuid,
gPcdDxeProtocolInterface,
NULL
);
ASSERT_EFI_ERROR (Status);
Status = gBS->InstallMultipleProtocolInterfaces (
&NewHandle,
&gPcdProtocolGuid,
gPcdDxeProtocolInterfaceEx,
&gPcdDxeProtocolGuid,
gPcdDxeProtocolInterfaceEx,
NULL
);
ASSERT_EFI_ERROR (Status);
//
// Step 4: install the PCD protocol on the handle
//
Status = gBS->InstallProtocolInterface (
&gImageHandle,
&gPcdProtocolGuid,
EFI_NATIVE_INTERFACE,
&gPcdProtocolInterface
);
ASSERT_EFI_ERROR (Status);
return Status;
}
/*=============================================================================
* PCD Database Initialization
*============================================================================*/
/*
* PcdDxeInitDatabase - Core PCD database initialization.
*
* 1. Gets DXE PCD DB from FV section (via GetSectionFromFv).
* 2. Validates that the retrieved section has GUID == gEfiPcdDxeDbGuid
* and type field == 6.
* 3. Allocates an owned copy of the DXE PCD database (doubled-sized
* buffer for local modifications).
* 4. Finds the PEI PCD DB from a GUID HOB in the HOB list.
* 5. Computes token counts, sizes, bit counts, and GUID table sizes.
* 6. Allocates the callback function table (LIST_ENTRY array) and
* initializes each entry as a self-linked circular list head.
* 7. Allocates the size info table.
*
* Address: 0x17B0
*/
EFI_STATUS
PcdDxeInitDatabase (
VOID
)
{
EFI_STATUS Status;
VOID *SectionData;
UINT32 SectionType;
UINT8 *OwnedDb;
VOID *PeiDbAddr;
UINT16 *HobPtr;
UINT32 PeiDbTokenCount;
UINT32 DxeDbTokenCount;
UINT32 PeiTokenSizeTableSize;
UINT32 DxeTokenSizeTableSize;
UINTN TotalTokenCount;
//
// Step 1: retrieve the DXE PCD DB from the firmware volume
//
SectionData = GetSectionFromFv (&gPcdDatabaseGuid);
ASSERT (SectionData != NULL);
ASSERT (CompareGuid (SectionData, &gEfiPcdDxeDbGuid));
ASSERT (*(UINT32 *)((UINT8 *)SectionData + 16) == 6);
mPcdDatabaseDxeDb = (UINT8 *)SectionData;
ASSERT (mPcdDatabaseDxeDb != NULL);
//
// Step 2: make an owned copy of the DXE PCD DB
//
OwnedDb = AllocateZeroPool (
*(UINT32 *)(mPcdDatabaseDxeDb + 20) +
*(UINT32 *)(mPcdDatabaseDxeDb + 32)
);
ASSERT (OwnedDb != NULL);
CopyMem (
OwnedDb,
mPcdDatabaseDxeDb,
*(UINT32 *)(mPcdDatabaseDxeDb + 20)
);
ZeroMem (mPcdDatabaseDxeDb, *(UINT32 *)(mPcdDatabaseDxeDb + 20));
mPcdDatabaseDxeDb = OwnedDb;
//
// Step 3: locate the PEI PCD DB from the HOB list
//
GetHobListInternal ();
PeiDbAddr = NULL;
if (mHobList != NULL) {
HobPtr = (UINT16 *)mHobList;
while (*HobPtr != HOB_TYPE_END_OF_LIST) {
if (*HobPtr == HOB_TYPE_GUID_EXTENSION) {
if (CompareGuid (
(GUID *)((UINT8 *)HobPtr + 8),
&gEfiPcdPeiDbGuid
)) {
PeiDbAddr = (VOID *)(*(UINTN *)((UINT8 *)HobPtr + 24));
break;
}
}
HobPtr = (UINT16 *)((UINT8 *)HobPtr + HobPtr[1]);
}
}
//
// Step 4: set up the PEI database pointer
//
if (PeiDbAddr != NULL) {
mPcdDatabasePeiDb = (UINT8 *)PeiDbAddr + 24;
//
// Link the PEI DB into the DXE DB (patch the self-marker)
//
*(UINTN *)(mPcdDatabaseDxeDb + 24) = *(UINTN *)(mPcdDatabasePeiDb + 24);
} else {
//
// No PEI DB found -- allocate an empty stub
//
mPcdDatabasePeiDb = AllocateZeroPool (72);
ASSERT (mPcdDatabasePeiDb != NULL);
}
//
// Step 5: compute token counts and table sizes
//
// The PCD database headers encode counts as UINT16 at known offsets:
// +0x40: Number of "local tokens" in this phase
// +0x42: Number of tokens in the other phase
//
DxeDbTokenCount = *(UINT16 *)(mPcdDatabaseDxeDb + 64);
PeiDbTokenCount = *(UINT16 *)(mPcdDatabasePeiDb + 64);
mPeiTokenCount = PeiDbTokenCount;
mPcdTotalTokenCount = PeiDbTokenCount + DxeDbTokenCount;
mDxeTokenOffset = PeiDbTokenCount - DxeDbTokenCount;
//
// Size bit counts = 8 * (UINT16 at +0x42)
//
mPeiTokenSizeBitCount = 8 * *(UINT16 *)(mPcdDatabasePeiDb + 66);
mDxeTokenSizeBitCount = 8 * *(UINT16 *)(mPcdDatabaseDxeDb + 66);
//
// GUID table sizes = 16 * (UINT16 at +0x44)
//
mPeiGuidTableSize = 16 * *(UINT16 *)(mPcdDatabasePeiDb + 68);
mDxeGuidTableSize = 16 * *(UINT16 *)(mPcdDatabaseDxeDb + 68);
//
// DXE EX token count
//
mDxeExTokenCount = PeiDbTokenCount - *(UINT16 *)(mPcdDatabasePeiDb + 66);
//
// Empty flags
//
mIsPeiDbEmpty = (PeiDbTokenCount == 0);
mIsDxeDbEmpty = (DxeDbTokenCount == 0);
mIsPeiSizeTableEmpty = (*(UINT16 *)(mPcdDatabasePeiDb + 66) == 0);
//
// Size info table: one entry per "size table slot" across both phases
//
mPcdInfoCount = (UINTN)*(UINT16 *)(mPcdDatabasePeiDb + 66)
+ (UINTN)*(UINT16 *)(mPcdDatabaseDxeDb + 66);
mPcdInfoSizeTable = AllocateZeroPool (8 * mPcdInfoCount);
ASSERT (mPcdInfoSizeTable != NULL);
//
// Step 6: allocate and initialize the callback function table
//
TotalTokenCount = (UINTN)(mPcdTotalTokenCount) + 1;
mCallbackFnTable = AllocateZeroPool (16 * TotalTokenCount);
ASSERT (mCallbackFnTable != NULL);
if (TotalTokenCount > 1) {
UINTN Index;
LIST_ENTRY *Entry;
for (Index = 1; Index < TotalTokenCount; Index++) {
Entry = &mCallbackFnTable[Index];
//
// Initialize as a self-linked circular list head
//
Entry->ForwardLink = Entry;
Entry->BackLink = Entry;
}
}
return EFI_SUCCESS;
}
/*
* GetDxePcdDatabase - Simple getter for the DXE PCD DB self-marker.
*
* Implements: return mPcdDatabaseDxeDb->SelfMarker
*
* Address: 0x634
*/
VOID *
GetDxePcdDatabase (
VOID
)
{
return *(VOID **)(mPcdDatabaseDxeDb + 0x18);
}
/*=============================================================================
* PCD Read Operations (Get*)
*============================================================================*/
/*
* DxePcdGetSize - Return the size of a PCD value by token number.
*
* The size is pre-encoded as a 4-bit index in the upper nibble of
* HIBYTE(LocalTokenNumber). When the pre-computed size field is
* absent (bit 24 clear), the function falls back to GetPcdInfoSize().
*
* Address: 0x7A0
*/
UINTN
DxePcdGetSize (
IN UINTN TokenNumber
)
{
UINTN LocalIndex;
UINT8 *PcdDb;
UINT32 *TokenSpace;
UINT32 LocalTokenNumber;
UINTN Size;
LocalIndex = TokenNumber - 1;
ASSERT (TokenNumber < mPcdTotalTokenCount + 1);
//
// Select the correct database (PEI vs DXE)
//
if (TokenNumber >= mPeiTokenCount + 1) {
PcdDb = mPcdDatabaseDxeDb;
LocalIndex -= mPeiTokenCount;
} else {
PcdDb = mPcdDatabasePeiDb;
}
TokenSpace = (UINT32 *)(PcdDb + *(UINT32 *)(PcdDb + 36));
LocalTokenNumber = TokenSpace[LocalIndex];
//
// Extract the 4-bit size field from the upper nibble of HIBYTE
//
Size = (LocalTokenNumber >> 28) & 0xF;
if ((LocalTokenNumber & 0xF000000) == 0) {
//
// No pre-computed size -- query dynamically
//
GetPcdInfoSize (TokenNumber - 1, &Size);
}
return Size;
}
/*
* GetLocalTokenNumber - Convert a global token number into a local one.
*
* Reads the token space entry and, if the token has SkuId variants
* (bit 29 = 0x20000000 set), calls GetSkuIdAdjustedTokenNumber().
*
* Address: 0xF34
*/
UINT32
GetLocalTokenNumber (
IN BOOLEAN IsPeiPhase,
IN UINTN TokenNumber
)
{
UINT8 *PcdDb;
UINT32 *TokenSpace;
UINT32 LocalIndex;
UINT32 LocalTokenNumber;
UINTN SlotIndex = 0;
if (IsPeiPhase) {
PcdDb = mPcdDatabasePeiDb;
} else {
PcdDb = mPcdDatabaseDxeDb;
}
TokenSpace = (UINT32 *)(PcdDb + *(UINT32 *)(PcdDb + 36));
if (IsPeiPhase) {
LocalIndex = (UINT32)(TokenNumber - 1);
} else {
LocalIndex = (UINT32)(TokenNumber - 1 - mPeiTokenCount);
}
LocalTokenNumber = TokenSpace[LocalIndex];
if ((LocalTokenNumber & PCD_ATTRIBUTE_SKUID_ENABLED) != 0) {
//
// Token has SkuId variants -- determine the actual size
//
if ((LocalTokenNumber & PCD_TYPE_MASK) == 0) {
GetPcdInfoSize ((UINTN)TokenNumber - 1, &SlotIndex);
}
LocalTokenNumber = GetSkuIdAdjustedTokenNumber (
LocalTokenNumber & 0xDFFFFFFF,
SlotIndex,
IsPeiPhase
);
}
return LocalTokenNumber;
}
/*
* DxePcdGetPtr - Return a pointer to a PCD value.
*
* Handles all token types:
* Normal (type 0x00000000): direct pointer into DB
* VPD (type 0x10000000): offset from VPD table in DB
* NV (type 0x40000000): read from NV store + allocate buffer
* HII (type 0x20000000): read via config string protocol
* String (type 0x80000000): string value in DB
* VPDString (type 0x90000000): string with VPD backing
*
* Address: 0x1358
*/
VOID *
DxePcdGetPtr (
IN UINTN TokenNumber,
IN UINTN GetSize
)
{
BOOLEAN IsPeiPhase;
UINT8 *PcdDb;
UINT32 LocalTokenNumber;
UINT32 TypeBits;
UINT32 *TokenSpace;
UINT8 *DbValuePtr;
UINT8 *TargetPtr;
VOID *Result;
AcquireLock ();
ASSERT (TokenNumber > 0);
ASSERT (TokenNumber < mPcdTotalTokenCount + 1);
if (GetSize != DxePcdGetSize (TokenNumber) && GetSize != 0) {
ASSERT (FALSE);
}
IsPeiPhase = (TokenNumber < mPeiTokenCount + 1);
LocalTokenNumber = GetLocalTokenNumber (IsPeiPhase, TokenNumber);
TypeBits = LocalTokenNumber & PCD_TYPE_MASK;
if (IsPeiPhase) {
PcdDb = mPcdDatabasePeiDb;
DbValuePtr = PcdDb + *(UINT32 *)(PcdDb + 48);
} else {
PcdDb = mPcdDatabaseDxeDb;
DbValuePtr = PcdDb + *(UINT32 *)(PcdDb + 48);
}
Result = NULL;
switch (TypeBits) {
case PCD_TYPE_NORMAL:
//
// Direct pointer into the database
//
Result = PcdDb + (LocalTokenNumber & PCD_TOKEN_INDEX_MASK);
break;
case PCD_TYPE_VPD:
//
// VPD token: the value lives in the VPD table section
//
Result = DbValuePtr + *(UINT32 *)((LocalTokenNumber & PCD_TOKEN_INDEX_MASK) + PcdDb);
break;
case PCD_TYPE_NV:
case PCD_TYPE_HII:
case PCD_TYPE_STRING:
case PCD_TYPE_STRING_VPD:
//
// For NV / HII / String tokens, return the value pointer
// into the database (the actual NV or HII access path is
// handled by the SetPtr / SetNvStorePcdValue functions).
//
{
UINT16 *Entry;
Entry = (UINT16 *)((LocalTokenNumber & PCD_TOKEN_INDEX_MASK) + PcdDb);
if (TypeBits == PCD_TYPE_STRING_VPD) {
TargetPtr = DbValuePtr + *(UINT32 *)(Entry[1] + (UINTN)PcdDb);
} else {
TargetPtr = PcdDb + Entry[1];
}
Result = TargetPtr;
}
break;
default:
ASSERT (FALSE);
break;
}
ReleaseLock ();
return Result;
}
/*
* GetSkuIdAdjustedTokenNumber - Adjust local token number for SkuId.
*
* Walks the SkuId table associated with the token and locates
* the entry matching the current platform's SkuId. Returns an
* adjusted local token number with the type bits preserved and
* offset recalculated based on the SkuId index.
*
* Address: 0x1BC0
*/
UINT32
GetSkuIdAdjustedTokenNumber (
IN UINT32 LocalTokenNumber,
IN UINTN SkuIdValue,
IN BOOLEAN IsPeiPhase
)
{
UINT8 *PcdDb;
UINTN *SkuIdTable;
UINTN TableCount;
UINTN Index;
UINTN *SkuEntry;
UINT32 TypeBits;
UINTN TableBase;
//
// This function should only be called for tokens WITHOUT
// SkuId variants at this level.
//
ASSERT ((LocalTokenNumber & PCD_ATTRIBUTE_SKUID_ENABLED) == 0);
if (IsPeiPhase) {
PcdDb = mPcdDatabasePeiDb;
} else {
PcdDb = mPcdDatabaseDxeDb;
}
//
// Get the SkuId table pointer (offset at +4 from the token slot)
//
SkuIdTable = (UINTN *)(PcdDb + *(UINT32 *)((LocalTokenNumber & PCD_TOKEN_INDEX_MASK) + PcdDb + 4));
TableCount = *SkuIdTable;
Index = 0;
//
// Walk the SkuId table looking for our SkuId matches the self-marker
//
if (TableCount != 0) {
SkuEntry = SkuIdTable + 1;
while (*SkuEntry != *(UINTN *)(mPcdDatabaseDxeDb + 24)) {
Index++;
SkuEntry++;
if (Index >= TableCount) {
goto FIND_EMPTY_SLOT;
}
}
//
// Found our SkuId entry at 'Index'
//
goto ADJUST;
}
FIND_EMPTY_SLOT:
//
// Not found: use slot 0
//
Index = 0;
if (TableCount != 0) {
//
// Try to find an empty slot
//
SkuEntry = SkuIdTable + 1;
while (*SkuEntry != 0) {
Index++;
SkuEntry++;
if (Index >= TableCount) {
Index = 0;
break;
}
}
}
ASSERT (Index < TableCount);
ADJUST:
TypeBits = LocalTokenNumber & PCD_TYPE_MASK;
//
// Recalculate the base table address and adjust offsets
//
TableBase = (UINTN)PcdDb + (LocalTokenNumber & PCD_TOKEN_INDEX_MASK);
if (TypeBits == 0) {
//
// Normal data: each SkuId occupies 'SkuIdValue' bytes
//
return (UINT32)(TypeBits | ((TableBase + SkuIdValue * Index - (UINTN)PcdDb) & PCD_TOKEN_INDEX_MASK));
}
if (TypeBits == PCD_TYPE_VPD || TypeBits == PCD_TYPE_NV) {
//
// VPD / NV: each SkuId entry is 4 bytes (UINT32 offset)
//
return TypeBits | ((UINT32)((UINTN)PcdDb + *(UINT32 *)(TableBase + 4) + 4 * Index - (UINTN)PcdDb) & PCD_TOKEN_INDEX_MASK);
}
//
// String types: 5 entries per SkuId (20 bytes each)
//
return TypeBits | ((UINT32)((UINTN)PcdDb + *(UINT32 *)(TableBase + 4) + 20 * Index - (UINTN)PcdDb) & PCD_TOKEN_INDEX_MASK);
}
/*
* GetPcdInfoSize - Query the actual size of a PCD value.
*
* For tokens with dynamic SkuId variants (bit 29 set, bit 30 clear),
* walks the SkuId table to retrieve the variant-specific size.
*
* Address: 0x2664
*/
UINTN
GetPcdInfoSize (
IN UINTN TokenNumber,
OUT UINTN *Size
)
{
BOOLEAN IsPeiPhase;
UINT8 *PcdDb;
UINT32 *TokenSpace;
UINT32 LocalTokenNumber;
UINTN LocalIndex;
UINTN *SizeTable;
UINTN *SkuIdTable;
UINTN TableCount;
UINTN Index;
UINTN *SkuEntry;
UINTN SlotIndex;
UINTN BaseSize;
IsPeiPhase = (TokenNumber + 1 < mPeiTokenCount + 1);
if (IsPeiPhase) {
PcdDb = mPcdDatabasePeiDb;
LocalIndex = TokenNumber;
} else {
PcdDb = mPcdDatabaseDxeDb;
LocalIndex = TokenNumber - mPeiTokenCount;
}
TokenSpace = (UINT32 *)(PcdDb + *(UINT32 *)(PcdDb + 36));
LocalTokenNumber = TokenSpace[LocalIndex];
//
// Must be a fixed-size token (bits 24-27 clear)
//
ASSERT ((LocalTokenNumber & PCD_TYPE_MASK) == 0);
SizeTable = (UINTN *)(PcdDb + *(UINT32 *)(PcdDb + 52));
SlotIndex = GetTokenIndex (LocalIndex, IsPeiPhase);
BaseSize = *(UINT16 *)((UINTN)SizeTable + 2 * SlotIndex);
*Size = BaseSize;
if ((LocalTokenNumber & PCD_ATTRIBUTE_NV) == 0) {
//
// Not NV-stored: check for dynamic SkuId entries
//
if ((LocalTokenNumber & PCD_ATTRIBUTE_SKUID_ENABLED) != 0) {
SkuIdTable = GetSkuIdTableForToken (LocalIndex, IsPeiPhase);
TableCount = *SkuIdTable;
Index = 0;
if (TableCount != 0) {
SkuEntry = SkuIdTable + 1;
while (*SkuEntry != *(UINTN *)(mPcdDatabaseDxeDb + 24)) {
Index++;
SkuEntry++;
if (Index >= TableCount) {
//
// Not found: return base size
//
*Size = *(UINT16 *)((UINTN)SizeTable + 2 * SlotIndex + 2);
return *Size;
}
}
}
//
// Return the SkuId-specific size
//
*Size = *(UINT16 *)((UINTN)SizeTable + 2 * (Index + SlotIndex) + 2);
return *Size;
} else {
//
// Fixed-size token: base + 1 slot for variant
//
*Size = *(UINT16 *)((UINTN)SizeTable + 2 * SlotIndex + 2);
return *Size;
}
}
return BaseSize;
}
/*
* GetTokenIndex - Count the number of "size slots" before the given token.
*
* Walks all tokens with a lower local index and counts their
* size table slot consumption (2 per fixed token, or 2 + SKU count
* for SkuId-enabled tokens).
*
* Address: 0x25CC
*/
UINTN
GetTokenIndex (
IN UINTN LocalIndex,
IN BOOLEAN IsPeiPhase
)
{
UINT8 *PcdDb;
UINT32 *TokenSpace;
UINTN SlotCount;
UINTN Token;
if (IsPeiPhase) {
PcdDb = mPcdDatabasePeiDb;
} else {
PcdDb = mPcdDatabaseDxeDb;
}
TokenSpace = (UINT32 *)(PcdDb + *(UINT32 *)(PcdDb + 36));
SlotCount = 0;
for (Token = 0; Token < LocalIndex; Token++) {
//
// Only fixed-size tokens contribute to the size table index
//
if ((TokenSpace[Token] & PCD_TYPE_MASK) == 0) {
if ((TokenSpace[Token] & PCD_ATTRIBUTE_NV) == 0 &&
(TokenSpace[Token] & PCD_ATTRIBUTE_SKUID_ENABLED) != 0) {
//
// Token has SkuId variants: count = 1 + SkuId table entries
//
SlotCount += *GetSkuIdTableForToken (Token, IsPeiPhase) + 1;
} else {
//
// Fixed token: always 2 slots (base + 1)
//
SlotCount += 2;
}
}
}
return SlotCount;
}
/*
* GetSkuIdTableForToken - Get the pointer to a token's SkuId table.
*
* Address: 0x255C
*/
UINTN *
GetSkuIdTableForToken (
IN UINTN LocalIndex,
IN BOOLEAN IsPeiPhase
)
{
UINT8 *PcdDb;
UINT32 *TokenSpace;
UINT32 LocalTokenNumber;
if (IsPeiPhase) {
PcdDb = mPcdDatabasePeiDb;
} else {
PcdDb = mPcdDatabaseDxeDb;
}
TokenSpace = (UINT32 *)(PcdDb + *(UINT32 *)(PcdDb + 36));
LocalTokenNumber = TokenSpace[LocalIndex];
//
// Must have the SkuId-enabled bit set
//
ASSERT ((LocalTokenNumber & PCD_ATTRIBUTE_SKUID_ENABLED) != 0);
return (UINTN *)(PcdDb + *(UINT32 *)((LocalTokenNumber & PCD_TOKEN_INDEX_MASK) + PcdDb + 4));
}
/*
* GetTokenNumberFromGuid - Find a PCD token number by GUID + token value.
*
* Searches the PEI and DXE databases for a matching GUID table entry.
* Compares the GUID at the given GuidTableAddr against each entry in
* the database's GUID table, then matches the token number field.
*
* Address: 0x2414
*/
UINT16
GetTokenNumberFromGuid (
IN UINTN GuidTableAddr,
IN UINT32 Token
)
{
UINTN Index;
UINT8 *PcdDb;
UINT16 *TokenEntries;
UINTN *ScanResult;
UINTN ScanIndex;
//
// Try the PEI database first
//
if (!mIsPeiDbEmpty) {
PcdDb = mPcdDatabasePeiDb;
TokenEntries = (UINT16 *)(PcdDb + *(UINT32 *)(PcdDb + 40));
ScanResult = (UINTN *)ScanGuidTable (
(GUID *)(PcdDb + *(UINT32 *)(PcdDb + 44)),
mPeiGuidTableSize,
(GUID *)GuidTableAddr
);
if (ScanResult != NULL) {
ScanIndex = ((UINTN)ScanResult -
(UINTN)(PcdDb + *(UINT32 *)(PcdDb + 44))) >> 4;
if (*(UINT16 *)(PcdDb + 66) != 0) {
for (Index = 0; Index < *(UINT16 *)(PcdDb + 66); Index++) {
if (*(UINT32 *)&TokenEntries[8 * Index] == Token &&
ScanIndex == *(UINT16 *)&TokenEntries[8 * Index + 6]) {
return TokenEntries[8 * Index + 4];
}
}
}
}
}
//
// Fall through to the DXE database
//
PcdDb = mPcdDatabaseDxeDb;
TokenEntries = (UINT16 *)(PcdDb + *(UINT32 *)(PcdDb + 40));
ScanResult = (UINTN *)ScanGuidTable (
(GUID *)(PcdDb + *(UINT32 *)(PcdDb + 44)),
mDxeGuidTableSize,
(GUID *)GuidTableAddr
);
ASSERT (ScanResult != NULL);
ScanIndex = ((UINTN)ScanResult -
(UINTN)(PcdDb + *(UINT32 *)(PcdDb + 44))) >> 4;
if (*(UINT16 *)(PcdDb + 66) != 0) {
for (Index = 0; Index < *(UINT16 *)(PcdDb + 66); Index++) {
if (*(UINT32 *)&TokenEntries[8 * Index] == Token &&
ScanIndex == *(UINT16 *)&TokenEntries[8 * Index + 6]) {
return TokenEntries[8 * Index + 4];
}
}
}
ASSERT (FALSE);
return 0;
}
/*=============================================================================
* PCD Write Operations (Set*)
*============================================================================*/
/*
* NotifyPcdCallbacks - Invoke all registered notification callbacks for a token.
*
* Walks the callback linked list head for the given token index and
* invokes each registered callback's function pointer (stored at +16
* relative to the LIST_ENTRY node).
*
* Address: 0x1D24
*/
VOID
NotifyPcdCallbacks (
IN UINTN GuidNumber,
IN UINTN TokenNumber,
IN UINTN Token,
IN VOID *Buffer,
IN UINTN Size
)
{
LIST_ENTRY *ListHead;
LIST_ENTRY *Node;
ListHead = &mCallbackFnTable[Token];
Node = GetFirstNode (ListHead);
while (Node != ListHead) {
//
// Callback function pointer is stored at Node + 16
//
((VOID (EFIAPI *)(UINTN, UINTN, VOID *, UINTN))(
*(UINTN *)((UINT8 *)Node + 16)
))(GuidNumber, TokenNumber, Buffer, Size);
Node = GetFirstNode (ListHead);
ASSERT (IsListValid (ListHead));
}
}
/*
* UpdatePcdSizeInfo - Update the size entry for a PCD token.
*
* When the current buffer size differs from the stored size, updates
* the size table entry (including SkuId variant tracking).
*
* Address: 0x2764
*/
BOOLEAN
UpdatePcdSizeInfo (
IN UINTN TokenNumber,
IN OUT UINTN *Size
)
{
BOOLEAN IsPeiPhase;
UINT8 *PcdDb;
UINT32 *TokenSpace;
UINT32 LocalTokenNumber;
UINTN LocalIndex;
UINTN *SizeTable;
UINTN *SkuIdTable;
UINTN TableCount;
UINTN Index;
UINTN *SkuEntry;
UINTN SlotIndex;
UINTN SlotSize;
IsPeiPhase = (TokenNumber + 1 < mPeiTokenCount + 1);
if (IsPeiPhase) {
PcdDb = mPcdDatabasePeiDb;
LocalIndex = TokenNumber;
} else {
PcdDb = mPcdDatabaseDxeDb;
LocalIndex = TokenNumber - mPeiTokenCount;
}
TokenSpace = (UINT32 *)(PcdDb + *(UINT32 *)(PcdDb + 36));
LocalTokenNumber = TokenSpace[LocalIndex];
//
// Must be a fixed-size-type token
//
ASSERT ((LocalTokenNumber & PCD_TYPE_MASK) == 0);
SizeTable = (UINTN *)(PcdDb + *(UINT32 *)(PcdDb + 52));
SlotIndex = GetTokenIndex (LocalIndex, IsPeiPhase);
SlotSize = *(UINT16 *)((UINTN)SizeTable + 2 * SlotIndex);
//
// If the token is NV-stored, the size field is the NV variable size
//
if ((LocalTokenNumber & PCD_ATTRIBUTE_NV) == 0) {
goto UPDATE_CHECK;
}
UPDATE_CHECK:
if (*Size > SlotSize || *Size == (UINTN)-1) {
*Size = SlotSize;
return FALSE;
}
//
// Check for SkuId variant size update
//
if ((LocalTokenNumber & PCD_ATTRIBUTE_SKUID_ENABLED) != 0) {
SkuIdTable = GetSkuIdTableForToken (LocalIndex, IsPeiPhase);
TableCount = *SkuIdTable;
Index = 0;
if (TableCount != 0) {
SkuEntry = SkuIdTable + 1;
while (*SkuEntry != *(UINTN *)(mPcdDatabaseDxeDb + 24)) {
Index++;
SkuEntry++;
if (Index >= TableCount) {
goto UPDATE_BASE;
}
}
*(UINT16 *)((UINTN)SizeTable + 2 * (Index + SlotIndex) + 2) = (UINT16)*Size;
return TRUE;
}
}
UPDATE_BASE:
*(UINT16 *)((UINTN)SizeTable + 2 * SlotIndex + 2) = (UINT16)*Size;
return TRUE;
}
/*
* DxePcdSetPtr - Set a PCD value by token number.
*
* Handles all token types:
* - Normal: direct aligned write (1/2/4/8 bytes)
* - NV: routes through SetNvStorePcdValue
* - HII/String: resize/allocate & copy through NV variable path
* - VPD: via NV variable path
*
* For variable-size tokens (IsResizeAllowed=TRUE), also updates
* the size info table via UpdatePcdSizeInfo().
*
* Address: 0x1DCC
*/
EFI_STATUS
DxePcdSetPtr (
IN UINTN TokenNumber,
IN VOID *Buffer,
IN OUT UINTN *Size,
IN BOOLEAN IsResizeAllowed
)
{
UINT8 *PcdDb;
UINT32 LocalTokenNumber;
UINT32 TypeBits;
UINT8 *TargetPtr;
EFI_STATUS Status;
BOOLEAN IsPeiPhase;
ASSERT (TokenNumber < mPcdTotalTokenCount + 1);
if (IsResizeAllowed) {
//
// Clamp the size to the maximum allowed
//
GetPcdInfoSize (TokenNumber - 1, Size);
} else {
//
// Must match the exact token size
//
if (*Size != DxePcdGetSize (TokenNumber)) {
return EFI_INVALID_PARAMETER;
}
}
//
// Notify callbacks before modifying the value
//
if (TokenNumber < mDxeExTokenCount + 1 ||
(TokenNumber >= mPeiTokenCount + 1 &&
TokenNumber < mPeiTokenCount + mDxeTokenOffset + 1)) {
NotifyPcdCallbacks (0, 0, TokenNumber, Buffer, *Size);
}
AcquireLock ();
IsPeiPhase = (TokenNumber < mPeiTokenCount + 1);
LocalTokenNumber = GetLocalTokenNumber (IsPeiPhase, TokenNumber);
TypeBits = LocalTokenNumber & PCD_TYPE_MASK;
if (IsPeiPhase) {
PcdDb = mPcdDatabasePeiDb;
} else {
PcdDb = mPcdDatabaseDxeDb;
}
TargetPtr = PcdDb + (LocalTokenNumber & PCD_TOKEN_INDEX_MASK);
switch (TypeBits) {
case PCD_TYPE_NORMAL:
if (!IsResizeAllowed) {
//
// Aligned direct write (1/2/4/8 bytes)
//
switch (*Size) {
case 1:
*(UINT8 *)TargetPtr = *(UINT8 *)Buffer;
break;
case 2:
*(UINT16 *)TargetPtr = *(UINT16 *)Buffer;
break;
case 4:
*(UINT32 *)TargetPtr = *(UINT32 *)Buffer;
break;
case 8:
*(UINT64 *)TargetPtr = *(UINT64 *)Buffer;
break;
default:
ASSERT (FALSE);
}
} else {
//
// Variable-size token: update size info then copy
//
if (UpdatePcdSizeInfo (TokenNumber - 1, Size)) {
CopyMem (TargetPtr, Buffer, *Size);
} else {
//
// Fall through to NV variable path
//
Status = SetNvStorePcdValue (
(UINTN)PcdDb,
(UINTN)(DbValuePtr + *(UINT32 *)TargetPtr),
*(UINT32 *)(TargetPtr + 12),
Buffer,
*Size,
*(UINT16 *)(TargetPtr + 10)
);
}
}
break;
case PCD_TYPE_NV:
case PCD_TYPE_HII:
case PCD_TYPE_STRING:
case PCD_TYPE_STRING_VPD:
//
// Route through the NV variable store path
//
Status = SetNvStorePcdValue (
(UINTN)PcdDb,
(UINTN)DbValuePtr + *(UINT32 *)TargetPtr,
*(UINT32 *)(TargetPtr + 12),
Buffer,
*Size,
*(UINT16 *)(TargetPtr + 10)
);
break;
default:
//
// VPD only -- direct write (should not normally happen)
//
CopyMem (TargetPtr, Buffer, *Size);
break;
}
ReleaseLock ();
return EFI_SUCCESS;
}
/*
* DxePcdSetByGuid - Set a PCD value identified by GUID + token number.
*
* Looks up the token number from the GUID table, notifies callbacks,
* then delegates to DxePcdSetPtr().
*
* Address: 0x2084
*/
EFI_STATUS
DxePcdSetByGuid (
IN UINT32 GuidNumber,
IN UINTN Token,
IN VOID *Buffer,
IN OUT UINTN *Size,
IN BOOLEAN IsResizeAllowed
)
{
UINT16 TokenNumber;
TokenNumber = GetTokenNumberFromGuid ((UINTN)Token, GuidNumber);
NotifyPcdCallbacks (GuidNumber, TokenNumber, TokenNumber, Buffer, *Size);
return DxePcdSetPtr (TokenNumber, Buffer, Size, IsResizeAllowed);
}
/*
* DxePcdSetValue16 - Wrapper for setting a 16-bit PCD value.
*
* Address: 0x9E8
*/
UINTN
DxePcdSetValue16 (
IN UINTN Token,
IN UINT32 GuidNumber,
IN UINT16 Value
)
{
UINTN Size;
UINT16 ValueCopy;
Size = 2;
ValueCopy = Value;
return DxePcdSetByGuid (GuidNumber, Token, &ValueCopy, &Size, FALSE);
}
/*
* SetNvStorePcdValue - Set a PCD value backed by NV storage.
*
* Reads the current NV variable via gRT->GetVariable, copies the
* new value data at the correct offset (after the string table
* data), then writes the variable back via gRT->SetVariable.
*
* If the variable does not exist (EFI_NOT_FOUND), it builds a
* new buffer from the GetVariableForPcd helper, copies the new
* value in, and creates the variable.
*
* Address: 0x2230
*/
EFI_STATUS
SetNvStorePcdValue (
IN UINTN VariableGuidTableEntry,
IN UINTN VariableOffset,
IN UINT32 Attributes,
IN VOID *ValueBuffer,
IN UINTN ValueSize,
IN UINTN StringTableSize
)
{
EFI_STATUS Status;
UINTN BufferSize;
VOID *Buffer;
UINT32 Attribs;
//
// Step 1: query the size of the existing NV variable
//
BufferSize = 0;
Buffer = NULL;
Status = gRT->GetVariable (
NULL,
(EFI_GUID *)VariableGuidTableEntry,
NULL,
&BufferSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// Step 2: allocate + read the variable
//
Attribs = 0;
BufferSize = BufferSize + StringTableSize;
if (BufferSize < ValueSize + StringTableSize) {
BufferSize = ValueSize + StringTableSize;
}
Buffer = AllocatePool (BufferSize);
ASSERT (Buffer != NULL);
Status = gRT->GetVariable (
NULL,
(EFI_GUID *)VariableGuidTableEntry,
(Attributes != 0) ? &Attribs : NULL,
&BufferSize,
Buffer
);
ASSERT_EFI_ERROR (Status);
//
// Step 3: overwrite the value at the correct offset
//
CopyMem ((UINT8 *)Buffer + StringTableSize, ValueBuffer, ValueSize);
//
// Step 4: write the variable back
//
Status = gRT->SetVariable (
NULL,
(EFI_GUID *)VariableGuidTableEntry,
(Attributes != 0) ? Attributes : 0,
BufferSize,
Buffer
);
FreePool (Buffer);
return Status;
}
if (Status == EFI_NOT_FOUND) {
//
// Variable does not exist -- create it
//
BufferSize = 0;
Buffer = AllocateZeroPool (ValueSize + StringTableSize);
ASSERT (Buffer != NULL);
GetVariableForPcd (
(GUID *)VariableGuidTableEntry,
0,
&BufferSize,
NULL
);
Buffer = AllocateZeroPool (BufferSize);
ASSERT (Buffer != NULL);
GetVariableForPcd (
(GUID *)VariableGuidTableEntry,
0,
&BufferSize,
Buffer
);
CopyMem ((UINT8 *)Buffer + StringTableSize, ValueBuffer, ValueSize);
Attribs = (Attributes != 0) ? Attributes : 7;
Status = gRT->SetVariable (
NULL,
(EFI_GUID *)VariableGuidTableEntry,
Attribs,
(BufferSize < ValueSize + StringTableSize) ?
ValueSize + StringTableSize : BufferSize,
Buffer
);
FreePool (Buffer);
return Status;
}
//
// Fallback path: allocate, read, modify, write back
//
Buffer = AllocatePool (BufferSize);
ASSERT (Buffer != NULL);
Status = gRT->GetVariable (
NULL,
(EFI_GUID *)VariableGuidTableEntry,
NULL,
&BufferSize,
Buffer
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
FreePool (Buffer);
return Status;
}
CopyMem ((UINT8 *)Buffer + StringTableSize, ValueBuffer, ValueSize);
Status = gRT->SetVariable (
NULL,
(EFI_GUID *)VariableGuidTableEntry,
7,
BufferSize,
Buffer
);
FreePool (Buffer);
return Status;
}
/*
* GetVariableForPcd - Gather all PCD data matching a GUID into a buffer.
*
* Iterates all PCD tokens that reference the given VariableGuid,
* collects their data into a combined NV variable buffer.
*
* Address: 0x20FC
*/
VOID
GetVariableForPcd (
IN GUID *VariableGuid,
IN UINTN VariableOffset,
OUT UINTN *VariableSize,
OUT VOID *VariableBuffer OPTIONAL
)
{
UINTN TokenIndex;
UINT32 LocalTokenNumber;
UINT8 *PcdDb;
UINTN TokenSize;
BOOLEAN IsPeiPhase;
*VariableSize = 0;
for (TokenIndex = 1; TokenIndex <= (UINTN)mPcdTotalTokenCount; TokenIndex++) {
IsPeiPhase = (TokenIndex + 1 < mPeiTokenCount + 1);
if (IsPeiPhase) {
PcdDb = mPcdDatabasePeiDb;
} else {
PcdDb = mPcdDatabaseDxeDb;
}
LocalTokenNumber = GetLocalTokenNumber (IsPeiPhase, TokenIndex);
if ((INT32)LocalTokenNumber < 0) {
//
// This token has valid data -- check if its GUID matches
//
if (CompareGuid (
VariableGuid,
(GUID *)(PcdDb + 16 * *(UINT16 *)&((UINT8 *)LocalTokenNumber)[8] +
*(UINT32 *)(PcdDb + 44))
)) {
TokenSize = DxePcdGetSize (TokenIndex);
if (TokenSize + *(UINT16 *)&((UINT8 *)LocalTokenNumber)[10] > *VariableSize) {
*VariableSize = TokenSize + *(UINT16 *)&((UINT8 *)LocalTokenNumber)[10];
}
if (VariableBuffer != NULL) {
CopyMem (
(UINT8 *)VariableBuffer + *(UINT16 *)&((UINT8 *)LocalTokenNumber)[10],
PcdDb + (LocalTokenNumber & PCD_TOKEN_INDEX_MASK) + 4,
TokenSize
);
}
}
}
}
}
/*
* GetPcdNvStoreData - Read PCD data from NV storage via GetVariable.
*
* Address: 0x1AB0
*/
EFI_STATUS
GetPcdNvStoreData (
IN UINTN VariableGuidTableEntry,
IN UINTN VariableOffset,
OUT VOID **Buffer,
OUT UINTN *BufferSize
)
{
EFI_STATUS Status;
UINTN Size;
*Buffer = NULL;
*BufferSize = 0;
//
// Query the size first
//
Status = gRT->GetVariable (
NULL,
(EFI_GUID *)VariableGuidTableEntry,
NULL,
&Size,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
*Buffer = AllocatePool (Size);
ASSERT (*Buffer != NULL);
Status = gRT->GetVariable (
NULL,
(EFI_GUID *)VariableGuidTableEntry,
NULL,
&Size,
*Buffer
);
if (Status != EFI_SUCCESS) {
ASSERT_EFI_ERROR (Status);
FreePool (*Buffer);
*Buffer = NULL;
return Status;
}
*BufferSize = Size;
return EFI_SUCCESS;
}
if (Status != EFI_NOT_FOUND) {
//
// Unexpected status -- must be EFI_NOT_FOUND
//
ASSERT (Status == EFI_NOT_FOUND);
}
return Status;
}
/*=============================================================================
* Callback Functions
*============================================================================*/
/*
* DxePcdCallbackNotifyAll - Invoke registered notification callbacks for all tokens.
*
* Iterates all tokens in either the PEI or DXE phase and, for each token
* that has the "notify on set" flag set (bit 0 of the token's attribute
* byte at offset +16 within the data entry), calls the registered callback.
*
* Address: 0x2898
*/
EFI_STATUS
DxePcdCallbackNotifyAll (
IN BOOLEAN IsPeiPhase
)
{
UINT8 *PcdDb;
UINTN Token;
UINTN TokenLimit;
UINT32 LocalTokenNumber;
UINT8 *DataEntry;
if (IsPeiPhase) {
PcdDb = mPcdDatabasePeiDb;
TokenLimit = mPeiTokenCount;
} else {
PcdDb = mPcdDatabaseDxeDb;
TokenLimit = mPeiTokenCount;
}
if (TokenLimit == 0) {
return EFI_SUCCESS;
}
for (Token = 1; Token <= TokenLimit; Token++) {
if (IsPeiPhase) {
LocalTokenNumber = GetLocalTokenNumber (TRUE, Token);
} else {
LocalTokenNumber = GetLocalTokenNumber (FALSE, Token + mPeiTokenCount);
}
if ((INT32)LocalTokenNumber < 0) {
//
// Valid token with data -- check the "notify on set" flag
//
DataEntry = PcdDb + (LocalTokenNumber & PCD_TOKEN_INDEX_MASK);
if ((*(UINT8 *)(DataEntry + 16) & 1) != 0) {
//
// Token has the notify flag set
//
PCD_PROTOCOL_SET_CALLBACK Callback;
EFI_STATUS CallbackStatus;
Callback = (PCD_PROTOCOL_SET_CALLBACK)(*(UINTN *)DataEntry);
CallbackStatus = Callback (
Callback,
PcdDb + *(UINT32 *)(PcdDb + 48) + *(UINT32 *)((LocalTokenNumber & PCD_TOKEN_INDEX_MASK) + PcdDb),
PcdDb + 16 * *(UINT16 *)(DataEntry + 8) + *(UINT32 *)(PcdDb + 44)
);
ASSERT_EFI_ERROR (CallbackStatus);
}
}
}
return EFI_SUCCESS;
}
/*
* DxePcdOnSetVariableEvent - Callback for ReadyToBoot / SetVariable events.
*
* Locates the PCD protocol interface and calls the notification
* callbacks for both the PEI and DXE phases.
*
* Address: 0x2998
*/
EFI_STATUS
DxePcdOnSetVariableEvent (
VOID
)
{
EFI_STATUS Status;
VOID *PcdProtocol;
Status = gBS->LocateProtocol (&gPcdProtocolGuid, NULL, &PcdProtocol);
if (!EFI_ERROR (Status)) {
DxePcdCallbackNotifyAll (TRUE);
DxePcdCallbackNotifyAll (FALSE);
}
return Status;
}
/*=============================================================================
* HOB Support Functions
*============================================================================*/
/*
* GetHobListInternal - Retrieve the HOB list pointer from the SystemTable.
*
* Walks SystemTable->ConfigurationTable to find the entry matching
* gEfiHobListGuid, and caches the pointer in mHobList.
*
* Address: 0x340C
*/
VOID *
GetHobListInternal (
VOID
)
{
if (mHobList == NULL) {
UINTN Index;
mHobList = NULL;
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
if (CompareGuid (
&gEfiHobListGuid,
gST->ConfigurationTable[Index].VendorGuid
)) {
mHobList = gST->ConfigurationTable[Index].VendorTable;
break;
}
}
if (mHobList == NULL) {
//
// HOB list not found -- this is an ASSERT condition
//
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
}
ASSERT (mHobList != NULL);
}
return mHobList;
}
/*
* GetNextGuidHob - Find the next GUID extension HOB in the HOB list.
*
* Returns a pointer to the GUID-extension HOB's data section,
* or NULL if the end of the HOB list is reached.
*
* Address: 0x34EC
*/
VOID *
GetNextGuidHob (
IN VOID *HobStart,
IN GUID *Guid
)
{
UINT16 *Hob;
Hob = (UINT16 *)HobStart;
ASSERT (Hob != NULL);
while (TRUE) {
if (*Hob == HOB_TYPE_END_OF_LIST) {
return NULL;
}
if (*Hob == HOB_TYPE_GUID_EXTENSION) {
if (CompareGuid (Guid, (GUID *)(Hob + 4))) {
return (VOID *)(((UINT8 *)Hob + 24));
}
}
Hob = (UINT16 *)((UINT8 *)Hob + Hob[1]);
}
}
/*
* GetSectionFromFv - Read a raw section from a firmware volume by GUID.
*
* Opens the FirmwareVolume2 protocol on gImageHandle and reads
* the section matching the given GUID.
*
* Address: 0x353C
*/
VOID *
GetSectionFromFv (
IN GUID *SectionGuid
)
{
EFI_STATUS Status;
EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
VOID *Buffer;
UINTN BufferSize;
UINT32 AuthenticationStatus;
Status = gBS->OpenProtocol (
gImageHandle,
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **)&FvProtocol,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
);
ASSERT_EFI_ERROR (Status);
if (FvProtocol != NULL) {
Status = FvProtocol->ReadSection (
FvProtocol,
SectionGuid,
NULL,
0,
&Buffer,
&BufferSize,
&AuthenticationStatus
);
ASSERT_EFI_ERROR (Status);
}
return Buffer;
}
/*
* GetSectionFromFfs - Read a section from a firmware file system file.
*
* Opens either the FirmwareVolume2 protocol or the FirmwareVolumeBlock2
* protocol and reads a SECTION_RAW (type 25) from the named file.
*
* Address: 0x35C4
*/
EFI_STATUS
GetSectionFromFfs (
IN EFI_HANDLE ImageHandle,
IN EFI_GUID *NameGuid,
OUT VOID **Buffer,
OUT UINTN *Size
)
{
EFI_STATUS Status;
EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *FvbProtocol;
UINT32 AuthenticationStatus;
if (Buffer == NULL || Size == NULL) {
ASSERT (Buffer != NULL);
ASSERT (Size != NULL);
return EFI_INVALID_PARAMETER;
}
*Buffer = NULL;
*Size = 0;
if (ImageHandle == NULL) {
return EFI_NOT_FOUND;
}
//
// Try to open FirmwareVolume2 protocol first
//
Status = gBS->OpenProtocol (
ImageHandle,
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **)&FvProtocol,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
);
if (EFI_ERROR (Status)) {
//
// Fall back to FirmwareVolumeBlock2 protocol
//
Status = gBS->OpenProtocol (
ImageHandle,
&gEfiFirmwareVolumeBlock2ProtocolGuid,
(VOID **)&FvbProtocol,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
return FvbProtocol->ReadSection (
FvbProtocol,
NameGuid,
EFI_SECTION_RAW,
0,
Buffer,
Size,
&AuthenticationStatus
);
}
return FvProtocol->ReadSection (
FvProtocol,
NameGuid,
EFI_SECTION_RAW,
0,
Buffer,
Size,
&AuthenticationStatus
);
}
/*=============================================================================
* Library Functions -- Memory, String, GUID, Linked List, Lock
*============================================================================*/
/*
* CopyMem - Copy memory between two buffers with overflow checking.
*
* Address: 0x3038
*/
VOID *
CopyMem (
OUT VOID *DestinationBuffer,
IN CONST VOID *SourceBuffer,
IN UINTN Length
)
{
if (Length == 0) {
return DestinationBuffer;
}
ASSERT ((Length - 1) <= (MAX_UINTN - (UINTN)DestinationBuffer));
ASSERT ((Length - 1) <= (MAX_UINTN - (UINTN)SourceBuffer));
if (DestinationBuffer == SourceBuffer) {
return DestinationBuffer;
}
//
// Call the internal REP MOVSB-based implementation
//
InternalCopyMem (DestinationBuffer, SourceBuffer, Length);
return DestinationBuffer;
}
/*
* ZeroMem - Zero a memory buffer with overflow checking.
*
* Address: 0x31F4
*/
VOID *
ZeroMem (
OUT VOID *Buffer,
IN UINTN Length
)
{
if (Length == 0) {
return Buffer;
}
ASSERT (Buffer != NULL);
ASSERT (Length <= (MAX_UINTN - (UINTN)Buffer + 1));
//
// Call the internal REP STOSB-based implementation
//
InternalZeroMem (Buffer, Length);
return Buffer;
}
/*
* AllocatePool - Allocate EfiBootServicesData memory via gBS.
*
* Address: 0x3264
*/
VOID *
AllocatePool (
IN UINTN Size
)
{
EFI_STATUS Status;
VOID *Buffer;
Status = gBS->AllocatePool (EfiBootServicesData, Size, &Buffer);
if (EFI_ERROR (Status)) {
return NULL;
}
return Buffer;
}
/*
* AllocateZeroPool - Allocate and zero memory.
*
* Address: 0x3294
*/
VOID *
AllocateZeroPool (
IN UINTN Size
)
{
VOID *Buffer;
Buffer = AllocatePool (Size);
if (Buffer != NULL) {
ZeroMem (Buffer, Size);
}
return Buffer;
}
/*
* FreePool - Free memory via gBS.
*
* Address: 0x3334
*/
VOID
FreePool (
IN VOID *Buffer
)
{
EFI_STATUS Status;
Status = gBS->FreePool (Buffer);
ASSERT_EFI_ERROR (Status);
}
/*
* AcquireLock - Acquire the PCD spin lock.
*
* Checks the lock is in released state, then raises TPL to
* HIGH_LEVEL and marks the lock as acquired.
*
* Address: 0x3378
*/
VOID
AcquireLock (
VOID
)
{
ASSERT (gLockState == EFI_LOCK_RELEASED);
gLockState = EFI_LOCK_ACQUIRED;
gBS->RaiseTPL (TPL_HIGH_LEVEL);
}
/*
* ReleaseLock - Release the PCD spin lock.
*
* Restores TPL to APPLICATION and marks the lock as released.
*
* Address: 0x33C4
*/
VOID
ReleaseLock (
VOID
)
{
ASSERT (gLockState == EFI_LOCK_ACQUIRED);
gBS->RestoreTPL (TPL_APPLICATION);
gLockState = EFI_LOCK_RELEASED;
}
/*
* CompareGuid - Compare two GUIDs for equality.
*
* Reads both 64-bit halves of each GUID and compares them.
*
* Address: 0x30D4
*/
BOOLEAN
CompareGuid (
IN GUID *Guid1,
IN GUID *Guid2
)
{
UINT64 Data1A, Data1B;
UINT64 Data2A, Data2B;
Data1A = ReadUnaligned64 (Guid1);
Data2A = ReadUnaligned64 (Guid2);
if (Data1A != Data2A) {
return FALSE;
}
Data1B = ReadUnaligned64 ((UINT64 *)((UINTN)Guid1 + 8));
Data2B = ReadUnaligned64 ((UINT64 *)((UINTN)Guid2 + 8));
return (Data1B == Data2B);
}
/*
* ScanGuidTable - Scan a 16-byte aligned GUID table for a match.
*
* Address: 0x313C
*/
VOID *
ScanGuidTable (
IN GUID *GuidTable,
IN UINTN TableSize,
IN GUID *Guid
)
{
UINTN Offset;
UINTN EndOffset;
ASSERT (((UINTN)GuidTable & (sizeof (Guid->Data1) - 1)) == 0);
ASSERT (TableSize <= (MAX_UINTN - (UINTN)GuidTable + 1));
ASSERT ((TableSize & (sizeof (*GuidTable) - 1)) == 0);
Offset = (UINTN)GuidTable;
EndOffset = (TableSize & ~0xFULL) + Offset;
while (Offset < EndOffset) {
if (CompareGuid ((GUID *)Offset, Guid)) {
return (VOID *)Offset;
}
Offset += sizeof (GUID);
}
return NULL;
}
/*
* IsListValid - Validate a doubly-linked list by checking non-NULL links.
*
* Address: 0x2A3C
*/
BOOLEAN
IsListValid (
IN LIST_ENTRY *ListHead
)
{
ASSERT (ListHead != NULL);
ASSERT (ListHead->ForwardLink != NULL);
ASSERT (ListHead->BackLink != NULL);
return TRUE;
}
/*
* GetFirstNode - Return the first node in the list.
*
* Address: 0x2AFC
*/
LIST_ENTRY *
GetFirstNode (
IN LIST_ENTRY *ListHead
)
{
ASSERT (IsListValid (ListHead));
return ListHead->ForwardLink;
}
/*
* StrLen - Return the length of a UCS-2 (wide) string.
*
* Maximum length is PCD_MAX_STRING_LENGTH (0xF4240).
*
* Address: 0x2B98
*/
UINTN
StrLen (
IN CONST UINT16 *String
)
{
CONST UINT16 *Ptr;
ASSERT (String != NULL);
ASSERT (((UINTN)String & 1) == 0);
Ptr = String;
while (*Ptr != 0) {
ASSERT ((UINTN)(Ptr - String) < PCD_MAX_STRING_LENGTH);
Ptr++;
}
return (UINTN)(Ptr - String);
}
/*
* StrCmp - Compare two UCS-2 (wide) strings.
*
* Address: 0x2C2C
*/
INTN
StrCmp (
IN CONST UINT16 *FirstString,
IN CONST UINT16 *SecondString
)
{
CONST UINT16 *Ptr;
ASSERT (StrLen (FirstString) != (UINTN)-1);
ASSERT (StrLen (SecondString) != (UINTN)-1);
Ptr = FirstString;
while (*Ptr != 0 && *Ptr == *SecondString) {
Ptr++;
SecondString++;
}
return (INTN)(*Ptr - *SecondString);
}
/*
* AsciiStrLen - Return the length of a null-terminated ASCII string.
*
* Maximum length is PCD_MAX_STRING_LENGTH (0xF4240).
*
* Address: 0x2CCC
*/
UINTN
AsciiStrLen (
IN CONST CHAR8 *String
)
{
UINTN Length;
ASSERT (String != NULL);
for (Length = 0; *String != '\0'; Length++) {
ASSERT (Length < PCD_MAX_STRING_LENGTH);
String++;
}
return Length;
}
/*
* AsciiStrCpyS - Safe ASCII string copy with overlap checking.
*
* Validates destination size, source length, and buffer overlap
* before copying the string.
*
* Address: 0x2D64
*/
EFI_STATUS
AsciiStrCpyS (
OUT CHAR8 *Destination,
IN UINTN DestMax,
IN CONST CHAR8 *Source
)
{
UINTN CopyLen;
UINTN SourceLen;
ASSERT (Destination != NULL);
ASSERT (Source != NULL);
if (DestMax > PCD_MAX_STRING_LENGTH) {
ASSERT (FALSE);
return EFI_INVALID_PARAMETER;
}
if (DestMax == 0) {
ASSERT (FALSE);
return EFI_INVALID_PARAMETER;
}
CopyLen = (UINTN)Destination - (UINTN)Source;
if (CopyLen == 0) {
ASSERT (FALSE);
return EFI_INVALID_PARAMETER;
}
SourceLen = AsciiStrLen (Source);
if (CopyLen <= SourceLen) {
ASSERT (FALSE);
return EFI_BUFFER_TOO_SMALL;
}
//
// Check for buffer overlap
//
if (((UINTN)Source > (UINTN)Destination &&
(UINTN)Destination < (UINTN)(Source + SourceLen + 1)) ||
((UINTN)Source < (UINTN)Destination &&
(UINTN)Source >= (UINTN)(Destination + DestMax))) {
//
// Overlap detected
//
ASSERT (FALSE);
return EFI_INVALID_PARAMETER;
}
//
// Safe to copy -- no overlap
//
while (*Source != '\0') {
*Destination++ = *Source++;
}
*Destination = '\0';
return EFI_SUCCESS;
}
/*
* ReadUnaligned64 - Read a 64-bit value from the given address.
*
* Address: 0x2A0C
*/
UINT64
ReadUnaligned64 (
IN VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(UINT64 *)Buffer;
}