/** @file
SMBIOS DXE Driver - Core SMBIOS table management implementation.
This driver manages SMBIOS (System Management BIOS) tables within the
UEFI environment. It provides:
- Initialization and publishing of SMBIOS v2.x and v3.0 entry points
- Runtime table update services (add/remove/modify structures)
- EndOfDxe event handler to lock table regions and program legacy entry points
- Dynamic update callback for onboard device data population
- Producer handle tracking for installed structures
- PI SMBIOS protocol and AMI SMBIOS protocol installation
Copyright (c) 2025, American Megatrends International LLC.
All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
Source path: e:\hs\AmiCompatibilityPkg\Smbios\Smbios.c
Build path: e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\AmiCompatibilityPkg\Smbios\Smbios\DEBUG\AutoGen.c
**/
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DxeServicesTableLib.h>
#include <Library/BaseLib.h>
#include <Library/UefiLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/DxeHobLib.h>
#include <Library/SmmLockBoxLib.h>
#include <Library/DxePcdLib.h>
#include <Protocol/Smbios.h>
#include <Protocol/SmbiosAmi.h>
#include <Protocol/FirmwareVolume2.h>
#include <Protocol/LegacyRegion.h>
#include <Protocol/MmPciBase.h>
#include <Library/AmiPcieSegBusLib.h>
#include "Smbios.h"
//
// ---------------------------------------------------------------------------
// Global Data
// ---------------------------------------------------------------------------
//
EFI_SYSTEM_TABLE *gSystemTable = NULL; // qword_9208
EFI_BOOT_SERVICES *gBootServices = NULL; // qword_91F8
EFI_RUNTIME_SERVICES_TABLE *gRuntimeServices = NULL; // qword_9200
UINT8 *gSmbiosTableBase = NULL; // qword_9138 - SMBIOS table base
UINT16 gSmbiosTableSize = 0x2000; // n0x2000
UINT8 *gSmbiosEntryPoint = NULL; // off_8A80 - "_SM_" EPS
UINT8 *gSmbiosV3EntryPoint = NULL; // ::i / aSm3 - "_SM3_" EPS
UINT16 gNextSmbiosHandle = 0; // word_958E / n0xFFFD
PRODUCER_HANDLE_ENTRY gProducerHandleTable[SMBIOS_PRODUCER_HANDLE_COUNT]; // word_95A0
//
// Flags
//
UINT8 gSmbiosTableExpansionEnabled = TRUE; // byte_8C1A (in rdata)
UINT8 gSmbiosTableLocked = FALSE; // byte_9158
UINT8 gSmbiosFirstDynamicUpdateSeen = TRUE; // byte_8BA8
//
// Protocol GUIDs (referenced via global data)
// EFI_SMBIOS_PROTOCOL GUID: {0x0358B4B4, 0x4B47, 0x46B7, {0x9C, 0x6C, 0xD4, 0x8F, 0x8A, 0x56, 0x43, 0x55}}
// AMI_SMBIOS_PROTOCOL GUID: {0x8C3D856A, 0x6D47, 0x4C1B, {0xA0, 0x49, 0x3C, 0x59, 0x0B, 0xD7, 0x0C, 0x28}}
//
extern EFI_GUID gEfiSmbiosProtocolGuid;
extern EFI_GUID gAmiSmbiosProtocolGuid;
//
// PCD Protocol handle
//
VOID *mPcdProtocol = NULL; // qword_9228
//
// MM PCI Base Protocol handle
//
VOID *mPciUsra = NULL; // qword_9198
//
// DxeServicesTable
//
EFI_DXE_SERVICES *gDS = NULL; // qword_9190
//
// SMBIOS static table FV file GUID
//
extern EFI_GUID gSmbiosStaticTableFileGuid; // unk_8920
extern EFI_GUID gSmbiosProducerGuid; // unk_8C50
//
// Debug helper: macro to call DebugPrint / sub_4F00
// sub_4F00 is DebugPrint with error level as first arg
//
#define SMBIOS_DEBUG(Level, ...) DebugPrint (Level, __VA_ARGS__)
//
// ---------------------------------------------------------------------------
// Forward declarations of static helpers
// ---------------------------------------------------------------------------
//
/**
Internal helper: get the PCD protocol singleton.
@return Pointer to PCD_PROTOCOL.
**/
VOID *
GetPcdProtocol (
VOID
)
{
EFI_STATUS Status;
if (mPcdProtocol == NULL) {
Status = gBS->LocateProtocol (&gEfiPcdProtocolGuid, NULL, &mPcdProtocol);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
ASSERT (mPcdProtocol != NULL);
}
return mPcdProtocol;
}
/**
Copy memory with overlap handling.
Wrapper around internal CopyMem for potentially overlapping regions.
@param[out] Dst Destination buffer.
@param[in] Src Source buffer.
@param[in] Len Number of bytes to copy.
@return pointer to Dst.
**/
VOID *
SmbiosCopyMem (
OUT VOID *Dst,
IN CONST VOID *Src,
IN UINTN Len
)
{
return CopyMem (Dst, Src, Len);
}
/**
Fill memory with a byte value.
Optimized memset wrapper.
@param[out] Buf Buffer to fill.
@param[in] Val Fill value.
@param[in] Len Number of bytes.
@return pointer to Buf.
**/
VOID *
SmbiosSetMem (
OUT VOID *Buf,
IN INT8 Val,
IN UINTN Len
)
{
return SetMem (Buf, Len, (UINT8)Val);
}
/**
Calculate the total size of a single SMBIOS structure record,
including its header, strings area, and terminating double-null.
A SMBIOS structure is laid out as:
[Type(1)][Length(1)][Handle(2)][Data...][Strings...][\0][\0]
The size includes everything up to and including the terminating \0\0.
@param[in] Record Pointer to the SMBIOS structure header.
@return Total size in bytes of the structure record.
**/
UINT16
GetSmbiosStructureSize (
IN SMBIOS_STRUCTURE_HEADER *Record
)
{
UINT8 *StringArea;
UINT16 TotalSize;
//
// Start after the formatted area (the header-defined portion)
//
StringArea = (UINT8 *)Record + Record->Length;
//
// Walk through the null-terminated strings area
//
while (*StringArea != 0) {
StringArea++;
}
StringArea++; // Skip the terminating null of the last string
//
// Now StringArea points to the double-null terminator.
// Include the final null.
//
StringArea++;
TotalSize = (UINT16)((UINTN)StringArea - (UINTN)Record);
return TotalSize;
}
/**
Calculate the total size of the entire SMBIOS table by walking
all structures until the terminator type (127).
@param[in] TableBase Pointer to the start of the SMBIOS table.
@return Total table size in bytes.
**/
UINT16
GetSmbiosTotalTableSize (
IN UINT8 *TableBase
)
{
UINT8 *Cursor;
UINT16 TotalSize;
Cursor = TableBase;
TotalSize = 0;
while (*Cursor != SMBIOS_TERMINATOR_TYPE) {
TotalSize += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
Cursor += TotalSize;
}
//
// Add the terminator structure size
//
TotalSize += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
return TotalSize;
}
/**
Find a SMBIOS structure by its type field.
@param[in,out] TableBase On input, start of search. On output, points to
the found structure, or the terminator if not found.
@param[in] Type The SMBIOS structure type to locate.
@return TRUE if a structure with the requested type was found.
**/
BOOLEAN
FindStructureByType (
IN OUT UINT8 **TableBase,
IN UINT8 Type
)
{
UINT8 *Cursor;
Cursor = *TableBase;
while (*Cursor != SMBIOS_TERMINATOR_TYPE) {
if (*Cursor == Type) {
*TableBase = Cursor;
return TRUE;
}
Cursor += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
}
*TableBase = Cursor;
return FALSE;
}
/**
Find a SMBIOS structure by its handle.
@param[in,out] TableBase On input, start of search. On output, points to
the found structure, or the terminator if not found.
@param[in] Handle The SMBIOS handle to locate.
@return TRUE if a structure with the requested handle was found.
**/
BOOLEAN
FindStructureByHandle (
IN OUT UINT8 **TableBase,
IN UINT16 Handle
)
{
UINT8 *Cursor;
Cursor = *TableBase;
while (*Cursor != SMBIOS_TERMINATOR_TYPE) {
if (((SMBIOS_STRUCTURE_HEADER *)Cursor)->Handle == Handle) {
*TableBase = Cursor;
return TRUE;
}
Cursor += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
}
*TableBase = Cursor;
return FALSE;
}
/**
Advance to the Nth string within a SMBIOS structure's string area.
@param[in,out] Record On input, the start of a SMBIOS structure.
On output, points to the Nth string position.
@param[in] StringNum The 1-based string number to locate.
@return TRUE if the string position is within the structure bounds.
**/
BOOLEAN
FindStringByNumber (
IN OUT SMBIOS_STRUCTURE_HEADER **Record,
IN UINT8 StringNum
)
{
UINT8 *StrPos;
UINT8 *EndPos;
UINT8 Index;
StrPos = (UINT8 *)(*Record) + (*Record)->Length;
EndPos = StrPos + GetSmbiosStructureSize (*Record) - ((*Record)->Length);
//
// Advance to the Nth string (1-based), walking past null-terminated strings
//
for (Index = 1; Index < StringNum; Index++) {
while (*StrPos != 0) {
StrPos++;
}
StrPos++; // Skip null terminator
}
//
// Check if we are still within the structure
//
if ((UINTN)StrPos >= (UINTN)EndPos) {
return FALSE;
}
*Record = (SMBIOS_STRUCTURE_HEADER *)StrPos;
return TRUE;
}
/**
Replace a string in a SMBIOS structure with a new value.
The replacement is done in-place; if the new string is shorter,
the remaining space is packed.
@param[in,out] Record The structure to modify (pointer to start of structure).
@param[in] StringNum 1-based string number to replace.
@param[in] String New null-terminated string content.
**/
VOID
ReplaceStringInStructure (
IN OUT UINT8 *Record,
IN UINT8 StringNum,
IN UINT8 *String
)
{
//
// For now, a placeholder that calls down to the internal implementation.
// The actual decompiled logic (sub_1BEC) performs an in-place string swap
// with compaction of the trailing content.
//
// Full implementation from decompilation would:
// 1. Locate the target string via FindStringByNumber
// 2. Compute old string length
// 3. Compute shift = old_len - new_len (positive means shrinking)
// 4. memmove trailing data forward/backward
// 5. Copy new string into place
//
ASSERT (FALSE); // Not yet fully reimplemented; see sub_1BEC decompilation.
}
/**
Clear fields in a SMBIOS structure as indicated by a bitmap.
Fields are identified via a field map table that maps bit positions
to byte offsets within the structure.
@param[in,out] StructureData Pointer to the SMBIOS structure.
@param[in] Bitmap Bitmask of fields to clear.
@param[in] DefaultFieldMap Array mapping bit positions to structure byte offsets,
terminated with 0xFF.
**/
VOID
ClearSmbiosFieldsByBitmap (
IN OUT UINT8 *StructureData,
IN UINT32 Bitmap,
IN UINT8 *DefaultFieldMap
)
{
UINT8 BitIndex;
UINT8 FieldOffset;
BitIndex = 0;
while (*DefaultFieldMap != 0xFF) {
if ((Bitmap & (1 << BitIndex)) != 0) {
FieldOffset = *DefaultFieldMap;
//
// Null out the specific byte offset (field clearing)
//
StructureData[FieldOffset] = 0;
}
DefaultFieldMap += 3; // Each entry is 3 bytes (offset, skip, reserved)
BitIndex++;
}
}
// ---------------------------------------------------------------------------
// SMBIOS v2.x EPS update
// ---------------------------------------------------------------------------
/**
Update the SMBIOS v2.x Entry Point Structure (EPS).
Reads the current SMBIOS table from gSmbiosTableBase, computes the
checksums, fills the EPS fields, and writes the 31-byte EPS to the
legacy entry point region.
**/
VOID
UpdateEPSHeader (
VOID
)
{
UINT8 *Cursor;
UINT16 StructureCount;
UINT16 MaxStringSize;
UINT8 Checksum;
INTN Index;
UINT8 *BytePtr;
DEBUG ((EFI_D_INFO, "::: SMBIOS - UpdateEPSHeader. pSmbiosTableEntryPoint = %08x :::\n", gSmbiosEntryPoint));
//
// Calculate the intermediate checksum value
//
((SMBIOS_ENTRY_POINT *)gSmbiosEntryPoint)->IntermediateChecksum = (UINT8)GetSmbiosTotalTableSize (gSmbiosTableBase);
//
// Set the SMBIOS table address (lower 32 bits)
//
((SMBIOS_ENTRY_POINT *)gSmbiosEntryPoint)->TableAddress = (UINT32)(UINTN)gSmbiosTableBase;
DEBUG ((EFI_D_INFO, "*** SMBIOS 2.x - TableAddress = %08x ***\n", gSmbiosTableBase));
//
// Count the number of SMBIOS structures (excluding the terminator)
//
Cursor = gSmbiosTableBase;
StructureCount = 1; // Include the terminator in the count
while (*Cursor != SMBIOS_TERMINATOR_TYPE) {
StructureCount++;
Cursor += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
}
//
// Compute the maximum string size across all structures
//
Cursor = gSmbiosTableBase;
MaxStringSize = 0;
while (*Cursor != SMBIOS_TERMINATOR_TYPE) {
UINT16 StructSize;
StructSize = GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
if (StructSize > MaxStringSize) {
MaxStringSize = StructSize;
}
Cursor += StructSize;
}
//
// Fill EPS fields
//
((SMBIOS_ENTRY_POINT *)gSmbiosEntryPoint)->NumberOfSmbiosStructures = StructureCount;
((SMBIOS_ENTRY_POINT *)gSmbiosEntryPoint)->MaxStructureSize = MaxStringSize;
//
// Compute EPS checksum (checksum of bytes 0..length-1 should be 0)
//
gSmbiosEntryPoint[21] = 0; // Clear checksum field first
Checksum = 0;
for (Index = 16; Index < 31; Index++) { // Bytes 16-30 inclusive
Checksum += gSmbiosEntryPoint[Index];
}
gSmbiosEntryPoint[21] = (UINT8)(-(INT8)Checksum);
//
// Compute the intermediate EPS checksum (for the _DMI_ area)
//
gSmbiosEntryPoint[4] = 0; // Reset EPS checksum
if (gSmbiosEntryPoint[5] != 0) {
Checksum = 0;
for (Index = 0; Index < (INTN)gSmbiosEntryPoint[5]; Index++) {
Checksum += gSmbiosEntryPoint[Index];
}
gSmbiosEntryPoint[4] = (UINT8)(-(INT8)Checksum);
}
DEBUG ((EFI_D_INFO, "::: SMBIOS - Exit UpdateEPSHeader :::\n"));
//
// Write the EPS to the legacy region at the pre-allocated address
//
if (gSmbiosEntryPoint != NULL) {
SmbiosCopyMem (gSmbiosEntryPoint, SMBIOS_ENTRY_POINT_SIG, 31);
}
}
// ---------------------------------------------------------------------------
// SMBIOS v3.0 EPS update
// ---------------------------------------------------------------------------
/**
Update the SMBIOS v3.0 Entry Point Structure (EPS).
Reads the current SMBIOS table from gSmbiosTableBase, computes the
checksum, fills the EPS fields, and writes the 24-byte EPS to the
legacy entry point region.
**/
VOID
UpdateEPSHeader3 (
VOID
)
{
UINT8 Checksum;
INTN Index;
DEBUG ((EFI_D_INFO, "::: SMBIOS - UpdateEPSHeader_3. pSmbiosV3TableEntryPoint = %016lx :::\n", gSmbiosV3EntryPoint));
//
// Set the table maximum size and address
//
((SMBIOS_V3_ENTRY_POINT *)gSmbiosV3EntryPoint)->TableMaximumSize = (UINT32)GetSmbiosTotalTableSize (gSmbiosTableBase);
((SMBIOS_V3_ENTRY_POINT *)gSmbiosV3EntryPoint)->TableAddress = (UINT64)(UINTN)gSmbiosTableBase;
DEBUG ((EFI_D_INFO, "*** SMBIOS 3.0 - TableAddress = %016lx ***\n", gSmbiosTableBase));
//
// Compute EPS checksum (bytes 5..23 sum to zero)
//
gSmbiosV3EntryPoint[5] = 0; // Clear checksum
Checksum = 0;
if (gSmbiosV3EntryPoint[6] != 0) {
for (Index = 0; Index < (INTN)gSmbiosV3EntryPoint[6]; Index++) {
Checksum += gSmbiosV3EntryPoint[Index];
}
gSmbiosV3EntryPoint[5] = (UINT8)(-(INT8)Checksum);
}
DEBUG ((EFI_D_INFO, "::: SMBIOS - Exit UpdateEPSHeader_3 :::\n"));
//
// Write the v3 EPS to the pre-allocated region
//
if (gSmbiosV3EntryPoint != NULL) {
SmbiosCopyMem (gSmbiosV3EntryPoint, SMBIOS_V3_ENTRY_POINT_SIG, 24);
}
}
// ---------------------------------------------------------------------------
// Update both EPS headers and re-program to legacy region
// ---------------------------------------------------------------------------
/**
Update both the SMBIOS v2.x and v3.0 entry point structures and
write them to the reserved entry point memory regions.
This function is called after any modification to the SMBIOS table
(add/remove/update structure) to keep the EPS in sync.
**/
VOID
UpdateSmbiosTableHeader (
VOID
)
{
INTN InterruptState;
InterruptState = 0;
DEBUG ((EFI_D_INFO, "*** SMBIOS - UpdateSmbiosTableHeader ***\n"));
//
// If a legacy region protocol is available, unprotect the E/F segment
//
// (In decompilation: qword_9140 points to LegacyRegionProtocol)
//
//
// Update v2.x EPS
//
UpdateEPSHeader ();
//
// Update v3.0 EPS
//
UpdateEPSHeader3 ();
//
// Re-protect the legacy region if applicable
//
DEBUG ((EFI_D_INFO, "::: SMBIOS - Exit UpdateSmbiosTableHeader :::\n"));
}
// ---------------------------------------------------------------------------
// Core SMBIOS table update function
// ---------------------------------------------------------------------------
/**
Add, delete, or update a SMBIOS structure in the table.
@param[in] Operation 0 = Add (append after last structure by handle),
1 = Delete (remove by pointer),
2 = Insert (insert before given handle).
@param[in] Handle For insert/delete, the target handle. For add, unused.
@param[in] Structure Pointer to the SMBIOS structure data to add.
@param[in] Size Size of the structure data.
@param[in] HandleValue The handle to assign to the new structure (for add/insert).
@return EFI_SUCCESS on success, or EFI error code.
**/
EFI_STATUS
UpdateSmbiosTable (
IN UINTN Operation,
IN SMBIOS_HANDLE *Handle OPTIONAL,
IN SMBIOS_STRUCTURE_HEADER *Structure,
IN UINT16 Size,
IN SMBIOS_HANDLE HandleValue
)
{
UINT8 *ScratchBuffer;
UINT16 NewTableSize;
UINT8 *WriteCursor;
UINT8 *OldCursor;
UINT16 StructSize;
UINT16 Cursor;
UINT16 StructIndex;
UINT8 *Target;
UINTN RemainingSize;
BOOLEAN HandleFound;
ScratchBuffer = NULL;
NewTableSize = 0;
HandleFound = FALSE;
//
// Debug trace
//
DEBUG ((EFI_D_INFO, "*** SMBIOS - UpdateSmbiosTable ***\n"));
DEBUG ((EFI_D_INFO, "Operation = %x, Size = %x, Handle = %x\n", (UINTN)Operation, Size, HandleValue));
//
// If a LegacyRegionProtocol is available, unprotect
//
//
if (Operation == SMBIOS_DELETE_STRUCTURE) {
//
// Delete the structure at the given pointer
//
DEBUG ((EFI_D_INFO, "Deleting structure\n"));
StructSize = GetSmbiosStructureSize (Structure);
RemainingSize = Size - StructSize;
//
// Compact: move trailing data forward
//
SmbiosCopyMem ((VOID *)Structure, (UINT8 *)Structure + StructSize, RemainingSize);
//
// Fill vacated space with 0xFF
//
SmbiosSetMem ((UINT8 *)Structure + RemainingSize, -1, StructSize);
DEBUG ((EFI_D_INFO, "Done deleting\n"));
goto Done;
}
if (Operation != SMBIOS_INSERT_STRUCTURE) {
//
// Unknown operation
//
return EFI_INVALID_PARAMETER;
}
//
// Add/Insert structure
//
DEBUG ((EFI_D_INFO, "Adding structure\n"));
if (Structure == NULL) {
goto Done;
}
//
// Check if the handle already exists (for inserts requiring uniqueness)
//
if (HandleValue <= 0xFFFD) {
OldCursor = gSmbiosTableBase;
if (FindStructureByHandle (&OldCursor, HandleValue)) {
return EFI_ALREADY_STARTED;
}
}
//
// Validate structure integrity
//
if (*(UINT16 *)((UINT8 *)Structure + Size - 2) != 0) {
DEBUG ((EFI_D_ERROR, "Invalid structure terminator\n"));
return EFI_INVALID_PARAMETER;
}
if (Size < (UINTN)(Structure->Length + 2)) {
DEBUG ((EFI_D_ERROR, "Invalid structure size\n"));
return EFI_INVALID_PARAMETER;
}
//
// Handle special case: Type 4 (Processor) with Length 42 (0x2A)
// that has empty type-specific fields - clip trailing zeros
//
if (Structure->Type == SMBIOS_TYPE_PROCESSOR_INFORMATION &&
Structure->Length == 42 &&
(*(UINT8 *)((UINT8 *)Structure + 4) == 0 &&
*(UINT8 *)((UINT8 *)Structure + 7) == 0 &&
*(UINT8 *)((UINT8 *)Structure + 16) == 0 &&
*(UINT8 *)((UINT8 *)Structure + 32) == 0 &&
*(UINT8 *)((UINT8 *)Structure + 33) == 0 &&
*(UINT8 *)((UINT8 *)Structure + 34) == 0) &&
*(UINT32 *)((UINT8 *)Structure + 42) == 0 &&
*(UINT16 *)((UINT8 *)Structure + 46) == 0)
{
//
// Compact away the empty fields at offset 42
//
SmbiosCopyMem ((UINT8 *)Structure + 42, (UINT8 *)Structure + 48, Size - 48);
Size -= 6;
}
//
// Allocate scratch buffer
//
ScratchBuffer = AllocatePool (gSmbiosTableSize + Size);
if (ScratchBuffer == NULL) {
NewTableSize = gSmbiosTableSize;
return EFI_OUT_OF_RESOURCES;
}
//
// Copy existing table into scratch
//
SmbiosCopyMem (ScratchBuffer, gSmbiosTableBase, gSmbiosTableSize);
//
// Calculate current used size
//
NewTableSize = GetSmbiosTotalTableSize (gSmbiosTableBase);
//
// Check if there is enough free space in the table
//
if (Size <= gSmbiosTableSize - NewTableSize) {
//
if (gSmbiosTableSize - NewTableSize >= Size) {
//
// Enough space - do in-place operation
//
WriteCursor = gSmbiosTableBase;
} else {
}
} else {
DEBUG ((EFI_D_INFO, "Not enough space\n"));
//
// Need to expand the table
//
if (!gSmbiosTableExpansionEnabled) {
//
// Cannot allocate after EndOfDxe
//
DEBUG ((EFI_D_ERROR,
"Can not expand Smbios Table after end of DXE\n"
"Try increase EXTRA_RESERVED_BYTES SDL token value to reserve more space for Smbios Table to grow\n"));
goto NoSpace;
}
DEBUG ((EFI_D_INFO, "Allocating memory for new table\n"));
NewTableSize = Size + NewTableSize;
{
UINT8 *NewTable;
UINTN Pages;
Pages = (NewTableSize + 0xFFF) >> 12;
NewTable = NULL;
NewTable = AllocatePages (Pages);
if (NewTable == NULL) {
DEBUG ((EFI_D_ERROR, "Failed to allocate memory\n"));
goto NoSpace;
}
SmbiosCopyMem (NewTable, gSmbiosTableBase, gSmbiosTableSize);
WriteCursor = NewTable;
DEBUG ((EFI_D_INFO, "Memory allocated = %x\n", NewTable));
}
}
//
// Locate insertion point
//
WriteCursor = gSmbiosTableBase;
OldCursor = ScratchBuffer;
StructIndex = 0;
if (Operation == SMBIOS_INSERT_STRUCTURE) {
//
// Find structure by handle to insert before
//
while (*WriteCursor != SMBIOS_TERMINATOR_TYPE) {
StructSize = GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)WriteCursor);
if (*(SMBIOS_HANDLE *)(WriteCursor + 2) == *Handle) {
break;
}
StructIndex++;
WriteCursor += StructSize;
OldCursor += StructSize;
}
} else {
//
// Find insertion point by handle value ordering
//
while (*WriteCursor != SMBIOS_TERMINATOR_TYPE &&
((SMBIOS_STRUCTURE_HEADER *)WriteCursor)->Handle <= HandleValue) {
StructSize = GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)WriteCursor);
WriteCursor += StructSize;
OldCursor += StructSize;
}
}
//
// Assign the handle
//
if (HandleValue > 0xFFFD) {
*((SMBIOS_HANDLE *)((UINT8 *)Structure + 2)) = HandleValue;
} else {
((SMBIOS_STRUCTURE_HEADER *)Structure)->Handle = HandleValue;
}
//
// Insert the new structure
//
SmbiosCopyMem (WriteCursor, Structure, Size);
//
// Copy the remaining structures after the insertion point
//
{
UINT8 *TrailSrc;
UINT8 *TrailDst;
UINT16 TrailSize;
TrailDst = WriteCursor + Size;
TrailSrc = OldCursor;
TrailSize = 0;
//
// Walk original table from OldCursor to end to compute trail size
//
while (*TrailSrc != SMBIOS_TERMINATOR_TYPE) {
TrailSize += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)TrailSrc);
TrailSrc += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)TrailSrc);
}
TrailSize += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)TrailSrc); // terminator
SmbiosCopyMem (TrailDst, OldCursor, TrailSize);
}
//
// Check for handle collision and fix up
//
{
UINT8 *CheckCursor;
UINT16 CheckIndex;
CheckCursor = WriteCursor;
CheckIndex = 0;
while (*CheckCursor != SMBIOS_TERMINATOR_TYPE) {
if (CheckCursor != WriteCursor &&
((SMBIOS_STRUCTURE_HEADER *)CheckCursor)->Handle == HandleValue) {
//
// Collision: increment the colliding handle
//
if (HandleValue >= ((SMBIOS_STRUCTURE_HEADER *)CheckCursor)->Handle) {
((SMBIOS_STRUCTURE_HEADER *)CheckCursor)->Handle++;
}
break;
}
CheckCursor += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)CheckCursor);
}
//
// Also update the global next handle tracker
//
if (HandleValue >= gNextSmbiosHandle) {
gNextSmbiosHandle = HandleValue + 1;
}
}
DEBUG ((EFI_D_INFO, "Done adding structure\n"));
//
// Free scratch and update EPS
//
if (ScratchBuffer != NULL) {
FreePool (ScratchBuffer);
}
UpdateSmbiosTableHeader ();
return EFI_SUCCESS;
NoSpace:
if (ScratchBuffer != NULL) {
FreePool (ScratchBuffer);
}
return EFI_OUT_OF_RESOURCES;
Done:
//
// Restore legacy region protection
//
//
DEBUG ((EFI_D_INFO, "Status = %r\n", EFI_SUCCESS));
if (EFI_ERROR (EFI_OUT_OF_RESOURCES)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_OUT_OF_RESOURCES));
ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
} else {
if (EFI_SUCCESS == EFI_SUCCESS) {
//
// If the table was reallocated, free the old one and update the pointer
//
if (ScratchBuffer != NULL) {
FreePool (ScratchBuffer);
}
UpdateSmbiosTableHeader ();
}
}
return EFI_SUCCESS;
}
// ---------------------------------------------------------------------------
// Delete a structure by handle
// ---------------------------------------------------------------------------
/**
Delete a SMBIOS structure identified by its handle.
@param[in] Handle The SMBIOS handle of the structure to delete.
@return EFI_SUCCESS if deleted, EFI_NOT_FOUND otherwise.
**/
EFI_STATUS
DeleteStructureByHandle (
IN SMBIOS_HANDLE Handle
)
{
UINT8 *Cursor;
Cursor = gSmbiosTableBase;
DEBUG ((EFI_D_INFO, "*** SMBIOS - DeleteStructureByHandle. Handle = %x ***\n", Handle));
if (FindStructureByHandle (&Cursor, Handle)) {
DEBUG ((EFI_D_INFO, "Deleting structure type %x\n", *Cursor));
return UpdateSmbiosTable (
SMBIOS_DELETE_STRUCTURE,
NULL,
(SMBIOS_STRUCTURE_HEADER *)Cursor,
0,
0
);
}
DEBUG ((EFI_D_ERROR, "Structure not found\n"));
return EFI_NOT_FOUND;
}
// ---------------------------------------------------------------------------
// Add a structure by handle
// ---------------------------------------------------------------------------
/**
Add a SMBIOS structure with the specified handle.
@param[in] Handle The handle to assign.
@param[in] Record The structure data.
@param[in] Size Size of the structure data.
@return EFI_SUCCESS or error.
**/
EFI_STATUS
AddStructureByHandle (
IN SMBIOS_HANDLE Handle,
IN UINT8 *Record,
IN UINT16 Size
)
{
EFI_STATUS Status;
DEBUG ((EFI_D_INFO, "*** SMBIOS - AddStructureByHandle ***\n"));
if (Record[0] == SMBIOS_TYPE_PROCESSOR_INFORMATION &&
(*(UINT32 *)Record & 0x3F) != 0) {
//
// Specialized path for Processor type structures with specific flags
//
Status = EFI_UNSUPPORTED; // Placeholder for sub_26E0 path
} else {
Status = UpdateSmbiosTable (
SMBIOS_INSERT_STRUCTURE,
NULL,
(SMBIOS_STRUCTURE_HEADER *)Record,
Size,
Handle
);
}
DEBUG ((EFI_D_INFO, "::: SMBIOS - Exit AddStructureByHandle. Status = %r :::\n", Status));
return Status;
}
// ---------------------------------------------------------------------------
// Add a structure by index (insert before given handle)
// ---------------------------------------------------------------------------
/**
Add a SMBIOS structure by index - inserts before the specified handle.
@param[in] InsertBeforeHandle Insert before this handle.
@param[in] Record The structure data.
@param[in] Size Size of the structure data.
@param[in] Handle Handle to assign to the new structure.
@return EFI_STATUS.
**/
EFI_STATUS
AddStructureByIndex (
IN SMBIOS_HANDLE InsertBeforeHandle,
IN UINT8 *Record,
IN UINT16 Size,
IN SMBIOS_HANDLE Handle
)
{
EFI_STATUS Status;
DEBUG ((EFI_D_INFO, "*** SMBIOS - AddStructureByIndex ***\n"));
if (Record[0] == SMBIOS_TYPE_PROCESSOR_INFORMATION &&
(*(UINT32 *)Record & 0x3F) != 0) {
//
// Specialized path
//
Status = EFI_UNSUPPORTED;
} else {
Status = UpdateSmbiosTable (
SMBIOS_INSERT_STRUCTURE,
&InsertBeforeHandle,
(SMBIOS_STRUCTURE_HEADER *)Record,
Size,
Handle
);
}
DEBUG ((EFI_D_INFO, "::: SMBIOS - Exit AddStructureByIndex. Status = %r :::\n", Status));
return Status;
}
// ---------------------------------------------------------------------------
// Read a structure by type
// ---------------------------------------------------------------------------
/**
Read a SMBIOS structure by its type.
@param[in] Type SMBIOS structure type to find.
@param[out] Buffer Allocated buffer containing a copy of the structure.
@param[out] Size Size of the returned buffer.
@return EFI_SUCCESS if found, EFI_NOT_FOUND otherwise.
**/
EFI_STATUS
ReadStructureByType (
IN UINT8 Type,
OUT UINT8 **Buffer,
OUT UINT16 *Size
)
{
UINT8 *Cursor;
UINT16 StructSize;
Cursor = gSmbiosTableBase;
DEBUG ((EFI_D_INFO, "*** SMBIOS - ReadStructureByType ***\n"));
if (FindStructureByType (&Cursor, Type)) {
StructSize = GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
*Size = StructSize;
*Buffer = AllocatePool (StructSize);
if (*Buffer == NULL) {
*Size = 0;
DEBUG ((EFI_D_ERROR, "Memory allocation failed. Exit\n"));
return EFI_OUT_OF_RESOURCES;
}
SmbiosCopyMem (*Buffer, Cursor, StructSize);
DEBUG ((EFI_D_INFO, "::: SMBIOS - Exit ReadStructureByType :::\n"));
return EFI_SUCCESS;
}
*Buffer = NULL;
*Size = 0;
DEBUG ((EFI_D_ERROR, "Structure type %x not found\n", Type));
return EFI_NOT_FOUND;
}
// ---------------------------------------------------------------------------
// Read a structure by handle
// ---------------------------------------------------------------------------
/**
Read a SMBIOS structure by its handle.
@param[in] Handle SMBIOS handle to look up.
@param[out] Buffer Allocated buffer with a copy of the structure.
@param[out] Size Size of the returned buffer.
@return EFI_SUCCESS if found, EFI_NOT_FOUND otherwise.
**/
EFI_STATUS
ReadStructureByHandle (
IN SMBIOS_HANDLE Handle,
OUT UINT8 **Buffer,
OUT UINT16 *Size
)
{
UINT8 *Cursor;
UINT16 StructSize;
Cursor = gSmbiosTableBase;
DEBUG ((EFI_D_INFO, "*** SMBIOS - ReadStructureByHandle ***\n"));
if (FindStructureByHandle (&Cursor, Handle)) {
StructSize = GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
*Size = StructSize;
*Buffer = AllocatePool (StructSize);
if (*Buffer == NULL) {
*Size = 0;
DEBUG ((EFI_D_ERROR, "Memory allocation failed. Exit\n"));
return EFI_OUT_OF_RESOURCES;
}
SmbiosCopyMem (*Buffer, Cursor, StructSize);
DEBUG ((EFI_D_INFO, "Exit ReadStructureByHandle\n"));
return EFI_SUCCESS;
}
*Buffer = NULL;
*Size = 0;
DEBUG ((EFI_D_ERROR, "Structure not found. Exit\n"));
return EFI_NOT_FOUND;
}
// ---------------------------------------------------------------------------
// Find the next available handle
// ---------------------------------------------------------------------------
/**
Scan the SMBIOS table for the next unused handle value.
@return The first handle value not currently in use, or 0xFFFF if none found.
**/
SMBIOS_HANDLE
FindNextAvailableHandle (
VOID
)
{
SMBIOS_HANDLE Candidate;
UINT8 *Cursor;
for (Candidate = 0; Candidate < 0xFFFF; Candidate++) {
Cursor = gSmbiosTableBase;
if (!FindStructureByHandle (&Cursor, Candidate)) {
return Candidate;
}
}
return 0xFFFF;
}
// ---------------------------------------------------------------------------
// Producer handle table management
// ---------------------------------------------------------------------------
/**
Register (or update) a producer EFI_HANDLE for a given SMBIOS structure handle.
The producer handle table is a fixed-size array that maps SMBIOS handles to
the EFI handles of the drivers that added them.
@param[in] SmbiosHandle The SMBIOS structure handle.
@param[in] ImageHandle The EFI handle of the producing driver.
@return Pointer to the entry in the producer handle table.
**/
PRODUCER_HANDLE_ENTRY *
RegisterProducerHandle (
IN SMBIOS_HANDLE SmbiosHandle,
IN EFI_HANDLE ImageHandle
)
{
UINTN Index;
PRODUCER_HANDLE_ENTRY *Entry;
Entry = &gProducerHandleTable[0];
//
// If the first entry is unused, take it
//
if (Entry->Handle == SMBIOS_INVALID_HANDLE) {
Entry->Handle = SmbiosHandle;
Entry->ImageHandle = ImageHandle;
return Entry;
}
//
// Search for an existing entry with this handle, or an unused slot
//
for (Index = 0; Index < SMBIOS_PRODUCER_HANDLE_COUNT; Index++) {
Entry = &gProducerHandleTable[Index];
if (Entry->Handle == SmbiosHandle) {
//
// Found existing entry for this handle (update in place)
//
Entry->ImageHandle = ImageHandle;
return Entry;
}
if (Entry->Handle == SMBIOS_INVALID_HANDLE) {
//
// Found an empty slot
//
Entry->Handle = SmbiosHandle;
Entry->ImageHandle = ImageHandle;
return Entry;
}
}
//
// Table full; silently fail (original behavior)
//
return NULL;
}
// ---------------------------------------------------------------------------
// EndOfDxe Event Handler
// ---------------------------------------------------------------------------
/**
EndOfDxe event callback. Locks the SMBIOS table memory regions and
programs the SMBIOS entry points into the legacy E/F segment.
@param[in] Event The EndOfDxe event.
@param[in] Context Event context (unused).
**/
VOID
EFIAPI
SmbiosEndOfDxe (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
UINT8 *ScratchBuffer;
EFI_LEGACY_REGION_PROTOCOL *LegacyRegion;
ScratchBuffer = NULL;
DEBUG ((EFI_D_INFO, "*** SmbiosEndOfDxe Entry ***\n"));
//
// Close the event (it is a one-shot notification)
//
gBS->CloseEvent (Event);
DEBUG ((EFI_D_INFO, "Allocating memory for Scratch buffer\n"));
//
// Allocate a scratch buffer to work with the SMBIOS table
//
ScratchBuffer = AllocatePool (gSmbiosTableSize);
if (ScratchBuffer == NULL) {
return;
}
//
// Locate LegacyRegionProtocol to unprotect E/F segment for EPS programming
//
Status = gBS->LocateProtocol (&gEfiLegacyRegionProtocolGuid, NULL, (VOID **)&LegacyRegion);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_WARN, "LegacyRegionProtocol not found\n"));
//
// Continue without legacy region programming if protocol unavailable
//
LegacyRegion = NULL;
}
//
// Copy the SMBIOS table to scratch for processing
// ... (the original code processes on-board device data, then updates EPS)
//
//
// Update both EPS headers and program to legacy region
//
UpdateSmbiosTableHeader ();
//
// Lock the table (no further expansion allowed)
//
gSmbiosTableExpansionEnabled = FALSE;
//
// Re-protect the legacy region
//
//
// Free scratch buffer
//
if (ScratchBuffer != NULL) {
FreePool (ScratchBuffer);
}
DEBUG ((EFI_D_INFO, "*** SmbiosEndOfDxe Exit ***\n"));
}
// ---------------------------------------------------------------------------
// DynamicUpdate Event Handler
// ---------------------------------------------------------------------------
/**
SmbiosDynamicUpdate event callback. Called at EndOfDxe to populate
onboard device data and perform dynamic structure updates from NVRAM.
@param[in] Event The notification event.
@param[in] Context Event context.
**/
VOID
EFIAPI
SmbiosDynamicUpdate (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
UINT8 *TableBase;
UINT8 *FoundStructure;
DEBUG ((EFI_D_INFO, "*** SmbiosDynamicUpdate Entry ***\n"));
//
// Close the event
//
gBS->CloseEvent (Event);
//
// Create onboard device data (type 41 structures)
//
// (via SmbiosCreateOnBoardDevData protocol call)
//
//
// Create onboard device extended info (type 42)
//
// (via SmbiosCreateOnBoardDevExtInfo protocol call)
//
//
// Populate runtime-configurable structures from NVRAM
//
UpdateStructuresWithNvramData ();
//
// Perform dynamic update of SMBIOS structures (types populated from data sources)
//
DEBUG ((EFI_D_INFO, "*** SMBIOS - DynamicUpdateStructures ***\n"));
if (gSmbiosFirstDynamicUpdateSeen) {
//
// First time through: populate from firmware data sources
//
// (sub_1AB8, sub_1CB8 - populate type-specific structures)
//
gSmbiosFirstDynamicUpdateSeen = FALSE;
}
//
// After all dynamic updates, trigger memory type information update
// Check if system is in S5/S6 state for memory data decisions
//
// (sub_5984 handles memory-based type 17 updates)
//
//
// Re-create TPM-related event
//
//
// Signal the new table to listeners
//
gBS->SignalEvent (Event);
//
// Close and re-create the periodic event
//
gBS->CloseEvent (Event);
//
// Update entry points
//
UpdateSmbiosTableHeader ();
DEBUG ((EFI_D_INFO, "::: Exitting SmbiosDynamicUpdate :::\n"));
}
/**
Update SMBIOS structures from NVRAM data.
Reads NVRAM variables, locates the corresponding SMBIOS structures
in the table, and updates their fields with saved values.
**/
VOID
UpdateStructuresWithNvramData (
VOID
)
{
//
// Decompiled logic from sub_B70:
//
// 1. Walk the SMBIOS table
// 2. For each structure, check if NVRAM data exists for that handle
// 3. If NVRAM data is present, overlay it onto the SMBIOS structure
// 4. Handle special cases for Type 4 (Processor), Type 17 (Memory), etc.
//
DEBUG ((EFI_D_INFO, "*** SMBIOS - UpdateStructuresWithNvramData ***\n"));
//
// Placeholder for the full NVRAM update logic.
// The decompiled function (sub_B70, ~800 instructions) performs:
// - NVRAM variable enumeration per SMBIOS handle
// - Field-level update with zero/skip masks
// - String reference count recalculation
//
}
// ---------------------------------------------------------------------------
// PI SMBIOS Protocol implementation
// ---------------------------------------------------------------------------
/**
Add a SMBIOS structure via the PI SMBIOS protocol.
@param[in] This The SMBIOS protocol instance.
@param[in] ProducerHandle Handle of the producing driver.
@param[in,out] Structure The SMBIOS structure to add.
@param[out] Key Returned handle key.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SmbiosPiAddStructure (
IN SMBIOS_PROTOCOL *This,
IN EFI_HANDLE ProducerHandle,
IN OUT SMBIOS_STRUCTURE_HEADER *Structure,
OUT SMBIOS_HANDLE *Key OPTIONAL
)
{
UINT16 StructSize;
SMBIOS_HANDLE AssignedHandle;
DEBUG ((EFI_D_INFO, "In SmbiosPiAddStructure\n"));
StructSize = GetSmbiosStructureSize (Structure);
if (Structure->Handle == SMBIOS_STARTING_HANDLE) {
//
// Auto-assign a new handle
//
if (!FindNextAvailableHandle (0) /* check availability */) {
// ...
}
AssignedHandle = (SMBIOS_HANDLE)FindNextAvailableHandle ();
Structure->Handle = AssignedHandle;
if (Key != NULL) {
*Key = AssignedHandle;
}
RegisterProducerHandle (AssignedHandle, ProducerHandle);
UpdateSmbiosTableHeader ();
} else {
//
// Use the provided handle (replace existing if applicable)
//
if (!AddStructureByHandle (Structure->Handle, (UINT8 *)Structure, StructSize)) {
if (Key != NULL) {
*Key = Structure->Handle;
}
RegisterProducerHandle (Structure->Handle, ProducerHandle);
UpdateSmbiosTableHeader ();
return EFI_SUCCESS;
}
return EFI_ALREADY_STARTED;
}
DEBUG ((EFI_D_INFO, "Exit SmbiosPiAddStructure. Status = %r\n", EFI_SUCCESS));
return EFI_SUCCESS;
}
/**
Update a string in a SMBIOS structure via the PI protocol.
@param[in] This The SMBIOS protocol instance.
@param[in,out] Handle Handle of the structure to update.
@param[in] StringNumber The 1-based string number to replace.
@param[in] String The new string value.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SmbiosPiUpdateString (
IN SMBIOS_PROTOCOL *This,
IN OUT SMBIOS_HANDLE *Handle,
IN UINT8 *StringNumber,
IN UINT8 *String
)
{
UINT8 *Cursor;
UINT16 StructSize;
UINT8 *ScratchBuffer;
UINT8 OldStringLen;
UINT8 NewStringLen;
Cursor = gSmbiosTableBase;
DEBUG ((EFI_D_INFO, "In SmbiosPiUpdateString\n"));
if (*StringNumber == 0) {
DEBUG ((EFI_D_ERROR, "String number missing!\n"));
return EFI_INVALID_PARAMETER;
}
//
// Locate the structure by handle
//
if (!FindStructureByHandle (&Cursor, *Handle)) {
DEBUG ((EFI_D_ERROR, "Structure not found!\n"));
return EFI_NOT_FOUND;
}
DEBUG ((EFI_D_INFO, "Found structure with handle = %x\n", *Handle));
//
// Validate string number
//
{
SMBIOS_STRUCTURE_HEADER *StrHeader;
StrHeader = (SMBIOS_STRUCTURE_HEADER *)Cursor;
if (!FindStringByNumber (&StrHeader, *StringNumber)) {
DEBUG ((EFI_D_ERROR, "StringNumber validation failed!\n"));
return EFI_INVALID_PARAMETER;
}
}
StructSize = GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
//
// Compute new string length
//
NewStringLen = 0;
if (*String != 0) {
while (String[NewStringLen] != 0) {
NewStringLen++;
}
}
//
// Allocate scratch buffer
//
ScratchBuffer = AllocatePool (StructSize + NewStringLen + 1);
if (ScratchBuffer == NULL) {
DEBUG ((EFI_D_ERROR, "Memory allocation failed. Exit\n"));
return EFI_OUT_OF_RESOURCES;
}
//
// Copy structure to scratch
//
SmbiosCopyMem (ScratchBuffer, Cursor, StructSize);
//
// Replace the string
//
ReplaceStringInStructure (ScratchBuffer, *StringNumber, String);
//
// Delete old structure
//
DEBUG ((EFI_D_INFO, "Deleting structure with handle = %x\n", *Handle));
DeleteStructureByHandle (*Handle);
//
// Add updated structure
//
DEBUG ((EFI_D_INFO, "Adding structure with handle = %x\n", *Handle));
AddStructureByHandle (*Handle, ScratchBuffer, StructSize);
//
// Clean up
//
FreePool (ScratchBuffer);
UpdateSmbiosTableHeader ();
DEBUG ((EFI_D_INFO, "Exiting SmbiosPiUpdateString. Status = %r\n", EFI_SUCCESS));
return EFI_SUCCESS;
}
/**
Remove a SMBIOS structure via the PI protocol.
@param[in] This The SMBIOS protocol instance.
@param[in] Handle Handle of the structure to remove.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SmbiosPiRemoveStructure (
IN SMBIOS_PROTOCOL *This,
IN SMBIOS_HANDLE Handle
)
{
UINTN Index;
UINTN EntryToRemove;
DEBUG ((EFI_D_INFO, "In SmbiosPiRemoveStructure\n"));
if (DeleteStructureByHandle (Handle)) {
DEBUG ((EFI_D_ERROR, "Failed to delete structure. Exit\n"));
return EFI_NOT_FOUND;
}
//
// Remove from producer handle table (compact entries)
//
EntryToRemove = SMBIOS_PRODUCER_HANDLE_COUNT;
for (Index = 0; Index < SMBIOS_PRODUCER_HANDLE_COUNT; Index++) {
if (gProducerHandleTable[Index].Handle == Handle) {
EntryToRemove = Index;
break;
}
}
if (EntryToRemove < SMBIOS_PRODUCER_HANDLE_COUNT) {
//
// Compact the table
//
SmbiosCopyMem (
&gProducerHandleTable[EntryToRemove],
&gProducerHandleTable[EntryToRemove + 1],
sizeof (PRODUCER_HANDLE_ENTRY) * (SMBIOS_PRODUCER_HANDLE_COUNT - EntryToRemove - 1)
);
//
// Clear the last entry
//
gProducerHandleTable[SMBIOS_PRODUCER_HANDLE_COUNT - 1].Handle = SMBIOS_INVALID_HANDLE;
gProducerHandleTable[SMBIOS_PRODUCER_HANDLE_COUNT - 1].ImageHandle = NULL;
}
UpdateSmbiosTableHeader ();
DEBUG ((EFI_D_INFO, "Exit SmbiosPiRemoveStructure\n"));
return EFI_SUCCESS;
}
/**
Get the next SMBIOS structure (enumerate via the PI protocol).
@param[in] This The SMBIOS protocol instance.
@param[in,out] Handle On input, starting handle. On output, next handle.
Use 0xFFFE for the first structure.
@param[in] Type Optional type filter. If non-NULL, return only
structures of this type.
@param[out] Record Pointer to the found structure.
@param[out] ProducerHandle Optional handle of the producer.
@return EFI_SUCCESS if a structure was found, EFI_NOT_FOUND if at end.
**/
EFI_STATUS
EFIAPI
SmbiosPiGetNextStructure (
IN SMBIOS_PROTOCOL *This,
IN OUT SMBIOS_HANDLE *Handle,
IN UINT8 *Type OPTIONAL,
OUT SMBIOS_STRUCTURE_HEADER **Record,
OUT EFI_HANDLE *ProducerHandle OPTIONAL
)
{
UINT8 *Cursor;
UINT8 *TypeFilter;
Cursor = gSmbiosTableBase;
TypeFilter = (Type != NULL) ? Type : NULL;
DEBUG ((EFI_D_INFO, "In SmbiosPiGetNextStructure\n"));
if (TypeFilter == NULL) {
//
// No type filter - just advance past current handle
//
if (*Handle != SMBIOS_STARTING_HANDLE) {
if (!FindStructureByHandle (&Cursor, *Handle)) {
goto NotFound;
}
//
// Advance to next
//
if (*Cursor == SMBIOS_TERMINATOR_TYPE) {
goto NotFound;
}
Cursor += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
}
} else {
//
// Type filter active
//
if (*Handle != SMBIOS_STARTING_HANDLE) {
if (!FindStructureByHandle (&Cursor, *Handle)) {
goto NotFound;
}
//
// Advance past current
//
if (*Cursor == SMBIOS_TERMINATOR_TYPE) {
goto NotFound;
}
Cursor += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)Cursor);
}
//
// Find next matching type
//
{
UINT8 SearchType;
SearchType = *TypeFilter;
if (!FindStructureByType (&Cursor, SearchType)) {
goto NotFound;
}
}
}
//
// Found a structure
//
if (*Cursor != SMBIOS_TERMINATOR_TYPE) {
*Record = (SMBIOS_STRUCTURE_HEADER *)Cursor;
*Handle = ((SMBIOS_STRUCTURE_HEADER *)Cursor)->Handle;
//
// Look up producer handle
//
if (ProducerHandle != NULL) {
UINTN ProdIndex;
*ProducerHandle = NULL;
//
// Search producer handle table
//
if (*Handle != SMBIOS_TERMINATOR_TYPE) {
for (ProdIndex = 0; ProdIndex < SMBIOS_PRODUCER_HANDLE_COUNT; ProdIndex++) {
if (gProducerHandleTable[ProdIndex].Handle == *Handle) {
*ProducerHandle = gProducerHandleTable[ProdIndex].ImageHandle;
break;
}
}
}
}
DEBUG ((EFI_D_INFO, "Exit SmbiosPiGetNextStructure. Status = %r\n", EFI_SUCCESS));
return EFI_SUCCESS;
}
NotFound:
*Handle = SMBIOS_STARTING_HANDLE; // No more structures
DEBUG ((EFI_D_INFO, "Exit SmbiosPiGetNextStructure. Status = %r\n", EFI_NOT_FOUND));
return EFI_NOT_FOUND;
}
// ---------------------------------------------------------------------------
// Driver Entry Point
// ---------------------------------------------------------------------------
/**
SMBIOS DXE driver entry point.
Initializes the SMBIOS driver:
1. Caches the UEFI boot/runtime services pointers
2. Allocates and initializes the SMBIOS v2.x and v3.0 Entry Point Structures
3. Allocates DXE services table and MM PCI Base protocol
4. Loads the static SMBIOS table from firmware volume (if present)
5. Allocates the SMBIOS table work buffer
6. Initializes the producer handle table
7. Installs EndOfDxe and dynamic update event callbacks
8. Publishes SMBIOS v2.x/v3.0 EPS to the system configuration table
9. Installs the PI SMBIOS protocol and AMI SMBIOS protocol
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
SmbiosDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE NewImageHandle;
UINT64 SmbiosEntryPointPages;
EFI_PHYSICAL_ADDRESS SmbiosEntryPointAddress;
UINT8 *StaticTableData;
UINT16 StaticTableSize;
BOOLEAN StaticTableFound;
UINT16 TableSizeAligned;
UINT16 Index;
UINTN Pages;
EFI_EVENT EndOfDxeEvent;
EFI_EVENT DynamicUpdateEvent;
UINTN NumberOfHandles;
EFI_HANDLE *HandleBuffer;
EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
UINTN SectionSize;
VOID *SectionData;
NewImageHandle = ImageHandle;
StaticTableFound = FALSE;
StaticTableData = NULL;
StaticTableSize = 0;
EndOfDxeEvent = NULL;
DynamicUpdateEvent = NULL;
//
// Initialize global service pointers
//
if (gSystemTable == NULL) {
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
}
DEBUG ((EFI_D_INFO, "In SmbiosDriverEntryPoint\n"));
// --------------------------------------------------------------------------
// 1. Allocate SMBIOS v2.x Entry Point Structure (31 bytes)
// --------------------------------------------------------------------------
DEBUG ((EFI_D_INFO, "Allocating memory for Smbios v2.x SmbiosEntryPointTable\n"));
SmbiosEntryPointAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)0xFFFFFFFFFFFFFFFFULL;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiRuntimeServicesData,
1, // 1 page = 4KB, more than enough for 31 bytes
&SmbiosEntryPointAddress
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
gSmbiosEntryPoint = (UINT8 *)(UINTN)SmbiosEntryPointAddress;
DEBUG ((EFI_D_INFO, "pSmbiosTableEntryPoint = %x\n", gSmbiosEntryPoint));
//
// Initialize the EPS memory with the "_SM_" signature
//
SmbiosCopyMem (gSmbiosEntryPoint, SMBIOS_ENTRY_POINT_SIG, 31);
// --------------------------------------------------------------------------
// 2. Allocate SMBIOS v3.0 Entry Point Structure (24 bytes)
// --------------------------------------------------------------------------
DEBUG ((EFI_D_INFO, "Allocating memory for Smbios v3.x Smbios3EntryPointTable\n"));
SmbiosEntryPointAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)0xFFFFFFFFFFFFFFFFULL;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiRuntimeServicesData,
1,
&SmbiosEntryPointAddress
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
gSmbiosV3EntryPoint = (UINT8 *)(UINTN)SmbiosEntryPointAddress;
DEBUG ((EFI_D_INFO, "pSmbiosV3TableEntryPoint = %x\n", gSmbiosV3EntryPoint));
//
// Initialize the v3 EPS memory with the "_SM3_" signature
//
SmbiosCopyMem (gSmbiosV3EntryPoint, SMBIOS_V3_ENTRY_POINT_SIG, 24);
// --------------------------------------------------------------------------
// 3. Locate MM PCI Base protocol
// --------------------------------------------------------------------------
Status = gBS->LocateProtocol (
&gEfiMmPciBaseProtocolGuid,
NULL,
&mPciUsra
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
ASSERT (mPciUsra != NULL);
// --------------------------------------------------------------------------
// 4. Read Setup and SecureBoot variables
// --------------------------------------------------------------------------
{
UINT32 SetupVar;
UINTN DataSize;
//
// Read "Setup" variable
//
DataSize = sizeof (SetupVar);
gRT->GetVariable (L"Setup", &gSetupVariableGuid, NULL, &DataSize, &SetupVar);
//
// Read "SecureBoot" variable
//
DataSize = sizeof (BOOLEAN);
gRT->GetVariable (L"SecureBoot", &gEfiGlobalVariableGuid, NULL, &DataSize, &gSmbiosTableExpansionEnabled);
}
// --------------------------------------------------------------------------
// 5. Load static SMBIOS table from Firmware Volume
// --------------------------------------------------------------------------
DEBUG ((EFI_D_INFO, "Get static table\n"));
DEBUG ((EFI_D_INFO, "::: SMBIOS - LoadRealModeFileSection :::\n"));
//
// Locate all handles that support FirmwareVolume2 protocol
//
HandleBuffer = NULL;
NumberOfHandles = 0;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolume2ProtocolGuid,
NULL,
&NumberOfHandles,
&HandleBuffer
);
if (!EFI_ERROR (Status)) {
//
// Search each FV for the SMBIOS static table file
//
for (Index = 0; Index < NumberOfHandles; Index++) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **)&FvProtocol
);
if (EFI_ERROR (Status)) {
continue;
}
//
// Read the raw section from the FV file
//
SectionData = NULL;
SectionSize = 0;
Status = FvProtocol->ReadSection (
FvProtocol,
&gSmbiosStaticTableFileGuid,
EFI_SECTION_RAW,
0,
&SectionData,
&SectionSize
);
if (!EFI_ERROR (Status)) {
//
// Found the static SMBIOS data section
//
StaticTableFound = TRUE;
StaticTableData = SectionData;
StaticTableSize = (UINT16)SectionSize;
break;
}
}
gBS->FreePool (HandleBuffer);
DEBUG ((EFI_D_INFO, "::: SMBIOS - Exit LoadRealModeFileSection. Status = %r :::\n", Status));
} else {
DEBUG ((EFI_D_WARN, "*** Failed to locate handle buffer EFI_FIRMWARE_VOLUME2_PROTOCOL ***\n"));
}
// --------------------------------------------------------------------------
// 5b. Process static table (if found)
// --------------------------------------------------------------------------
if (!EFI_ERROR (Status)) {
DEBUG ((EFI_D_INFO, "=== Static table found! ===\n"));
StaticTableFound = TRUE;
StaticTableSize -= 16; // Skip section header
StaticTableData += 16;
//
// Locate the last handle in the static table and fix up
//
{
UINT8 *WalkCursor;
UINT16 LastHandle;
WalkCursor = StaticTableData;
if (*WalkCursor == SMBIOS_TERMINATOR_TYPE) {
LastHandle = ((SMBIOS_STRUCTURE_HEADER *)WalkCursor)->Handle;
} else {
do {
LastHandle = ((SMBIOS_STRUCTURE_HEADER *)WalkCursor)->Handle;
WalkCursor += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)WalkCursor);
} while (*WalkCursor != SMBIOS_TERMINATOR_TYPE);
//
// Update the terminator handle to the next available
//
((SMBIOS_STRUCTURE_HEADER *)WalkCursor)->Handle = LastHandle + 1;
}
gNextSmbiosHandle = LastHandle + 1;
DEBUG ((EFI_D_INFO, "Static table built from ASM file\n"));
TableSizeAligned = StaticTableSize;
}
} else {
DEBUG ((EFI_D_INFO, "=== Static table NOT found! ===\n"));
//
// No static table; start with an empty table (just a terminator)
//
StaticTableSize = 0x2000; // Use default table size
TableSizeAligned = 0x2000;
}
// --------------------------------------------------------------------------
// 6. Allocate the main SMBIOS table buffer
// --------------------------------------------------------------------------
DEBUG ((EFI_D_INFO, "Allocating memory for Smbios 2.x and 3.x table\n"));
{
EFI_PHYSICAL_ADDRESS TableAddress;
TableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)0xFFFFFFFFFFFFFFFFULL;
Pages = (TableSizeAligned + 0xFFF) >> 12;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiRuntimeServicesData,
Pages,
&TableAddress
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
return Status;
}
gSmbiosTableBase = (UINT8 *)(UINTN)TableAddress;
gSmbiosTableSize = TableSizeAligned;
//
// Initialize to 0xFF
//
SmbiosSetMem (gSmbiosTableBase, -1, gSmbiosTableSize);
if (StaticTableFound) {
//
// Copy the static table into the new buffer
//
SmbiosCopyMem (gSmbiosTableBase, StaticTableData, StaticTableSize);
//
// Free the FV section data
//
DEBUG ((EFI_D_INFO, "Freeing Buffer\n"));
gBS->FreePool (StaticTableData);
} else {
//
// Create an empty terminator structure
//
gSmbiosTableBase[0] = SMBIOS_TERMINATOR_TYPE;
gSmbiosTableBase[1] = 4; // Length = 4 bytes (Type, Length, Handle[2])
*(UINT16 *)(gSmbiosTableBase + 2) = 0;
gNextSmbiosHandle = 0;
}
//
// Read any NVRAM-based structures from Setup variable
//
{
UINT8 *NvramBuffer;
UINT16 NvramSize;
Status = ReadStructureByType (4, &NvramBuffer, &NvramSize);
if (!EFI_ERROR (Status)) {
FreePool (NvramBuffer);
}
}
//
// Populate structures from the NVRAM data store
//
DEBUG ((EFI_D_INFO, "Before UpdateStructuresWithNvramData\n"));
UpdateStructuresWithNvramData ();
DEBUG ((EFI_D_INFO, "After UpdateStructuresWithNvramData\n"));
}
// --------------------------------------------------------------------------
// 7. Create EndOfDxe event
// --------------------------------------------------------------------------
DEBUG ((EFI_D_INFO, "Registering SmbiosDynamicUpdate callback on EndOfDxeEvent\n"));
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
SmbiosEndOfDxe,
NULL,
&EndOfDxeEvent
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to create event for SmbiosEndOfDxe\n"));
}
//
// Register the EndOfDxe event group notification
//
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
SmbiosDynamicUpdate,
NULL,
&gEfiEndOfDxeEventGroupGuid,
&DynamicUpdateEvent
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to create event for SmbiosDynamicUpdate\n"));
}
// --------------------------------------------------------------------------
// 8. Initialize Producer Handle Table and register existing structures
// --------------------------------------------------------------------------
DEBUG ((EFI_D_INFO, "Initializing Producer Handle Table\n"));
//
// Initialize the producer handle table to invalid state
//
for (Index = 0; Index < SMBIOS_PRODUCER_HANDLE_COUNT; Index++) {
gProducerHandleTable[Index].Handle = SMBIOS_INVALID_HANDLE;
gProducerHandleTable[Index].ImageHandle = NULL;
}
//
// Walk the table and register each existing structure's handle
//
{
UINT8 *WalkCursor;
WalkCursor = gSmbiosTableBase;
while (*WalkCursor != SMBIOS_TERMINATOR_TYPE) {
RegisterProducerHandle (
((SMBIOS_STRUCTURE_HEADER *)WalkCursor)->Handle,
NewImageHandle
);
WalkCursor += GetSmbiosStructureSize ((SMBIOS_STRUCTURE_HEADER *)WalkCursor);
}
RegisterProducerHandle (
((SMBIOS_STRUCTURE_HEADER *)WalkCursor)->Handle,
NewImageHandle
);
}
//
// Update both EPS headers
//
UpdateSmbiosTableHeader ();
// --------------------------------------------------------------------------
// 9. Publish SMBIOS v2.x EPS to system configuration table
// --------------------------------------------------------------------------
{
EFI_GUID SmbiosV2Guid = {
0xEB9D2D31, 0x2D88, 0x11D3, {0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}
};
DEBUG ((EFI_D_INFO, "*** Publishing Smbios 2x to System Configuration Table ***\n"));
Status = gBS->InstallConfigurationTable (&SmbiosV2Guid, gSmbiosEntryPoint);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
}
// ---------------------------------------------------------------------------
// 10. Publish SMBIOS v3.0 EPS to system configuration table
// ---------------------------------------------------------------------------
{
EFI_GUID SmbiosV3Guid = {
0x4A2C9794, 0xF2FD, 0x1544, {0xA3, 0xB4, 0x67, 0x2E, 0x8B, 0x98, 0x97, 0x7E}
};
DEBUG ((EFI_D_INFO, "*** Publishing Smbios 3x to System Configuration Table ***\n"));
Status = gBS->InstallConfigurationTable (&SmbiosV3Guid, gSmbiosV3EntryPoint);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
}
// ---------------------------------------------------------------------------
// 11. Install the PI SMBIOS protocol
// ---------------------------------------------------------------------------
{
SMBIOS_PROTOCOL *SmbiosProtocol;
//
// NOTE: The protocol struct and function pointers are populated from
// the .data section (off_8A38). This is a reconstructed representation.
//
SmbiosProtocol = AllocateZeroPool (sizeof (SMBIOS_PROTOCOL));
if (SmbiosProtocol != NULL) {
SmbiosProtocol->Revision = 0x0002000000000001ULL;
SmbiosProtocol->Add = SmbiosPiAddStructure;
SmbiosProtocol->UpdateString = SmbiosPiUpdateString;
SmbiosProtocol->Remove = SmbiosPiRemoveStructure;
SmbiosProtocol->GetNext = SmbiosPiGetNextStructure;
}
DEBUG ((EFI_D_INFO, "Installing PI Smbios protocol\n"));
Status = gBS->InstallMultipleProtocolInterfaces (
&NewImageHandle,
&gEfiSmbiosProtocolGuid,
SmbiosProtocol,
NULL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to install EfiSmbiosProtocol\n"));
}
}
// ---------------------------------------------------------------------------
// 12. Install the AMI SMBIOS protocol
// ---------------------------------------------------------------------------
{
SMBIOS_AMI_PROTOCOL *AmiSmbiosProtocol;
AmiSmbiosProtocol = AllocateZeroPool (sizeof (SMBIOS_AMI_PROTOCOL));
if (AmiSmbiosProtocol != NULL) {
AmiSmbiosProtocol->Revision = 0;
}
DEBUG ((EFI_D_INFO, "Installing AMI Smbios protocol\n"));
Status = gBS->InstallMultipleProtocolInterfaces (
&NewImageHandle,
&gAmiSmbiosProtocolGuid,
AmiSmbiosProtocol,
NULL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to install AmiSmbiosProtocol\n"));
}
}
DEBUG ((EFI_D_INFO, "Exiting SmbiosDriverEntryPoint. Status = %r\n", Status));
return EFI_SUCCESS;
}
/**
Auto-generated entry point.
Performs library constructor initialization and then calls the main
driver entry point.
@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
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// Library constructors (ProcessLibraryConstructorList) are called
// in the AutoGen before reaching the entry function body.
//
// sub_3BC() performs the UefiBootServicesTableLib, UefiRuntimeServicesTableLib,
// DxeServicesTableLib, and DxeMmPciBaseLib constructor initializations.
//
ProcessLibraryConstructorList (ImageHandle, SystemTable);
//
// Call the real driver entry point
//
return SmbiosDriverEntryPoint (ImageHandle, SystemTable);
}