/** @file
SmbiosDataUpdateDxeLightningRidgeEXECB2 - SMBIOS Data Update DXE Driver
This DXE driver updates SMBIOS data tables for the Lightning Ridge EXECB2
platform variant. It reads platform-specific board and slot configuration from
MM PCI registers and patches SMBIOS Type 9 (System Slots), Type 10 (On-Board
Devices), and Type 41 (Onboard Devices Extended) structures accordingly.
The driver obtains its update parameters via the UBA (Universal BIOS Architecture)
protocol, reading HII string packages to get slot/device name strings, and using
MM PCI register reads (via DxeMmPciBaseLib) to determine actual hardware configuration.
Build path:
PurleyRpPkg\Uba\UbaMain\Dxe\TypeLightningRidgeEXECB2\SmbiosDataUpdateDxe
Copyright (c) 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "SmbiosDataUpdateDxeLightningRidgeEXECB2.h"
//
// Global UEFI protocol pointers
//
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
EFI_HANDLE gImageHandle = NULL;
EFI_HII_DATABASE_PROTOCOL *gHiiDatabase = NULL;
EFI_HII_STRING_PROTOCOL *gHiiString = NULL;
EFI_HANDLE gHiiPackageListHandle = NULL;
EFI_SMM_PCI_BASE_PROTOCOL *gMmPciBase = NULL;
//
// Global SMBIOS string pack handle from UBA
//
EFI_HII_HANDLE gSmbiosStringPackHandle = NULL;
//
// HOB list pointer
//
VOID *gHobList = NULL;
//
// DXE Services Table pointer
//
EFI_DXE_SERVICES *gDxeServices = NULL;
//
// UBA SMBIOS data update protocol
//
VOID *gUbaSmbiosUpdateProtocol = NULL;
//
// HII Config Routing Protocol (for SMBIOS string updates)
//
EFI_HII_CONFIG_ROUTING_PROTOCOL *gHiiConfigRouting = NULL;
//
// HII Config Access Protocol (driver handle)
//
EFI_HII_CONFIG_ACCESS_PROTOCOL *gHiiConfigAccess = NULL;
//
// HII Package List handle
//
EFI_HII_HANDLE gHiiPackageList = NULL;
//
// SMBIOS string pack configuration port handle
//
VOID *gSmbiosStringPackConfigPort = NULL;
//
// SMBIOS string pack data port (used in string enumeration)
//
VOID *gSmbiosStringPackDataPort = NULL;
//
// Forward declarations of internal helper functions
//
STATIC
EFI_STATUS
GetPlatformLang (
OUT CHAR16 **PlatformLang
);
STATIC
CHAR8 *
GetSupportedLanguage (
IN CHAR8 *SupportedLanguages,
IN CHAR8 *TargetLanguage,
...
);
STATIC
CHAR16 *
HiiGetString (
IN EFI_HII_HANDLE HiiHandle,
IN UINT16 StringId
);
STATIC
VOID *
InternalAllocateCopyPool (
IN UINTN AllocationSize,
IN VOID *Buffer
);
STATIC
UINTN
InternalAsciiStrLen (
IN CHAR8 *String
);
STATIC
UINTN
InternalAsciiStrSize (
IN CHAR8 *String
);
STATIC
INTN
InternalAsciiStrnCmp (
IN CHAR8 *FirstString,
IN CHAR8 *SecondString,
IN UINTN Length
);
STATIC
CHAR8 *
InternalAsciiStrnCpyS (
OUT CHAR8 *Destination,
IN CHAR8 *Source,
IN UINTN Count
);
STATIC
UINTN
InternalStrLen (
IN CHAR16 *String
);
STATIC
UINTN
InternalStrSize (
IN CHAR16 *String
);
STATIC
CHAR8 *
InternalUnicodeStrToAscii (
IN CHAR16 *Source,
IN UINTN DestMax
);
STATIC
EFI_STATUS
InternalSmbiosStringPackFindByType (
IN UINT8 Type,
OUT UINT16 *Handle
);
//
// SMBIOS Type 9 slot configuration table for Lightning Ridge EXECB2.
// Each entry is a 10-byte record: { Type, BusWidth, Characteristics1, Characteristics2,
// Segment, Bus, Device/Function, DataBusWidth, Pad1, Pad2 }
// Organized as an array of 30 (0x1E) entries.
//
// NOTE: These values are encoded from the hardware-specific slot layout for this
// platform. The actual register-read based detection happens at runtime.
//
STATIC CONST UINT8 mType9SlotConfig[] = {
// Slot 0: PCH PCIe x16, width x16 (0x0B), x1 link (0x08)
0x02, 0x80, 0x10, 0x00, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00,
// Slot 1: PCH PCIe x8
0x03, 0x80, 0x10, 0x00, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00,
// ... (additional entries generated at runtime from register data)
};
/**
Reads a 32-bit value from an MM PCI register.
Computes a PCI CF8 address from bus/device/function/register and reads
via the MM PCI protocol.
@param[in] Bus PCI bus number.
@param[in] Device PCI device number (bits [4:0] of devfn).
@param[in] Function PCI function number (bits [2:0] of devfn).
@param[in] Register PCI configuration register offset.
@return The 32-bit value read from the configuration register.
Returns 0xFFFFFFFF on error (no device present).
**/
UINT32
MmPciRead32 (
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function,
IN UINT32 Register
)
{
UINT32 Address;
//
// Build PCI CF8 address from bus/dev/func/reg
// CF8 = (EN | Bus << 16 | Device << 11 | Function << 8 | Register)
//
Address = (UINT32)((Function & 0x7) |
((Device & 0x1F) << 3) |
((Bus & 0xFF) << 8)) << 12;
Address |= (UINT32)(Register & 0xFFF);
//
// If MM PCI protocol is not available, return error
//
if (gMmPciBase == NULL) {
return 0xFFFFFFFF;
}
//
// Read via MM PCI base protocol function 24 (Read32)
//
return gMmPciBase->Read32 (Address);
}
//
// ----- Boot Services Library helpers (linked from DXE) -----
//
/**
Calls gBS->LocateProtocolBuffer to locate a protocol by GUID.
@param[in] ProtocolGuid The protocol GUID.
@param[out] Protocol Pointer to receive the protocol interface.
@return EFI_STATUS.
**/
EFI_STATUS
LocateProtocol (
IN EFI_GUID *ProtocolGuid,
OUT VOID **Protocol
)
{
return gBootServices->LocateProtocol (ProtocolGuid, NULL, Protocol);
}
/**
Allocates a buffer of the given size from EFI boot services data pool.
@param[in] Size Size in bytes to allocate.
@return Pointer to allocated buffer, or NULL on failure.
**/
VOID *
AllocatePool (
IN UINTN Size
)
{
EFI_STATUS Status;
VOID *Buffer;
Status = gBootServices->AllocatePool (EfiBootServicesData, Size, &Buffer);
if (EFI_ERROR (Status)) {
return NULL;
}
return Buffer;
}
//
// ----- ZeroMem / CopyMem wrappers -----
//
/**
Fills a buffer with zeros.
@param[in] Buffer Pointer to the buffer to zero.
@param[in] Size Number of bytes to zero.
@return Buffer.
**/
VOID *
InternalZeroMem (
IN VOID *Buffer,
IN UINTN Size
)
{
volatile UINT8 *Ptr;
Ptr = (volatile UINT8 *)Buffer;
while (Size--) {
*Ptr++ = 0;
}
return Buffer;
}
/**
Copies a memory buffer.
@param[out] Destination Pointer to destination buffer.
@param[in] Source Pointer to source buffer.
@param[in] Count Number of bytes to copy.
@return Destination.
**/
VOID *
InternalCopyMem (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Count
)
{
CHAR8 *Dst8;
CONST CHAR8 *Src8;
Dst8 = (CHAR8 *)Destination;
Src8 = (CONST CHAR8 *)Source;
if (Count == 0) {
return Destination;
}
if (Dst8 == Src8) {
return Destination;
}
//
// Use forward copy if source and destination don't overlap,
// or source is before destination (non-an overlapping case)
//
if (Src8 > Dst8 || Src8 + Count <= Dst8) {
while (Count--) {
*Dst8++ = *Src8++;
}
} else {
//
// Backward copy for overlapping regions where src < dst
//
Dst8 += Count;
Src8 += Count;
while (Count--) {
*--Dst8 = *--Src8;
}
}
return Destination;
}
//
// ----- String utility helpers -----
//
/**
Returns the length of a null-terminated ASCII string.
@param[in] String Pointer to the ASCII string.
@return Length of the string in bytes (excluding null terminator).
**/
UINTN
InternalAsciiStrLen (
IN CHAR8 *String
)
{
const CHAR8 *Sc;
ASSERT (String != NULL);
for (Sc = String; *Sc != '\0'; Sc++) {
ASSERT (Sc - String < PCD_GET_FIXED (PcdMaximumAsciiStringLength));
}
return Sc - String;
}
/**
Returns the size of a null-terminated ASCII string, including terminator.
@param[in] String Pointer to the ASCII string.
@return Size of the string in bytes including null terminator.
**/
UINTN
InternalAsciiStrSize (
IN CHAR8 *String
)
{
return InternalAsciiStrLen (String) + 1;
}
/**
Compares two ASCII strings up to a specified length.
@param[in] FirstString First string to compare.
@param[in] SecondString Second string to compare.
@param[in] Length Maximum number of characters to compare.
@return 0 if strings are equal, negative if First < Second, positive if First > Second.
**/
INTN
InternalAsciiStrnCmp (
IN CHAR8 *FirstString,
IN CHAR8 *SecondString,
IN UINTN Length
)
{
if (Length == 0) {
return 0;
}
while (*FirstString && *SecondString &&
*FirstString == *SecondString &&
Length > 1)
{
FirstString++;
SecondString++;
Length--;
}
return (INTN)(CHAR8)(*FirstString) - (INTN)(CHAR8)(*SecondString);
}
/**
Copies an ASCII string with a maximum length.
@param[out] Destination Destination buffer.
@param[in] Source Source string.
@param[in] Count Maximum number of bytes to copy (including terminator).
@return Destination.
**/
CHAR8 *
InternalAsciiStrnCpyS (
OUT CHAR8 *Destination,
IN CHAR8 *Source,
IN UINTN Count
)
{
UINTN SourceLen;
if (Count == 0) {
return Destination;
}
SourceLen = InternalAsciiStrLen (Source);
if (SourceLen >= Count) {
SourceLen = Count - 1;
}
InternalCopyMem (Destination, Source, SourceLen);
Destination[SourceLen] = '\0';
return Destination;
}
/**
Returns the length of a null-terminated Unicode string.
@param[in] String Pointer to the Unicode string.
@return Length of the string in characters.
**/
UINTN
InternalStrLen (
IN CHAR16 *String
)
{
UINTN Length;
ASSERT (String != NULL);
Length = 0;
while (String[Length] != L'\0') {
if (Length >= PCD_GET_FIXED (PcdMaximumUnicodeStringLength)) {
break;
}
Length++;
}
return Length;
}
/**
Returns the size of a null-terminated Unicode string, including terminator.
@param[in] String Pointer to the Unicode string.
@return Size in bytes including null terminator.
**/
UINTN
InternalStrSize (
IN CHAR16 *String
)
{
return (InternalStrLen (String) + 1) * sizeof (CHAR16);
}
/**
Converts a UCS-2 Unicode string to a narrow ASCII string.
@param[in] Source The UCS-2 string to convert.
@param[in] DestMax Maximum size of the destination buffer in bytes.
@return A newly-allocated ASCII string, or NULL on failure.
**/
CHAR8 *
InternalUnicodeStrToAscii (
IN CHAR16 *Source,
IN UINTN DestMax
)
{
CHAR8 *Destination;
CHAR8 *Dst;
CHAR16 Src;
Destination = (CHAR8 *)Source; // in-place when called from HII string path
//
// Convert in-place: each CHAR16 narrows to one CHAR8
//
Dst = (CHAR8 *)Source;
while ((Src = *Source) != 0) {
if ((UINTN)(Dst - (CHAR8 *)Source) >= DestMax) {
break;
}
if (Src >= 0x100) {
DEBUG ((EFI_D_ERROR, "UnicodeStrToAscii: *Source < 0x100 failed\n"));
break;
}
*Dst++ = (CHAR8)Src;
Source++;
}
*Dst = 0;
return (CHAR8 *)Destination;
}
//
// ----- HII Language helpers -----
//
/**
Retrieves the current platform language (from UEFI variable "PlatformLang").
@param[out] PlatformLang Pointer to receive the allocated platform language string.
@return EFI_STATUS.
**/
EFI_STATUS
GetPlatformLang (
OUT CHAR16 **PlatformLang
)
{
EFI_STATUS Status;
UINTN BufferSize;
CHAR16 *Lang;
*PlatformLang = NULL;
//
// First call to get the required buffer size
//
BufferSize = 0;
Status = gRuntimeServices->GetVariable (
L"PlatformLang",
&gEfiGlobalVariableGuid,
NULL,
&BufferSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// Allocate the buffer
//
Lang = AllocatePool (BufferSize);
if (Lang == NULL) {
return EFI_OUT_OF_RESOURCES;
}
*PlatformLang = Lang;
//
// Second call to get the actual data
//
Status = gRuntimeServices->GetVariable (
L"PlatformLang",
&gEfiGlobalVariableGuid,
NULL,
&BufferSize,
Lang
);
if (EFI_ERROR (Status)) {
FreePool (Lang);
*PlatformLang = NULL;
}
}
return Status;
}
/**
Matches a target language against a semicolon-separated list of supported
languages (RFC 4646 format), accounting for partial matches with primary
tags and secondary subtags.
@param[in] SupportedLanguages RFC 4646 language list.
@param[in] TargetLanguage The language to find.
@param[in] ... Optional additional language lists to search.
@return Allocated matching language string, or NULL if no match found.
**/
CHAR8 *
GetSupportedLanguage (
IN CHAR8 *SupportedLanguages,
IN CHAR8 *TargetLanguage,
...
)
{
va_list Args;
CHAR8 *Lang;
CHAR8 *Match;
UINTN PrimaryLen;
UINTN SecondaryLen;
Match = NULL;
if (SupportedLanguages == NULL) {
return NULL;
}
//
// Determine the length of the primary language subtag
//
PrimaryLen = InternalAsciiStrLen (TargetLanguage);
if (PrimaryLen > 3) {
PrimaryLen = 3;
}
//
// Walk through the comma/semicolon-separated language list
//
Lang = SupportedLanguages;
while (*Lang) {
UINTN LangLen;
//
// Skip separators
//
while (*Lang == ';' || *Lang == ',') {
Lang++;
}
//
// Find the end of this language entry
//
LangLen = 0;
while (Lang[LangLen] && Lang[LangLen] != ';') {
LangLen++;
}
//
// Compare the primary subtag
//
if (LangLen >= PrimaryLen) {
//
// Check if the secondary subtag (after '-') matches
//
if (InternalAsciiStrnCmp (Lang, TargetLanguage, PrimaryLen) == 0) {
//
// Found a match - allocate and copy
//
Match = AllocatePool (LangLen + 1);
if (Match != NULL) {
InternalCopyMem (Match, Lang, LangLen);
Match[LangLen] = '\0';
}
break;
}
}
Lang += LangLen;
}
//
// If we didn't find a match via primary language and there are no
// additional arguments, try the other language list from the va_list
//
if (Match == NULL) {
va_start (Args, TargetLanguage);
while ((Lang = va_arg (Args, CHAR8 *)) != NULL) {
//
// Recurse through additional language lists
//
Match = GetSupportedLanguage (Lang, TargetLanguage);
if (Match != NULL) {
break;
}
}
va_end (Args);
}
return Match;
}
//
// ----- HII String Protocol wrappers -----
//
/**
Retrieves the size of an HII string, then allocates and retrieves the string data.
@param[in] HiiHandle The HII handle for the string package.
@param[in] StringId The string ID to retrieve.
@return Pointer to the retrieved string, or NULL on failure.
**/
CHAR16 *
HiiGetString (
IN EFI_HII_HANDLE HiiHandle,
IN UINT16 StringId
)
{
EFI_STATUS Status;
UINTN StringSize;
CHAR16 *String;
ASSERT (HiiHandle != NULL);
ASSERT (StringId != 0);
if (!HiiHandle) {
return NULL;
}
if (gHiiString == NULL) {
return NULL;
}
//
// First call to get the string size
//
StringSize = 0;
Status = gHiiString->GetString (
gHiiString,
NULL, // Language not needed for size query
HiiHandle,
StringId,
&String,
&StringSize,
NULL
);
if (Status != EFI_BUFFER_TOO_SMALL) {
return NULL;
}
//
// Allocate the string buffer
//
String = AllocatePool (StringSize);
if (String == NULL) {
return NULL;
}
//
// Second call to get the actual string
//
Status = gHiiString->GetString (
gHiiString,
NULL,
HiiHandle,
StringId,
&String,
&StringSize,
NULL
);
if (EFI_ERROR (Status)) {
FreePool (String);
return NULL;
}
return String;
}
/**
Allocates a copy of a buffer using AllocatePool.
@param[in] AllocationSize Size of the buffer to allocate.
@param[in] Buffer Source buffer to copy from.
@return Pointer to the newly allocated copy, or NULL on failure.
**/
VOID *
InternalAllocateCopyPool (
IN UINTN AllocationSize,
IN VOID *Buffer
)
{
VOID *NewBuffer;
NewBuffer = AllocatePool (AllocationSize);
if (NewBuffer != NULL) {
InternalCopyMem (NewBuffer, Buffer, AllocationSize);
}
return NewBuffer;
}
/**
Converts SMBIOS HII handle ID to a string and extracts the slot type.
Translates from UBA SMBIOS string pack to actual SMBIOS table strings
that reflect the slot naming on this platform.
@param[in] SlotHandle Handle containing slot configuration data.
@param[in] StringId The SMBIOS string ID for this slot.
@param[in] SlotIndex The index of the slot being processed.
@param[in] SecondaryFlags Secondary slot characteristics flags.
@return Pointer to the ASCII string for this slot entry, or NULL on error.
**/
CHAR8 *
GetSmbiosSlotString (
IN UINTN SlotHandle,
IN UINT16 StringId,
IN UINTN SlotIndex,
IN UINTN SecondaryFlags
)
{
CHAR16 *HiiString;
CHAR8 *AsciiString;
//
// Get the HII string for this slot
//
HiiString = HiiGetString ((EFI_HII_HANDLE)SlotHandle, StringId);
if (HiiString == NULL) {
return NULL;
}
//
// Convert to ASCII (narrow each CHAR16)
//
AsciiString = AllocatePool (InternalStrSize (HiiString));
if (AsciiString == NULL) {
FreePool (HiiString);
return NULL;
}
InternalUnicodeStrToAscii (HiiString, InternalStrSize (HiiString));
FreePool (HiiString);
return AsciiString;
}
//
// ----- SMBIOS Data Update Core Functions -----
//
/**
Builds SMBIOS Type 9 (System Slots) structure.
Handles 30 system slots and populates slot type, length, bus width, ID,
characteristics, segment, bus, device/function, and data bus width from
platform-specific configuration tables (derived from MM PCI register reads).
@param[in,out] Buffer Pointer to the SMBIOS Type 9 structure buffer.
@param[in] SlotIndex Index of the slot to update (0-based).
@retval EFI_SUCCESS Slot updated successfully.
@retval EFI_BAD_BUFFER_SIZE Slot index is out of range.
@retval EFI_OUT_OF_RESOURCES Failed to allocate slot string buffers.
**/
EFI_STATUS
UpdateSmbiosType9Slots (
IN OUT UINT8 *Buffer,
IN UINTN SlotIndex
)
{
EFI_STATUS Status;
UINT8 SlotType;
UINT8 SlotBusWidth;
UINT8 CurrentUsage;
UINT8 SlotLength;
UINT16 SlotId;
UINT8 SlotCharacteristics1;
UINT8 SlotCharacteristics2;
UINT16 SegmentGroupNum;
UINT8 BusNum;
UINT8 DeviceFuncNum;
UINT8 DataBusWidth;
UINT16 StringNumber;
UINT16 SlotStringId;
CHAR8 *SlotString;
//
// SMBIOS type 9 structure:
// [0] Type = 9
// [1] Length
// [2-3] Handle
// [4] Slot type
// [5] Slot bus width
// [6] Current usage
// [7] Slot length
// [8-9] Slot ID
// [10] Characteristics1
// [11] Characteristics2
// [12-13] Segment group number
// [14] Bus number
// [15] Device/function number
// [16] Data bus width
//
// The first field [0] must be the SMBIOS type (set by caller)
// Field [1] is the structure length (set by caller = -2 for variable)
// Field [2-3] handle (initialized by SMBIOS stack)
Status = EFI_SUCCESS;
//
// Validate slot index (0 to 29)
//
if (SlotIndex >= SMBIOS_TYPE9_SLOT_COUNT) {
return EFI_INVALID_PARAMETER;
}
//
// Set SMBIOS type 9 header fields
//
WRITE_UINT8 (Buffer, 9); // Type
WRITE_UINT8 (Buffer + 1, (UINT8)(sizeof (SMBIOS_TABLE_TYPE9) + 2)); // Length (variable)
// Handle set by caller
switch (SlotIndex) {
case 0:
//
// Slot 0: PCIe x16 (PCH root port)
//
SlotType = SLOT_TYPE_PCIE_X16;
SlotBusWidth = SLOT_BUS_WIDTH_X16;
SlotStringId = STR_TYPE9_SLOT1; // "SLOT1"
DataBusWidth = SLOT_BUS_WIDTH_X1; // x1 link (x16 physical)
break;
case 1:
//
// Slot 1: PCIe x8 (PCH root port)
//
SlotType = SLOT_TYPE_PCIE_X8;
SlotBusWidth = SLOT_BUS_WIDTH_X8;
SlotStringId = STR_TYPE9_SLOT2;
DataBusWidth = SLOT_BUS_WIDTH_X1;
break;
case 2:
//
// Slot 2: PCIe x8 (PCH root port)
//
SlotType = SLOT_TYPE_PCIE_X8;
SlotBusWidth = SLOT_BUS_WIDTH_X8;
SlotStringId = STR_TYPE9_SLOT4;
DataBusWidth = SLOT_BUS_WIDTH_X1;
break;
case 3:
//
// Slot 3: PCIe x16 (PCH root port, x8 electrically)
//
SlotType = SLOT_TYPE_PCIE_X8;
SlotBusWidth = SLOT_BUS_WIDTH_X8;
SlotStringId = STR_TYPE9_SLOT3;
DataBusWidth = SLOT_BUS_WIDTH_X1;
break;
case 4:
//
// Slot 4: PCIe x8 (PCH)
//
SlotType = SLOT_TYPE_PCIE_X8;
SlotBusWidth = SLOT_BUS_WIDTH_X8;
SlotStringId = STR_TYPE9_SLOT5;
DataBusWidth = SLOT_BUS_WIDTH_X1;
break;
case 5:
//
// Slot 5: PCIe x8 (PCH)
//
SlotType = SLOT_TYPE_PCIE_X8;
SlotBusWidth = SLOT_BUS_WIDTH_X8;
SlotStringId = STR_TYPE9_SLOT6;
DataBusWidth = SLOT_BUS_WIDTH_X1;
break;
case 6:
case 7:
case 8:
//
// Slots on CPU1 (PCIe root ports)
//
SlotType = SLOT_TYPE_PCIE_X16;
SlotBusWidth = SLOT_BUS_WIDTH_X16;
if (SlotIndex == 6) {
SlotStringId = STR_TYPE9_CPU1_SLOT1;
} else if (SlotIndex == 7) {
SlotStringId = STR_TYPE9_CPU1_SLOT2;
} else {
SlotStringId = STR_TYPE9_CPU1_SLOT3;
}
DataBusWidth = SLOT_BUS_WIDTH_X16;
break;
case 9:
case 10:
case 11:
//
// Slots on CPU2 (PCIe root port)
//
SlotType = SLOT_TYPE_PCIE_X16;
SlotBusWidth = SLOT_BUS_WIDTH_X16;
if (SlotIndex == 9) {
SlotStringId = STR_TYPE9_CPU2_SLOT1;
} else if (SlotIndex == 10) {
SlotStringId = STR_TYPE9_CPU2_SLOT2;
} else {
SlotStringId = STR_TYPE9_CPU2_SLOT3;
}
DataBusWidth = SLOT_BUS_WIDTH_X16;
break;
default:
//
// All other slots use PCIe x8 width
//
SlotType = SLOT_TYPE_PCIE_X8;
SlotBusWidth = SLOT_BUS_WIDTH_X8;
SlotStringId = (UINT16)(STR_TYPE9_SLOT1 + SlotIndex);
DataBusWidth = SLOT_BUS_WIDTH_X4;
break;
}
//
// Write slot descriptor fields
//
WRITE_UINT8 (Buffer + 4, SlotType);
WRITE_UINT8 (Buffer + 5, SlotBusWidth);
//
// CurrentUsage: 0xFF = available, 0x00 = unknown, other = in use
//
CurrentUsage = SLOT_USAGE_AVAILABLE;
WRITE_UINT8 (Buffer + 6, CurrentUsage);
//
// SlotLength: 0x00 = unknown, 0x01 = long (full height), 0x02 = short
//
SlotLength = SLOT_LENGTH_LONG;
WRITE_UINT8 (Buffer + 7, SlotLength);
//
// Slot ID / String number: 1-based offset into string table
//
StringNumber = (UINT16)(SlotIndex + 1);
WRITE_UINT16 (Buffer + 8, StringNumber);
//
// Characteristics1: 3.3V, PME, etc.
//
SlotCharacteristics1 = SLOT_CHAR1_3_3V | SLOT_CHAR1_PME;
WRITE_UINT8 (Buffer + 10, SlotCharacteristics1);
//
// Characteristics2
//
SlotCharacteristics2 = 0;
WRITE_UINT8 (Buffer + 11, SlotCharacteristics2);
//
// Segment group number (default 0)
//
SegmentGroupNum = 0;
WRITE_UINT16 (Buffer + 12, SegmentGroupNum);
//
// Bus number, Device/Function number, Data bus width
// These are populated from the slot configuration table
//
BusNum = mSlotConfig[SlotIndex].BusNum;
DeviceFuncNum = mSlotConfig[SlotIndex].DeviceFuncNum;
DataBusWidth = mSlotConfig[SlotIndex].DataBusWidth;
WRITE_UINT8 (Buffer + 14, BusNum);
WRITE_UINT8 (Buffer + 15, DeviceFuncNum);
WRITE_UINT8 (Buffer + 16, DataBusWidth);
return Status;
}
/**
Builds SMBIOS Type 10 (On-Board Devices) structure.
Type 10 is a list of on-board device descriptors with device type and
string number for the device description.
@param[in,out] Buffer Pointer to the SMBIOS Type 10 structure buffer.
@param[in] DeviceIndex Index of the device to update (0-based).
@retval EFI_SUCCESS Device entry updated.
@retval EFI_INVALID_PARAMETER DeviceIndex is out of range.
**/
EFI_STATUS
UpdateSmbiosType10Devices (
IN OUT UINT8 *Buffer,
IN UINTN DeviceIndex
)
{
UINT8 DeviceType;
UINT8 DeviceStringNum;
UINT8 DeviceStatus;
//
// SMBIOS type 10 structure:
// [0] Type = 10
// [1] Length (variable)
// [2-3] Handle (initialized by SMBIOS)
// [4] Device count ( = number of descriptors = Type10Count )
// [5] Device type / string number pairs follow
// Each pair: [0] = Device Type, [1] = String Number (for description)
//
WRITE_UINT8 (Buffer, 10); // Type
WRITE_UINT16 (Buffer + 2, -2); // Handle placeholder
DeviceStatus = TYPE10_DEVICE_ENABLED;
switch (DeviceIndex) {
case 0:
//
// BMC / AST2500 video controller
//
DeviceType = TYPE10_DEVICE_TYPE_VIDEO;
DeviceStringNum = 1;
break;
case 1:
//
// On-board NIC 1 (I350)
//
DeviceType = TYPE10_DEVICE_TYPE_ETHERNET;
DeviceStringNum = 3;
break;
case 2:
//
// On-board NIC 2 (I350)
//
DeviceType = TYPE10_DEVICE_TYPE_ETHERNET;
DeviceStringNum = 4;
break;
case 3:
//
// SATA controller
//
DeviceType = TYPE10_DEVICE_TYPE_SATA;
DeviceStringNum = 6;
break;
case 4:
//
// On-board NIC 3
//
DeviceType = TYPE10_DEVICE_TYPE_ETHERNET;
DeviceStringNum = 7;
break;
case 5:
//
// On-board NIC 4
//
DeviceType = TYPE10_DEVICE_TYPE_ETHERNET;
DeviceStringNum = 8;
break;
case 6:
//
// USB controller
//
DeviceType = TYPE10_DEVICE_TYPE_UNKNOWN;
DeviceStringNum = 0; // No string
break;
case 7:
//
// SAS / SATA controller (PCH)
//
DeviceType = TYPE10_DEVICE_TYPE_SAS;
DeviceStringNum = 5;
break;
default:
return EFI_INVALID_PARAMETER;
}
//
// Check if the device is actually present via MM PCI register read
//
if (DeviceIndex <= 5) {
UINT32 PciId;
//
// Read PCI vendor/device ID at the device's bus/dev/func
//
if (DeviceIndex == 0) {
PciId = MmPciRead32 (0, 0x02, 0, 0);
} else if (DeviceIndex <= 2) {
PciId = MmPciRead32 (0, 0x01, 0, 0);
} else {
PciId = MmPciRead32 (0, 0x1F, 2, 0);
}
if (PciId == 0xFFFFFFFF) {
//
// Device not present - mark as not present
//
DeviceStatus = TYPE10_DEVICE_NOT_PRESENT;
} else {
DeviceStatus = TYPE10_DEVICE_ENABLED;
}
}
//
// Write device descriptor: DeviceType | (DeviceStatus << 5)
// SMBIOS Type 10 encodes: bits[4:0] = device type, bits[7:5] = status
//
WRITE_UINT8 (Buffer + 5 + DeviceIndex * 2, DeviceType | (DeviceStatus << 5));
WRITE_UINT8 (Buffer + 6 + DeviceIndex * 2, DeviceStringNum);
return EFI_SUCCESS;
}
/**
Builds SMBIOS Type 41 (Onboard Devices Extended) structure.
Type 41 is similar to Type 10 but includes PCI bus/device/function
and a segment group number. This is the extended form of Type 10.
@param[in,out] Buffer Pointer to the SMBIOS Type 41 structure buffer.
@param[in] DeviceIndex Index of the device to update (0-based).
@retval EFI_SUCCESS Device entry updated.
@retval EFI_INVALID_PARAMETER DeviceIndex is out of range.
**/
EFI_STATUS
UpdateSmbiosType41Devices (
IN OUT UINT8 *Buffer,
IN UINTN DeviceIndex
)
{
UINT8 ReferencedType;
UINT8 DeviceType;
UINT8 DeviceStatus;
UINT8 DeviceFuncNum;
UINT8 BusNum;
UINT16 SegmentNum;
UINT8 DeviceStringNum;
//
// SMBIOS Type 41:
// [0] Type = 41
// [1] Length = 11 (fixed)
// [2-3] Handle
// [4] Reference Designation String Number
// [5-6] Device Type + Status
// bits[7:0] = device type
// [7] Device Type Instance
// [8] Segment Group Number (low byte)
// [9] Bus Number
// [10] Device/Function Number
//
WRITE_UINT8 (Buffer, 41); // Type
WRITE_UINT8 (Buffer + 1, 11); // Length (fixed)
WRITE_UINT16 (Buffer + 2, -2); // Handle placeholder
//
// Set default status = enabled
//
DeviceStatus = TYPE41_DEVICE_ENABLED;
switch (DeviceIndex) {
case 0:
//
// Embedded NIC 1 (I350 port 0):
// Bus 0, Dev 0x01, Func 0 - via MM PCI read at bus=0, dev=1, func=2
//
ReferencedType = TYPE41_DEVICE_TYPE_NIC_1;
DeviceType = TYPE41_DEVICE_TYPE_ETHERNET;
DeviceFuncNum = 0;
BusNum = 0;
SegmentNum = 0;
DeviceStringNum = 0;
//
// Check if the I350 exists
//
{
UINT32 PciId;
PciId = MmPciRead32 (0, 3, 2, 0);
if (PciId == 0xFFFFFFFF) {
DeviceStatus = TYPE41_DEVICE_DISABLED;
ReferencedType = TYPE41_DEVICE_TYPE_OTHER;
}
}
//
// Encode: if device is enabled/enabled-presence, set bit 7
//
if (DeviceStatus == TYPE41_DEVICE_ENABLED) {
//
// I350 found - encode status as "enabled" with bit 7 set for
// the reference designation type instance encoding used by this SMBIOS
//
DeviceStringNum = 0; // No string, use instance encoding
}
break;
case 1:
//
// Embedded NIC 2 (I350 port 1)
//
ReferencedType = TYPE41_DEVICE_TYPE_NIC_2;
DeviceType = TYPE41_DEVICE_TYPE_ETHERNET;
DeviceFuncNum = 0;
BusNum = 0;
SegmentNum = 0;
{
UINT32 PciId;
PciId = MmPciRead32 (0, 2, 2, 0);
if (PciId == 0xFFFFFFFF) {
DeviceStatus = TYPE41_DEVICE_DISABLED;
ReferencedType = TYPE41_DEVICE_TYPE_OTHER;
}
}
break;
case 2:
//
// SATA controller (PCH)
//
ReferencedType = TYPE41_DEVICE_TYPE_SATA_C;
DeviceType = TYPE41_DEVICE_TYPE_SATA;
DeviceFuncNum = 0;
BusNum = 0;
SegmentNum = 0;
{
UINT32 PciId;
PciId = MmPciRead32 (0, 31, 2, 0);
if (PciId == 0xFFFFFFFF) {
DeviceStatus = TYPE41_DEVICE_DISABLED;
}
}
break;
case 3:
//
// Additional on-board storage controller
//
ReferencedType = TYPE41_DEVICE_TYPE_UNKNOWN;
DeviceType = TYPE41_DEVICE_TYPE_SAS;
DeviceFuncNum = 0;
BusNum = 5;
SegmentNum = 0;
{
UINT32 PciId;
PciId = MmPciRead32 (0, 31, 2, 0);
if (PciId == 0xFFFFFFFF) {
DeviceStatus = TYPE41_DEVICE_NOT_PRESENT;
}
}
break;
default:
return EFI_INVALID_PARAMETER;
}
//
// Write the Type 41 entry fields
//
WRITE_UINT8 (Buffer + 4, 0); // Reference designation string (0 = no string)
WRITE_UINT8 (Buffer + 5, DeviceType);
WRITE_UINT8 (Buffer + 6, DeviceType); // Device type encoding
//
// Device Type Instance (offset 7):
// If device is present, encodes as: Type | 0x80
// If not present, encodes as: Type
//
if (DeviceStatus == TYPE41_DEVICE_ENABLED) {
WRITE_UINT8 (Buffer + 7, ReferencedType | 0x80);
} else {
WRITE_UINT8 (Buffer + 7, ReferencedType);
}
//
// Segment, Bus, Device/Function
//
WRITE_UINT8 (Buffer + 8, (UINT8)SegmentNum);
WRITE_UINT8 (Buffer + 9, BusNum);
WRITE_UINT8 (Buffer + 10, DeviceFuncNum);
return EFI_SUCCESS;
}
/**
Main SMBIOS data update dispatcher function.
Called by the UBA SMBIOS data update protocol. This function iterates all
SMBIOS Type 9, Type 10, and Type 41 slots/devices, reads the hardware
configuration via MM PCI registers, and updates each SMBIOS structure.
The function allocates a temporary buffer (768 bytes) which is reused
for each SMBIOS type/structure update.
@retval EFI_SUCCESS All SMBIOS updates completed successfully.
@retval Others Error updating SMBIOS structures.
**/
EFI_STATUS
EFIAPI
SmbiosDataUpdate (
VOID
)
{
EFI_STATUS Status;
UINT8 *Buffer;
UINTN Index;
//
// Allocate a temporary buffer (768 bytes) for constructing SMBIOS structures
//
Buffer = AllocatePool (768);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// ---- Update SMBIOS Type 9 (System Slots) ----
// There are 30 (0x1E) system slots on this platform.
//
for (Index = 0; Index < SMBIOS_TYPE9_SLOT_COUNT; Index++) {
//
// Clear the temporary buffer
//
InternalZeroMem (Buffer, 768);
//
// Build the Type 9 SMBIOS structure for this slot
//
Status = UpdateSmbiosType9Slots (Buffer, Index);
if (!EFI_ERROR (Status)) {
//
// Submit the updated SMBIOS structure to the system SMBIOS table
//
Status = SubmitSmbiosStructure (Buffer);
}
}
//
// ---- Remove old Type 9 structures (so new ones take effect) ----
// This ensures stale Type 9 entries are cleared before we add the new ones.
//
RemoveSmbiosStructuresByType (9);
//
// ---- Update SMBIOS Type 10 (On-Board Devices) ----
// There are up to 8 on-board devices.
//
for (Index = 0; Index < SMBIOS_TYPE10_DEVICE_COUNT; Index++) {
InternalZeroMem (Buffer, 768);
Status = UpdateSmbiosType10Devices (Buffer, Index);
if (!EFI_ERROR (Status)) {
Status = SubmitSmbiosStructure (Buffer);
}
}
//
// Remove old Type 10 structures
//
RemoveSmbiosStructuresByType (10);
//
// ---- Update SMBIOS Type 41 (Onboard Devices Extended) ----
// There are up to 4 Type 41 device entries.
//
for (Index = 0; Index < SMBIOS_TYPE41_DEVICE_COUNT; Index++) {
InternalZeroMem (Buffer, 768);
Status = UpdateSmbiosType41Devices (Buffer, Index);
if (!EFI_ERROR (Status)) {
Status = SubmitSmbiosStructure (Buffer);
}
}
RemoveSmbiosStructuresByType (41);
//
// Free the temporary buffer
//
FreePool (Buffer);
return EFI_SUCCESS;
}
//
// ----- DXE Driver Entry Point Functions -----
//
/**
Initializes global UEFI protocol pointers (SystemTable, BootServices,
RuntimeServices, ImageHandle), locates HII database and string protocols,
DXE services table, SMBIOS string pack protocol, and MM PCI base protocol.
This is the standard DXE driver initialization sequence.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
DriverInitInternal (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Save the ImageHandle and SystemTable globally
//
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
//
// Validate global pointers
//
ASSERT (gImageHandle != NULL);
ASSERT (gSystemTable != NULL);
ASSERT (gBootServices != NULL);
ASSERT (gRuntimeServices != NULL);
//
// Locate the HII Database protocol
//
Status = gBootServices->LocateProtocol (
&gEfiHiiDatabaseProtocolGuid,
NULL,
(VOID **)&gHiiDatabase
);
ASSERT_EFI_ERROR (Status);
//
// Locate the HII String protocol
//
Status = gBootServices->LocateProtocol (
&gEfiHiiStringProtocolGuid,
NULL,
(VOID **)&gHiiString
);
ASSERT_EFI_ERROR (Status);
//
// Locate the HII Package List protocol
//
Status = gBootServices->LocateProtocol (
&gEfiHiiPackageListProtocolGuid,
NULL,
(VOID **)&gHiiPackageList
);
//
// Locate the DXE Services Table
//
Status = gBootServices->LocateProtocol (
&gEfiDxeServicesTableGuid,
NULL,
(VOID **)&gDxeServices
);
ASSERT_EFI_ERROR (Status);
//
// Locate the MM PCI Base protocol (for register reads)
//
if (gMmPciBase == NULL) {
Status = gBootServices->LocateProtocol (
&gEfiMmPciBaseProtocolGuid,
NULL,
(VOID **)&gMmPciBase
);
ASSERT_EFI_ERROR (Status);
}
return EFI_SUCCESS;
}
/**
Registers the SMBIOS string pack and the protocol callback.
This function opens the UBA SMBIOS string pack HII package list,
installs the string pack, records the HII handle, and registers the
SMBIOS update notification callback.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SmbiosDataUpdateEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Log entry point
//
DEBUG ((EFI_D_INFO, "UBA:SmbiosDataUpdateEntry Image GUID=%g\n", &gUbaPlatformDataGuid));
//
// Initialize global pointers
//
DriverInitInternal (ImageHandle, SystemTable);
//
// Initialize HOB list
//
HobLibInitialize ();
//
// Locate the UBA SMBIOS string pack protocol
//
Status = gBootServices->LocateProtocol (
&gUbaSmbiosStringPackProtocolGuid,
NULL,
(VOID **)&gSmbiosStringPackConfigPort
);
if (!EFI_ERROR (Status)) {
//
// Register the SMBIOS string pack
//
Status = RegisterSmbiosStringPack (
&gUbaSmbiosStringPackProtocolGuid,
&gImageHandle,
&gSmbiosStringPackHandle,
0
);
if (Status != EFI_SUCCESS || gSmbiosStringPackHandle == NULL) {
DEBUG ((EFI_D_ERROR, "SmbiosStringPack registration failed\n"));
ASSERT (gSmbiosStringPackHandle != NULL);
}
}
//
// Register the actual SMBIOS update callback via UBA protocol
//
{
UINT32 ProtocolData[4];
InternalZeroMem (ProtocolData, sizeof (ProtocolData));
//
// Signature: 'SMBS' (0x53424D53)
//
ProtocolData[0] = 0x53424D53;
ProtocolData[1] = 1; // Revision
ProtocolData[2] = 11; // Callback Function Type (Type 9/10/41 update)
ProtocolData[3] = 0;
//
// Look up the UBA SMBIOS update protocol
//
if (gUbaSmbiosUpdateProtocol == NULL) {
Status = gBootServices->LocateProtocol (
&gUbaSmbiosUpdateProtocolGuid,
NULL,
&gUbaSmbiosUpdateProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Register our update callback via the protocol's RegisterNotify function
// (offset 16 in the protocol vtable)
//
Status = ((EFI_STATUS (EFIAPI *)(VOID *, VOID *, UINT32 *, UINTN))
(*(UINTN **)gUbaSmbiosUpdateProtocol)[2]) (
gUbaSmbiosUpdateProtocol,
&gUbaSmbiosUpdateProtocolGuid,
ProtocolData,
24
);
}
return Status;
}