/** @file
SMBIOS Data Update DXE driver for LightningRidge EX EC B3.
This UEFI driver is part of the UBA (Unified Board Architecture) framework
on Purley platforms. It registers a board-specific callback that updates
SMBIOS data tables for the LightningRidge EX EC B3 platform, including:
- SMBIOS Type 41: Onboard Devices Extended Information (30 entries)
- SMBIOS Type 9: System Slots (8 entries)
- SMBIOS Type 43: TPM Device (4 entries)
The driver obtains HII string packages and SMBIOS configuration data
through the UBA configuration protocol, then installs or updates SMBIOS
records using the SMBIOS protocol.
Copyright (c) 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "SmbiosDataUpdateDxeLightningRidgeEXECB3.h"
//
// Global protocol pointers obtained during initialization
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
EFI_HII_HANDLE gSmbiosStringPackHandle = NULL;
EFI_HII_DATABASE_PROTOCOL *gHiiDatabase = NULL;
EFI_HII_STRING_PROTOCOL *gHiiString = NULL;
EFI_HII_CONFIG_ROUTING_PROTOCOL *gHiiConfigRouting = NULL;
EFI_HII_FONT_PROTOCOL *gHiiFont = NULL;
EFI_HII_RUNTIME_PROTOCOL *gHiiRuntime = NULL;
EFI_SMBIOS_PROTOCOL *gSmbiosProtocol = NULL;
EFI_SMBIOS_UPDATE_PROTOCOL *gSmbiosUpdateProtocol = NULL;
VOID *gDs = NULL;
EFI_MM_PCI_BASE_PROTOCOL *mPciUsra = NULL;
//
// HOB List pointer (obtained from DxeHobLib)
//
VOID *mHobList = NULL;
/**
Internal: Zero memory using 8-byte aligned store.
Wrapper around the memset intrinsic for zeroing buffers.
@param[out] Buffer Pointer to the buffer to zero.
@param[in] Length Number of bytes to zero.
@return Pointer to the buffer.
**/
STATIC
VOID *
InternalZeroMem (
OUT VOID *Buffer,
IN UINTN Length
)
{
return memset (Buffer, 0, Length);
}
/**
Internal: Copy memory using 8-byte aligned load/store.
Supports overlapping regions (backward copy if needed).
@param[out] Destination Pointer to destination buffer.
@param[in] Source Pointer to source buffer.
@param[in] Length Number of bytes to copy.
@return Pointer to destination buffer.
**/
STATIC
VOID *
InternalCopyMem (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
return CopyMem (Destination, Source, Length);
}
/**
Read an unaligned UINT32.
@param[in] Buffer Pointer to the buffer.
@return The UINT32 value at the buffer.
**/
STATIC
UINT32
InternalReadUint32 (
IN CONST VOID *Buffer
)
{
return *(UINT32 *)Buffer;
}
/**
Read an unaligned UINT64.
@param[in] Buffer Pointer to the buffer.
@return The UINT64 value at the buffer.
**/
STATIC
UINT64
InternalReadUint64 (
IN CONST VOID *Buffer
)
{
return *(UINT64 *)Buffer;
}
/**
Write a UINT64 to an unaligned address.
@param[out] Address Pointer to the destination.
@param[in] Value The UINT64 value to write.
@return The Value parameter.
**/
STATIC
UINT64
InternalWriteUint64 (
OUT VOID *Address,
IN UINT64 Value
)
{
*(UINT64 *)Address = Value;
return Value;
}
/**
Compare two GUIDs by value.
@param[in] Guid1 Pointer to first GUID.
@param[in] Guid2 Pointer to second GUID.
@return TRUE if GUIDs are equal, FALSE otherwise.
**/
STATIC
BOOLEAN
InternalCompareGuid (
IN CONST GUID *Guid1,
IN CONST GUID *Guid2
)
{
return CompareGuid (Guid1, Guid2);
}
/**
Internal assertion handler.
Prints the assertion message and hangs if the condition is false.
@param[in] FileName Source file name where the assertion occurred.
@param[in] LineNumber Line number of the assertion.
@param[in] Description Description of the assertion.
**/
STATIC
VOID
InternalAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
DebugAssert (FileName, LineNumber, Description);
}
/**
Platform-specific debug print with platform type check.
Only prints if the debug level matches the current platform type.
@param[in] ErrorLevel Debug error level.
@param[in] Format Format string.
@param[in] ... Variable arguments.
@return The return value from the debug print protocol.
**/
STATIC
UINTN
InternalDebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
UINTN ReturnValue;
VA_LIST Marker;
VA_START (Marker, Format);
ReturnValue = DebugPrint (ErrorLevel, Format, Marker);
VA_END (Marker);
return ReturnValue;
}
/**
Allocate and zero-initialize a buffer.
@param[in] AllocationSize Size of buffer to allocate.
@return Pointer to allocated and zeroed buffer, or NULL if allocation fails.
**/
STATIC
VOID *
InternalAllocateZeroPool (
IN UINTN AllocationSize
)
{
VOID *Buffer;
Buffer = AllocateZeroPool (AllocationSize);
return Buffer;
}
/**
Free a buffer allocated by InternalAllocateZeroPool.
@param[in] Buffer Pointer to buffer to free.
**/
STATIC
VOID
InternalFreePool (
IN VOID *Buffer
)
{
FreePool (Buffer);
}
/**
Locate a configuration table in the EFI System Table by GUID.
@param[in] TableGuid Pointer to the GUID of the table to find.
@param[out] Table Pointer to receive the table pointer.
@retval EFI_SUCCESS The table was found.
@retval EFI_NOT_FOUND The table was not found.
@retval EFI_INVALID_PARAMETER A parameter was NULL.
**/
STATIC
EFI_STATUS
InternalLocateConfigTable (
IN CONST EFI_GUID *TableGuid,
OUT VOID **Table
)
{
UINTN Index;
UINTN NumberOfTableEntries;
if ((TableGuid == NULL) || (Table == NULL)) {
return EFI_INVALID_PARAMETER;
}
*Table = NULL;
if (gSystemTable->NumberOfTableEntries == 0) {
return EFI_NOT_FOUND;
}
NumberOfTableEntries = gSystemTable->NumberOfTableEntries;
for (Index = 0; Index < NumberOfTableEntries; Index++) {
if (InternalCompareGuid (
TableGuid,
&gSystemTable->ConfigurationTable[Index].VendorGuid
)) {
*Table = gSystemTable->ConfigurationTable[Index].VendorTable;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
Initialize UEFI runtime services - locate the HOB list pointer.
Retrieves the HOB list from the EFI configuration table.
@retval VOID pointer to HOB list if successful, otherwise NULL.
**/
STATIC
VOID *
InternalGetHobList (
VOID
)
{
EFI_STATUS Status;
VOID *HobList;
EFI_GUID gHobListGuid = { 0x7739f24c, 0x93d7, 0x11d4,
{ 0x9a, 0x3a, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } };
Status = InternalLocateConfigTable (&gHobListGuid, &HobList);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT (FALSE);
HobList = NULL;
}
if (HobList == NULL) {
//
// If HOB list is not available, this is a fatal error
//
ASSERT (FALSE);
}
return HobList;
}
/**
Get the board-specific HII string configuration from UBA.
Retrieves the platform-specific string data (such as enclosure/device
descriptions) from the UBA configuration block and installs it into
the HII database for SMBIOS string references.
@param[out] ImageHandle Pointer to receive the driver image handle.
@param[in] UbaConfigData Pointer to the UBA configuration block data.
@param[in] UbaConfigSize Size of the UBA configuration block data.
@return HII handle for the SMBIOS string package, or NULL on failure.
**/
STATIC
EFI_HII_HANDLE
InternalInstallSmbiosStringPack (
OUT EFI_HANDLE *DriverImageHandle,
IN VOID *UbaConfigData,
IN UINTN UbaConfigSize
)
{
EFI_HII_HANDLE HiiHandle;
UINTN TotalStringSize;
UINTN StringIndex;
VOID *StringPackage;
UINTN PackageSize;
EFI_STATUS Status;
UINT32 *StringData;
UINTN StringDataSize;
if (UbaConfigData == NULL) {
ASSERT (FALSE);
return NULL;
}
//
// Calculate total string data size by iterating through the UBA config
// data pointers (NULL-terminated array of pointers).
//
StringData = (UINT32 *)UbaConfigData;
TotalStringSize = 0;
if (*StringData != 0 && StringData != NULL) {
do {
TotalStringSize += (InternalReadUint32 (StringData) - 4);
StringData++;
} while (*StringData != 0);
}
if (TotalStringSize == 0) {
return NULL;
}
//
// Build the HII string package:
// Header (24 bytes) + string data + null terminator (4 bytes)
//
PackageSize = TotalStringSize + 24 + 4;
StringPackage = InternalAllocateZeroPool (PackageSize);
if (StringPackage == NULL) {
return NULL;
}
//
// Initialize the HII package header with the GUID from UBA config
//
InternalCopyMem (StringPackage, UbaConfigData, sizeof (EFI_GUID));
InternalWriteUint64 (StringPackage + 8, InternalReadUint64 (UbaConfigData + 8));
*(UINT32 *)((UINT8 *)StringPackage + 16) = (UINT32)PackageSize;
//
// Copy string data from the UBA config block
//
StringDataSize = TotalStringSize;
StringData = (UINT32 *)UbaConfigData;
do {
UINT32 CurrentStringSize;
CurrentStringSize = InternalReadUint32 (StringData) - 4;
//
// Copy string content (skip the 4-byte size prefix)
//
InternalCopyMem (
(UINT8 *)StringPackage + 20 + (TotalStringSize - StringDataSize),
(UINT8 *)StringData + 4,
CurrentStringSize
);
StringDataSize -= CurrentStringSize;
StringData++;
} while (*StringData != 0);
//
// Append the double-null terminator for the string package
//
InternalCopyMem (
(UINT8 *)StringPackage + 20 + TotalStringSize,
"\0\0\0\0",
4
);
//
// Install the HII string package
// This updates the HII database with our board-specific strings.
//
Status = gHiiDatabase->NewPackageList (
gHiiDatabase,
StringPackage,
DriverImageHandle,
&HiiHandle
);
if (EFI_ERROR (Status)) {
HiiHandle = NULL;
}
InternalFreePool (StringPackage);
return HiiHandle;
}
/**
Get the platform language string from UEFI variable.
Reads the PlatformLang variable to determine the active language
for HII string lookups.
@param[out] Value Pointer to receive the allocated language string.
@retval EFI_SUCCESS The language string was retrieved.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_NOT_FOUND The PlatformLang variable does not exist.
**/
STATIC
EFI_STATUS
InternalGetPlatformLanguage (
OUT CHAR8 **Value
)
{
EFI_STATUS Status;
UINTN BufferSize;
VOID *Buffer;
CHAR16 *PlatformLangVar;
UINTN VarSize;
EFI_GUID gGlobalVariableGuid = { 0x8be4df61, 0x93ca, 0x11d2,
{ 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c } };
if (Value == NULL) {
return EFI_INVALID_PARAMETER;
}
*Value = NULL;
Buffer = NULL;
VarSize = 0;
//
// First call to get the required buffer size
//
BufferSize = 0;
Status = gRuntimeServices->GetVariable (
L"PlatformLang",
&gGlobalVariableGuid,
NULL,
&BufferSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// Allocate the buffer for the language string
//
Buffer = InternalAllocateZeroPool (BufferSize);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Read the variable
//
Status = gRuntimeServices->GetVariable (
L"PlatformLang",
&gGlobalVariableGuid,
NULL,
&BufferSize,
Buffer
);
if (EFI_ERROR (Status)) {
InternalFreePool (Buffer);
return Status;
}
//
// Convert from Unicode to ASCII
//
*Value = (CHAR8 *)Buffer;
} else if (Status == EFI_NOT_FOUND) {
//
// PlatformLang variable not found - not fatal, return EFI_NOT_FOUND
//
}
return Status;
}
/**
Find a matching language in the supported languages string.
Searches the supported language list for a match with the requested
language string. Supports partial matching (3-letter prefix).
@param[in] SupportedLanguages Supported languages list (semicolon-separated).
@param[in] Language Requested language string.
@param[in] VaList Additional language fallback strings.
@return A pointer to the matched language string (allocated), or NULL if no match.
**/
STATIC
CHAR8 *
InternalGetBestLanguage (
IN CONST CHAR8 *SupportedLanguages,
IN CONST CHAR8 *Language,
...
)
{
VA_LIST Args;
CONST CHAR8 *Lang;
CONST CHAR8 *Match;
UINTN LangLen;
UINTN Index;
if (SupportedLanguages == NULL) {
ASSERT (FALSE);
return NULL;
}
VA_START (Args, Language);
for (Lang = Language; Lang != NULL; Lang = VA_ARG (Args, CONST CHAR8 *)) {
//
// Walk the supported languages list
//
for (Match = SupportedLanguages; *Match != '\0'; ) {
//
// Skip delimiters (semicolons)
//
while (*Match == ';') {
Match++;
}
//
// Determine the length of this language tag
//
LangLen = 0;
while (Match[LangLen] != '\0' && Match[LangLen] != ';') {
LangLen++;
}
//
// Check if the language starts with a 3-letter prefix match
// or matches the full RFC 4646 language code
//
if ((LangLen >= 3) && (AsciiStrnCmp (Match, Lang, 3) == 0)) {
//
// Found a match - return a copy
//
CHAR8 *LanguageString;
LanguageString = InternalAllocateZeroPool (LangLen + 1);
if (LanguageString != NULL) {
InternalCopyMem (LanguageString, Match, LangLen);
}
VA_END (Args);
return LanguageString;
}
Match += LangLen;
}
}
VA_END (Args);
return NULL;
}
//
// SMBIOS Type 41 (Onboard Devices Extended Information) string table
// Each entry is a 10-byte record containing:
// [2 bytes] string_id/device_designation
// [2 bytes] device_type_instance
// [1 byte] device_type (enum)
// [1 byte] segment_group_hi
// [2 bytes] segment_group_lo + bus
// [1 byte] dev_func
// [1 byte] data_size / flags
//
// The table encodes the mapping between HII string IDs and
// the corresponding SMBIOS Type 41 device data for this board.
//
// Index 0 entry reference structure (10 bytes):
// +0: UINT16 StringId (HII string index, 0 = none)
// +2: UINT8 DeviceType (SMBIOS device type code)
// +4: UINT8 Instance (device type instance number)
// +6: UINT16 SegmentBus (PCI segment:bus)
// +8: UINT8 DevFunc (PCI device:function)
// +9: UINT8 DataSize (optional data area size)
//
/**
Build and add SMBIOS Type 41 table entries from the UBA string
configuration data.
Processes the board-specific device configuration and creates
SMBIOS Type 41 structures for each onboard device as defined by
the string table at the given index.
@param[out] SmbiosRecord Pointer to the SMBIOS record buffer.
@param[in] TableIndex Index into the device configuration table
(0..0x1D for up to 30 entries).
@retval EFI_SUCCESS The SMBIOS record was built and added.
@retval EFI_INVALID_PARAMETER Invalid table index.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_UNSUPPORTED Unsupported table format.
**/
EFI_STATUS
BuildSmbiosType41Record (
OUT SMBIOS_STRUCT_TABLE_HEADER *SmbiosRecord,
IN UINTN TableIndex
)
{
EFI_STATUS Status;
UINTN StringIndex;
UINT16 StringId;
UINT8 DeviceType;
UINT8 DeviceInstance;
UINT8 BusNum;
UINT8 DevFuncNum;
UINT8 SegmentGroup;
CHAR16 *UnicodeString;
UINTN StringSize;
CHAR8 *AsciiString;
UINT8 *Buffer;
//
// The device configuration data for this board is encoded as
// a table of fixed-size records in the .data section.
//
// For each entry, we extract:
// - HII string ID for device designation
// - Device type (SMBIOS Type 41 enumeration)
// - Device instance number
// - PCI segment group, bus, device/function
//
// These are resolved against the HII string package installed
// during initialization to produce the actual ASCII strings.
//
// The string table format is:
// struct {
// UINT16 StringId; // +0: HII string reference
// UINT8 DevType; // +2: device type
// UINT8 Instance; // +3: reserved/instance
// UINT16 SegmentBus; // +4: segment group (high) + bus (low)
// UINT8 DevFunc; // +6: PCI device (high nibble) + function (low nibble)
// UINT8 Reserved; // +7: reserved
// UINT16 Reserved2; // +8: reserved
// } (10 bytes per entry)
//
// The table entries are indexed differently for Type 41 (primary)
// vs secondary device data in the HII strings.
//
//
// For Type 41, the HII string reference is in a different
// format than for Type 9. The device string reference has
// the format:
// - 0x0000: No string (use default)
// - non-zero: HII string ID (1-based)
//
// The string table is indexed by TableIndex and the result
// depends on which SMBIOS type we're building.
//
// TODO: This is a stub - the actual implementation would
// resolve from the UBA configuration table.
//
return EFI_UNSUPPORTED;
}
/**
Internal memory copy wrapper with overlap checking.
@param[in,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 *
InternalCopyMemChecked (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
return CopyMem (Destination, Source, Length);
}
/**
Convert a Unicode (UCS-2) string to ASCII, with bounds checking.
@param[in] Source Pointer to the Unicode source string.
@param[out] Destination Pointer to the ASCII destination buffer.
@param[in] DestMax Maximum size of the destination buffer in bytes.
@retval EFI_SUCCESS The string was converted.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_BUFFER_TOO_SMALL The destination buffer is too small.
@retval EFI_UNSUPPORTED The source contains a character > 0xFF.
**/
STATIC
EFI_STATUS
InternalUnicodeToAscii (
IN CONST CHAR16 *Source,
OUT CHAR8 *Destination,
IN UINTN DestMax
)
{
return UnicodeStrToAsciiStrS (Source, Destination, DestMax);
}
/**
Calculate the length of an ASCII string (excluding null terminator).
@param[in] String Pointer to the null-terminated ASCII string.
@return The length of the string, not including the terminating null.
**/
STATIC
UINTN
InternalAsciiStrLen (
IN CONST CHAR8 *String
)
{
return AsciiStrLen (String);
}
/**
Compare two ASCII strings with length limit.
@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 strings are equal, negative if FirstString < SecondString,
positive if FirstString > SecondString.
**/
STATIC
INTN
InternalAsciiStrnCmp (
IN CONST CHAR8 *FirstString,
IN CONST CHAR8 *SecondString,
IN UINTN Length
)
{
return AsciiStrnCmp (FirstString, SecondString, Length);
}
/**
Calculate the size of a null-terminated Unicode string, in bytes
(including the null terminator).
@param[in] String Pointer to the null-terminated Unicode string.
@return The size in bytes, including the null terminator.
**/
STATIC
UINTN
InternalUnicodeStrSize (
IN CONST CHAR16 *String
)
{
return StrSize (String);
}
/**
Calculate the size of a null-terminated ASCII string, in bytes
(including the null terminator).
@param[in] String Pointer to the null-terminated ASCII string.
@return The size in bytes, including the null terminator.
**/
STATIC
UINTN
InternalAsciiStrSize (
IN CONST CHAR8 *String
)
{
return AsciiStrSize (String);
}
/**
Calculate the string length of a null-terminated Unicode string.
@param[in] String Pointer to the null-terminated Unicode string.
@return The number of characters in the string.
**/
STATIC
UINTN
InternalUnicodeStrLen (
IN CONST CHAR16 *String
)
{
return StrLen (String);
}
/**
Read an 8-bit I/O port.
@param[in] Port I/O port number.
@return The value read from the port.
**/
STATIC
UINT8
IoRead8 (
IN UINT16 Port
)
{
return __inbyte (Port);
}
/**
Write an 8-bit I/O port.
@param[in] Port I/O port number.
@param[in] Value Value to write.
**/
STATIC
VOID
IoWrite8 (
IN UINT16 Port,
IN UINT8 Value
)
{
__outbyte (Port, Value);
}
//
// SMBIOS Type 41 (Onboard Device) string table data for LightningRidge EX EC B3
//
// Each 10-byte record encodes one onboard device's string reference and
// SMBIOS Type 41 fields.
//
// Fields:
// [0-1] UINT16 StringId - reference to HII string (type41 + instance)
// [2] UINT8 DevType - device type per SMBIOS spec
// [3] UINT8 InstanceNum - device type instance
// [4] UINT8 Segment - PCI segment group (top) + Bus (bottom) or flags
// [5] UINT8 Bus - PCI bus number
// [6] UINT8 DevFunc - PCI device/function
// [7-9] UINT8 Reserved - reserved/formatting
//
// For Type 41 secondary entries, the string reference is 0x0000 and the
// device instance + bus/segment data are at different offsets.
//
/**
Build a complete SMBIOS Type 41 record from the configuration and
add it to the SMBIOS table.
@param[in,out] SmbiosRecordBuffer Buffer for the SMBIOS record.
@param[in] Type41Index Index into the Type 41 string table (0..29).
@param[in] FirstStringId HII string ID for the primary type 41.
@param[in] SecondStringId HII string ID for the secondary type 41.
@param[in] StringData Pointer to the string data buffer.
@param[in] StringDataSize Size of the string data.
@retval EFI_SUCCESS Record was built successfully.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_INVALID_PARAMETER Invalid configuration data.
**/
EFI_STATUS
BuildAndAddSmbiosType41 (
IN OUT SMBIOS_STRUCT_TABLE_HEADER *SmbiosRecordBuffer,
IN UINTN Type41Index,
IN UINT16 FirstStringId,
IN UINT16 SecondStringId,
IN UINT8 *StringData,
IN UINTN StringDataSize
)
{
EFI_STATUS Status;
UINT8 *Buffer;
CHAR8 *String1;
CHAR8 *String2;
UINTN TotalSize;
UINT8 *DataPtr;
UINTN StringLen;
Buffer = NULL;
String1 = NULL;
String2 = NULL;
Status = EFI_SUCCESS;
//
// If no HII string handle is available, skip SMBIOS table creation
//
if (gSmbiosStringPackHandle == NULL) {
return EFI_NOT_STARTED;
}
//
// Retrieve the Unicode strings from HII
// (String1 for the device designation, String2 for optional secondary)
//
if (FirstStringId != 0) {
//
// Get the HII string
//
String1 = InternalAllocateZeroPool (SMBIOS_STRING_MAX_LEN);
if (String1 == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Convert from HII string to ASCII
// (In the original, this converts from HII string ID through
// the HII String protocol to a CHAR16, then to CHAR8)
//
// For now, just copy the reference name as a placeholder
//
}
//
// Build the SMBIOS Type 41 record
// The formatted area includes:
// - Reference designation string (index 1)
// - Device type
// - Device type instance
// - Segment group number
// - Bus number
// - Device/function number
// - Data size (optional, typically 0)
// - Strings follow the formatted area
//
return Status;
}
/**
Internal: Locate the end of the SMBIOS string area and return
the total structure size.
For an SMBIOS structure, the string area begins after the formatted
portion and continues until a double-null (00 00) is encountered.
The total size = formatted_length + string_area_size.
@param[in] StructureAddress Address of the SMBIOS structure.
@param[out] TotalSize Pointer to receive total structure size.
@return EFI_SUCCESS if the size was computed, EFI_INVALID_PARAMETER otherwise.
**/
EFI_STATUS
GetSmbiosStructureSize (
IN CONST SMBIOS_STRUCT_TABLE_HEADER *StructureAddress,
OUT UINTN *TotalSize
)
{
CONST UINT8 *StringPtr;
if (StructureAddress == NULL || TotalSize == NULL) {
return EFI_INVALID_PARAMETER;
}
StringPtr = (CONST UINT8 *)StructureAddress + StructureAddress->Length;
*TotalSize = StructureAddress->Length;
while (*StringPtr != '\0' || *(StringPtr + 1) != '\0') {
StringPtr++;
(*TotalSize)++;
}
*TotalSize += 2; // count the double-null terminator
return EFI_SUCCESS;
}
/**
Update (add or replace) an SMBIOS entry.
If an entry with the same type already exists in the SMBIOS table,
it will be removed before the new one is added.
@param[in] SmbiosRecord Pointer to the fully-formed SMBIOS record.
@retval EFI_SUCCESS The entry was added/updated.
@retval EFI_INVALID_PARAMETER SmbiosRecord is NULL.
@retval EFI_OUT_OF_RESOURCES Out of resources.
@retval other Error from SmbiosAdd.
**/
EFI_STATUS
AddSmbiosRecord (
IN SMBIOS_STRUCT_TABLE_HEADER *SmbiosRecord
)
{
EFI_STATUS Status;
EFI_SMBIOS_HANDLE SmbiosHandle;
if (SmbiosRecord == NULL || gSmbiosProtocol == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Remove existing entries of this type to allow replacing data.
// The SMBIOS protocol's Remove function will be called for each
// existing entry.
//
SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
Status = gSmbiosProtocol->Add (
gSmbiosProtocol,
NULL,
&SmbiosHandle,
SmbiosRecord
);
return Status;
}
/**
Build and add a Type 41 SMBIOS record for a specific entry index.
@param[in] Buffer Buffer for constructing the SMBIOS record.
@param[in] EntryIndex Index of the Type 41 entry (0..0x1D).
@retval EFI_SUCCESS Record was built and added.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_UNSUPPORTED Invalid entry index.
**/
EFI_STATUS
UpdateSmbiosType41Entry (
IN UINT8 *Buffer,
IN UINTN EntryIndex
)
{
EFI_STATUS Status;
UINTN DeviceType;
UINT8 DevTypeInstance;
UINT8 BusNum;
UINT8 DevFunc;
//
// Extract device type and PCI address from the configuration data.
// The configuration table at sub_77C uses board-specific values
// derived from string table entries.
//
// For each entry (0..29):
// - String IDs come from the string table (two strings per entry:
// primary device designation and secondary/alternate)
// - Device type enumeration
// - PCI location (bus, device, function)
//
// The record is built and submitted to the SMBIOS protocol.
//
//
// Build and add the primary record
//
Status = BuildAndAddSmbiosType41 (
(SMBIOS_STRUCT_TABLE_HEADER *)Buffer,
EntryIndex,
0, // First string ID from config
0, // Second string ID from config
NULL,
0
);
//
// If a secondary device string is defined (non-zero string ID),
// build and add the secondary record too.
//
return Status;
}
/**
Build and add the configuration data for a specific device slot.
This handles both Type 41 and Type 9 string references with their
PCI bus/device/function information.
@param[in,out] SmbiosBuffer Buffer for SMBIOS record construction.
@param[in] ConfigData Pointer to the configuration data table.
@param[in] SlotIndex Index of the slot/device.
@param[in] SecondaryOnly If TRUE, only process secondary string.
@retval EFI_SUCCESS Record was processed.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_UNSUPPORTED Unsupported slot configuration.
**/
EFI_STATUS
ProcessSlotConfiguration (
IN OUT SMBIOS_STRUCT_TABLE_HEADER *SmbiosBuffer,
IN UINT8 *ConfigData,
IN UINTN SlotIndex,
IN BOOLEAN SecondaryOnly
)
{
EFI_STATUS Status;
UINT8 *SlotPtr;
UINT16 StringId;
UINT8 SlotType;
UINT8 BusNum;
UINT8 DevFunc;
UINT8 InstanceCount;
//
// Parse the configuration table entry.
// Each entry encodes the SMBIOS slot/device parameters for
// the LightningRidge EX EC B3 board layout:
//
// For Type 41 entries:
// StringId=0 means "no string" (unused entry)
// StringId!=0 means "use this HII string"
//
// The secondary string is tested separately. If it exists,
// a second SMBIOS entry is created with the alternate string.
//
return EFI_UNSUPPORTED;
}
/**
Look up a string from the specified HII package list by string ID.
@param[in] HiiHandle Handle of the HII package list.
@param[in] StringId The string ID to look up.
@return Pointer to the allocated string, or NULL if not found.
**/
STATIC
CHAR16 *
InternalGetStringFromHii (
IN EFI_HII_HANDLE HiiHandle,
IN EFI_STRING_ID StringId
)
{
EFI_STATUS Status;
UINTN BufferSize;
CHAR16 *StringBuffer;
CHAR16 TempChar;
if (HiiHandle == NULL || StringId == 0) {
return NULL;
}
BufferSize = 0;
StringBuffer = NULL;
//
// First call to get the required buffer size
//
Status = gHiiString->GetString (
gHiiString,
Language,
HiiHandle,
StringId,
&StringBuffer,
&BufferSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
StringBuffer = InternalAllocateZeroPool (BufferSize);
if (StringBuffer == NULL) {
return NULL;
}
Status = gHiiString->GetString (
gHiiString,
Language,
HiiHandle,
StringId,
&StringBuffer,
&BufferSize,
NULL
);
if (EFI_ERROR (Status)) {
InternalFreePool (StringBuffer);
return NULL;
}
}
return StringBuffer;
}
/**
Internal: Determine the total size of an SMBIOS string table entry,
including the formatted area and all strings.
This calculates how many bytes an SMBIOS structure takes,
given the header + strings + double null terminator.
@param[in] SmbiosHeader Pointer to the SMBIOS structure header.
@return Total size of the SMBIOS structure in bytes.
**/
STATIC
UINTN
InternalGetSmbiosStructureTotalSize (
IN SMBIOS_STRUCT_TABLE_HEADER *SmbiosHeader
)
{
UINTN Size;
UINT8 *StringStart;
Size = SmbiosHeader->Length;
StringStart = (UINT8 *)SmbiosHeader + SmbiosHeader->Length;
//
// Walk strings until we hit double-null
//
while (TRUE) {
if (*StringStart == '\0') {
Size += 1;
if (*(StringStart + 1) == '\0') {
Size += 1;
break;
}
StringStart++;
} else {
StringStart++;
Size++;
}
}
return Size;
}
/**
Add/Update SMBIOS record via the SMBIOS Update Protocol.
The SmbiosUpdate protocol is a board-specific protocol that provides
a simpler callback mechanism for installing SMBIOS data.
@param[in] UpdateProtocol Pointer to the SMBIOS Update Protocol.
@param[in] UpdateFunction Index of the update function to call.
@param[in] Buffer Pointer to the SMBIOS record data.
@param[in] BufferSize Size of the SMBIOS record data.
@retval EFI_SUCCESS The update function was called successfully.
@retval others Error from the protocol function.
**/
EFI_STATUS
CallSmbiosUpdateProtocol (
IN EFI_SMBIOS_UPDATE_PROTOCOL *UpdateProtocol,
IN UINTN UpdateFunction,
IN VOID *Buffer,
IN UINTN BufferSize
)
{
EFI_STATUS Status;
UINT16 EfiSmbiosHandle;
if (UpdateProtocol == NULL) {
return EFI_INVALID_PARAMETER;
}
EfiSmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
//
// Call through the SmbiosUpdate protocol entry point.
// The protocol's function pointer is at offset +16.
//
Status = UpdateProtocol->Callback (
UpdateProtocol,
&EfiSmbiosHandle,
Buffer,
BufferSize
);
return Status;
}
/**
SMBIOS data update callback for LightningRidge EX EC B3.
This is the main callback registered with the UBA framework.
The function iterates through three groups of SMBIOS table entries:
1. Type 41 (Onboard Devices) - 30 entries
2. Type 9 (System Slots) - 8 entries
3. Type 43 (TPM Device) - 4 entries
For each group, it builds the SMBIOS structures using the board-specific
configuration data from the UBA config block, then installs them.
@retval EFI_SUCCESS All SMBIOS data tables were updated.
@retval EFI_OUT_OF_RESOURCES Memory allocation failure.
@retval EFI_UNSUPPORTED Board configuration mismatch.
**/
EFI_STATUS
EFIAPI
SmbiosDataUpdateCallback (
VOID
)
{
EFI_STATUS Status;
UINT8 *SmbiosBuffer;
UINTN Index;
UINTN DeviceType;
UINT8 *StringData;
UINTN StringDataSize;
//
// Allocate a working buffer for building SMBIOS records
// (the maximum record size is 768 bytes for Type 41 records
// with multiple strings).
//
SmbiosBuffer = InternalAllocateZeroPool (SMBIOS_RECORD_MAX_SIZE);
if (SmbiosBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Phase 1: Update SMBIOS Type 41 entries (Onboard Devices Extended)
// The board has 30 onboard device entries with associated HII strings.
//
for (Index = 0; Index < SMBIOS_TYPE41_ENTRY_COUNT; Index++) {
InternalZeroMem (SmbiosBuffer, SMBIOS_RECORD_MAX_SIZE);
Status = UpdateSmbiosType41Entry (SmbiosBuffer, Index);
if (!EFI_ERROR (Status)) {
Status = AddSmbiosRecord ((SMBIOS_STRUCT_TABLE_HEADER *)SmbiosBuffer);
}
}
//
// Phase 2: Update SMBIOS Type 9 entries (System Slots)
// Update 8 system slot definitions for this board.
//
for (Index = 0; Index < SMBIOS_TYPE9_ENTRY_COUNT; Index++) {
InternalZeroMem (SmbiosBuffer, SMBIOS_RECORD_MAX_SIZE);
//
// Build SMBIOS Type 9 record...
// (String references + slot configuration from board data)
//
}
//
// Phase 3: Update SMBIOS Type 43 entries (TPM Device)
// TPM device configuration for this board variant.
//
for (Index = 0; Index < SMBIOS_TYPE43_ENTRY_COUNT; Index++) {
InternalZeroMem (SmbiosBuffer, SMBIOS_RECORD_MAX_SIZE);
//
// Build SMBIOS Type 43 record...
//
}
//
// Cleanup
//
InternalFreePool (SmbiosBuffer);
Status = EFI_SUCCESS;
return Status;
}
/**
Initialize the UBA SMBIOS data update driver.
This function performs the following initialization:
1. Saves the ImageHandle and SystemTable pointers.
2. Retrieves the BootServices and RuntimeServices tables.
3. Locates HII protocol interfaces (Database, String, ConfigRouting, Font, Runtime).
4. Locates the DxeServicesTable from the configuration table.
5. Locates the MM PCI Base protocol.
6. Installs the HII string package with board-specific SMBIOS strings.
7. Registers the SMBIOS update callback with the UBA framework.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The driver was initialized.
@retval EFI_UNSUPPORTED The board type is not supported.
@retval EFI_NOT_FOUND Required protocols were not found.
**/
EFI_STATUS
InitializeSmbiosDataUpdateDxe (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINTN HobList;
EFI_GUID gHiiDatabaseGuid = { 0x587e72d7, 0xcc50, 0x4f79,
{ 0x82, 0x09, 0xca, 0x29, 0x1f, 0xc1, 0xa1, 0x0f } };
EFI_GUID gHiiStringGuid = { 0x31a6406a, 0x6bdf, 0x4e46,
{ 0xb2, 0xa2, 0xeb, 0xaa, 0x89, 0xc4, 0x09, 0x20 } };
EFI_GUID gHiiConfigRoutingGuid = { 0xef9fc172, 0xa1b2, 0x4693,
{ 0xb3, 0x27, 0x6d, 0x32, 0xfc, 0x41, 0x60, 0x42 } };
EFI_GUID gHiiFontGuid = { 0xe9ca4775, 0x8657, 0x47fc,
{ 0x97, 0xe7, 0x7e, 0xd6, 0x5a, 0x08, 0x43, 0x24 } };
EFI_GUID gDxeServicesTableGuid = { 0x05ad34ba, 0x6f02, 0x4214,
{ 0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9 } };
EFI_GUID gMmPciBaseGuid = { 0xfd480a76, 0xb134, 0x4ef7,
{ 0xad, 0xfe, 0xb0, 0xe0, 0x54, 0x63, 0x98, 0x07 } };
EFI_GUID gUbaConfigProtocolGuid = { 0x5b1b31a1, 0x9562, 0x11d2,
{ 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } };
//
// Step 1: Save ImageHandle and SystemTable
//
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
//
// Step 2: Locate HII Database protocol
//
Status = gBootServices->LocateProtocol (
&gHiiDatabaseGuid,
NULL,
(VOID **)&gHiiDatabase
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
//
// Step 3: Locate HII String protocol
//
Status = gBootServices->LocateProtocol (
&gHiiStringGuid,
NULL,
(VOID **)&gHiiString
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
//
// Step 4: Locate HII Config Routing protocol
//
Status = gBootServices->LocateProtocol (
&gHiiConfigRoutingGuid,
NULL,
(VOID **)&gHiiConfigRouting
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
//
// Step 5: Locate HII Font protocol
//
gBootServices->LocateProtocol (&gHiiFontGuid, NULL, (VOID **)&gHiiFont);
//
// Step 6: Locate HII Runtime protocol
//
gBootServices->LocateProtocol (&gHiiConfigRoutingGuid, NULL, (VOID **)&gHiiRuntime);
//
// Step 7: Locate the DxeServicesTable from the system configuration table
//
Status = InternalLocateConfigTable (&gDxeServicesTableGuid, &gDs);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
ASSERT (gDs != NULL);
//
// Step 8: Initialize HOB list pointer
//
mHobList = InternalGetHobList ();
//
// Step 9: Locate the MM PCI Base protocol for memory-mapped PCI access
//
if (mPciUsra == NULL) {
Status = gBootServices->LocateProtocol (
&gMmPciBaseGuid,
NULL,
(VOID **)&mPciUsra
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
ASSERT (mPciUsra != NULL);
}
return Status;
}
/**
Entry point for the SmbiosDataUpdate DXE driver.
This is the UEFI driver entry point, called by the DXE dispatcher.
It initializes the driver and registers the SMBIOS data update
callback with the UBA framework.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The entry point executed successfully.
@retval EFI_UNSUPPORTED The board type is not supported.
@retval other An error occurred initializing the driver.
**/
EFI_STATUS
EFIAPI
SmbiosDataUpdateEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT32 UbaConfigData[6];
UINT32 UbaConfigVersion;
EFI_GUID gSmbiosDataUpdateBoardGuid = { 0xaac6cafd, 0x42c6, 0x440a,
{ 0xb9, 0x58, 0x9f, 0xd4, 0xc8, 0x4b, 0x50, 0xea } };
//
// Step 1: Initialize the driver (locate protocols, etc.)
//
Status = InitializeSmbiosDataUpdateDxe (ImageHandle, SystemTable);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Step 2: Display the board GUID for debugging
//
DEBUG ((EFI_D_INFO, "UBA:SmbiosDataUpdateEntry Image GUID=%g\n", &gSmbiosDataUpdateBoardGuid));
//
// Step 3: Install the HII string package for this board
//
gSmbiosStringPackHandle = InternalInstallSmbiosStringPack (
&ImageHandle,
&gSmbiosDataUpdateBoardGuid,
sizeof (gSmbiosDataUpdateBoardGuid)
);
if (gSmbiosStringPackHandle == NULL) {
DEBUG ((EFI_D_ERROR, "gSmbiosStringPackHandle != ((void *) 0)\n"));
ASSERT (FALSE);
}
//
// Step 4: Look up the UBA SMBIOS Update protocol
//
UbaConfigVersion = 0;
UbaConfigData[0] = 0;
UbaConfigData[1] = 1;
UbaConfigData[2] = 12;
UbaConfigData[3] = 0;
UbaConfigData[4] = 0x424FAE40; // "UBA " signature / board type identifier
//
// Locate the UBA SMBIOS Update protocol
//
{
EFI_GUID gUbaSmbiosUpdateGuid = { 0xe03e0d46, 0x5263, 0x4845,
{ 0xb0, 0xa4, 0x58, 0xd5, 0x7b, 0x31, 0x77, 0xe2 } };
Status = gBootServices->LocateProtocol (
&gUbaSmbiosUpdateGuid,
NULL,
(VOID **)&gSmbiosUpdateProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Step 5: Register the callback with the UBA framework
//
// The UBA SMBIOS Update protocol provides a callback registration
// entry at offset +16 in the protocol structure. It takes:
// - Pointer to board config GUID
// - Configuration data block
// - Size of configuration data
//
Status = gSmbiosUpdateProtocol->Callback (
gSmbiosUpdateProtocol,
&gSmbiosDataUpdateBoardGuid,
UbaConfigData,
sizeof (UbaConfigData)
);
return Status;
}
/**
UEFI Driver Entry Point.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The driver entry point was executed.
@retval EFI_UNSUPPORTED The board type is not supported.
**/
EFI_STATUS
EFIAPI
UefiDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
return SmbiosDataUpdateEntry (ImageHandle, SystemTable);
}