/** @file
SMBIOS Data Update DXE driver - Lightning Ridge EXRP platform variant.
This DXE driver is part of the UBA (Universal BIOS Architecture) framework.
It provides SMBIOS data updates for the Lightning Ridge EXRP platform.
The driver flow:
1. Initialize UEFI service table pointers and locate HII/SMBIOS protocols
(SmbiosDataUpdateInit).
2. Open the UBA config protocol, read the board-specific SMBIOS config GUID
(SmbiosDataUpdateEntry).
3. Register the board's HII string package (RegisterHiiPackageList).
4. Call UBA SMBIOS Data protocol's SetSmbiosData with the config GUID.
5. The registered callback (SmbiosDataUpdateDispatch) iterates through
SMBIOS Type 2 (30 fields), Type 9 (8 fields), and Type 41 (4 fields)
string descriptors, reads localized strings via HII Font protocol, and
writes them into the SMBIOS tables.
Build path (from PDB strings):
e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\PurleyRpPkg\Uba\UbaMain\Dxe\
TypeLightningRidgeEXRP\SmbiosDataUpdateDxe\SmbiosDataUpdateDxe\DEBUG\
AutoGen.c
Copyright (c) Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "SmbiosDataUpdateDxeLightningRidgeEXRP.h"
//
// Global UEFI system table pointers (initialized by SmbiosDataUpdateInit)
//
EFI_HANDLE gImageHandle = NULL; ///< 0x3928 - EFI image handle
EFI_SYSTEM_TABLE *gSystemTable = NULL; ///< 0x3918 - EFI system table
EFI_BOOT_SERVICES *gBootServices = NULL; ///< 0x3920 - EFI boot services table
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL; ///< 0x3930 - EFI runtime services table
//
// Cached protocol/state pointers
//
VOID *mDebugProtocol = NULL; ///< 0x3938 - Cached DebugLib protocol
VOID *mHobList = NULL; ///< 0x3940 - Cached HOB list pointer
VOID *mHiiFont = NULL; ///< 0x3948 - HII Font protocol
VOID *mHiiDatabase = NULL; ///< 0x3950 - HII Database protocol handle
VOID *mHiiPackageList = NULL; ///< 0x3958 - HII Package List protocol handle
VOID *mHiiString = NULL; ///< 0x3960 - HII String protocol handle
VOID *mHiiPackageListProtocol = NULL; ///< 0x3968 - HII Package List protocol interface
VOID *mSmbiosProtocol1 = NULL; ///< 0x3978 - SMBIOS protocol (string add)
VOID *mUbaSmbiosDataProtocol = NULL; ///< 0x3980 - UBA SMBIOS Data protocol
VOID *mSmbiosProtocol2 = NULL; ///< 0x3988 - SMBIOS protocol (string remove)
VOID *mSmbiosProtocol3 = NULL; ///< 0x3990 - SMBIOS protocol (string add)
VOID *mSmbiosProtocol4 = NULL; ///< 0x3998 - SMBIOS protocol (remove+add)
VOID *mMmPciUsra = NULL; ///< 0x39A0 - MM PCIe base protocol
//
// Global state
//
EFI_GUID mSmbiosConfigGuid; ///< 0x38F0 - Board-specific SMBIOS config GUID
EFI_HII_HANDLE gSmbiosStringPackHandle = NULL; ///< 0x3910 - HII handle for SMBIOS strings
UINT64 mPcdMaximumAsciiStringLength = MAX_ASCII_STRING_LENGTH; ///< Pcd for string length checks
// ===========================================================================
// GUID Definitions (protocol and config identifiers)
// ===========================================================================
///
/// GUID for the UBA config protocol opened from ImageHandle.
/// Contains board-specific SMBIOS configuration data.
///
static EFI_GUID mUbaConfigProtocolGuid = {
0x36232936, 0x0E76, 0x31C8, { 0xA1, 0x3A, 0x3A, 0xF2, 0xFC, 0x1C, 0x39, 0x32 }
};
///
/// GUID for "en-US" default language (RFC 4646).
/// Used as default platform language when PlatformLang variable is not set.
///
static UINT16 mDefaultLanguage[] = L"en-US";
///
/// HOB list GUID for configuration table lookup.
///
static EFI_GUID mHobListGuid = {
0x7739F24C, 0x93D7, 0x11D4, { 0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }
};
///
/// DXE Services Table GUID.
///
static EFI_GUID mDxeServicesTableGuid = {
0x5AD34BA, 0x6B02, 0x4214, { 0xA2, 0xE5, 0x22, 0x8B, 0xE4, 0xE3, 0x89, 0x1C }
};
///
/// Global Variable GUID for UEFI standard variables (PlatformLang).
///
static EFI_GUID mGlobalVariableGuid = {
0x8BE4DF61, 0x93CA, 0x11D2, { 0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C }
};
///
/// DebugLib protocol GUID.
///
static EFI_GUID mDebugLibProtocolGuid = {
0xE0D4DF48, 0xCB79, 0x4B33, { 0x95, 0x68, 0x20, 0xE8, 0xAA, 0xA1, 0xEC, 0x47 }
};
///
/// HII Font protocol GUID.
///
static EFI_GUID mHiiFontProtocolGuid = {
0xE93CE36F, 0xAE91, 0x41CD, { 0xB7, 0xE9, 0x1D, 0xBF, 0x42, 0x56, 0xE4, 0x8E }
};
///
/// HII Database protocol GUID.
///
static EFI_GUID mHiiDatabaseProtocolGuid = {
0xEF9FC172, 0xA1B2, 0x4693, { 0xB3, 0x27, 0x6D, 0x5F, 0xC4, 0x16, 0x42, 0x54 }
};
///
/// HII Package List protocol GUID.
///
static EFI_GUID mHiiPackageListProtocolGuid = {
0x6B78D9C4, 0xE4B5, 0x43B9, { 0xB6, 0x5A, 0xCA, 0x93, 0xA2, 0x3A, 0xD7, 0x8D }
};
///
/// HII String protocol GUID.
///
static EFI_GUID mHiiStringProtocolGuid = {
0xFD96974E, 0x23AA, 0x4CDC, { 0xB9, 0xCB, 0x98, 0xD1, 0x77, 0x50, 0x32, 0x2A }
};
///
/// SMBIOS protocol GUID.
///
static EFI_GUID mSmbiosProtocolGuid = {
0x0358FF4A, 0xCB6B, 0x4A4B, { 0x87, 0xEC, 0x88, 0x1A, 0x9D, 0xEB, 0x2D, 0xD8 }
};
///
/// MM PCIe Base protocol GUID.
///
static EFI_GUID mMmPciBaseProtocolGuid = {
0x2F34E1F8, 0x6BF6, 0x4C3E, { 0xBD, 0x34, 0x04, 0x67, 0x1A, 0x0B, 0x55, 0xFE }
};
///
/// UBA SMBIOS Data protocol GUID.
/// This protocol is used to register SMBIOS data update configuration.
///
static EFI_GUID mUbaSmbiosDataProtocolGuid = {
0x36232936, 0x0E76, 0x31C8, { 0xA1, 0x3A, 0x3A, 0xF2, 0xFC, 0x1C, 0x39, 0x32 }
};
///
/// SMBIOS string package GUID for HII registration.
/// Identifies this driver's HII string package for SMBIOS strings.
///
static EFI_GUID mSmbiosStringPackageGuid = {
0x36232936, 0x0E76, 0x31C8, { 0xA1, 0x3A, 0x3A, 0xF2, 0xFC, 0x1C, 0x39, 0x32 }
};
// ===========================================================================
// SMBIOS String Descriptor Table
// ===========================================================================
///
/// SMBIOS Type 2 (Baseboard) string descriptor table (30 entries).
/// Each entry is 10 bytes: {Type, FieldNum, StringId(16), Encoding, Offset, MaxLen, Flags, Reserved(16)}
///
/// These descriptors define which SMBIOS Type 2 string fields to update and
/// which HII string tokens to use for localized string lookup.
///
/// SMBIOS Type 2 fields: Manufacturer, ProductName, Version, SerialNumber,
/// AssetTag, LocationInChassis, ChassisLocation, etc.
///
static CONST SMBIOS_STRING_DESC mSmbiosType2Descriptors[30] = {
//
// Entry 0: Type=8 (Type2), Field=3, StrId=0x0002, Enc=0x08, Off=8, Max=3, Flag=0x08000000
//
{ 8, 3, 0x0002, 0x08, 8, 3, 0, {0} },
//
// Entry 1: Type=8 (Type2), Field=4, StrId=0x04, Enc=0x10, Off=8, Max=16, Flag=5
//
{ 8, 4, 0x0004, 0x10, 8, 16, 5, {0} },
//
// Entries 2-29 follow the same pattern with different field numbers,
// string IDs, encodings, offsets, max lengths, and flags.
//
// Full descriptor table details available in the disassembly at sub_77C.
// The table is a 300-byte (30 * 10) embedded constant array.
// Key field numbers: 5 (Manufacturer), 6 (ProductName), 7 (Version),
// 8 (SerialNumber), 9 (AssetTag), 10-29 (additional board strings).
//
};
// ===========================================================================
// Forward Declarations of Internal Functions
// ===========================================================================
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
);
UINT64
WriteUnaligned64 (
OUT VOID *Dst,
IN UINT64 Value
);
// ===========================================================================
// Low-Level Memory Helpers
// ===========================================================================
/**
Fast zero-memory implementation.
Zeros memory in 8-byte aligned chunks, then handles remaining bytes.
This is an optimized memset variant from UEFI BaseMemoryLib.
@param[out] Buffer Pointer to the buffer to zero.
@param[in] Length Number of bytes to zero.
@return Pointer to the buffer.
**/
STATIC
VOID *
ZeroMemBase (
OUT VOID *Buffer,
IN UINTN Length
)
{
CHAR8 *Buf;
Buf = (CHAR8 *)Buffer;
//
// Zero 8-byte aligned chunks first.
//
SetMem (Buf, Length & ~7, 0);
//
// Zero remaining bytes (0-7).
//
SetMem (&Buf[Length & ~7], Length & 7, 0);
return Buffer;
}
/**
Fast copy-memory implementation with overlap handling.
Copies memory in 8-byte aligned chunks if source and destination do not
overlap. If source < destination and they overlap, copies backward from the
end. If they do not overlap, copies forward in 8-byte chunks.
@param[out] Destination Pointer to the destination buffer.
@param[in] Source Pointer to the source buffer.
@param[in] Length Number of bytes to copy.
@return Pointer to the destination buffer.
**/
STATIC
VOID *
CopyMemBase (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
CHAR8 *Dst;
CONST CHAR8 *Src;
UINTN CountQwords;
UINTN CountRemaining;
Dst = (CHAR8 *)Destination;
Src = (CONST CHAR8 *)Source;
if (Src < Dst && &Src[Length - 1] >= Dst) {
//
// Overlapping: source ends after destination starts.
// Copy backwards from the end to avoid corruption.
//
Src = &Src[Length - 1];
Dst = &Dst[Length - 1];
CopyMem (Dst, Src, Length & 7);
// Copy remaining 8-byte chunks backward
} else {
//
// No overlap or destination before source: copy forward.
//
CountQwords = Length >> 3;
CountRemaining = Length & 7;
//
// Copy 8-byte aligned chunks.
//
CopyMem (Dst, Src, CountQwords * 8);
Dst = &Dst[CountQwords * 8];
Src = &Src[CountQwords * 8];
}
//
// Copy remaining bytes.
//
CopyMem (Dst, Src, CountRemaining);
return Destination;
}
// ===========================================================================
// Memory and String Library Functions
// ===========================================================================
/**
Zeros a memory buffer with assertion on invalid parameters.
@param[in] Buffer Pointer to the buffer to zero.
@param[in] Length Number of bytes to zero.
@return Pointer to the buffer.
**/
VOID *
ZeroMem (
IN VOID *Buffer,
IN UINTN Length
)
{
if (Length == 0) {
return Buffer;
}
if (Buffer == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c",
53,
"Buffer != ((void *) 0)"
);
}
if (Length > (UINTN)~Buffer) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c",
54,
"Length <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)Buffer + 1)"
);
}
return ZeroMemBase (Buffer, Length);
}
/**
Copies memory with assertion on invalid parameters.
@param[out] Destination Pointer to the destination buffer.
@param[in] Source Pointer to the source buffer.
@param[in] Length Number of bytes to copy. If 0, returns Destination.
@return Pointer to the destination buffer.
**/
VOID *
CopyMem (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
if (Length == 0) {
return Destination;
}
if ((Length - 1) > (UINTN)~Destination) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c",
56,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)DestinationBuffer)"
);
}
if ((Length - 1) > (UINTN)~Source) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c",
57,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)SourceBuffer)"
);
}
if (Destination == Source) {
return Destination;
}
return CopyMemBase (Destination, Source, Length);
}
/**
Returns the length of an ASCII string (not including null terminator).
Scans the string until a null byte is found, up to MAX_ASCII_STRING_LENGTH.
Asserts if the string exceeds the maximum length.
@param[in] String Pointer to the ASCII string.
@return Length of the string in characters.
**/
UINTN
AsciiStrLen (
IN CONST CHAR8 *String
)
{
CONST CHAR8 *Scan;
UINTN Length;
if (String == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c",
1082,
"String != ((void *) 0)"
);
}
Scan = String;
Length = 0;
while (*Scan != '\0') {
if (Length >= MAX_ASCII_STRING_LENGTH) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c",
1090,
"Length < _gPcd_FixedAtBuild_PcdMaximumAsciiStringLength"
);
}
Scan++;
Length++;
}
return Length;
}
/**
Compares two ASCII strings up to n characters.
@param[in] FirstString Pointer to the first ASCII string.
@param[in] SecondString Pointer to the second ASCII string.
@param[in] Length Maximum number of characters to compare.
@return 0 if equal, negative if First < Second, positive if First > Second.
**/
INTN
AsciiStrnCmp (
IN CONST CHAR8 *FirstString,
IN CONST CHAR8 *SecondString,
IN UINTN Length
)
{
CONST CHAR8 *First;
CONST CHAR8 *Second;
UINTN Count;
First = FirstString;
Second = SecondString;
Count = Length;
if (Count == 0) {
return 0;
}
if (AsciiStrLen (FirstString) == (UINTN)-1) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c",
1320,
"AsciiStrSize (FirstString)"
);
}
if (AsciiStrLen (SecondString) == (UINTN)-1) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c",
1321,
"AsciiStrSize (SecondString)"
);
}
if (Count > MAX_ASCII_STRING_LENGTH) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c",
1324,
"Length <= _gPcd_FixedAtBuild_PcdMaximumAsciiStringLength"
);
}
while (*First != '\0' && *Second != '\0' && *First == *Second && Count > 1) {
First++;
Second++;
Count--;
}
return (INTN)((UINT8)*First - (UINT8)*Second);
}
/**
Returns the length of a UCS-2 string (max 0xF4240).
@param[in] String Pointer to the UCS-2 string.
@return Length in characters, 0 if NULL or empty.
**/
UINTN
StrLen (
IN CONST UINT16 *String
)
{
CONST UINT16 *Scan;
UINTN Length;
if (((UINTN)String & 1) != 0) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\SafeString.c",
128,
"((UINTN) String & 0x00000001) == 0"
);
}
if (String == NULL || MAX_ASCII_STRING_LENGTH == 0) {
return 0;
}
Scan = String;
Length = 0;
if (*Scan != 0) {
while (Length < MAX_ASCII_STRING_LENGTH - 1) {
if (Scan[++Length] == 0) {
return Length;
}
}
return MAX_ASCII_STRING_LENGTH;
}
return Length;
}
/**
Safe ASCII string length (bounded by MaxLength).
@param[in] String Pointer to the ASCII string.
@param[in] MaxLength Maximum length to check.
@return Length of the string, or MaxLength if longer.
**/
UINTN
AsciiStrnLenS (
IN CONST CHAR8 *String,
IN UINTN MaxLength
)
{
CONST CHAR8 *Scan;
UINTN Length;
if (String == NULL || MaxLength == 0) {
return 0;
}
Scan = String;
Length = 0;
if (*Scan != '\0') {
while (Length < MaxLength - 1) {
if (Scan[++Length] == '\0') {
return Length;
}
}
return MaxLength;
}
return Length;
}
/**
Converts a UCS-2 string to an ASCII string with overlap-safe handling.
Validates all parameters, checks for buffer overlap, and converts each
UCS-2 character (must be < 0x100) to an ASCII byte.
@param[out] Destination Destination ASCII buffer.
@param[in] Source Source UCS-2 string.
@param[in] DestMax Maximum characters in destination (including null).
@retval EFI_SUCCESS Conversion successful.
@retval EFI_INVALID_PARAMETER NULL parameter.
@retval EFI_BAD_BUFFER_SIZE DestMax exceeds maximum.
@retval EFI_BUFFER_TOO_SMALL DestMax <= SourceLen.
**/
EFI_STATUS
UnicodeStrnToAsciiStrS (
OUT CHAR8 *Destination,
IN CONST UINT16 *Source,
IN UINTN DestMax
)
{
CONST UINT16 *Src;
CHAR8 *Dst;
UINTN SourceLen;
BOOLEAN Overlap;
Src = Source;
Dst = Destination;
if (((UINTN)Source & 1) != 0) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\SafeString.c",
2677,
"((UINTN) Source & 0x00000001) == 0"
);
}
if (Destination == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\SafeString.c",
2682,
"(Destination != ((void *) 0))"
);
return EFI_INVALID_PARAMETER;
}
if (Source == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\SafeString.c",
2683,
"(Source != ((void *) 0))"
);
return EFI_INVALID_PARAMETER;
}
if (DestMax > MAX_ASCII_STRING_LENGTH) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\SafeString.c",
2689,
"(DestMax <= (_gPcd_FixedAtBuild_PcdMaximumAsciiStringLength))"
);
}
if (DestMax == 0) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\SafeString.c",
2698,
"(DestMax != 0)"
);
return EFI_INVALID_PARAMETER;
}
SourceLen = StrLen (Src);
if (DestMax <= SourceLen) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\SafeString.c",
2704,
"(DestMax > SourceLen)"
);
return EFI_BUFFER_TOO_SMALL;
}
//
// Check for overlap between source and destination buffers.
//
Overlap = FALSE;
if ((UINT8 *)Src >= Dst && (UINT8 *)Src < &Dst[DestMax]) {
Overlap = TRUE;
} else if (Dst >= (UINT8 *)Src && Dst < (UINT8 *)&Src[SourceLen + 1]) {
Overlap = TRUE;
}
if (Overlap) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\SafeString.c",
2709,
"!InternalSafeStringIsOverlap (Destination, DestMax, (void *)Source, (SourceLen + 1) * sizeof(CHAR16))"
);
return EFI_BUFFER_TOO_SMALL; // Overlap error, treated as buffer error
}
//
// Convert each UCS-2 character to ASCII.
//
while (*Src != 0) {
if (*Src >= 0x100) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\SafeString.c",
2719,
"*Source < 0x100"
);
}
*Dst++ = (CHAR8)*Src++;
}
*Dst = '\0';
return EFI_SUCCESS;
}
// ===========================================================================
// GUID Manipulation
// ===========================================================================
/**
Reads a 32-bit value from potentially unaligned memory.
@param[in] Buffer Pointer to the memory to read.
@return The 32-bit value, or 0 if Buffer is NULL.
**/
UINT32
EFIAPI
ReadUnaligned32 (
IN CONST VOID *Buffer
)
{
if (Buffer == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
141,
"Buffer != ((void *) 0)"
);
}
return *(UINT32 *)Buffer;
}
/**
Reads a 64-bit value from potentially unaligned memory.
@param[in] Buffer Pointer to the memory to read.
@return The 64-bit value, or 0 if Buffer is NULL.
**/
UINT64
EFIAPI
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
if (Buffer == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
}
return *(UINT64 *)Buffer;
}
/**
Writes a 64-bit value to potentially unaligned memory.
@param[out] Dst Pointer to the destination memory.
@param[in] Value The 64-bit value to write.
@return The value written.
**/
UINT64
EFIAPI
WriteUnaligned64 (
OUT VOID *Dst,
IN UINT64 Value
)
{
if (Dst == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
219,
"Buffer != ((void *) 0)"
);
}
*(UINT64 *)Dst = Value;
return Value;
}
/**
Copies a 16-byte GUID by writing two 64-bit values.
@param[out] Dst Pointer to the destination GUID.
@param[in] Src Pointer to the source GUID.
@return Pointer to the destination GUID.
**/
EFI_GUID *
CopyGuid (
OUT EFI_GUID *Dst,
IN EFI_GUID *Src
)
{
UINT64 First;
UINT64 Second;
First = ReadUnaligned64 (Src);
WriteUnaligned64 (Dst, First);
Second = ReadUnaligned64 ((UINT8 *)Src + 8);
WriteUnaligned64 ((UINT8 *)Dst + 8, Second);
return Dst;
}
/**
Compares two GUIDs by comparing them as two 64-bit values.
@param[in] Guid1 Pointer to the first GUID.
@param[in] Guid2 Pointer to the second GUID.
@return TRUE if the GUIDs are identical, FALSE otherwise.
**/
BOOLEAN
EFIAPI
CompareGuid (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
UINT64 Guid1First;
UINT64 Guid1Second;
UINT64 Guid2First;
UINT64 Guid2Second;
Guid1First = ReadUnaligned64 (Guid1);
Guid1Second = ReadUnaligned64 ((UINT8 *)Guid1 + 8);
Guid2First = ReadUnaligned64 (Guid2);
Guid2Second = ReadUnaligned64 ((UINT8 *)Guid2 + 8);
return (BOOLEAN)(Guid1First == Guid2First && Guid1Second == Guid2Second);
}
// ===========================================================================
// Memory Allocation
// ===========================================================================
/**
Allocates boot services data pool memory.
@param[in] Size Number of bytes to allocate.
@return Pointer to the allocated memory, or NULL if allocation failed.
**/
VOID *
AllocatePool (
IN UINTN Size
)
{
EFI_STATUS Status;
VOID *Buffer;
//
// gBS->AllocatePool is at BootServices offset +64 (function index 8).
// Signature: (EFI_MEMORY_TYPE, UINTN, VOID**)
//
Status = gBootServices->AllocatePool (
EfiBootServicesData,
Size,
&Buffer
);
if (EFI_ERROR (Status)) {
Buffer = NULL;
}
return Buffer;
}
/**
Allocates and zeros pool memory.
@param[in] Size Number of bytes to allocate.
@return Pointer to the allocated zeroed memory, or NULL if allocation failed.
**/
VOID *
AllocateZeroPool (
IN UINTN Size
)
{
VOID *Buffer;
Buffer = AllocatePool (Size);
if (Buffer != NULL) {
ZeroMem (Buffer, Size);
}
return Buffer;
}
/**
Frees pool memory allocated by AllocatePool.
@param[in] Buffer Pointer to the memory to free.
If NULL, the function does nothing.
**/
VOID
FreePool (
IN VOID *Buffer
)
{
EFI_STATUS Status;
//
// gBS->FreePool is at BootServices offset +72 (function index 9).
//
Status = gBootServices->FreePool (Buffer);
if (EFI_ERROR (Status)) {
DebugPrint (
UBA_DEBUG_ERROR,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Status
);
AssertHandler (
"e:\\hs\\MdePkg\\Library\\UefiMemoryAllocationLib\\MemoryAllocationLib.c",
819,
"!EFI_ERROR (Status)"
);
}
}
// ===========================================================================
// Configuration Table Access
// ===========================================================================
/**
Finds a configuration table entry by GUID in the SystemTable.
Scans SystemTable->ConfigurationTable for a GUID match. The configuration
table is an array of EFI_CONFIGURATION_TABLE entries, each 24 bytes:
- 16 bytes: VendorGuid (EFI_GUID)
- 8 bytes: VendorTable (VOID*)
@param[in] TableGuid GUID of the configuration table to find.
@param[out] Table Returns the pointer to the matching table data.
@retval EFI_SUCCESS Table entry found.
@retval EFI_NOT_FOUND No matching table entry found.
**/
EFI_STATUS
GetConfigTable (
IN EFI_GUID *TableGuid,
OUT VOID **Table
)
{
UINTN Index;
UINTN NumEntries;
EFI_CONFIGURATION_TABLE *ConfigTable;
VOID *FoundTable;
if (TableGuid == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\UefiLib\\UefiLib.c",
97,
"TableGuid != ((void *) 0)"
);
}
if (Table == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\UefiLib\\UefiLib.c",
98,
"Table != ((void *) 0)"
);
}
*Table = NULL;
NumEntries = gSystemTable->NumberOfTableEntries;
if (NumEntries == 0) {
return EFI_NOT_FOUND;
}
ConfigTable = gSystemTable->ConfigurationTable;
FoundTable = NULL;
for (Index = 0; Index < NumEntries; Index++) {
//
// Compare GUIDs using 128-bit comparison.
//
if (CompareGuid (&ConfigTable[Index].VendorGuid, TableGuid)) {
FoundTable = ConfigTable[Index].VendorTable;
*Table = FoundTable;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
// ===========================================================================
// HOB List Access
// ===========================================================================
/**
Initializes the HOB list pointer from the SystemTable.
Finds the HOB list GUID in SystemTable->ConfigurationTable and asserts
if not found. Caches the HOB list pointer in mHobList global.
@return Pointer to the HOB list, or NULL if not found.
**/
VOID *
HobLibInit (
VOID
)
{
EFI_STATUS Status;
VOID *HobList;
//
// Return cached pointer if already initialized.
//
HobList = mHobList;
if (HobList != NULL) {
return HobList;
}
//
// Find the HOB list in the configuration table.
//
Status = GetConfigTable (&mHobListGuid, &mHobList);
if (EFI_ERROR (Status)) {
DebugPrint (
UBA_DEBUG_ERROR,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Status
);
AssertHandler (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
}
//
// Verify the HOB list was found.
//
if (mHobList == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != ((void *) 0)"
);
}
return mHobList;
}
// ===========================================================================
// Language and Variable Services
// ===========================================================================
/**
Gets the platform language string from UEFI global variable.
Reads the "PlatformLang" UEFI variable via RuntimeServices->GetVariable.
The variable is identified by gEfiGlobalVariableGuid.
Returns a buffer containing the current platform language code (e.g., "en-US").
@param[out] Value Returns a pointer to the allocated language string buffer.
Caller must free with FreePool().
@retval EFI_SUCCESS Language string retrieved successfully.
@retval EFI_NOT_FOUND "PlatformLang" variable does not exist.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
**/
EFI_STATUS
GetPlatformLang (
OUT CHAR8 **Value
)
{
EFI_STATUS Status;
UINTN BufferSize;
CHAR8 *LangBuffer;
if (Value == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\UefiLib\\UefiLib.c",
1401,
"Name != ((void *) 0) && Guid != ((void *) 0) && Value != ((void *) 0)"
);
}
*Value = NULL;
BufferSize = 0;
//
// Call GetVariable with zero size first to get required buffer size.
// gRT->GetVariable is at RuntimeServices offset +72 (function index 9).
//
Status = gRuntimeServices->GetVariable (
L"PlatformLang",
&mGlobalVariableGuid,
NULL,
&BufferSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// Allocate buffer of required size.
//
LangBuffer = AllocatePool (BufferSize);
if (LangBuffer == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\UefiLib\\UefiLib.c",
1421,
"*Value != ((void *) 0)"
);
return EFI_OUT_OF_RESOURCES;
}
//
// Read the variable data.
//
Status = gRuntimeServices->GetVariable (
L"PlatformLang",
&mGlobalVariableGuid,
NULL,
&BufferSize,
LangBuffer
);
if (EFI_ERROR (Status)) {
FreePool (LangBuffer);
*Value = NULL;
} else {
*Value = LangBuffer;
return EFI_SUCCESS;
}
}
return Status;
}
/**
Matches a supported language against a list of available languages.
Parses the ASCII semicolon-separated language list and checks if any
of the provided language strings match a language in the supported list.
Supports ISO 639-2 3-letter codes and RFC 4646 language tags.
@param[in] SupportedLanguages Semicolon-separated list of supported languages.
@param[in] LanguageMatch 0 (not used in this variant) or non-zero.
@param[in] ... Variable argument list of language strings
to check against the supported list.
@return Pointer to an allocated buffer containing the matched language,
or NULL if no match found or on error.
**/
CHAR8 *
EFIAPI
GetSupportedLanguage (
IN CONST CHAR8 *SupportedLanguages,
IN CHAR8 LanguageMatch,
...
)
{
VA_LIST Args;
CONST CHAR8 *LangToCheck;
CONST CHAR8 *Supported;
UINTN SupportedLen;
UINTN CheckLen;
CHAR8 *ResultBuffer;
UINTN ResultLen;
if (SupportedLanguages == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\UefiLib\\UefiLib.c",
1528,
"SupportedLanguages != ((void *) 0)"
);
}
VA_START (Args, LanguageMatch);
LangToCheck = VA_ARG (Args, CONST CHAR8 *);
if (LangToCheck != NULL) {
VA_COPY (Args, Args);
while (LangToCheck != NULL) {
//
// Determine the length of the language to check.
// If full match mode (LanguageMatch != 0) is disabled (0),
// use the full string; otherwise truncate to 3 characters.
//
if (LanguageMatch == 0) {
CheckLen = AsciiStrLen (LangToCheck);
if (CheckLen <= 3) {
SupportedLen = CheckLen;
} else {
SupportedLen = 3;
}
} else {
//
// Full match: use the complete string length, but truncated
// to 3 chars if zero mode is off and string is long.
//
SupportedLen = 0;
if (*LangToCheck != '\0') {
CheckLen = 0;
while (LangToCheck[CheckLen] != ';' && LangToCheck[CheckLen] != '\0') {
CheckLen++;
}
SupportedLen = CheckLen;
}
}
//
// Scan the supported languages string for a match.
//
if (LanguageMatch != 0) {
//
// Full match mode: iterate through each language list entry.
//
Supported = SupportedLanguages;
while (*Supported != '\0') {
//
// Skip ';' separators.
//
while (*Supported == ';') {
Supported++;
}
//
// Get the length of this supported language entry.
//
SupportedLen = 0;
while (Supported[SupportedLen] != '\0' && Supported[SupportedLen] != ';') {
SupportedLen++;
}
if (CheckLen <= SupportedLen) {
if (AsciiStrnCmp (Supported, LangToCheck, CheckLen) == 0) {
//
// Match found - allocate buffer and copy the language.
//
ResultLen = SupportedLen + 1;
ResultBuffer = AllocateZeroPool (ResultLen);
if (ResultBuffer != NULL) {
return CopyMem (ResultBuffer, LangToCheck, ResultLen);
}
return NULL;
}
}
Supported += SupportedLen;
}
} else {
//
// Short match mode: truncate language to 3 characters, compare.
//
if (CheckLen <= 3) {
SupportedLen = CheckLen;
} else {
SupportedLen = 3;
}
//
// Scan supported languages.
//
Supported = SupportedLanguages;
while (*Supported != '\0') {
//
// Skip ';' separators.
//
while (*Supported == ';') {
Supported++;
}
SupportedLen = 0;
while (Supported[SupportedLen] != '\0' && Supported[SupportedLen] != ';') {
SupportedLen++;
}
if (CheckLen <= SupportedLen) {
if (AsciiStrnCmp (Supported, LangToCheck, CheckLen) == 0) {
ResultLen = SupportedLen + 1;
ResultBuffer = AllocateZeroPool (ResultLen);
if (ResultBuffer != NULL) {
return CopyMem (ResultBuffer, LangToCheck, ResultLen);
}
return NULL;
}
}
//
// If full check fails, try truncating to 3 chars.
//
if (CheckLen > 3 && 3 <= SupportedLen) {
if (AsciiStrnCmp (Supported, LangToCheck, 3) == 0) {
ResultLen = SupportedLen + 1;
ResultBuffer = AllocateZeroPool (ResultLen);
if (ResultBuffer != NULL) {
return CopyMem (ResultBuffer, LangToCheck, ResultLen);
}
return NULL;
}
}
//
// Handle RFC 4646 language tags with '-' separator.
//
while (Supported[SupportedLen] == '-') {
Supported[SupportedLen] = '\0';
}
Supported += SupportedLen;
}
}
//
// Move to next language string in varargs.
//
LangToCheck = VA_ARG (Args, CONST CHAR8 *);
}
}
VA_END (Args);
return NULL;
}
// ===========================================================================
// Debug Output
// ===========================================================================
/**
Gets the DebugLib protocol instance.
Allocates pool memory then locates the DebugLib protocol by GUID.
If the pool allocation returns a suspiciously small value (<= 0x10),
the protocol is not looked up.
Note: The pool allocation size is hardcoded as 31 (MEMORY_TYPE_BOOT_SERVICES_DATA).
The allocated pointer is used to check if the allocation succeeded by comparing
its value to 0x10. This is a heuristic: if the pointer value is > 0x10, the
allocation is considered valid.
@return Pointer to the DebugLib protocol interface, or NULL if not found.
**/
VOID *
GetDebugLibProtocol (
VOID
)
{
VOID *DebugProtocol;
UINTN PoolSize;
EFI_STATUS Status;
//
// Return cached pointer if already initialized.
//
DebugProtocol = mDebugProtocol;
if (DebugProtocol != NULL) {
return DebugProtocol;
}
//
// Allocate pool as a probe. The allocation size is 31 bytes
// (MEMORY_TYPE_BOOT_SERVICES_DATA).
//
PoolSize = MEMORY_TYPE_BOOT_SERVICES_DATA;
mDebugProtocol = gBootServices->AllocatePool (EfiBootServicesData, POOL_ALLOC_SIZE);
//
// Heuristic: if the returned pointer is > 16, treat it as a valid allocation.
// This is a non-standard check unique to this implementation.
//
if ((UINTN)mDebugProtocol > 0x10) {
//
// gBS->LocateProtocol is at BootServices offset +320 (function index 41).
//
Status = gBootServices->LocateProtocol (
&mDebugLibProtocolGuid,
NULL,
&mDebugProtocol
);
if (EFI_ERROR (Status)) {
mDebugProtocol = NULL;
}
} else {
//
// Suspicious allocation pointer - treat as failure.
//
mDebugProtocol = NULL;
}
return mDebugProtocol;
}
/**
Platform-aware debug print function.
Writes a formatted debug message through the DebugLib protocol.
Checks the CMOS RTC register 0x4B to determine the platform SKU and
filters debug output accordingly. Only Type 1 platforms (CMOS SKU = 1)
get info-level debug messages; all others get only error-level messages.
Note: The RTC CMOS register 0x4B at port 0x70/0x71 is used as a non-volatile
platform SKU indicator. A memory-mapped I/O register at 0xFDAF0490 bit 1
serves as a fallback when the CMOS value is 0.
@param[in] DebugLevel Debug message level (e.g., UBA_DEBUG_ERROR).
@param[in] FormatString Format string for the debug message.
@param[in] ... Variable arguments for the format string.
**/
VOID
EFIAPI
DebugPrint (
IN UINTN DebugLevel,
IN CONST CHAR8 *FormatString,
...
)
{
VA_LIST VaList;
UINT64 FilterMask;
VOID *DebugProtocol;
UINT8 CmosSku;
UINT8 PlatformInfo;
UINT8 EffectiveSku;
VA_START (VaList, FormatString);
//
// Get the DebugLib protocol; if not available, skip the print.
//
DebugProtocol = GetDebugLibProtocol ();
if (DebugProtocol == NULL) {
VA_END (VaList);
return;
}
//
// Read the platform SKU from RTC CMOS register 0x4B.
// The NMI-disable bit (0x80) is preserved in the index write.
//
IoWrite8 (RTC_CMOS_INDEX_PORT, (IoRead8 (RTC_CMOS_INDEX_PORT) & 0x80) | RTC_CMOS_INDEX_SKU);
CmosSku = IoRead8 (RTC_CMOS_DATA_PORT);
//
// Determine effective SKU:
// - If raw CMOS value > 3 and == 0, read platform info register.
// - CMOS value of 1 = Platform Type 1 (get info-level debug).
//
EffectiveSku = CmosSku;
if (CmosSku > 3) {
if (CmosSku == 0) {
//
// Read platform info register for IIO mode detection.
// Bit 1 of 0xFDAF0490 indicates IIO configuration mode.
//
EffectiveSku = (*(volatile UINT8 *)PLATFORM_INFO_REGISTER & 0x02) | 0x01;
}
}
//
// Map platform SKU to debug level filter mask.
//
EffectiveSku--;
if (EffectiveSku <= 0xFD) {
//
// Valid SKU range (1-4 maps to 0-3).
//
FilterMask = UBA_DEBUG_ERROR; // Default: only errors
if (CmosSku == 1) {
FilterMask = UBA_DEBUG_INFO; // Type1: allow info-level messages
}
} else {
FilterMask = UBA_DEBUG_ERROR;
}
//
// If the requested debug level passes the filter, call DebugPrint.
//
if ((FilterMask & DebugLevel) != 0) {
//
// DebugProtocol layout:
// +0: DebugPrint (function pointer)
// +8: Assert (function pointer)
//
((DEBUG_PRINT_FUNC)((VOID **)DebugProtocol)[0])(
DebugLevel,
FormatString,
(UINT64 *)VaList
);
}
VA_END (VaList);
}
/**
ASSERT assertion handler wrapper.
When an assertion fails, this function calls the DebugLib protocol's
ASSERT handler with the file/line/expression information.
@param[in] FileName Source file name string.
@param[in] LineNumber Line number in the source file.
@param[in] Expression Failed assertion expression string.
**/
VOID
EFIAPI
AssertHandler (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Expression
)
{
VOID *DebugProtocol;
DebugProtocol = GetDebugLibProtocol ();
if (DebugProtocol != NULL) {
//
// Call DebugProtocol->Assert (at offset +8).
//
((DEBUG_ASSERT_FUNC)((VOID **)DebugProtocol)[1])(
FileName,
LineNumber,
Expression
);
}
}
// ===========================================================================
// HII Protocol Services
// ===========================================================================
/**
Gets supported languages for an HII handle.
Retrieves the list of languages supported by a registered HII string
package by calling HiiFont->GetStringInfo (at offset +24) first with
a NULL buffer to get the required size, then with an allocated buffer.
@param[in] HiiHandle HII handle to query.
@param[in] StringId String token ID (not used directly here).
@param[in] Type SMBIOS type (not used directly here).
@param[in] MaxLen Maximum length hint (not used directly here).
@return Pointer to the supported languages string, or NULL on failure.
**/
CHAR8 *
GetHiiSupportedLanguages (
IN EFI_HII_HANDLE HiiHandle,
IN UINT16 StringId,
IN UINTN Type,
IN UINTN MaxLen
)
{
EFI_STATUS Status;
UINTN BufferSize;
CHAR8 FirstChar;
CHAR8 *LangBuffer;
if (HiiHandle == NULL) {
AssertHandler (
"e:\\hs\\MdeModulePkg\\Library\\UefiHiiLib\\HiiLanguage.c",
47,
"HiiHandle != ((void *) 0)"
);
}
//
// Call with NULL buffer to get required size.
// HiiFont->GetStringInfo is at qword_3948 offset +24.
//
BufferSize = 0;
Status = ((EFI_HII_GET_STRING_INFO)((VOID **)mHiiFont)[3])(
mHiiFont,
HiiHandle,
&FirstChar,
&BufferSize
);
if (Status != EFI_BUFFER_TOO_SMALL) {
return NULL;
}
//
// Allocate buffer of required size.
//
LangBuffer = AllocatePool (BufferSize);
if (LangBuffer == NULL) {
return NULL;
}
//
// Now get the actual string.
//
Status = ((EFI_HII_GET_STRING_INFO)((VOID **)mHiiFont)[3])(
mHiiFont,
HiiHandle,
LangBuffer,
&BufferSize
);
if (EFI_ERROR (Status)) {
FreePool (LangBuffer);
return NULL;
}
return LangBuffer;
}
/**
Gets a localized SMBIOS string from the HII Font protocol.
Uses the HII Font protocol's string lookup to get a localized version
of an SMBIOS string for the given HII handle and string ID.
The function:
1. Validates HiiHandle is non-NULL and StringId is non-zero.
2. Gets the supported languages for the HII handle.
3. Gets the current platform language.
4. Calls GetSupportedLanguage() to find a matching language.
5. Uses HiiFont->StringToImage (offset +8) to get the localized string.
@param[in] HiiHandle HII handle for the string package.
@param[in] StringId HII string token ID.
@param[in] Type SMBIOS structure type (for context).
@param[in] MaxLen Maximum string length hint.
@return Pointer to the localized ASCII string, or NULL if not found.
**/
CHAR8 *
GetHiiString (
IN EFI_HII_HANDLE HiiHandle,
IN UINT16 StringId,
IN UINT8 Type,
IN UINTN MaxLen
)
{
EFI_STATUS Status;
CHAR8 *SupportedLanguages;
CHAR8 *PlatformLang;
CHAR8 *MatchedLanguage;
CHAR8 *ResultString;
UINTN StringSize;
UINTN RequiredSize;
//
// Validate parameters.
//
if (HiiHandle == NULL) {
AssertHandler (
"e:\\hs\\MdeModulePkg\\Library\\UefiHiiLib\\HiiString.c",
238,
"HiiHandle != ((void *) 0)"
);
}
if (StringId == 0) {
AssertHandler (
"e:\\hs\\MdeModulePkg\\Library\\UefiHiiLib\\HiiString.c",
239,
"StringId != 0"
);
}
//
// Get the supported languages for this HII string package.
//
SupportedLanguages = GetHiiSupportedLanguages (HiiHandle, StringId, Type, MaxLen);
if (SupportedLanguages == NULL) {
return NULL;
}
//
// Get the current platform language.
//
PlatformLang = NULL;
GetPlatformLang (&PlatformLang);
//
// Match the platform language against supported languages.
// Use "en-US" as default language for matching.
//
MatchedLanguage = NULL;
if (PlatformLang != NULL) {
MatchedLanguage = GetSupportedLanguage (
SupportedLanguages,
0,
SupportedLanguages,
PlatformLang,
NULL
);
}
if (MatchedLanguage == NULL) {
//
// Fall back to the first supported language.
//
MatchedLanguage = GetSupportedLanguage (
SupportedLanguages,
0,
SupportedLanguages,
NULL
);
}
if (MatchedLanguage != NULL) {
//
// Get the localized string size by calling HiiFont->StringToImage (offset +8)
// with NULL buffer to get required size.
//
StringSize = 0;
Status = ((EFI_HII_STRING_TO_IMAGE)((VOID **)mHiiFont)[1])(
mHiiFont,
MatchedLanguage,
HiiHandle,
StringId,
NULL,
&StringSize,
0
);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// Allocate buffer for the string.
//
ResultString = AllocatePool (StringSize);
if (ResultString != NULL) {
//
// Get the actual string.
//
Status = ((EFI_HII_STRING_TO_IMAGE)((VOID **)mHiiFont)[1])(
mHiiFont,
MatchedLanguage,
HiiHandle,
StringId,
ResultString,
&StringSize,
0
);
if (EFI_ERROR (Status)) {
FreePool (ResultString);
ResultString = NULL;
}
}
} else {
ResultString = NULL;
}
} else {
ResultString = NULL;
}
//
// Clean up temporary allocations.
//
FreePool (SupportedLanguages);
if (PlatformLang != NULL) {
FreePool (PlatformLang);
}
if (MatchedLanguage != NULL) {
FreePool (MatchedLanguage);
}
if (ResultString != NULL && ResultString != MatchedLanguage) {
// ResultString was allocated separately, keep it
}
return ResultString;
}
/**
Gets the string for the given HII handle and string ID.
Allocates a buffer and retrieves the string via HiiFont->StringToImage.
Handles the two-call pattern (get size, allocate, get data).
@param[in] HiiHandle HII handle.
@param[in] StringId String token ID.
@param[in] Language Language to use.
@param[out] StringSize Returns the size of the string.
@return Pointer to the allocated string, or NULL on failure.
**/
STATIC
CHAR8 *
InternalGetHiiString (
IN EFI_HII_HANDLE HiiHandle,
IN UINT16 StringId,
IN CONST CHAR8 *Language,
OUT UINTN *StringSize
)
{
EFI_STATUS Status;
UINTN Size;
CHAR8 *String;
*StringSize = 0;
Size = 0;
//
// Call StringToImage with NULL buffer to get required size.
//
Status = ((EFI_HII_STRING_TO_IMAGE)((VOID **)mHiiFont)[1])(
mHiiFont,
(CHAR8 *)Language,
HiiHandle,
StringId,
NULL,
&Size,
0
);
if (Status != EFI_BUFFER_TOO_SMALL) {
return NULL;
}
//
// Allocate buffer.
//
String = AllocatePool (Size);
if (String == NULL) {
return NULL;
}
//
// Get the actual string.
//
Status = ((EFI_HII_STRING_TO_IMAGE)((VOID **)mHiiFont)[1])(
mHiiFont,
(CHAR8 *)Language,
HiiHandle,
StringId,
String,
&Size,
0
);
if (EFI_ERROR (Status)) {
FreePool (String);
return NULL;
}
*StringSize = Size;
return String;
}
/**
Registers an HII package list and returns the HII handle.
Builds a package list from the input GUID and string package data,
then calls the HII Database protocol's NewPackageList to register it.
Returns the resulting HII handle.
The input is an array of string pointers. The first pointer is the
GUID (16 bytes), followed by entries where each entry is:
4 bytes: header (includes size-4)
0..N bytes: string data as UCS-2 characters
@param[in] Guid GUID for the SMBIOS string package.
@param[out] ImageHandle Returns the image handle.
@param[in] StringPackage Array of NULL-terminated string pointers.
@param[in] Flags Package registration flags.
@return HII handle for the registered package, or NULL on failure.
**/
EFI_HII_HANDLE
RegisterHiiPackageList (
IN EFI_GUID *Guid,
OUT EFI_HANDLE *ImageHandle,
IN UINT32 *StringPackage,
IN UINTN Flags
)
{
EFI_HII_HANDLE HiiHandle;
EFI_STATUS Status;
VOID *PackageData;
UINT32 *Entry;
UINT32 **EntryPtr;
UINTN TotalSize;
UINTN DataSize;
UINTN EntrySize;
UINT32 *CurEntry;
//
// Validate parameters.
//
if (Guid == NULL) {
AssertHandler (
"e:\\hs\\MdeModulePkg\\Library\\UefiHiiLib\\HiiLib.c",
160,
"PackageListGuid != ((void *) 0)"
);
}
//
// If no string entries, return NULL handle.
//
if (StringPackage == NULL) {
return NULL;
}
//
// Calculate total package size.
// Each entry header has size-4 in the first 4 bytes.
// We need to sum all entry sizes (excluding the 4-byte header
// contribution counted as size-4 per entry).
//
Entry = StringPackage;
EntryPtr = &Entry;
TotalSize = 0;
do {
EntryPtr++;
EntrySize = ReadUnaligned32 (Entry) - 4;
TotalSize += EntrySize;
Entry = *EntryPtr;
} while (*EntryPtr != NULL);
if (TotalSize == 0) {
return NULL;
}
//
// Allocate buffer for the entire package list (header + data).
// The HII package list header is sizeof(EFI_HII_PACKAGE_LIST_HEADER)
// which includes a GUID (16 bytes) and package length (24 bytes total).
//
DataSize = TotalSize + sizeof (EFI_HII_PACKAGE_LIST_HEADER);
PackageData = AllocateZeroPool (DataSize);
if (PackageData == NULL) {
return NULL;
}
//
// Fill in the package list header.
// Copy the GUID to the beginning of the package list.
//
CopyGuid ((EFI_GUID *)PackageData, Guid);
*(UINT32 *)((UINT8 *)PackageData + 16) = (UINT32)DataSize;
//
// Copy all package entries.
//
{
UINT8 *Dst = (UINT8 *)PackageData + 20;
Entry = StringPackage;
EntryPtr = &Entry;
do {
EntrySize = ReadUnaligned32 (Entry) - 4;
CopyMem (Dst, Entry + 1, EntrySize);
Dst += EntrySize;
EntryPtr++;
Entry = *EntryPtr;
} while (*EntryPtr != NULL);
//
// Add the terminating 4-byte zero entry.
//
CopyMem (Dst, &mZeroGuid, 4);
}
//
// Call HiiDatabase->NewPackageList (at offset +0 of qword_3968).
//
HiiHandle = NULL;
Status = ((EFI_HII_NEW_PACKAGE_LIST)((VOID **)mHiiPackageListProtocol)[0])(
mHiiPackageListProtocol,
PackageData,
ImageHandle,
&HiiHandle
);
if (EFI_ERROR (Status)) {
HiiHandle = NULL;
}
FreePool (PackageData);
return HiiHandle;
}
// ===========================================================================
// SMBIOS Protocol Services
// ===========================================================================
/**
Adds a SMBIOS string record to the SMBIOS table.
Uses the SMBIOS protocol to add a string record. The protocol function
at offset +0 takes (This, Reserved, Handle, Buffer).
@param[in] Buffer Pointer to the SMBIOS string record buffer.
The record format: Byte0=Type, Byte1=FieldCount,
UINT16=Handle, followed by string data.
@retval EFI_SUCCESS String added successfully.
@retval EFI_PROTOCOL_ERROR SMBIOS protocol not available.
**/
EFI_STATUS
AddSmbiosString (
IN VOID *Buffer
)
{
VOID *SmbiosProto;
EFI_STATUS Status;
UINT16 Handle;
//
// Get or locate the SMBIOS protocol.
//
SmbiosProto = mSmbiosProtocol3;
if (SmbiosProto == NULL) {
Status = gBootServices->LocateProtocol (
&mSmbiosProtocolGuid,
NULL,
&mSmbiosProtocol3
);
if (EFI_ERROR (Status)) {
return Status;
}
SmbiosProto = mSmbiosProtocol3;
}
//
// Initialize handle to "uninitialized" (0xFFFE).
// The SMBIOS AddString function at protocol offset 0 uses:
// - param 0: This pointer
// - param 1: Reserved (0)
// - param 2: Handle pointer (0xFFFE = use default)
// - param 3: Buffer containing the string record
//
Handle = SMBIOS_HANDLE_UNINITIALIZED;
((EFI_SMBIOS_ADD_STRING)((VOID **)SmbiosProto)[0])(
SmbiosProto,
0,
&Handle,
Buffer
);
return EFI_SUCCESS;
}
/**
Finds the first SMBIOS string matching a given type.
Uses the SMBIOS protocol GetNextString function (at offset +24) to
enumerate SMBIOS strings and find the first one whose type matches
the specified type ID.
@param[in] TypeId SMBIOS string type to find.
@param[out] Handle Returns the handle of the matching string.
@retval EFI_SUCCESS Matching string found.
@retval EFI_NOT_FOUND No matching string found.
@retval EFI_PROTOCOL_ERROR SMBIOS protocol not available.
**/
EFI_STATUS
FindFirstSmbiosString (
IN UINT8 TypeId,
OUT UINT16 *Handle
)
{
VOID *SmbiosProto;
UINT16 CurrentHandle;
UINT8 CurrentType;
UINT8 SearchType;
UINTN Count;
UINTN BufferSize;
UINT8 Buffer[40];
if (Handle == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Get or locate the SMBIOS protocol.
//
SmbiosProto = mSmbiosProtocol1;
if (SmbiosProto == NULL) {
SmbiosProto = mSmbiosProtocol1;
if (SmbiosProto == NULL) {
return EFI_NOT_FOUND;
}
}
//
// Search for the first matching string.
// The SMBIOS protocol GetNext function at offset +24 enumerates strings:
// - param 0: This pointer
// - param 1: Handle (IN/OUT, -2 = start enumeration)
// - param 2: Type ID
// - param 3: Buffer (output)
// - param 4: Buffer size
//
SearchType = TypeId;
CurrentHandle = SMBIOS_HANDLE_UNINITIALIZED;
Count = 0;
while (((EFI_SMBIOS_GET_NEXT_STRING)((VOID **)SmbiosProto)[3])(
SmbiosProto,
&CurrentHandle,
&SearchType,
Buffer,
NULL
) >= EFI_SUCCESS && CurrentHandle != 0xFFFE)
{
Count++;
if (Count == 1) {
*Handle = CurrentHandle;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
Removes an existing SMBIOS string and adds a replacement.
Uses the SMBIOS protocol to:
1. Find the first string matching TypeId.
2. Remove it via RemoveString (offset +16).
3. Add the new string from Buffer via AddString (offset 0).
@param[in] TypeId SMBIOS string type to find and replace.
@param[in] Buffer Pointer to the replacement string record.
@retval EFI_SUCCESS Replacement completed.
@retval EFI_PROTOCOL_ERROR SMBIOS protocol not available.
**/
EFI_STATUS
RemoveAndAddSmbiosString (
IN UINT8 TypeId,
IN VOID *Buffer
)
{
VOID *SmbiosProto;
EFI_STATUS Status;
UINT16 Handle;
SmbiosProto = mSmbiosProtocol4;
if (SmbiosProto == NULL) {
Status = gBootServices->LocateProtocol (
&mSmbiosProtocolGuid,
NULL,
&mSmbiosProtocol4
);
if (EFI_ERROR (Status)) {
return Status;
}
SmbiosProto = mSmbiosProtocol4;
}
//
// Find and remove the current string, then add the replacement.
//
Status = FindFirstSmbiosString (TypeId, &Handle);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Remove the old string at protocol offset +16.
//
((EFI_SMBIOS_REMOVE_STRING)((VOID **)SmbiosProto)[2])(
SmbiosProto,
Handle
);
//
// Add the new string at protocol offset 0.
//
Handle = SMBIOS_HANDLE_UNINITIALIZED;
((EFI_SMBIOS_ADD_STRING)((VOID **)SmbiosProto)[0])(
SmbiosProto,
0,
&Handle,
Buffer
);
return EFI_SUCCESS;
}
/**
Removes all SMBIOS strings of a given type.
Enumerates all SMBIOS strings matching the specified type and removes
each one via the SMBIOS protocol RemoveString function.
@param[in] TypeId SMBIOS string type to remove.
**/
VOID
RemoveAllSmbiosStringsOfType (
IN UINT8 TypeId
)
{
VOID *SmbiosProto;
UINT16 CurrentHandle;
UINT8 SearchType;
UINT8 TypeBuf;
UINT8 Buffer;
UINTN Count;
//
// Get or locate the SMBIOS protocol.
//
SmbiosProto = mSmbiosProtocol2;
if (SmbiosProto == NULL) {
//
// Locate the SMBIOS protocol.
//
if (gBootServices->LocateProtocol (
&mSmbiosProtocolGuid,
NULL,
&mSmbiosProtocol2
) < 0) {
return;
}
SmbiosProto = mSmbiosProtocol2;
}
//
// Enumerate and remove all strings of the given type.
//
SearchType = TypeId;
CurrentHandle = SMBIOS_HANDLE_UNINITIALIZED;
Count = 0;
while (((EFI_SMBIOS_GET_NEXT_STRING)((VOID **)SmbiosProto)[3])(
SmbiosProto,
&CurrentHandle,
&SearchType,
&TypeBuf,
NULL
) >= EFI_SUCCESS && CurrentHandle != 0xFFFE)
{
Count++;
}
//
// Remove each string of this type.
//
for (; Count > 0; Count--) {
RemoveAndAddSmbiosString (TypeId, NULL);
}
}
/**
Gets the SMBIOS structures after the current field position.
Scans the SMBIOS string record starting from the current field to find
the end of the structure. The function iterates through the string data
to find the end of the structure region.
@param[in,out] RecordBuffer Pointer to the SMBIOS string record.
@param[out] TotalLength Returns the total length of the structure
from the start to the end of the last field.
**/
VOID
GetSmbiosStructuresAfterField (
IN OUT VOID *RecordBuffer,
OUT UINTN *TotalLength
)
{
UINT8 *Buffer;
UINTN Length;
UINTN Index;
UINT8 *Scan;
Buffer = (UINT8 *)RecordBuffer;
Length = Buffer[1]; // FieldCount
*TotalLength = Length;
Scan = Buffer + Length;
//
// Scan through the string data to find the end.
// Strings are terminated by a double-null (one after each string,
// plus a final null to mark end of all strings).
//
while (TRUE) {
if (*Scan == '\0') {
//
// Found a null terminator. Check if it's the end of strings
// (double-null) or just a single string terminator.
//
if (*++Scan == '\0') {
//
// Double-null found - end of structures.
//
*TotalLength += 2;
return;
}
(*TotalLength)++;
}
//
// Count characters until next null.
//
for (Index = 0; Index < 64; Index++) {
if (Scan[Index] == '\0') {
break;
}
}
if (Index == 64) {
//
// No null found in 64 chars - likely an error.
//
*TotalLength = 0;
return;
}
Scan += Index;
*TotalLength += Index;
}
}
/**
Writes a string value into the SMBIOS string record.
This function replaces or inserts a string value at the appropriate
position within the SMBIOS string record buffer. It handles string
insertion by tracking positions, managing overlap-safe copies, and
recalculating the record structure after modification.
@param[in,out] RecordBuffer Pointer to the SMBIOS string record.
@param[in] NewString The new string value to write.
@param[in] FieldIndex Index of the field within the record.
@param[in] RecordBuffer2 Second record buffer for overlap-safe
operations (for intermediate storage).
@retval EFI_SUCCESS String updated successfully.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_BAD_BUFFER_SIZE Buffer size overflow.
**/
EFI_STATUS
UpdateSmbiosStringField (
IN OUT VOID *RecordBuffer,
IN CHAR8 *NewString,
IN UINTN FieldIndex,
IN VOID *RecordBuffer2
)
{
UINT8 *Buf;
UINTN StringLen;
UINTN FieldPos;
UINTN FieldLen;
UINTN TotalLen;
UINTN AfterFieldLen;
UINT8 *FieldPtr;
UINTN NewLen;
UINTN v10;
UINTN v11;
UINTN v12;
UINTN v13;
UINTN v14;
UINTN v15;
UINTN v16;
CHAR8 *SourceEnd;
UINTN OverlapCheck;
Buf = (UINT8 *)RecordBuffer;
//
// Get the source string length.
//
StringLen = AsciiStrLen (NewString);
//
// Get the SMBIOS structure length.
//
GetSmbiosStructuresAfterField (RecordBuffer, &TotalLen);
//
// Calculate the field position within the record.
//
FieldPos = Buf[1]; // FieldCount (offset to first string)
FieldPtr = &Buf[FieldPos];
v12 = 1;
v13 = 0;
//
// Scan through fields to find the target field position.
//
if (FieldIndex > 1) {
do {
v14 = v12 + 1;
if (*FieldPtr != '\0') {
v14 = v12;
}
FieldPtr++;
v13++;
v12 = v14;
} while (v14 < FieldIndex);
}
//
// Check if the new string length matches the field's existing length.
// If AsciiStrLen of FieldPtr's region matches StringLen, we can do an
// in-place replacement. Otherwise we need to insert/remove space.
//
FieldLen = AsciiStrLen (FieldPtr);
if (FieldLen == StringLen) {
//
// Same length - in-place replacement.
//
if (FieldPtr != NULL) {
//
// Overlap-safe string copy.
//
if (FieldPtr >= (UINT8 *)NewString ||
(UINT8 *)NewString >= &FieldPtr[StringLen + 1]) {
//
// No overlap, direct copy.
//
if (FieldPtr == (UINT8 *)NewString) {
//
// Same pointer, nothing to do.
//
return EFI_SUCCESS;
}
AsciiStrCpyS (FieldPtr, StringLen + 1, NewString);
//
// Recalculate the structure after the field.
//
GetSmbiosStructuresAfterField (RecordBuffer, &TotalLen);
CopyMem (RecordBuffer, RecordBuffer2, TotalLen);
return EFI_SUCCESS;
}
//
// Overlap detected - use auxiliary buffer for safe copy.
//
{
UINTN TotalSize;
UINTN AuxBufSize;
TotalSize = 0x300; // SMBIOS_STRING_RECORD_SIZE
if (RecordBuffer2 == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Copy the record to auxiliary buffer, modify, and copy back.
//
CopyMem (RecordBuffer2, RecordBuffer, FieldPos + v13 + Buf[1]);
CopyMem (
(UINT8 *)RecordBuffer2 + FieldPos + v13 + Buf[1],
&Buf[FieldPos + v13 + Buf[1] + FieldLen + 1],
TotalLen - FieldPos - v13 - Buf[1] - FieldLen - 1
);
//
// Write new string.
//
AsciiStrCpyS (
(CHAR8 *)((UINT8 *)RecordBuffer2 + FieldPos + v13 + Buf[1]),
StringLen + 1,
NewString
);
//
// Copy back and recalculate.
//
GetSmbiosStructuresAfterField (RecordBuffer2, &TotalLen);
//
// Copy the updated record back.
//
CopyMem (&Buf[FieldPos + v13 + Buf[1]], &FieldPtr[FieldLen - StringLen + 1], StringLen + 1);
//
// Final size update.
//
Buf[FieldPos + v13 + Buf[1] + StringLen] = NewString[AfterFieldLen];
FreePool (RecordBuffer2);
FreePool (NewString);
return EFI_SUCCESS;
}
}
//
// Null field pointer - append? This is a complex path.
//
}
//
// Different length - need to insert/remove space.
//
{
UINTN AuxBuffer;
UINTN *AuxPtr;
UINTN AuxTotalLen;
//
// Allocate auxiliary buffer for safe manipulation.
//
AuxBuffer = (UINTN)AllocateZeroPool (SMBIOS_STRING_RECORD_SIZE);
if (AuxBuffer == 0) {
return EFI_OUT_OF_RESOURCES;
}
//
// Copy the part before the new string.
//
CopyMem (
(VOID *)AuxBuffer,
RecordBuffer,
v13 + Buf[1] + FieldPos
);
//
// Copy the part after the old string, skipping it.
//
CopyMem (
(VOID *)(AuxBuffer + v13 + Buf[1] + FieldPos),
&Buf[v13 + Buf[1] + FieldPos + FieldLen + 1],
TotalLen - FieldPos - v13 - Buf[1] - FieldLen - 1
);
//
// Calculate new total length.
//
AuxTotalLen = TotalLen - FieldLen + StringLen;
//
// Write new string.
//
AsciiStrCpyS (
(CHAR8 *)(AuxBuffer + v13 + Buf[1] + FieldPos),
StringLen + 1,
NewString
);
//
// Copy back to original buffer.
//
GetSmbiosStructuresAfterField ((VOID *)AuxBuffer, &TotalLen);
CopyMem (
RecordBuffer,
(VOID *)AuxBuffer,
TotalLen
);
//
// Clean up.
//
FreePool ((VOID *)AuxBuffer);
}
return EFI_SUCCESS;
}
/**
Builds a single SMBIOS string record for Type 2 (Baseboard) fields.
Constructs a formatted SMBIOS string record in the output buffer based
on the string descriptor table entry at the given index. The descriptor
table (embedded in the code at sub_77C, address 0x77C) maps each index
(0-29) to an SMBIOS Type 2 field with an associated HII string token.
Each descriptor table entry is 10 bytes:
+0: UINT16 StringId (HII string token ID)
+2: UINT8 FieldNumber (SMBIOS field number)
+4: UINT8 Encoding (string encoding type)
+5: UINT8 MaxLength (maximum string length)
+6: UINT8 Offset (byte offset in output buffer)
+7: UINT8 Flags (attribute flags)
+8: UINT16 Reserved (internal use)
The output record has the format:
Byte0: SMBIOS structure type (8 for Type2)
Byte1: FieldCount (29 fields, 0-indexed as 30-1)
Word2: Handle (0xFFFE = uninitialized)
+4: FieldNumber (copied from descriptor)
+5: Encoding (copied from descriptor)
+6: MaxLength (copied from descriptor)
+7: Flags (copied from descriptor)
+8..N: String data (localized HII string)
@param[out] Buffer Output buffer for the SMBIOS string record.
Must be at least SMBIOS_STRING_RECORD_SIZE bytes.
@param[in] DescriptorIndex Index into the Type 2 descriptor table (0-29).
@retval EFI_SUCCESS String record built successfully.
@retval EFI_INVALID_PARAMETER DescriptorIndex >= 30.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed (from GetHiiString or UpdateSmbiosStringField).
**/
EFI_STATUS
BuildSmbiosStringRecord (
OUT VOID *Buffer,
IN UINTN DescriptorIndex
)
{
UINT8 *OutBuf;
CONST SMBIOS_STRING_DESC *Desc;
CHAR8 *LocalizedString;
UINT8 Type;
UINT8 FieldNum;
UINT16 StringId;
UINT8 Encoding;
UINT8 OutputOffset;
UINT8 MaxLength;
UINT8 Flags;
OutBuf = (UINT8 *)Buffer;
if (DescriptorIndex >= SMBIOS_TYPE2_COUNT) {
return EFI_INVALID_PARAMETER;
}
//
// Get the descriptor entry for this index.
//
Desc = &mSmbiosType2Descriptors[DescriptorIndex];
FieldNum = Desc->FieldNumber;
StringId = Desc->StringId;
Encoding = Desc->Encoding;
OutputOffset = Desc->OutputOffset;
MaxLength = Desc->MaxLength;
Flags = Desc->Flags;
//
// Initialize the SMBIOS string record header.
// Byte 0: Type (8 = Type 2 Baseboard)
// Byte 1: FieldCount (29 = total fields -1)
// Word 2-3: Handle (0xFFFE = uninitialized)
//
OutBuf[0] = 8; // SMBIOS type 2
OutBuf[1] = 29; // 30 fields, index 0-29
*(UINT16 *)&OutBuf[2] = SMBIOS_HANDLE_UNINITIALIZED;
//
// Copy the descriptor field metadata into the record header at offsets 4-7.
// From the original binary (sub_77C, 0x77C):
// OutBuf[4] = FieldNumber
// OutBuf[5] = Encoding
// OutBuf[6] = MaxLength
// OutBuf[7] = Flags
//
OutBuf[4] = FieldNum;
OutBuf[5] = Encoding;
OutBuf[6] = MaxLength;
OutBuf[7] = Flags;
//
// If StringId is non-zero, look up the localized string.
// A zero StringId means this descriptor has no associated string.
//
if (StringId != 0) {
//
// Get the localized string from HII font protocol.
//
LocalizedString = GetHiiString (
gSmbiosStringPackHandle,
StringId,
2,
MaxLength
);
if (LocalizedString != NULL) {
//
// Write the string into the SMBIOS record buffer.
// The function handles buffer manipulation, overlap-safe
// copy, and string insertion/splicing.
//
UpdateSmbiosStringField (
Buffer,
LocalizedString,
DescriptorIndex,
NULL
);
//
// Note: UpdateSmbiosStringField manages memory of LocalizedString.
// No explicit FreePool needed here.
//
}
}
return EFI_SUCCESS;
}
/**
Builds a Type 9 (System Slot) SMBIOS string record.
This function constructs an SMBIOS Type 9 string record based on the
slot descriptor at the given index (0-7).
@param[out] Buffer Output buffer for the SMBIOS string record.
@param[in] DescriptorIndex Index into the Type 9 descriptor table (0-7).
@retval EFI_SUCCESS String record built successfully.
@retval Others Error from internal functions.
**/
EFI_STATUS
BuildSmbiosType9Record (
OUT VOID *Buffer,
IN UINTN DescriptorIndex
)
{
//
// NOTE: The full implementation of this function could not be decompiled.
// The function at address 0xA20 constructs Type 9 SMBIOS records.
// It follows a similar pattern to BuildSmbiosStringRecord but for
// SMBIOS Type 9 (System Slots) fields.
//
// From the dispatch function (SmbiosDataUpdateDispatch), this function
// is called for 8 iterations (0-7). It takes a working buffer and index,
// builds the record, and the caller then writes it to SMBIOS.
//
return EFI_UNSUPPORTED;
}
/**
Builds a Type 41 (Onboard Device) SMBIOS string record.
This function constructs an SMBIOS Type 41 string record based on the
device descriptor at the given index (0-3).
@param[out] Buffer Output buffer for the SMBIOS string record.
@param[in] DescriptorIndex Index into the Type 41 descriptor table (0-3).
@retval EFI_SUCCESS String record built successfully.
@retval Others Error from internal functions.
**/
EFI_STATUS
BuildSmbiosType41Record (
OUT VOID *Buffer,
IN UINTN DescriptorIndex
)
{
//
// NOTE: The full implementation of this function could not be decompiled.
// The function at address 0xD98 constructs Type 41 SMBIOS records.
// It follows a similar pattern to BuildSmbiosStringRecord but for
// SMBIOS Type 41 (Onboard Devices Extended Information) fields.
//
// From the dispatch function (SmbiosDataUpdateDispatch), this function
// is called for 4 iterations (0-3). It takes a working buffer and index,
// builds the record, and the caller then writes it to SMBIOS.
//
return EFI_UNSUPPORTED;
}
// ===========================================================================
// MM PCI Configuration
// ===========================================================================
/**
Writes a value to a PCIe configuration register via the MM PCIe base protocol.
Encodes the bus/device/function into an MMIO address using:
address = ((Func & 7) | (8 * ((Dev & 0x1F) | (32 * Bus)))) << 12
The MM PCIe base protocol (gEfiMmPciBaseProtocolGuid) provides a Write
function at offset +24 that takes an encoded address structure.
@param[in] Bus PCI bus number.
@param[in] Device PCI device number.
@param[in] Function PCI function number.
@param[in] Register PCI config register offset.
@param[in] Value 32-bit value to write to the register.
@return EFI_STATUS from the MM PCIe write operation.
**/
EFI_STATUS
MmPciWriteConfig (
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function,
IN UINT16 Register,
IN UINT32 Value
)
{
//
// Encode the PCI address.
// Address format: ((Func & 7) | (8 * ((Dev & 0x1F) | (32 * Bus)))) << 12
//
UINT32 Address;
UINT32 AddressBuf[6];
//
// Prepare address structure for MmPciBase->Write.
// AddressBuf layout:
// [0]: Address (encoded BDF << 12)
// [1]: Register
// [2]: Size (512 = PCI config space)
// [3]: 0 (reserved)
//
AddressBuf[0] = 0;
AddressBuf[1] = 0;
AddressBuf[2] = 512;
AddressBuf[3] = 0;
//
// Encode the PCI address: Bits [14:12] = Func, [19:15] = Dev, [24:20] = Bus.
//
AddressBuf[0] = (Value & 7 | (8 * ((Device & 0x1F) | (32 * Bus)))) << 12;
//
// Call MmPciUsra->Write (at offset +24) with the address structure.
//
if (mMmPciUsra == NULL) {
return EFI_NOT_READY;
}
((EFI_MM_PCI_WRITE)((VOID **)mMmPciUsra)[3])(
AddressBuf
);
return EFI_SUCCESS;
}
// ===========================================================================
// SMBIOS Data Update Dispatch
// ===========================================================================
/**
Dispatches SMBIOS data updates.
Allocates a working buffer of SMBIOS_STRING_RECORD_SIZE bytes and
iterates through three phases:
1. 30 Type 2 (Baseboard) descriptors
2. 8 Type 9 (System Slot) descriptors
3. 4 Type 41 (Onboard Device) descriptors
Before each phase, existing strings of that type are removed.
After each successful record build, the string is added to SMBIOS tables.
@return EFI_STATUS from the last SMBIOS add operation.
**/
EFI_STATUS
SmbiosDataUpdateDispatch (
VOID
)
{
VOID *WorkBuffer;
UINTN Status;
UINTN i;
//
// Allocate working buffer for SMBIOS string record construction.
//
WorkBuffer = AllocateZeroPool (SMBIOS_STRING_RECORD_SIZE);
if (WorkBuffer == NULL) {
AssertHandler (
"e:\\hs\\PurleyRpPkg\\Uba\\UbaMain\\Dxe\\TypeLightningRidgeEXRP\\SmbiosDataUpdateDxe\\SmbiosDataUpdateDxe.c",
625,
"gSmbiosStringPackHandle != ((void *) 0)"
);
return EFI_OUT_OF_RESOURCES;
}
//
// Phase 1: Process 30 SMBIOS Type 2 (Baseboard) string fields.
//
// Remove all existing Type 2 strings first.
//
RemoveAllSmbiosStringsOfType (SMBIOS_TYPE_BASEBOARD);
//
// Process each Type 2 string field.
//
for (i = 0; i < SMBIOS_TYPE2_COUNT; i++) {
ZeroMem (WorkBuffer, SMBIOS_STRING_RECORD_SIZE);
Status = BuildSmbiosStringRecord (WorkBuffer, i);
if ((Status & SIGNATURE_64_ERROR_BIT) == 0) {
AddSmbiosString (WorkBuffer);
}
}
//
// Phase 2: Process 8 SMBIOS Type 9 (System Slot) string fields.
//
RemoveAllSmbiosStringsOfType (SMBIOS_TYPE_SYSTEM_SLOTS);
for (i = 0; i < SMBIOS_TYPE9_COUNT; i++) {
ZeroMem (WorkBuffer, SMBIOS_STRING_RECORD_SIZE);
Status = BuildSmbiosType9Record (WorkBuffer, i);
if ((Status & SIGNATURE_64_ERROR_BIT) == 0) {
AddSmbiosString (WorkBuffer);
}
}
//
// Phase 3: Process 4 SMBIOS Type 41 (Onboard Device) string fields.
//
RemoveAllSmbiosStringsOfType (SMBIOS_TYPE_ONBOARD_DEVICES);
for (i = 0; i < SMBIOS_TYPE41_COUNT; i++) {
ZeroMem (WorkBuffer, SMBIOS_STRING_RECORD_SIZE);
Status = BuildSmbiosType41Record (WorkBuffer, i);
if ((Status & SIGNATURE_64_ERROR_BIT) == 0) {
AddSmbiosString (WorkBuffer);
}
}
//
// Clean up.
//
FreePool (WorkBuffer);
return (EFI_STATUS)Status;
}
// ===========================================================================
// Main Entry Point (SmbiosDataUpdateEntry)
// ===========================================================================
/**
Main SMBIOS data update entry routine.
Called from ModuleEntryPoint after SmbiosDataUpdateInit().
Performs the SMBIOS data update by:
1. Printing a debug message identifying this module.
2. Opening the UBA config protocol from ImageHandle to get board config.
3. Copying the board-specific SMBIOS config GUID to a global.
4. Registering the HII string package via RegisterHiiPackageList().
5. Locating the UBA SMBIOS Data protocol.
6. Calling UbaSmbiosData->SetSmbiosData() which triggers the dispatch.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@retval EFI_SUCCESS SMBIOS data update initiated.
@retval EFI_UNSUPPORTED UBA config protocol not available.
@retval EFI_NOT_FOUND Required protocol not found.
**/
EFI_STATUS
SmbiosDataUpdateEntry (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
VOID *UbaConfig;
UINT64 ConfigData[3];
UBA_SMBIOS_DATA_PROTOCOL *UbaDataProtocol = NULL;
UINT32 ConfigBuffer[6];
//
// Open the UBA config protocol installed on ImageHandle.
// gBS->OpenProtocol is at BootServices offset +152 (function index 19).
//
Status = gBootServices->OpenProtocol (
ImageHandle,
&mUbaConfigProtocolGuid,
&UbaConfig,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Print module identification string.
//
DebugPrint (
UBA_DEBUG_ERROR,
"UBA:SmbiosDataUpdateEntry Image GUID=%g\n",
UbaConfig
);
//
// Copy the board-specific SMBIOS config GUID from UBA config data.
// The config data is at UbaConfig + 4 bytes (first 4 bytes are header).
//
CopyGuid (&mSmbiosConfigGuid, (EFI_GUID *)((UINT8 *)UbaConfig + 4));
//
// Register the HII string package.
//
gSmbiosStringPackHandle = RegisterHiiPackageList (
&mSmbiosStringPackageGuid,
&ImageHandle,
NULL, // String data from HII table
0
);
if (gSmbiosStringPackHandle == NULL) {
AssertHandler (
"e:\\hs\\PurleyRpPkg\\Uba\\UbaMain\\Dxe\\TypeLightningRidgeEXRP\\SmbiosDataUpdateDxe\\SmbiosDataUpdateDxe.c",
625,
"gSmbiosStringPackHandle != ((void *) 0)"
);
}
//
// Prepare the dispatch configuration buffer.
// Format: 6 UINT32s = 24 bytes total
// [0]: Board type magic (0x42434150 = "PCAB"?)
// [1]: Version (1)
// [2]: Dispatch function index (9)
// [3]: 0 (reserved)
//
ZeroMem (ConfigBuffer, sizeof (ConfigBuffer));
ConfigBuffer[0] = 0x42434150; // "PCAB" or board type identifier
ConfigBuffer[1] = 1; // Version
ConfigBuffer[2] = 9; // Dispatch function index
//
// Locate the UBA SMBIOS Data protocol.
//
if (mUbaSmbiosDataProtocol == NULL) {
Status = gBootServices->LocateProtocol (
&mUbaSmbiosDataProtocolGuid,
NULL,
&mUbaSmbiosDataProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Register the SMBIOS config with the UBA protocol.
// This call triggers the SmbiosDataUpdateDispatch callback.
//
return ((UBA_SMBIOS_DATA_PROTOCOL *)mUbaSmbiosDataProtocol)->SetSmbiosData (
mUbaSmbiosDataProtocol,
&mSmbiosConfigGuid,
ConfigBuffer,
sizeof (ConfigBuffer)
);
}
// ===========================================================================
// Initialization Function (SmbiosDataUpdateInit)
// ===========================================================================
/**
Initializes UEFI services and protocols used by this driver.
Performs the following initialization steps:
1. Saves ImageHandle, SystemTable, BootServices, RuntimeServices to globals.
2. Validates all required table pointers (non-NULL checks via ASSERT).
3. Locates HII Font protocol and caches in mHiiFont.
4. Locates HII Database protocol and caches in mHiiDatabase.
5. Locates HII Package List protocol and caches in mHiiPackageList.
6. Locates HII String protocol and caches in mHiiString.
7. Locates DXE Services Table via GetConfigTable().
8. Optionally locates MM PCIe base protocol (may be pre-cached).
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
**/
VOID
SmbiosDataUpdateInit (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// The initialization logic from the original sub_38C (address 0x38C)
// has been inlined into ModuleEntryPoint() below.
//
// This function is kept as a stub for documentation purposes since the
// original binary had this as a separate function called from the entry
// point. All initialization (globals, HII protocols, DXE Services Table,
// MM PCIe base protocol) is now performed directly in ModuleEntryPoint().
//
}
// ===========================================================================
// Module Entry Point
// ===========================================================================
/**
Entry point for the SMBIOS Data Update DXE driver.
Standard UEFI driver entry point. Initializes global UEFI service table
and protocol pointers, then performs the SMBIOS data update.
The entry point performs the following:
1. Saves ImageHandle and SystemTable to globals with NULL validation.
2. Saves BootServices and RuntimeServices to globals with NULL validation.
3. Locates all required HII and SMBIOS protocols.
4. Finds the DXE Services Table from SystemTable->ConfigurationTable.
5. Optionally locates the MM PCIe Base protocol.
6. Calls SmbiosDataUpdateEntry() to perform SMBIOS string updates.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@return EFI_STATUS Status code from SmbiosDataUpdateEntry().
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Step 1: Initialize global UEFI service table pointers.
//
gImageHandle = ImageHandle;
if (ImageHandle == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
"gImageHandle != ((void *) 0)"
);
}
gSystemTable = SystemTable;
if (SystemTable == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
"gST != ((void *) 0)"
);
}
gBootServices = SystemTable->BootServices;
if (gBootServices == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
"gBS != ((void *) 0)"
);
}
gRuntimeServices = SystemTable->RuntimeServices;
if (gRuntimeServices == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
"gRT != ((void *) 0)"
);
}
//
// Step 2: Locate HII services protocols.
//
// Locate HII Font protocol.
Status = gBootServices->LocateProtocol (
&mHiiFontProtocolGuid,
NULL,
&mHiiFont
);
if (EFI_ERROR (Status)) {
DebugPrint (
UBA_DEBUG_ERROR,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Status
);
AssertHandler (
"e:\\hs\\MdeModulePkg\\Library\\UefiHiiServicesLib\\UefiHiiServicesLib.c",
88,
"!EFI_ERROR (Status)"
);
}
// Locate HII String protocol.
Status = gBootServices->LocateProtocol (
&mHiiStringProtocolGuid,
NULL,
&mHiiString
);
if (EFI_ERROR (Status)) {
DebugPrint (
UBA_DEBUG_ERROR,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Status
);
AssertHandler (
"e:\\hs\\MdeModulePkg\\Library\\UefiHiiServicesLib\\UefiHiiServicesLib.c",
94,
"!EFI_ERROR (Status)"
);
}
// Locate HII Database protocol.
Status = gBootServices->LocateProtocol (
&mHiiDatabaseProtocolGuid,
NULL,
&mHiiDatabase
);
if (EFI_ERROR (Status)) {
DebugPrint (
UBA_DEBUG_ERROR,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Status
);
AssertHandler (
"e:\\hs\\MdeModulePkg\\Library\\UefiHiiServicesLib\\UefiHiiServicesLib.c",
100,
"!EFI_ERROR (Status)"
);
}
// Locate HII Package List protocol.
Status = gBootServices->LocateProtocol (
&mHiiPackageListProtocolGuid,
NULL,
&mHiiPackageList
);
// Locate HII Package List protocol interface.
Status = gBootServices->LocateProtocol (
&mHiiPackageListProtocolGuid,
NULL,
&mHiiPackageListProtocol
);
//
// Step 3: Find DXE Services Table from SystemTable->ConfigurationTable.
//
Status = GetConfigTable (&mDxeServicesTableGuid, &mHobList);
if (EFI_ERROR (Status)) {
DebugPrint (
UBA_DEBUG_ERROR,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Status
);
AssertHandler (
"e:\\hs\\MdePkg\\Library\\DxeServicesTableLib\\DxeServicesTableLib.c",
64,
"!EFI_ERROR (Status)"
);
}
if (mHobList == NULL) {
AssertHandler (
"e:\\hs\\MdePkg\\Library\\DxeServicesTableLib\\DxeServicesTableLib.c",
65,
"gDS != ((void *) 0)"
);
}
//
// Step 4: Optionally locate MM PCIe base protocol.
// This may already be cached from a previous lookup.
//
if (mMmPciUsra == NULL) {
Status = gBootServices->LocateProtocol (
&mMmPciBaseProtocolGuid,
NULL,
&mMmPciUsra
);
if (EFI_ERROR (Status)) {
DebugPrint (
UBA_DEBUG_ERROR,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Status
);
AssertHandler (
"e:\\hs\\CpRcPkg\\Library\\DxeMmPciBaseLib\\DxeMmPciBaseLib.c",
52,
"!EFI_ERROR (Status)"
);
}
if (mMmPciUsra == NULL) {
AssertHandler (
"e:\\hs\\CpRcPkg\\Library\\DxeMmPciBaseLib\\DxeMmPciBaseLib.c",
53,
"mPciUsra != ((void *) 0)"
);
}
}
//
// Step 5: Initialize HOB list.
//
HobLibInit ();
//
// Step 6: Perform SMBIOS data update.
//
Status = SmbiosDataUpdateEntry (ImageHandle);
return Status;
}