/** @file
SmbiosDataUpdateDxeNeonCityFPGA -- UBA SMBIOS Data Update implementation.
This file implements the UBA SMBIOS data update driver for the NeonCityFPGA
platform. It constructs and updates SMBIOS types 8 (Port Connector), 9
(System Slots), and 41 (Onboard Devices Extended Information) by reading
platform-specific hardware configuration through PCI config space via the
MmPciBase protocol (gEfiMmPciBaseProtocolGuid / USRA protocol).
Copyright (C) Lenovo. All rights reserved.
**/
#include "SmbiosDataUpdateDxeNeonCityFPGA.h"
// ===========================================================================
// Global variable declarations (from UEFI library headers and this module)
// ===========================================================================
// System table and boot services (set by sub_38C, consumed by all functions)
extern EFI_SYSTEM_TABLE *gST;
extern EFI_BOOT_SERVICES *gBS;
extern EFI_HANDLE gImageHandle;
extern EFI_RUNTIME_SERVICES *gRT;
// Module-specific globals
static EFI_HII_STRING_PROTOCOL *gHiiString = NULL; // 0x3928
static EFI_HII_DATABASE_PROTOCOL *gHiiDatabase = NULL; // 0x3948
static EFI_HII_IMAGE_PROTOCOL *gHiiImage = NULL; // 0x3930
static EFI_HII_FONT_PROTOCOL *gHiiFont = NULL; // 0x3940
static EFI_HII_CONFIG_ROUTING_PROTOCOL *gHiiConfigRouting = NULL; // 0x3938
static VOID *gHobList = NULL; // 0x3920
static EFI_SMBIOS_PROTOCOL *gSmbios = NULL; // 0x3958
static EFI_SMBIOS_PROTOCOL *gSmbios2 = NULL; // 0x3968
static EFI_SMBIOS_PROTOCOL *gSmbios3 = NULL; // 0x3970
static EFI_SMBIOS_PROTOCOL *gSmbios4 = NULL; // 0x3978
static UBA_SMBIOS_UPDATE_PROTOCOL *gUbaSmbiosUpdate = NULL; // 0x3960
static EFI_HII_HANDLE gSmbiosStringPack = NULL; // 0x38F0
static MM_PCI_BASE_PROTOCOL *gMmPciBase = NULL; // 0x3980
static EFI_DXE_SERVICES *gDS = NULL; // 0x3950
// Debug output protocol and HOB list
static VOID *mDebugProtocol = NULL; // 0x3918 (DebugLib protocol)
// ===========================================================================
// Low-level utility functions
// ===========================================================================
/**
ZeroMem wrapper -- zeroes a memory buffer.
@param[in] Buffer Pointer to buffer to zero.
@param[in] Length Number of bytes to zero.
@return Buffer.
*/
static
VOID *
EFIAPI
ZeroMem (
IN VOID *Buffer,
IN UINTN Length
)
{
// sub_280 at 0x280 -- memset to 0 in 8-byte chunks plus trailing bytes
// (inline implementation from BaseMemoryLibRepStr)
CHAR8 *Buf = (CHAR8 *)Buffer;
if (Length == 0) return Buffer;
SetMem (Buf, Length, 0);
return Buffer;
}
/**
CopyMem wrapper -- copies a memory buffer with overlap handling.
@param[out] Destination Destination buffer.
@param[in] Source Source buffer.
@param[in] Length Number of bytes to copy.
@return Destination.
*/
static
VOID *
EFIAPI
CopyMem (
OUT VOID *Destination,
IN const VOID *Source,
IN UINTN Length
)
{
// sub_300 at 0x300 + sub_20AC at 0x20AC
// CopyMem implementation with forward/backward overlap detection
return CopyMem (Destination, Source, Length);
}
// ===========================================================================
// PCI config access via MmPciBase (USRA protocol)
// ===========================================================================
/**
Read a 32-bit value from PCI config space using the MmPciBase protocol.
This uses the USRA (Unified System Register Access) protocol identified by
gEfiMmPciBaseProtocolGuid. The register address is encoded as:
Address = ((Bus & 0xFF) | (Device & 0x1F) << 8 | (Function & 7) << 13) << 12
@param[in] Bus PCI bus number.
@param[in] Device PCI device number (5 bits).
@param[in] Function PCI function number (3 bits).
@param[in] Register Register offset within PCI config space.
@param[out] Value Pointer to receive the 32-bit value.
@retval EFI_SUCCESS Read successful.
@retval EFI_INVALID_PARAMETER Invalid register.
@retval EFI_NOT_FOUND Protocol not located.
*/
EFI_STATUS
MmioPciRead32 (
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function,
IN UINT16 Register,
OUT UINT32 *Value
)
{
// sub_2064 at 0x2064
UINT32 Address;
UINT32 Result;
// Build the USRA address: (Bus|Device<<8|Func<<13) << 12 + Register
Address = ((Function & 7) | ((Device & 0x1F) << 3) | (Bus << 8)) << 12;
Address |= (Register & 0xFFF);
// Call MmPciBase->Read(Address) at offset +24 (0x18)
Result = gMmPciBase->Read (gMmPciBase, Address);
*Value = Result;
return EFI_SUCCESS;
}
// ===========================================================================
// Debug output functions
// ===========================================================================
/**
Get the debug output protocol instance (lazily resolved).
Uses AllocatePool/FreePool as a UEFI environment guard before calling
LocateProtocol. If the protocol cannot be located, returns NULL.
@return Pointer to the debug output protocol, or NULL.
*/
static
VOID *
GetDebugProtocol (
VOID
)
{
// sub_1044 at 0x1044
// Allocates 31 bytes then frees as a guard, then calls LocateProtocol
// for the DebugLib protocol GUID at 0x31C0
if (mDebugProtocol == NULL) {
VOID *Guard;
Guard = AllocatePool (31);
if (Guard == NULL) return NULL;
FreePool (Guard);
// Only proceed if AllocatePool succeeded (environments may not have it)
if ((UINTN)Guard <= 0x10) {
return NULL;
}
gBS->LocateProtocol (&gEfiDebugProtocolGuid, NULL, &mDebugProtocol);
if (mDebugProtocol == NULL) {
mDebugProtocol = NULL;
}
}
return mDebugProtocol;
}
/**
Debug print with platform-specific debug level filtering.
Reads the CMOS debug level register (0x4B) through RTC ports 0x70/0x71.
Falls back to MMIO 0xFDAF0490 if CMOS level is 0. Filters output against
the requested ErrorLevel.
@param[in] ErrorLevel Debug error level mask.
@param[in] Format Print format string.
@param[in] ... Variable arguments.
*/
static
VOID
DebugPrint (
IN UINTN ErrorLevel,
IN const CHAR8 *Format,
...
)
{
// sub_10C4 at 0x10C4
VOID *Protocol;
UINT8 CmosLevel;
VA_LIST Va;
Protocol = GetDebugProtocol ();
if (Protocol == NULL) return;
// Read CMOS register 0x4B via RTC ports 0x70/0x71
CmosLevel = IoRead8 (0x70);
IoWrite8 (0x70, (CmosLevel & 0xCB) | 0x4B); // 0x4B = CMOS debug level register
CmosLevel = IoRead8 (0x71);
if (CmosLevel > 3) {
// Values > 3 are invalid -- use current value unless it's 0
if (CmosLevel == 0) {
// Fallback: MMIO at fixed address 0xFDAF0490
CmosLevel = (MmioRead8 (0xFDAF0490) & 2) | 1;
}
}
if ((CmosLevel - 1) <= 0xFD) {
UINTN FilterLevel;
if (CmosLevel == 1) {
FilterLevel = 0x80000004; // EFI_DWORD(0x80000004) -- error only
} else {
FilterLevel = 0x80000006; // EFI_DWORD(0x80000006) -- warning & error
}
if (FilterLevel & ErrorLevel) {
VA_START (Va, Format);
((DEBUG_OUTPUT_PROTOCOL *)Protocol)->DebugPrint (ErrorLevel, Format, Va);
VA_END (Va);
}
}
}
/**
Debug assertion handler.
@param[in] FileName Source file name.
@param[in] LineNumber Line number where assertion occurred.
@param[in] Description Assertion description string.
*/
static
VOID
DebugAssert (
IN const CHAR8 *FileName,
IN UINTN LineNumber,
IN const CHAR8 *Description
)
{
// sub_114C at 0x114C
VOID *Protocol;
Protocol = GetDebugProtocol ();
if (Protocol != NULL) {
((DEBUG_OUTPUT_PROTOCOL *)Protocol)->DebugAssert (
FileName, LineNumber, Description
);
}
}
// ===========================================================================
// ReadUnaligned helpers
// ===========================================================================
/**
Read a 32-bit unaligned value from memory.
@param[in] Buffer Pointer to read from (may be unaligned).
@return The 32-bit value.
*/
static
UINT32
ReadUnaligned32 (
IN const VOID *Buffer
)
{
// sub_12AC at 0x12AC
ASSERT (Buffer != NULL);
return *(volatile UINT32 *)Buffer;
}
/**
Read a 64-bit unaligned value from memory.
@param[in] Buffer Pointer to read from (may be unaligned).
@return The 64-bit value.
*/
static
UINT64
ReadUnaligned64 (
IN const VOID *Buffer
)
{
// sub_12DC at 0x12DC
ASSERT (Buffer != NULL);
return *(volatile UINT64 *)Buffer;
}
/**
Write a 64-bit unaligned value to memory.
@param[out] Buffer Pointer to write to (may be unaligned).
@param[in] Value Value to write.
@return Value.
*/
static
UINT64
WriteUnaligned64 (
OUT VOID *Buffer,
IN UINT64 Value
)
{
// sub_130C at 0x130C
ASSERT (Buffer != NULL);
*(volatile UINT64 *)Buffer = Value;
return Value;
}
/**
Compare two GUIDs using unaligned 64-bit reads.
@param[in] Guid1 First GUID pointer.
@param[in] Guid2 Second GUID pointer.
@return TRUE if identical, FALSE otherwise.
*/
static
BOOLEAN
IsGuidEqual (
IN const EFI_GUID *Guid1,
IN const EFI_GUID *Guid2
)
{
// sub_11D4 at 0x11D4
return ReadUnaligned64 (Guid1) == ReadUnaligned64 (Guid2) &&
ReadUnaligned64 ((UINT8 *)Guid1 + 8) == ReadUnaligned64 ((UINT8 *)Guid2 + 8);
}
// ===========================================================================
// Memory allocation helpers
// ===========================================================================
/**
Allocate zero-filled pool.
@param[in] Size Number of bytes to allocate.
@return Pointer to allocated and zeroed buffer, or NULL.
*/
static
VOID *
AllocateZeroPool (
IN UINTN Size
)
{
// sub_137C at 0x137C
VOID *Buf = AllocatePool (Size);
if (Buf != NULL) {
ZeroMem (Buf, Size);
}
return Buf;
}
/**
Free pool and assert on failure.
@param[in] Buffer Pointer to free.
@return EFI_STATUS from FreePool call.
*/
static
EFI_STATUS
FreePoolAssert (
IN VOID *Buffer
)
{
// sub_13A4 at 0x13A4
EFI_STATUS Status = gBS->FreePool (Buffer);
ASSERT_EFI_ERROR (Status);
return Status;
}
// ===========================================================================
// UEFI configuration table / HOB list operations
// ===========================================================================
/**
Locate a configuration table by GUID.
Scans gST->ConfigurationTable[] for a matching GUID. Each table entry
is 0x18 (24) bytes: 16-byte GUID + 8-byte pointer.
@param[in] TableGuid GUID of the table to find.
@param[out] Table Receives the pointer to the table data.
@return EFI_SUCCESS Table found.
@return EFI_NOT_FOUND Table not found.
*/
EFI_STATUS
GetConfigurationTable (
IN EFI_GUID *TableGuid,
OUT VOID **Table
)
{
// sub_13E8 at 0x13E8 (EfiGetSystemConfigurationTable)
UINTN Index;
ASSERT (TableGuid != NULL);
ASSERT (Table != NULL);
*Table = NULL;
if (gST->NumberOfTableEntries == 0) {
return EFI_NOT_FOUND;
}
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
if (IsGuidEqual (TableGuid, &gST->ConfigurationTable[Index].VendorGuid)) {
*Table = gST->ConfigurationTable[Index].VendorTable;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
Get the HOB list pointer (lazily resolved).
@return Pointer to the HOB list, or NULL.
*/
static
VOID *
GetHobList (
VOID
)
{
// sub_16E8 at 0x16E8
EFI_STATUS Status;
if (gHobList == NULL) {
Status = GetConfigurationTable (&gEfiHobListGuid, &gHobList);
ASSERT_EFI_ERROR (Status);
ASSERT (gHobList != NULL);
}
return gHobList;
}
// ===========================================================================
// Language / platform string operations
// ===========================================================================
/**
Get the current platform language.
Uses RT->GetVariable to read "PlatformLang" UEFI variable.
@param[out] Language Receives allocated platform language string.
Caller must free with FreePool.
@return EFI_SUCCESS Language retrieved.
@return EFI_NOT_FOUND PlatformLang variable not found.
@return EFI_OUT_OF_RESOURCES Memory allocation failed.
*/
EFI_STATUS
GetPlatformLanguage (
OUT CHAR8 **Language
)
{
// sub_14AC at 0x14AC
UINTN Size;
EFI_STATUS Status;
ASSERT (Language != NULL);
Size = 0;
*Language = NULL;
Status = gRT->GetVariable (
L"PlatformLang",
&gEfiPlatformLangGuid,
NULL,
&Size,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
*Language = AllocatePool (Size);
if (*Language == NULL) {
ASSERT (*Language != NULL);
return EFI_OUT_OF_RESOURCES;
}
Status = gRT->GetVariable (
L"PlatformLang",
&gEfiPlatformLangGuid,
NULL,
&Size,
*Language
);
if (EFI_ERROR (Status)) {
FreePoolAssert (*Language);
*Language = NULL;
}
}
return Status;
}
/**
Find a supported language within a language list and return a copy.
Languages are separated by semicolons in the SupportedLanguages string.
Matches up to 3 characters of each language tag.
@param[in] SupportedLanguages Semicolon-separated language list.
@param[in] DefaultLanguage Default to use if SupportedLanguages is empty
(first character of the format argument).
@param[in] ... Variable arguments; second arg is the target
language to search for.
@return Allocated copy of the matched language string, or NULL.
*/
static
CHAR8 *
FindLanguage (
IN CHAR8 *SupportedLanguages,
IN CHAR8 DefaultLanguage,
...
)
{
// sub_15A4 at 0x15A4
CHAR8 *TargetLanguage;
VA_LIST Args;
UINTN TargetLen;
CHAR8 *LangPtr;
UINTN LangLen;
ASSERT (SupportedLanguages != NULL);
VA_START (Args, DefaultLanguage);
TargetLanguage = VA_ARG (Args, CHAR8 *);
VA_END (Args);
TargetLen = AsciiStrLen (TargetLanguage);
if (TargetLen > 3) TargetLen = 3;
LangPtr = SupportedLanguages;
while (*LangPtr != '\0') {
// Skip over semicolons
while (*LangPtr == ';') LangPtr++;
LangLen = 0;
while (LangPtr[LangLen] != '\0' && LangPtr[LangLen] != ';') {
LangLen++;
}
if (TargetLen <= LangLen) {
if (AsciiStrnCmp (LangPtr, TargetLanguage, TargetLen) == 0) {
CHAR8 *Result = AllocateZeroPool (LangLen + 1);
if (Result != NULL) {
CopyMem (Result, LangPtr, LangLen);
}
return Result;
}
}
LangPtr += LangLen;
}
// Fall back to first language in SupportedLanguages
LangPtr = SupportedLanguages;
while (*LangPtr == ';') LangPtr++;
LangLen = 0;
while (LangPtr[LangLen] != '\0' && LangPtr[LangLen] != ';') {
LangLen++;
}
if (LangLen > 0) {
CHAR8 *Result = AllocateZeroPool (LangLen + 1);
if (Result != NULL) {
CopyMem (Result, LangPtr, LangLen);
}
return Result;
}
return NULL;
}
// ===========================================================================
// HII string and package operations
// ===========================================================================
/**
Get the HII string for a given StringId, localized to the current platform
language.
@param[in] HiiHandle HII handle for the string package.
@param[in] StringId String token ID.
@param[in] Language Pointer to NULL (accepts default language).
@return Allocated string value, or NULL on failure. Caller must free.
*/
static
CHAR16 *
GetHiiString (
IN EFI_HII_HANDLE HiiHandle,
IN EFI_STRING_ID StringId,
IN CHAR8 *Language
)
{
// sub_1A44 at 0x1A44
UINTN Size;
CHAR16 *String;
EFI_STATUS Status;
ASSERT (HiiHandle != NULL);
ASSERT (StringId != 0);
// Skip the "Language" lookup used in the original code
// which calls gHiiString->GetString() at [gHiiString + 0x18] (offset +24)
Size = 0;
String = NULL;
// Query required buffer size
Status = gHiiString->GetString (
gHiiString,
Language, // Language string (or NULL for auto)
HiiHandle,
StringId,
&Size,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
String = AllocateZeroPool (Size);
if (String != NULL) {
Status = gHiiString->GetString (
gHiiString,
Language,
HiiHandle,
StringId,
&Size,
String
);
if (EFI_ERROR (Status)) {
FreePoolAssert (String);
return NULL;
}
}
}
return String;
}
/**
Get the HII string for a given StringId with explicit language handling.
Resolves the platform language via GetPlatformLanguage, then calls
GetHiiString. Falls back to "en-US" hardcoded at 0x2AFA if no platform
language is found.
@param[in] HiiHandle HII handle.
@param[in] StringId String token ID.
@param[in] Language Optional language override (NULL = auto-detect).
@return Allocated string, or NULL.
*/
static
CHAR16 *
GetLocalizedHiiString (
IN EFI_HII_HANDLE HiiHandle,
IN EFI_STRING_ID StringId,
IN CHAR8 *Language OPTIONAL
)
{
// sub_176C at 0x176C (full version with language resolution)
CHAR8 *PlatformLang;
CHAR16 *Result;
EFI_STATUS Status;
ASSERT (HiiHandle != NULL);
ASSERT (StringId != 0);
Result = NULL;
PlatformLang = NULL;
// Get platform language
Status = GetPlatformLanguage (&PlatformLang);
if (!EFI_ERROR (Status) && PlatformLang != NULL) {
// The HII handle might need language-specific string resolution
// FindLanguage would select the best match
Result = GetHiiString (HiiHandle, StringId, "en-US");
} else {
// Fallback to default language
Result = GetHiiString (HiiHandle, StringId, "en-US");
}
if (PlatformLang != NULL) {
FreePoolAssert (PlatformLang);
}
return Result;
}
// ===========================================================================
// SMBIOS string management
// ===========================================================================
/**
Search for the next string in an SMBIOS structure's string area.
The string area in an SMBIOS record starts after the fixed-length structure.
Two consecutive NULL bytes terminate the string area.
@param[in,out] StringArea Pointer to pointer to current position.
@param[out] StringOffset Receives the offset of the next string within
the original structure.
@return EFI_SUCCESS Next string found.
@return EFI_NOT_FOUND No more strings (end of string area).
*/
static
EFI_STATUS
GetNextSmbiosString (
IN OUT CHAR8 **StringArea,
OUT UINTN *StringOffset
)
{
// sub_1DC8 at 0x1DC8
CHAR8 *Base;
UINTN Offset;
UINTN Index;
Base = *StringArea;
Offset = (UINTN)(*StringArea)[1]; // Original code: reads from offset +1
while (TRUE) {
if (Base[Offset] == '\0') {
Offset++;
if (Base[Offset] == '\0') {
// End of string area
*StringOffset = Offset + 2;
return EFI_NOT_FOUND;
}
Offset++;
}
for (Index = 0; Index < 64; Index++) {
if (Base[Offset + Index] == '\0') break;
}
if (Index == 64) break; // String too long, abort
Offset += Index;
*StringOffset = Offset;
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}
/**
Remove and reinsert an SMBIOS record (or add if not present).
Uses gSmbios->RemoveString() then gSmbios->Add() to update a string
in an existing SMBIOS record. This is how string overrides are applied.
@param[in] Buffer Pointer to the SMBIOS record buffer.
*/
static
EFI_STATUS
UpdateSmbiosRecord (
IN VOID *Buffer
)
{
// sub_1E1C at 0x1E1C -- calls gSmbios3->RemoveString(0, -2, Buffer)
// and gSmbios3->Add()
EFI_STATUS Status;
// Lazy init of gSmbios3
if (gSmbios3 == NULL) {
Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID **)&gSmbios3);
if (EFI_ERROR (Status)) return Status;
}
// Remove all strings from the record (StringId=0, then call with -2 marker)
// then add the record
gSmbios3->RemoveString (gSmbios3, 0, 0xFFFE, Buffer);
return EFI_SUCCESS;
}
/**
Enumerate and remove SMBIOS records of a given type, then remove them.
This counts how many records of the given type exist, then removes them
one at a time by calling gSmbios4->Remove(-1) and gSmbios4->Add().
@param[in] Type SMBIOS type to process (8, 9, or 41).
@return EFI_SUCCESS All records of the given type were processed.
*/
static
EFI_STATUS
RemoveSmbiosRecordsByType (
IN UINT8 Type
)
{
// sub_1FB4 at 0x1FB4
EFI_STATUS Status;
UINTN Count;
EFI_SMBIOS_HANDLE SmbiosHandle;
UINTN Index;
// Lazy init gSmbios2
if (gSmbios2 == NULL) {
Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID **)&gSmbios2);
if (EFI_ERROR (Status)) return Status;
}
// Count existing records of this type
Count = 0;
SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
while (gSmbios2->GetNext (gSmbios2, &SmbiosHandle, NULL, NULL, NULL) == EFI_SUCCESS) {
Count++;
}
// Remove each record
for (Index = 0; Index < Count; Index++) {
Status = RemoveSingleSmbiosRecord (Type);
if (EFI_ERROR (Status)) break;
}
if (Count == 0) return EFI_SUCCESS;
return EFI_SUCCESS;
}
/**
Remove a single SMBIOS record by using the Smbios protocol's string remove.
@param[in] Type SMBIOS type to target.
@return EFI_STATUS from the operation.
*/
static
EFI_STATUS
RemoveSingleSmbiosRecord (
IN UINT8 Type
)
{
// sub_1F50 at 0x1F50
EFI_SMBIOS_HANDLE Handle;
EFI_STATUS Status;
// Lazy init gSmbios4
if (gSmbios4 == NULL) {
Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID **)&gSmbios4);
if (EFI_ERROR (Status)) return Status;
}
Status = gSmbios4->GetNext (gSmbios4, &Handle, NULL, NULL, NULL);
if (EFI_ERROR (Status)) return Status;
gSmbios4->RemoveString (gSmbios4, Handle, SMBIOS_HANDLE_PI_RESERVED, NULL);
return EFI_SUCCESS;
}
// ===========================================================================
// String handling utilities
// ===========================================================================
/**
Get the length of a NULL-terminated ASCII string (max 1000000 chars).
@param[in] String The string to measure.
@return Length in bytes, excluding the terminator.
*/
static
UINTN
AsciiStrLen (
IN const CHAR8 *String
)
{
// sub_2148 at 0x2148
UINTN Len;
ASSERT (String != NULL);
Len = 0;
while (String[Len] != '\0') {
if (Len >= PCD_MAX_ASCII_STRING_LENGTH) {
ASSERT (Len < PCD_MAX_ASCII_STRING_LENGTH);
}
Len++;
}
return Len;
}
/**
Compare two ASCII strings up to n characters.
@param[in] FirstString First string.
@param[in] SecondString Second string.
@param[in] Length Maximum number of characters to compare.
@return 0 if equal, negative if First < Second, positive if First > Second.
*/
static
INTN
AsciiStrnCmp (
IN const CHAR8 *FirstString,
IN const CHAR8 *SecondString,
IN UINTN Length
)
{
// sub_21B4 at 0x21B4
ASSERT (AsciiStrSize (FirstString) != -1);
ASSERT (AsciiStrSize (SecondString) != -1);
ASSERT (Length <= PCD_MAX_ASCII_STRING_LENGTH);
while (*FirstString && *SecondString && *FirstString == *SecondString && Length > 1) {
FirstString++;
SecondString++;
Length--;
}
return (INTN)(UINT8)*FirstString - (INTN)(UINT8)*SecondString;
}
/**
Get the size of a NULL-terminated ASCII string including the terminator.
@param[in] String The string.
@return Size in bytes including the NULL terminator.
*/
static
UINTN
AsciiStrSize (
IN const CHAR8 *String
)
{
// Equivalent to AsciiStrLen(String) + 1
return AsciiStrLen (String) + 1;
}
/**
Get the length of a NULL-terminated Unicode string (max DestMax).
@param[in] String The string.
@param[in] DestMax Maximum length.
@return Length in characters, excluding the terminator.
@return DestMax if the string is longer than DestMax-1.
@return 0 if String is NULL or DestMax is 0.
*/
static
UINTN
StrnLenS (
IN const CHAR16 *String,
IN UINTN DestMax
)
{
// sub_2280 at 0x2280
ASSERT (((UINTN)String & 1) == 0); // Must be 2-byte aligned
if (String == NULL || DestMax == 0) return 0;
if (String[0] == L'\0') return 0;
UINTN Len = 0;
while (Len < DestMax - 1) {
if (String[++Len] == L'\0') return Len;
}
return DestMax;
}
/**
Get the length of a NULL-terminated ASCII string (max DestMax).
@param[in] String The string.
@param[in] DestMax Maximum length.
@return Length in bytes, excluding the terminator.
@return DestMax if the string is longer than DestMax-1.
@return 0 if String is NULL or DestMax is 0.
*/
static
UINTN
AsciiStrnLenS (
IN const CHAR8 *String,
IN UINTN DestMax
)
{
// sub_22E8 at 0x22E8
if (String == NULL || DestMax == 0) return 0;
if (String[0] == '\0') return 0;
UINTN Len = 0;
while (Len < DestMax - 1) {
if (String[++Len] == '\0') return Len;
}
return DestMax;
}
/**
Copy a Unicode string to an ASCII buffer with safe length handling.
This is an inline implementation of UnicodeStrnToAsciiStrS that handles
overlap detection (similar to internal SafeString functions).
@param[out] Destination ASCII destination buffer.
@param[in] Source Unicode source string.
@param[in] DestMax Maximum size of destination buffer.
@return EFI_SUCCESS Copy completed.
@return EFI_INVALID_PARAMETER Destination or Source is NULL.
@return EFI_BUFFER_TOO_SMALL DestMax <= SourceLen.
@return EFI_SECURITY_VIOLATION Overlap detected.
*/
static
EFI_STATUS
UnicodeStrnToAsciiStrS (
OUT CHAR8 *Destination,
IN CHAR16 *Source,
IN UINTN DestMax
)
{
// sub_2314 at 0x2314
UINTN SourceLen;
ASSERT (((UINTN)Source & 1) == 0);
ASSERT (Destination != NULL);
ASSERT (Source != NULL);
ASSERT (DestMax <= PCD_MAX_ASCII_STRING_LENGTH);
ASSERT (DestMax != 0);
SourceLen = StrnLenS (Source, DestMax);
if (DestMax <= SourceLen) {
ASSERT (DestMax > SourceLen);
return EFI_BUFFER_TOO_SMALL;
}
// Check overlap
if ((CHAR8 *)Source >= Destination) {
if (Destination >= (CHAR8 *)&Source[SourceLen + 1]) {
// No overlap -- safe forward copy
} else {
// Overlap detected
ASSERT (!InternalSafeStringIsOverlap (
Destination, DestMax, (VOID *)Source, (SourceLen + 1) * sizeof (CHAR16)
));
return EFI_SECURITY_VIOLATION;
}
} else {
if ((CHAR16 *)Destination >= &Source[DestMax / sizeof (CHAR16)]) {
// No overlap
} else {
ASSERT (!InternalSafeStringIsOverlap (
Destination, DestMax, (VOID *)Source, (SourceLen + 1) * sizeof (CHAR16)
));
return EFI_SECURITY_VIOLATION;
}
}
// Perform the copy, narrowing CHAR16 to CHAR8
CHAR8 *Dst = Destination;
CHAR16 *Src = Source;
while (*Src) {
ASSERT (*Src < 0x100); // Must fit in CHAR8
*Dst++ = (CHAR8)*Src++;
}
*Dst = '\0';
return EFI_SUCCESS;
}
// ===========================================================================
// HII string concatenation with SMBIOS record update
// ===========================================================================
/**
Concatenate a new ASCII string into an SMBIOS record's string area,
updating the record via the SMBIOS protocol.
This function takes an SMBIOS record buffer (pointed to by a1), a language
index (a3), and a source Unicode string (a4). It:
1. Converts the Unicode string to ASCII
2. Finds the correct insertion point in the SMBIOS string area based on
the language index
3. Rebuilds the string area with the new string inserted
4. Calls Smbios protocol to string-update the record
@param[in] Buffer SMBIOS record buffer.
@param[in] StringId SMBIOS string ID (1-based index).
@param[in] LanguageIndex Language variant index.
@param[in] StringSrc Source Unicode string from HII.
@return EFI_SUCCESS String updated.
@return EFI_OUT_OF_RESOURCES Memory allocation failed.
*/
static
EFI_STATUS
UpdateSmbiosString (
IN VOID *Buffer,
IN UINTN StringId,
IN UINTN LanguageIndex,
IN CHAR16 *StringSrc
)
{
// sub_1AE8 at 0x1AE8
CHAR8 *AsciiString;
UINTN AsciiLen;
UINTN StringAreaOffset;
CHAR8 *TargetPos;
UINTN TargetLen;
UINTN ScratchSize;
CHAR8 *Scratch;
UINTN ScratchOffset;
CHAR8 *RecordBase;
EFI_STATUS Status;
RecordBase = (CHAR8 *)Buffer;
AsciiLen = StrnLenS (StringSrc, SMBIOS_MAX_STRING_LENGTH) + 1;
// Convert Unicode to ASCII
AsciiString = AllocateZeroPool (AsciiLen);
if (AsciiString == NULL) {
ASSERT (AsciiString != NULL);
return EFI_OUT_OF_RESOURCES;
}
Status = UnicodeStrnToAsciiStrS (AsciiString, StringSrc, AsciiLen);
if (EFI_ERROR (Status)) {
FreePoolAssert (AsciiString);
return Status;
}
// Find the string's insertion position in the SMBIOS string area
StringAreaOffset = 0;
GetNextSmbiosString (&RecordBase, &StringAreaOffset);
// (StringAreaOffset now points past the SMBIOS header + strings)
UINTN SkipCount = 0;
if (LanguageIndex > 1) {
// Skip strings for higher language indices
TargetPos = RecordBase + (UINTN)RecordBase[1];
while (SkipCount < LanguageIndex - 1) {
if (TargetPos[0] != '\0') {
// Count this string as 1 language variant
SkipCount++;
TargetPos++;
} else {
// NULL byte -- skip
TargetPos++;
}
}
} else {
TargetPos = RecordBase + (UINTN)RecordBase[1];
}
TargetLen = AsciiStrLen (TargetPos);
// Build the concatenated string area
if (AsciiLen != TargetLen) {
Scratch = AllocateZeroPool (SMBIOS_STRING_SCRATCH_SIZE);
if (Scratch == NULL) {
FreePoolAssert (AsciiString);
return EFI_OUT_OF_RESOURCES;
}
// Copy header + strings before insertion point
CopyMem (Scratch, RecordBase, SkipCount + (UINTN)RecordBase[1]);
// Copy new string
CopyMem (Scratch + SkipCount + (UINTN)RecordBase[1], AsciiString, AsciiLen);
// Copy remaining string area
CopyMem (
Scratch + SkipCount + (UINTN)RecordBase[1] + AsciiLen,
RecordBase + SkipCount + (UINTN)RecordBase[1] + TargetLen,
StringAreaOffset - (UINTN)RecordBase[1] - TargetLen - SkipCount
);
// Calculate final size
GetNextSmbiosString (&RecordBase, &ScratchOffset);
CopyMem (RecordBase, Scratch, ScratchOffset);
FreePoolAssert (Scratch);
}
FreePoolAssert (AsciiString);
return EFI_SUCCESS;
}
// ===========================================================================
// SMBIOS Type 8 update (Port Connector Information)
// ===========================================================================
// Data table for SMBIOS type 8 port connector entries.
// Each entry is 10 bytes (5 words), defining the string token IDs for each
// port slot on this platform. There are 0x15 (21) entries -- one per slot.
//
// Entry structure (10 bytes = 5 x UINT16):
// Word 0: String token for PortInternalConnectorDesignator
// Word 1: String token for PortExternalConnectorDesignator (repeated for second string)
// Byte at +4: port type byte for internal variant
// Byte at +5: port type byte for external variant
// Byte at +6: port type byte
// Byte at +7: port type byte
// Byte at +8: port type byte
// Byte at +9: port type byte (continued)
// (Actually 2 UINT16s followed by packed single bytes.)
/**
Build and update SMBIOS type 8 (Port Connector Information) records.
Constructs 0x15 (21) type 8 SMBIOS records, one for each physical port
connector on the NeonCityFPGA platform. Each record's string fields are
populated from HII string tokens embedded in the module's HII package list.
@param[in,out] Buffer 768-byte temp buffer for record construction.
@param[in] Index Port index (0..0x14, validated to < 0x15).
@return EFI_SUCCESS Record built and submitted.
@return EFI_UNSUPPORTED Invalid index.
@return EFI_OUT_OF_RESOURCES Memory allocation failed.
*/
static
EFI_STATUS
UpdateSmbiosType08 (
IN OUT VOID *Buffer,
IN UINTN Index
)
{
// sub_77C at 0x77C
//
// Table of string token ID pairs for each of the 21 port connector entries.
// Each entry occupies 10 bytes:
// [0..1]: UINT16 stringId for PortInternalConnectorDesignator
// [2..3]: UINT16 stringId for PortExternalConnectorDesignator
// [4]: UINT8 InternalReferenceDesignator type
// [5]: UINT8 ExternalReferenceDesignator type
// [6]: UINT8 PortType
// [7]: UINT8 PortType (extended)
// [8]: UINT8 PortType / misc
// [9]: UINT8 misc
//
static const UINT8 mPortConnectorTable[0x15 * 10] = {
// strId1 strId2 typ1 typ2 ptyp ext misc misc
0x02, 0x00, 0x00, 0x80, 0x08, 0x03, 0x00, 0x00, 0x08, 0x04, // Index 0
0x10, 0x00, 0x00, 0x80, 0x08, 0x03, 0x00, 0x00, 0x08, 0x04, // Index 1
0x10, 0x00, 0x00, 0x80, 0x10, 0x04, 0x00, 0x00, 0x10, 0x04, // Index 2
0x10, 0x00, 0x00, 0x80, 0x10, 0x04, 0x00, 0x00, 0x10, 0x04, // Index 3
0x00, 0x01, 0x00, 0x12, 0x10, 0x05, 0x00, 0x00, 0x10, 0x05, // Index 4
0x00, 0x01, 0x00, 0x12, 0x10, 0x05, 0x00, 0x00, 0x10, 0x05, // Index 5
0x00, 0x01, 0x00, 0x12, 0x1C, 0x07, 0x00, 0x07, 0x1C, 0x07, // Index 6
0x00, 0x01, 0x00, 0x12, 0x1C, 0x07, 0x00, 0x07, 0x1C, 0x07, // Index 7
0x00, 0x01, 0x00, 0x0B, 0x1C, 0x08, 0x00, 0x0B, 0x1C, 0x08, // Index 8
0x00, 0x01, 0x00, 0x0B, 0x1C, 0x08, 0x00, 0x0B, 0x1C, 0x08, // Index 9
0x00, 0x01, 0x00, 0x0B, 0x1F, 0x09, 0x00, 0x0B, 0x1F, 0x09, // Index 10
0x00, 0x01, 0x00, 0x0B, 0x1F, 0x09, 0x00, 0x0B, 0x1F, 0x09, // Index 11
0x00, 0x01, 0x00, 0x0B, 0x1F, 0x0A, 0x00, 0x0B, 0x1F, 0x0A, // Index 12
0x00, 0x01, 0x00, 0xFF, 0x1F, 0x0B, 0x00, 0xFF, 0x1F, 0x0B, // Index 13
0x00, 0x01, 0x00, 0xFF, 0x1F, 0x0C, 0x00, 0xFF, 0x1F, 0x0C, // Index 14
0x00, 0x01, 0x00, 0x22, 0x20, 0x0D, 0x00, 0x22, 0x20, 0x0D, // Index 15
0x00, 0x01, 0x00, 0x22, 0x20, 0x0E, 0x00, 0x22, 0x20, 0x0E, // Index 16
0x00, 0x01, 0x00, 0x22, 0x20, 0x0F, 0x00, 0x22, 0x20, 0x0F, // Index 17
0x00, 0x01, 0x00, 0x22, 0x20, 0x10, 0x00, 0x22, 0x20, 0x10, // Index 18
0x00, 0x01, 0x00, 0x22, 0x20, 0x11, 0x00, 0x22, 0x20, 0x11, // Index 19
0x00, 0x01, 0x00, 0x22, 0x20, 0x12, 0x00, 0x22, 0x20, 0x12 // Index 20
};
SMBIOS_TABLE_TYPE8 *Type8Rec;
CHAR8 *StringArea;
EFI_STATUS Status;
if (Index >= 0x15) {
return EFI_UNSUPPORTED;
}
// Initialize SMBIOS type 8 record header
Type8Rec = (SMBIOS_TABLE_TYPE8 *)Buffer;
Type8Rec->Hdr.Type = SMBIOS_TYPE_PORT_CONNECTOR; // 8
Type8Rec->Hdr.Length = sizeof (SMBIOS_TABLE_TYPE8); // 9
Type8Rec->Hdr.Handle = SMBIOS_HANDLE_PI_RESERVED; // 0xFFFE
// Set port connector fields from table
const UINT8 *Entry = &mPortConnectorTable[Index * 10];
Type8Rec->InternalReferenceDesignator = Entry[4]; // +0x04
Type8Rec->ExternalReferenceDesignator = Entry[5]; // +0x05
Type8Rec->PortType = Entry[6]; // +0x06
Type8Rec->PortTypeExtension = Entry[7]; // +0x07
// Get HII strings for this port
UINT16 StrId1 = *(UINT16 *)&Entry[0];
UINT16 StrId2 = *(UINT16 *)&Entry[2];
if (StrId1 != 0) {
CHAR16 *HiiStr = GetHiiString (gSmbiosStringPack, StrId1, NULL);
if (HiiStr != NULL) {
UpdateSmbiosString (Buffer, 0, Entry[4], HiiStr);
FreePoolAssert (HiiStr);
} else {
return EFI_OUT_OF_RESOURCES; // sub_77C returns 0x8000000000000009
}
// Update the second string if present
if (Entry[5] != 0) {
HiiStr = GetHiiString (gSmbiosStringPack, StrId2, NULL);
if (HiiStr != NULL) {
UpdateSmbiosString (Buffer, 0, Entry[5], HiiStr);
FreePoolAssert (HiiStr);
}
}
}
// Second pair of string IDs
StrId1 = *(UINT16 *)&Entry[0]; // repeated as per original table layout
StrId2 = *(UINT16 *)&Entry[2];
if (StrId1 != 0) {
CHAR16 *HiiStr = GetHiiString (gSmbiosStringPack, StrId1, NULL);
if (HiiStr != NULL) {
UpdateSmbiosString (Buffer, 0, Entry[8], HiiStr);
FreePoolAssert (HiiStr);
} else {
return EFI_OUT_OF_RESOURCES;
}
}
// Submit or update the SMBIOS record
Status = UpdateSmbiosRecord (Buffer);
return Status;
}
// ===========================================================================
// SMBIOS Type 9 update (System Slot Information)
// ===========================================================================
/**
Build and update SMBIOS type 9 (System Slot Information) records.
Constructs up to 8 SMBIOS type 9 records for expansion slots, reading
PCI configuration space to determine slot type and characteristics.
@param[in,out] Buffer 768-byte temp buffer.
@param[in] Index Slot index (0..7).
@return EFI_SUCCESS Record built.
@return EFI_UNSUPPORTED Invalid index.
@return EFI_NOT_FOUND PCI config read failed.
@return EFI_OUT_OF_RESOURCES Memory allocation failed.
*/
static
EFI_STATUS
UpdateSmbiosType09 (
IN OUT VOID *Buffer,
IN UINTN Index
)
{
// sub_A20 at 0xA20
SMBIOS_TABLE_TYPE9 *Type9Rec;
UINT32 PciValue;
UINT8 Device;
UINT8 Function;
Type9Rec = (SMBIOS_TABLE_TYPE9 *)Buffer;
Type9Rec->Hdr.Type = SMBIOS_TYPE_SYSTEM_SLOTS; // 9
Type9Rec->Hdr.Length = 17; // 0x11
Type9Rec->Hdr.Handle = SMBIOS_HANDLE_PI_RESERVED; // 0xFFFE
// Clear reserved flags
Type9Rec->SlotDataBusWidth = 1;
Type9Rec->CurrentUsage = 4;
Type9Rec->SlotLength = 0;
Type9Rec->SlotCharacteristics1 = 0x04; // Bit 2: 3.3V
Type9Rec->SlotCharacteristics2 = 0x01; // Bit 0: reserved
// Determine PCI device/function from index
switch (Index) {
case 0: // PCIe Slot 1
Type9Rec->SlotType = 0xB6; // 0x0B6 = PCI Express x16
Device = 3;
Function = 0;
PciValue = 0xB601;
Type9Rec->SlotDataBusWidth = 0x0B; // x16
break;
case 1: // PCIe Slot 2
Type9Rec->SlotType = 0xB5; // 0x0B5 = PCI Express x8
Device = 3;
Function = 2;
PciValue = 0xB501;
break;
case 2: // PCIe Slot 3
Type9Rec->SlotType = 0xB6; // PCI Express x16
Device = 3;
Function = 1;
PciValue = 0xB601;
break;
case 3: // PCIe Slot 4
Type9Rec->SlotType = 0xB6; // PCI Express x16
Device = 3;
Function = 0;
Type9Rec->CurrentUsage = 6;
Type9Rec->SlotCharacteristics1 |= 0x80;
break;
case 4: // PCIe Slot 5
Type9Rec->SlotType = 0xB6; // PCI Express x16
Device = 2;
Function = 0;
Type9Rec->SlotCharacteristics1 |= 0x80;
Type9Rec->SlotCharacteristics2 = 0x10;
break;
case 5: // PCIe Slot 6
Type9Rec->SlotType = 0xB5; // PCI Express x8
Device = 3;
Function = 2;
Type9Rec->SlotCharacteristics1 |= 0x80;
Type9Rec->SlotCharacteristics2 = 0x1A;
break;
case 6: // PCIe Slot 7
Type9Rec->SlotType = 0xB5; // PCI Express x8
Device = 2;
Function = 2;
Type9Rec->SlotCharacteristics1 |= 0x80;
PciValue = 0xB501;
break;
case 7: // OCP (mezzanine) slot
Type9Rec->SlotType = 0xA1; // 0x0A1 = OCP NIC 3.0
Device = 3;
Function = 2;
// Read PCI vendor/device via MmPciBase to detect presence
MmioPciRead32 (0, Device, Function, 0, &PciValue);
if (PciValue == 0xFFFFFFFF) {
return EFI_NOT_FOUND; // No device present
}
// Read programming interface / revision
MmioPciRead32 (0, Device, Function, 0x19, &PciValue);
Type9Rec->CurrentUsage = ((PciValue != 0xFFFFFFFF) ? 4 : 1);
break;
default:
return EFI_UNSUPPORTED;
}
// Read segment group / bus number
MmioPciRead32 (0, Device, Function, 0x19, &PciValue);
Type9Rec->SegmentGroupNum = (UINT8)(PciValue >> 24);
Type9Rec->BusNum = (UINT8)(PciValue >> 16);
// Set slot characteristics based on function
MmioPciRead32 (0, Device, Function, 0, &PciValue);
if (PciValue != 0xFFFFFFFF) {
// Device present -- check for hotplug
MmioPciRead32 (0, Device, Function, 0x19, &PciValue);
if ((PciValue != 0xFFFFFFFF) || Index >= 4) {
// Use hotplug or default
}
}
// Get HII string for slot designation
static const UINT16 SlotStringIds[8] = {
24, // Slot 1 designation
26, // Slot 2 designation
8, // Slot 3 designation
16, // Slot 4 designation
18, // Slot 5 designation
25, // Slot 6 designation
26, // Slot 7 designation
27 // OCP slot designation
};
if (Index < 8) {
CHAR16 *HiiStr = GetHiiString (gSmbiosStringPack, SlotStringIds[Index], NULL);
if (HiiStr != NULL) {
UpdateSmbiosString (Buffer, 0, Type9Rec->SlotDataBusWidth, HiiStr);
FreePoolAssert (HiiStr);
}
}
// Submit record
return UpdateSmbiosRecord (Buffer);
}
// ===========================================================================
// SMBIOS Type 41 update (Onboard Devices Extended Information)
// ===========================================================================
/**
Build and update SMBIOS type 41 (Onboard Devices Extended Information) records.
Constructs up to 4 type 41 records for onboard devices (SATA, USB, LAN,
management controllers etc.) based on PCI config reads.
@param[in,out] Buffer 768-byte temp buffer.
@param[in] Index Device index (0..3).
@return EFI_SUCCESS Record built.
@return EFI_UNSUPPORTED Invalid index.
@return EFI_OUT_OF_RESOURCES Memory allocation failed.
*/
static
EFI_STATUS
UpdateSmbiosType41 (
IN OUT VOID *Buffer,
IN UINTN Index
)
{
// sub_D98 at 0xD98
SMBIOS_TABLE_TYPE41 *Type41Rec;
UINT32 PciValue;
UINT8 Device;
UINT8 Function;
Type41Rec = (SMBIOS_TABLE_TYPE41 *)Buffer;
Type41Rec->Hdr.Type = SMBIOS_TYPE_ONBOARD_DEVICE; // 41
Type41Rec->Hdr.Length = 11; // 0x0B
Type41Rec->Hdr.Handle = SMBIOS_HANDLE_PI_RESERVED; // 0xFFFE
Type41Rec->Type = 1; // Other
Type41Rec->Status.Status = 1; // Enabled
switch (Index) {
case 0: // Onboard SATA controller
{
Device = 28;
Function = 3;
MmioPciRead32 (0, Device, Function, 0x19, &PciValue);
UINT8 DevFunc = (UINT8)(PciValue >> 16);
MmioPciRead32 (0, DevFunc, 0, 0, &PciValue);
Type41Rec->Type = (PciValue != 0xFFFFFFFF) ? 0x83 : 3; // SATA or other
Type41Rec->ReferenceDesignation = 28; // Token 28
break;
}
case 1: // Onboard USB controller
{
Device = 2;
Function = 2;
MmioPciRead32 (0, Device, Function, 0, &PciValue);
Type41Rec->Type = (PciValue != 0xFFFFFFFF) ? 0x85 : 5; // USB or other
Type41Rec->ReferenceDesignation = 29; // Token 29
break;
}
case 2: // Onboard network / BMC
{
Device = 1;
Function = 0;
MmioPciRead32 (0, Device, Function, 0, &PciValue);
Type41Rec->Type = (PciValue != 0xFFFFFFFF) ? 0x8A : 10; // LAN or other
Type41Rec->ReferenceDesignation = 30; // Token 30
MmioPciRead32 (0, Device, Function, 0x0A, &PciValue);
// Extended info
break;
}
case 3: // Onboard management controller
{
Device = 23;
Function = 0;
MmioPciRead32 (0, Device, Function, 0, &PciValue);
Type41Rec->Type = (PciValue != 0xFFFFFFFF) ? 0x89 : 9; // BMC or other
Type41Rec->ReferenceDesignation = 31; // Token 31
Type41Rec->Status.Status = 0; // Disabled
break;
}
default:
return EFI_UNSUPPORTED;
}
// Get HII string for this device
static const UINT16 DeviceStringIds[4] = {
28, // SATA
29, // USB
30, // LAN
31 // BMC
};
if (Index < 4) {
CHAR16 *HiiStr = GetHiiString (gSmbiosStringPack, DeviceStringIds[Index], NULL);
if (HiiStr != NULL) {
UpdateSmbiosString (Buffer, 0, 1, HiiStr);
FreePoolAssert (HiiStr);
}
}
return UpdateSmbiosRecord (Buffer);
}
// ===========================================================================
// UBA SmbiosUpdate callback (main entry for SMBIOS record updates)
// ===========================================================================
/**
UBA SmbiosUpdateCallback -- called by the UBA framework to update SMBIOS data.
This function performs the following SMBIOS record updates:
1. Creates 0x15 (21) SMBIOS type 8 records (Port Connector Info)
2. Removes and recreates type 9 records (System Slots, 8 records)
3. Removes and recreates type 41 records (Onboard Devices, 4 records)
@param[in] This Pointer to the UBA_SMBIOS_UPDATE_PROTOCOL interface.
@param[in] Buffer Unused (may be NULL).
@param[in] Size Unused.
@return EFI_SUCCESS All SMBIOS records updated.
*/
static
EFI_STATUS
EFIAPI
SmbiosUpdateCallback (
IN UBA_SMBIOS_UPDATE_PROTOCOL *This,
IN VOID *Buffer,
IN UINTN Size
)
{
// sub_F54 at 0xF54
VOID *RecordBuffer;
UINTN Index;
RecordBuffer = AllocateZeroPool (SMBIOS_RECORD_BUFFER_SIZE);
if (RecordBuffer == NULL) {
ASSERT (RecordBuffer != NULL);
return EFI_OUT_OF_RESOURCES;
}
// Step 1: Update SMBIOS type 8 records (21 port connectors)
for (Index = 0; Index < 0x15; Index++) {
ZeroMem (RecordBuffer, SMBIOS_RECORD_BUFFER_SIZE);
UpdateSmbiosType08 (RecordBuffer, Index);
UpdateSmbiosRecord (RecordBuffer);
}
// Step 2: Remove old type 9 records, then recreate 8 slot records
RemoveSmbiosRecordsByType (SMBIOS_TYPE_SYSTEM_SLOTS);
for (Index = 0; Index < 8; Index++) {
ZeroMem (RecordBuffer, SMBIOS_RECORD_BUFFER_SIZE);
UpdateSmbiosType09 (RecordBuffer, Index);
UpdateSmbiosRecord (RecordBuffer);
}
// Step 3: Remove old type 41 records, then recreate 4 device records
RemoveSmbiosRecordsByType (SMBIOS_TYPE_ONBOARD_DEVICE);
for (Index = 0; Index < 4; Index++) {
ZeroMem (RecordBuffer, SMBIOS_RECORD_BUFFER_SIZE);
UpdateSmbiosType41 (RecordBuffer, Index);
UpdateSmbiosRecord (RecordBuffer);
}
// Free temp buffer
FreePoolAssert (RecordBuffer);
return EFI_SUCCESS;
}
// ===========================================================================
// HII package list management
// ===========================================================================
/**
Build and register the HII package list for this module's SMBIOS strings.
The HII package list contains the localized string tokens referenced by
the SMBIOS type 8, 9, and 41 records. The raw package list data is
embedded in the module at the .rdata section offset corresponding to
the GUID at 0x31F0.
@param[in] HiiPackageList Pointer to the HII package list data.
@param[in] PackageListSize Total size of the package list.
@param[out] HiiHandle Receives the HII handle for the registered
package list.
@return EFI_SUCCESS Package list registered.
@return EFI_INVALID_PARAMETER HiiHandle is NULL.
@return EFI_OUT_OF_RESOURCES Memory allocation failed.
*/
static
EFI_STATUS
RegisterHiiPackageList (
IN VOID *HiiPackageList,
IN UINTN PackageListSize,
OUT EFI_HII_HANDLE *HiiHandle
)
{
// sub_1908 at 0x1908
EFI_HII_PACKAGE_LIST_HEADER *PackageListHdr;
UINTN BufferSize;
UINT8 *Buffer;
UINTN Offset;
EFI_STATUS Status;
ASSERT (HiiPackageList != NULL);
ASSERT (HiiHandle != NULL);
// Build a properly formatted package list from the raw GUID + string data
// The raw package list starts with GUID + size header
// Calculate total buffer size
UINT8 *RawData = (UINT8 *)HiiPackageList;
UINT32 *pSize = (UINT32 *)RawData;
BufferSize = *pSize;
// Allocate buffer for the package list
BufferSize += sizeof (EFI_HII_PACKAGE_LIST_HEADER);
Buffer = AllocateZeroPool (BufferSize);
if (Buffer == NULL) return EFI_OUT_OF_RESOURCES;
// Copy the raw data and package
PackageListHdr = (EFI_HII_PACKAGE_LIST_HEADER *)Buffer;
PackageListHdr->PackageLength = (UINT32)BufferSize;
// Copy GUID (from 0x3890 area) and package data
CopyGuid (&PackageListHdr->PackageListGuid, &gEfiHiiPackageListGuid);
// Copy string packages
// Offset 0x20 in buffer = first string package
// (raw data from 0x31F0 + 4)
CopyMem (Buffer + sizeof (EFI_HII_PACKAGE_LIST_HEADER), RawData + 4, *pSize - 4);
// Register via HII database protocol
Status = gHiiDatabase->NewPackageList (
gHiiDatabase,
PackageListHdr,
gImageHandle,
HiiHandle
);
if (EFI_ERROR (Status)) {
*HiiHandle = NULL;
}
FreePool (Buffer);
return Status;
}
// ===========================================================================
// Driver initialization
// ===========================================================================
/**
Initialize global protocol pointers for the UBA SMBIOS update driver.
Locates and caches the following protocols:
- HII String Protocol (gEfiHiiStringProtocolGuid)
- HII Database Protocol (gEfiHiiDatabaseProtocolGuid)
- HII Image Protocol (gEfiHiiImageProtocolGuid)
- HII Font Protocol (gEfiHiiFontProtocolGuid)
- HII Config Routing Protocol (gEfiHiiConfigRoutingGuid)
- DxeServicesTable (gEfiDxeServicesTableGuid)
- MmPciBase protocol (gEfiMmPciBaseProtocolGuid)
Also registers the module's HII package list and UBA SmbiosUpdate callback.
@param[in] ImageHandle Image handle for this driver.
@param[in] SystemTable Pointer to the UEFI system table.
@return EFI_SUCCESS All protocols located and callback registered.
*/
EFI_STATUS
InitializeDriver (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
// sub_38C at 0x38C
EFI_STATUS Status;
// --- Initialize UEFI library globals ---
gImageHandle = ImageHandle;
ASSERT (gImageHandle != NULL);
gST = SystemTable;
ASSERT (gST != NULL);
gBS = gST->BootServices;
ASSERT (gBS != NULL);
gRT = gST->RuntimeServices;
ASSERT (gRT != NULL);
// --- Locate HOB list ---
GetHobList ();
// --- Locate HII protocol interfaces ---
Status = gBS->LocateProtocol (
&gEfiHiiStringProtocolGuid,
NULL,
(VOID **)&gHiiString
);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (
&gEfiHiiDatabaseProtocolGuid,
NULL,
(VOID **)&gHiiDatabase
);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (
&gEfiHiiConfigRoutingGuid, // Actually gEfiHiiConfigRoutingProtocolGuid
NULL,
(VOID **)&gHiiConfigRouting
);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (
&gEfiHiiImageProtocolGuid,
NULL,
(VOID **)&gHiiImage
);
Status = gBS->LocateProtocol (
&gEfiHiiFontProtocolGuid,
NULL,
(VOID **)&gHiiFont
);
// --- Locate DxeServicesTable ---
Status = GetConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **)&gDS);
ASSERT_EFI_ERROR (Status);
ASSERT (gDS != NULL);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
// --- Locate MmPciBase (USRA) protocol ---
if (gMmPciBase == NULL) {
Status = gBS->LocateProtocol (
&gEfiMmPciBaseProtocolGuid,
NULL,
(VOID **)&gMmPciBase
);
ASSERT_EFI_ERROR (Status);
ASSERT (gMmPciBase != NULL);
}
return Status;
}
// ===========================================================================
// Driver entry point -- register UBA SmbiosUpdate callback
// ===========================================================================
/**
Register this module's SMBIOS update callback with the UBA framework.
After initialization (InitializeDriver), this function:
1. Locates the UBA board-protocol protocol
2. Copies the board protocol GUID into the UBA protocol
3. Registers an HII package list with platform-specific strings
4. Registers the SmbiosUpdateCallback with the UBA protocol
@param[in] ImageHandle Image handle for this driver.
@param[out] ... Unused extra argument (from calling convention).
@return EFI_SUCCESS Callback registered successfully.
*/
EFI_STATUS
RegisterSmbiosUpdate (
IN EFI_HANDLE ImageHandle,
...
)
{
// sub_654 at 0x654
EFI_STATUS Status;
UBA_SMBIOS_UPDATE_PROTOCOL *Protocol;
EFI_HANDLE Handle;
EFI_GUID BoardGuid;
EFI_GUID *BoardGuidPtr;
// Locate the UBA board protocol
Status = gBS->LocateProtocol (
&gUbaSmbiosUpdateProtocolGuid, // Same GUID as RomLayoutDxe uses
NULL,
(VOID **)&Protocol
);
if (!EFI_ERROR (Status)) {
DebugPrint (0x80000000, "UBA:SmbiosDataUpdateEntry Image GUID=%g\n", &BoardGuid);
// Copy board protocol GUID into the protocol interface
BoardGuidPtr = (EFI_GUID *)((UINT8 *)Protocol + 4);
CopyMem (BoardGuidPtr, &gUbaSmbiosUpdateProtocolGuid, sizeof (EFI_GUID));
// Register HII package list
gSmbiosStringPack = RegisterHiiPackageList (
Protocol,
&Handle,
&gEfiHiiPackageListGuid,
0
);
ASSERT (gSmbiosStringPack != NULL);
// Build the SmbiosUpdate callback registration block
// v6 layout:
// +0x00: UINT32 Signature = 0x424C5542 ("UBLB" / "BULB")
// +0x04: UINT32 Version = 1
// +0x08: UINT8 Type = 19 (0x13)
// +0x0C: UINT32 Reserved = 0
struct {
UINT32 Signature;
UINT32 Version;
UINT8 Type;
UINT8 Reserved[3];
} CallbackInfo;
// Get the UBA SmbiosUpdate protocol (event protocol at 0x31E0)
if (gUbaSmbiosUpdate == NULL) {
Status = gBS->LocateProtocol (
&gUbaSmbiosUpdateProtocolGuid,
NULL,
(VOID **)&gUbaSmbiosUpdate
);
if (EFI_ERROR (Status)) return Status;
}
CallbackInfo.Signature = 0x424C5542; // "BULB" (UBA signature)
CallbackInfo.Version = 1;
CallbackInfo.Type = 19; // Update type for SMBIOS data
// Register the callback with the UBA SmbiosUpdate protocol
// gUbaSmbiosUpdate->UpdateSmbios(This, &CallbackInfo, sizeof(CallbackInfo))
return gUbaSmbiosUpdate->UpdateSmbios (
gUbaSmbiosUpdate,
&CallbackInfo,
sizeof (CallbackInfo)
);
}
return Status;
}
// ===========================================================================
// Module entry point
// ===========================================================================
/**
Module entry point called by the DXE dispatcher.
@param[in] ImageHandle Image handle for this driver.
@param[in] SystemTable Pointer to the UEFI system table.
@return EFI_STATUS from the registration.
*/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
// _ModuleEntryPoint at 0x370
InitializeDriver (ImageHandle, SystemTable);
return RegisterSmbiosUpdate (ImageHandle);
}