/** @file | |
This code produces the Smbios protocol. It also responsible for constructing | |
SMBIOS table into system table. | |
Copyright (c) 2009 - 2021, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "SmbiosDxe.h" | |
// | |
// Module Global: | |
// Since this driver will only ever produce one instance of the | |
// protocol you are not required to dynamically allocate the PrivateData. | |
// | |
SMBIOS_INSTANCE mPrivateData; | |
UINTN mPreAllocatedPages = 0; | |
UINTN mPre64BitAllocatedPages = 0; | |
// | |
// Chassis for SMBIOS entry point structure that is to be installed into EFI system config table. | |
// | |
SMBIOS_TABLE_ENTRY_POINT *EntryPointStructure = NULL; | |
SMBIOS_TABLE_ENTRY_POINT EntryPointStructureData = { | |
// | |
// AnchorString | |
// | |
{ | |
0x5f, | |
0x53, | |
0x4d, | |
0x5f | |
}, | |
// | |
// EntryPointStructureChecksum,TO BE FILLED | |
// | |
0, | |
// | |
// EntryPointStructure Length | |
// | |
0x1f, | |
// | |
// MajorVersion | |
// | |
0, | |
// | |
// MinorVersion | |
// | |
0, | |
// | |
// MaxStructureSize, TO BE FILLED | |
// | |
0, | |
// | |
// EntryPointRevision | |
// | |
0, | |
// | |
// FormattedArea | |
// | |
{ | |
0, | |
0, | |
0, | |
0, | |
0 | |
}, | |
// | |
// IntermediateAnchorString | |
// | |
{ | |
0x5f, | |
0x44, | |
0x4d, | |
0x49, | |
0x5f | |
}, | |
// | |
// IntermediateChecksum, TO BE FILLED | |
// | |
0, | |
// | |
// TableLength, TO BE FILLED | |
// | |
0, | |
// | |
// TableAddress, TO BE FILLED | |
// | |
0, | |
// | |
// NumberOfSmbiosStructures, TO BE FILLED | |
// | |
0, | |
// | |
// SmbiosBcdRevision | |
// | |
0 | |
}; | |
SMBIOS_TABLE_3_0_ENTRY_POINT *Smbios30EntryPointStructure = NULL; | |
SMBIOS_TABLE_3_0_ENTRY_POINT Smbios30EntryPointStructureData = { | |
// | |
// AnchorString _SM3_ | |
// | |
{ | |
0x5f, | |
0x53, | |
0x4d, | |
0x33, | |
0x5f, | |
}, | |
// | |
// EntryPointStructureChecksum,TO BE FILLED | |
// | |
0, | |
// | |
// EntryPointLength | |
// | |
0x18, | |
// | |
// MajorVersion | |
// | |
0, | |
// | |
// MinorVersion | |
// | |
0, | |
// | |
// DocRev | |
// | |
0, | |
// | |
// EntryPointRevision | |
// | |
0x01, | |
// | |
// Reserved | |
// | |
0, | |
// | |
// TableMaximumSize,TO BE FILLED | |
// | |
0, | |
// | |
// TableAddress,TO BE FILLED | |
// | |
0 | |
}; | |
IS_SMBIOS_TABLE_VALID_ENTRY mIsSmbiosTableValid[] = { | |
{ &gUniversalPayloadSmbios3TableGuid, IsValidSmbios30Table }, | |
{ &gUniversalPayloadSmbiosTableGuid, IsValidSmbios20Table } | |
}; | |
/** | |
Get the full size of SMBIOS structure including optional strings that follow the formatted structure. | |
@param This The EFI_SMBIOS_PROTOCOL instance. | |
@param Head Pointer to the beginning of SMBIOS structure. | |
@param Size The returned size. | |
@param NumberOfStrings The returned number of optional strings that follow the formatted structure. | |
@retval EFI_SUCCESS Size retured in Size. | |
@retval EFI_INVALID_PARAMETER Input SMBIOS structure mal-formed or Size is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetSmbiosStructureSize ( | |
IN CONST EFI_SMBIOS_PROTOCOL *This, | |
IN EFI_SMBIOS_TABLE_HEADER *Head, | |
OUT UINTN *Size, | |
OUT UINTN *NumberOfStrings | |
) | |
{ | |
UINTN FullSize; | |
UINTN StrLen; | |
UINTN MaxLen; | |
INT8 *CharInStr; | |
if ((Size == NULL) || (NumberOfStrings == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
FullSize = Head->Length; | |
CharInStr = (INT8 *)Head + Head->Length; | |
*Size = FullSize; | |
*NumberOfStrings = 0; | |
StrLen = 0; | |
// | |
// look for the two consecutive zeros, check the string limit by the way. | |
// | |
while (*CharInStr != 0 || *(CharInStr+1) != 0) { | |
if (*CharInStr == 0) { | |
*Size += 1; | |
CharInStr++; | |
} | |
if ((This->MajorVersion < 2) || ((This->MajorVersion == 2) && (This->MinorVersion < 7))) { | |
MaxLen = SMBIOS_STRING_MAX_LENGTH; | |
} else if (This->MajorVersion < 3) { | |
// | |
// Reference SMBIOS 2.7, chapter 6.1.3, it will have no limit on the length of each individual text string. | |
// However, the length of the entire structure table (including all strings) must be reported | |
// in the Structure Table Length field of the SMBIOS Structure Table Entry Point, | |
// which is a WORD field limited to 65,535 bytes. | |
// | |
MaxLen = SMBIOS_TABLE_MAX_LENGTH; | |
} else { | |
// | |
// SMBIOS 3.0 defines the Structure table maximum size as DWORD field limited to 0xFFFFFFFF bytes. | |
// Locate the end of string as long as possible. | |
// | |
MaxLen = SMBIOS_3_0_TABLE_MAX_LENGTH; | |
} | |
for (StrLen = 0; StrLen < MaxLen; StrLen++) { | |
if (*(CharInStr+StrLen) == 0) { | |
break; | |
} | |
} | |
if (StrLen == MaxLen) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// forward the pointer | |
// | |
CharInStr += StrLen; | |
*Size += StrLen; | |
*NumberOfStrings += 1; | |
} | |
// | |
// count ending two zeros. | |
// | |
*Size += 2; | |
return EFI_SUCCESS; | |
} | |
/** | |
Determin whether an SmbiosHandle has already in use. | |
@param Head Pointer to the beginning of SMBIOS structure. | |
@param Handle A unique handle will be assigned to the SMBIOS record. | |
@retval TRUE Smbios handle already in use. | |
@retval FALSE Smbios handle is NOT used. | |
**/ | |
BOOLEAN | |
EFIAPI | |
CheckSmbiosHandleExistance ( | |
IN LIST_ENTRY *Head, | |
IN EFI_SMBIOS_HANDLE Handle | |
) | |
{ | |
LIST_ENTRY *Link; | |
SMBIOS_HANDLE_ENTRY *HandleEntry; | |
for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { | |
HandleEntry = SMBIOS_HANDLE_ENTRY_FROM_LINK (Link); | |
if (HandleEntry->SmbiosHandle == Handle) { | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Get the max SmbiosHandle that could be use. | |
@param This The EFI_SMBIOS_PROTOCOL instance. | |
@param MaxHandle The max handle that could be assigned to the SMBIOS record. | |
**/ | |
VOID | |
EFIAPI | |
GetMaxSmbiosHandle ( | |
IN CONST EFI_SMBIOS_PROTOCOL *This, | |
IN OUT EFI_SMBIOS_HANDLE *MaxHandle | |
) | |
{ | |
if ((This->MajorVersion == 2) && (This->MinorVersion == 0)) { | |
*MaxHandle = 0xFFFE; | |
} else { | |
*MaxHandle = 0xFEFF; | |
} | |
} | |
/** | |
Get an SmbiosHandle that could use. | |
@param This The EFI_SMBIOS_PROTOCOL instance. | |
@param SmbiosHandle A unique handle will be assigned to the SMBIOS record. | |
@retval EFI_SUCCESS Smbios handle got. | |
@retval EFI_OUT_OF_RESOURCES Smbios handle is NOT available. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetAvailableSmbiosHandle ( | |
IN CONST EFI_SMBIOS_PROTOCOL *This, | |
IN OUT EFI_SMBIOS_HANDLE *Handle | |
) | |
{ | |
LIST_ENTRY *Head; | |
SMBIOS_INSTANCE *Private; | |
EFI_SMBIOS_HANDLE MaxSmbiosHandle; | |
EFI_SMBIOS_HANDLE AvailableHandle; | |
GetMaxSmbiosHandle (This, &MaxSmbiosHandle); | |
Private = SMBIOS_INSTANCE_FROM_THIS (This); | |
Head = &Private->AllocatedHandleListHead; | |
for (AvailableHandle = 0; AvailableHandle < MaxSmbiosHandle; AvailableHandle++) { | |
if (!CheckSmbiosHandleExistance (Head, AvailableHandle)) { | |
*Handle = AvailableHandle; | |
return EFI_SUCCESS; | |
} | |
} | |
return EFI_OUT_OF_RESOURCES; | |
} | |
/** | |
Add an SMBIOS record. | |
@param This The EFI_SMBIOS_PROTOCOL instance. | |
@param ProducerHandle The handle of the controller or driver associated with the SMBIOS information. NULL | |
means no handle. | |
@param SmbiosHandle On entry, the handle of the SMBIOS record to add. If FFFEh, then a unique handle | |
will be assigned to the SMBIOS record. If the SMBIOS handle is already in use, | |
EFI_ALREADY_STARTED is returned and the SMBIOS record is not updated. | |
@param Record The data for the fixed portion of the SMBIOS record. The format of the record is | |
determined by EFI_SMBIOS_TABLE_HEADER.Type. The size of the formatted area is defined | |
by EFI_SMBIOS_TABLE_HEADER.Length and either followed by a double-null (0x0000) or | |
a set of null terminated strings and a null. | |
@retval EFI_SUCCESS Record was added. | |
@retval EFI_OUT_OF_RESOURCES Record was not added due to lack of system resources. | |
@retval EFI_ALREADY_STARTED The SmbiosHandle passed in was already in use. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmbiosAdd ( | |
IN CONST EFI_SMBIOS_PROTOCOL *This, | |
IN EFI_HANDLE ProducerHandle OPTIONAL, | |
IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle, | |
IN EFI_SMBIOS_TABLE_HEADER *Record | |
) | |
{ | |
VOID *Raw; | |
UINTN TotalSize; | |
UINTN RecordSize; | |
UINTN StructureSize; | |
UINTN NumberOfStrings; | |
EFI_STATUS Status; | |
LIST_ENTRY *Head; | |
SMBIOS_INSTANCE *Private; | |
EFI_SMBIOS_ENTRY *SmbiosEntry; | |
EFI_SMBIOS_HANDLE MaxSmbiosHandle; | |
SMBIOS_HANDLE_ENTRY *HandleEntry; | |
EFI_SMBIOS_RECORD_HEADER *InternalRecord; | |
BOOLEAN Smbios32BitTable; | |
BOOLEAN Smbios64BitTable; | |
if (SmbiosHandle == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Private = SMBIOS_INSTANCE_FROM_THIS (This); | |
// | |
// Check whether SmbiosHandle is already in use | |
// | |
Head = &Private->AllocatedHandleListHead; | |
if ((*SmbiosHandle != SMBIOS_HANDLE_PI_RESERVED) && CheckSmbiosHandleExistance (Head, *SmbiosHandle)) { | |
return EFI_ALREADY_STARTED; | |
} | |
// | |
// when SmbiosHandle is 0xFFFE, an available handle will be assigned | |
// | |
if (*SmbiosHandle == SMBIOS_HANDLE_PI_RESERVED) { | |
Status = GetAvailableSmbiosHandle (This, SmbiosHandle); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} else { | |
// | |
// Check this handle validity | |
// | |
GetMaxSmbiosHandle (This, &MaxSmbiosHandle); | |
if (*SmbiosHandle > MaxSmbiosHandle) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
// | |
// Calculate record size and string number | |
// | |
Status = GetSmbiosStructureSize (This, Record, &StructureSize, &NumberOfStrings); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Smbios32BitTable = FALSE; | |
Smbios64BitTable = FALSE; | |
if ((This->MajorVersion < 0x3) || | |
((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT0) == BIT0))) | |
{ | |
// | |
// For SMBIOS 32-bit table, the length of the entire structure table (including all strings) must be reported | |
// in the Structure Table Length field of the SMBIOS Structure Table Entry Point, | |
// which is a WORD field limited to 65,535 bytes. So the max size of 32-bit table should not exceed 65,535 bytes. | |
// | |
if ((EntryPointStructure != NULL) && | |
(EntryPointStructure->TableLength + StructureSize > SMBIOS_TABLE_MAX_LENGTH)) | |
{ | |
DEBUG ((DEBUG_INFO, "SmbiosAdd: Total length exceeds max 32-bit table length with type = %d size = 0x%x\n", Record->Type, StructureSize)); | |
} else { | |
Smbios32BitTable = TRUE; | |
DEBUG ((DEBUG_INFO, "SmbiosAdd: Smbios type %d with size 0x%x is added to 32-bit table\n", Record->Type, StructureSize)); | |
} | |
} | |
// | |
// For SMBIOS 3.0, Structure table maximum size in Entry Point structure is DWORD field limited to 0xFFFFFFFF bytes. | |
// | |
if ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT1) == BIT1)) { | |
// | |
// For SMBIOS 64-bit table, Structure table maximum size in SMBIOS 3.0 (64-bit) Entry Point | |
// is a DWORD field limited to 0xFFFFFFFF bytes. So the max size of 64-bit table should not exceed 0xFFFFFFFF bytes. | |
// | |
if ((Smbios30EntryPointStructure != NULL) && | |
(Smbios30EntryPointStructure->TableMaximumSize + StructureSize > SMBIOS_3_0_TABLE_MAX_LENGTH)) | |
{ | |
DEBUG ((DEBUG_INFO, "SmbiosAdd: Total length exceeds max 64-bit table length with type = %d size = 0x%x\n", Record->Type, StructureSize)); | |
} else { | |
DEBUG ((DEBUG_INFO, "SmbiosAdd: Smbios type %d with size 0x%x is added to 64-bit table\n", Record->Type, StructureSize)); | |
Smbios64BitTable = TRUE; | |
} | |
} | |
if ((!Smbios32BitTable) && (!Smbios64BitTable)) { | |
// | |
// If both 32-bit and 64-bit table are not updated, quit | |
// | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Enter into critical section | |
// | |
Status = EfiAcquireLockOrFail (&Private->DataLock); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
RecordSize = sizeof (EFI_SMBIOS_RECORD_HEADER) + StructureSize; | |
TotalSize = sizeof (EFI_SMBIOS_ENTRY) + RecordSize; | |
// | |
// Allocate internal buffer | |
// | |
SmbiosEntry = AllocateZeroPool (TotalSize); | |
if (SmbiosEntry == NULL) { | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
HandleEntry = AllocateZeroPool (sizeof (SMBIOS_HANDLE_ENTRY)); | |
if (HandleEntry == NULL) { | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Build Handle Entry and insert into linked list | |
// | |
HandleEntry->Signature = SMBIOS_HANDLE_ENTRY_SIGNATURE; | |
HandleEntry->SmbiosHandle = *SmbiosHandle; | |
InsertTailList (&Private->AllocatedHandleListHead, &HandleEntry->Link); | |
InternalRecord = (EFI_SMBIOS_RECORD_HEADER *)(SmbiosEntry + 1); | |
Raw = (VOID *)(InternalRecord + 1); | |
// | |
// Build internal record Header | |
// | |
InternalRecord->Version = EFI_SMBIOS_RECORD_HEADER_VERSION; | |
InternalRecord->HeaderSize = (UINT16)sizeof (EFI_SMBIOS_RECORD_HEADER); | |
InternalRecord->RecordSize = RecordSize; | |
InternalRecord->ProducerHandle = ProducerHandle; | |
InternalRecord->NumberOfStrings = NumberOfStrings; | |
// | |
// Insert record into the internal linked list | |
// | |
SmbiosEntry->Signature = EFI_SMBIOS_ENTRY_SIGNATURE; | |
SmbiosEntry->RecordHeader = InternalRecord; | |
SmbiosEntry->RecordSize = TotalSize; | |
SmbiosEntry->Smbios32BitTable = Smbios32BitTable; | |
SmbiosEntry->Smbios64BitTable = Smbios64BitTable; | |
InsertTailList (&Private->DataListHead, &SmbiosEntry->Link); | |
CopyMem (Raw, Record, StructureSize); | |
((EFI_SMBIOS_TABLE_HEADER *)Raw)->Handle = *SmbiosHandle; | |
// | |
// Some UEFI drivers (such as network) need some information in SMBIOS table. | |
// Here we create SMBIOS table and publish it in | |
// configuration table, so other UEFI drivers can get SMBIOS table from | |
// configuration table without depending on PI SMBIOS protocol. | |
// | |
SmbiosTableConstruction (Smbios32BitTable, Smbios64BitTable); | |
// | |
// Leave critical section | |
// | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_SUCCESS; | |
} | |
/** | |
Update the string associated with an existing SMBIOS record. | |
@param This The EFI_SMBIOS_PROTOCOL instance. | |
@param SmbiosHandle SMBIOS Handle of structure that will have its string updated. | |
@param StringNumber The non-zero string number of the string to update | |
@param String Update the StringNumber string with String. | |
@retval EFI_SUCCESS SmbiosHandle had its StringNumber String updated. | |
@retval EFI_INVALID_PARAMETER SmbiosHandle does not exist. | |
@retval EFI_UNSUPPORTED String was not added because it is longer than the SMBIOS Table supports. | |
@retval EFI_NOT_FOUND The StringNumber.is not valid for this SMBIOS record. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmbiosUpdateString ( | |
IN CONST EFI_SMBIOS_PROTOCOL *This, | |
IN EFI_SMBIOS_HANDLE *SmbiosHandle, | |
IN UINTN *StringNumber, | |
IN CHAR8 *String | |
) | |
{ | |
UINTN InputStrLen; | |
UINTN TargetStrLen; | |
UINTN StrIndex; | |
UINTN TargetStrOffset; | |
UINTN NewEntrySize; | |
CHAR8 *StrStart; | |
VOID *Raw; | |
LIST_ENTRY *Link; | |
LIST_ENTRY *Head; | |
EFI_STATUS Status; | |
SMBIOS_INSTANCE *Private; | |
EFI_SMBIOS_ENTRY *SmbiosEntry; | |
EFI_SMBIOS_ENTRY *ResizedSmbiosEntry; | |
EFI_SMBIOS_HANDLE MaxSmbiosHandle; | |
EFI_SMBIOS_TABLE_HEADER *Record; | |
EFI_SMBIOS_RECORD_HEADER *InternalRecord; | |
// | |
// Check args validity | |
// | |
GetMaxSmbiosHandle (This, &MaxSmbiosHandle); | |
if (*SmbiosHandle > MaxSmbiosHandle) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (String == NULL) { | |
return EFI_ABORTED; | |
} | |
if (*StringNumber == 0) { | |
return EFI_NOT_FOUND; | |
} | |
InputStrLen = AsciiStrLen (String); | |
if ((This->MajorVersion < 2) || ((This->MajorVersion == 2) && (This->MinorVersion < 7))) { | |
if (InputStrLen > SMBIOS_STRING_MAX_LENGTH) { | |
return EFI_UNSUPPORTED; | |
} | |
} else if (This->MajorVersion < 3) { | |
// | |
// Reference SMBIOS 2.7, chapter 6.1.3, it will have no limit on the length of each individual text string. | |
// However, the length of the entire structure table (including all strings) must be reported | |
// in the Structure Table Length field of the SMBIOS Structure Table Entry Point, | |
// which is a WORD field limited to 65,535 bytes. | |
// | |
if (InputStrLen > SMBIOS_TABLE_MAX_LENGTH) { | |
return EFI_UNSUPPORTED; | |
} | |
} else { | |
if (InputStrLen > SMBIOS_3_0_TABLE_MAX_LENGTH) { | |
// | |
// SMBIOS 3.0 defines the Structure table maximum size as DWORD field limited to 0xFFFFFFFF bytes. | |
// The input string length should not exceed 0xFFFFFFFF bytes. | |
// | |
return EFI_UNSUPPORTED; | |
} | |
} | |
Private = SMBIOS_INSTANCE_FROM_THIS (This); | |
// | |
// Enter into critical section | |
// | |
Status = EfiAcquireLockOrFail (&Private->DataLock); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Head = &Private->DataListHead; | |
for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { | |
SmbiosEntry = SMBIOS_ENTRY_FROM_LINK (Link); | |
Record = (EFI_SMBIOS_TABLE_HEADER *)(SmbiosEntry->RecordHeader + 1); | |
if (Record->Handle == *SmbiosHandle) { | |
// | |
// Find out the specified SMBIOS record | |
// | |
if (*StringNumber > SmbiosEntry->RecordHeader->NumberOfStrings) { | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Point to unformed string section | |
// | |
StrStart = (CHAR8 *)Record + Record->Length; | |
for (StrIndex = 1, TargetStrOffset = 0; StrIndex < *StringNumber; StrStart++, TargetStrOffset++) { | |
// | |
// A string ends in 00h | |
// | |
if (*StrStart == 0) { | |
StrIndex++; | |
} | |
// | |
// String section ends in double-null (0000h) | |
// | |
if ((*StrStart == 0) && (*(StrStart + 1) == 0)) { | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_NOT_FOUND; | |
} | |
} | |
if (*StrStart == 0) { | |
StrStart++; | |
TargetStrOffset++; | |
} | |
// | |
// Now we get the string target | |
// | |
TargetStrLen = AsciiStrLen (StrStart); | |
if (InputStrLen == TargetStrLen) { | |
AsciiStrCpyS (StrStart, TargetStrLen + 1, String); | |
// | |
// Some UEFI drivers (such as network) need some information in SMBIOS table. | |
// Here we create SMBIOS table and publish it in | |
// configuration table, so other UEFI drivers can get SMBIOS table from | |
// configuration table without depending on PI SMBIOS protocol. | |
// | |
SmbiosTableConstruction (SmbiosEntry->Smbios32BitTable, SmbiosEntry->Smbios64BitTable); | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_SUCCESS; | |
} | |
SmbiosEntry->Smbios32BitTable = FALSE; | |
SmbiosEntry->Smbios64BitTable = FALSE; | |
if ((This->MajorVersion < 0x3) || | |
((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT0) == BIT0))) | |
{ | |
// | |
// 32-bit table is produced, check the valid length. | |
// | |
if ((EntryPointStructure != NULL) && | |
(EntryPointStructure->TableLength + InputStrLen - TargetStrLen > SMBIOS_TABLE_MAX_LENGTH)) | |
{ | |
// | |
// The length of the entire structure table (including all strings) must be reported | |
// in the Structure Table Length field of the SMBIOS Structure Table Entry Point, | |
// which is a WORD field limited to 65,535 bytes. | |
// | |
DEBUG ((DEBUG_INFO, "SmbiosUpdateString: Total length exceeds max 32-bit table length\n")); | |
} else { | |
DEBUG ((DEBUG_INFO, "SmbiosUpdateString: New smbios record add to 32-bit table\n")); | |
SmbiosEntry->Smbios32BitTable = TRUE; | |
} | |
} | |
if ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT1) == BIT1)) { | |
// | |
// 64-bit table is produced, check the valid length. | |
// | |
if ((Smbios30EntryPointStructure != NULL) && | |
(Smbios30EntryPointStructure->TableMaximumSize + InputStrLen - TargetStrLen > SMBIOS_3_0_TABLE_MAX_LENGTH)) | |
{ | |
DEBUG ((DEBUG_INFO, "SmbiosUpdateString: Total length exceeds max 64-bit table length\n")); | |
} else { | |
DEBUG ((DEBUG_INFO, "SmbiosUpdateString: New smbios record add to 64-bit table\n")); | |
SmbiosEntry->Smbios64BitTable = TRUE; | |
} | |
} | |
if ((!SmbiosEntry->Smbios32BitTable) && (!SmbiosEntry->Smbios64BitTable)) { | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Original string buffer size is not exactly match input string length. | |
// Re-allocate buffer is needed. | |
// | |
NewEntrySize = SmbiosEntry->RecordSize + InputStrLen - TargetStrLen; | |
ResizedSmbiosEntry = AllocateZeroPool (NewEntrySize); | |
if (ResizedSmbiosEntry == NULL) { | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
InternalRecord = (EFI_SMBIOS_RECORD_HEADER *)(ResizedSmbiosEntry + 1); | |
Raw = (VOID *)(InternalRecord + 1); | |
// | |
// Build internal record Header | |
// | |
InternalRecord->Version = EFI_SMBIOS_RECORD_HEADER_VERSION; | |
InternalRecord->HeaderSize = (UINT16)sizeof (EFI_SMBIOS_RECORD_HEADER); | |
InternalRecord->RecordSize = SmbiosEntry->RecordHeader->RecordSize + InputStrLen - TargetStrLen; | |
InternalRecord->ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle; | |
InternalRecord->NumberOfStrings = SmbiosEntry->RecordHeader->NumberOfStrings; | |
// | |
// Copy SMBIOS structure and optional strings. | |
// | |
CopyMem (Raw, SmbiosEntry->RecordHeader + 1, Record->Length + TargetStrOffset); | |
CopyMem ((VOID *)((UINTN)Raw + Record->Length + TargetStrOffset), String, InputStrLen + 1); | |
CopyMem ( | |
(CHAR8 *)((UINTN)Raw + Record->Length + TargetStrOffset + InputStrLen + 1), | |
(CHAR8 *)Record + Record->Length + TargetStrOffset + TargetStrLen + 1, | |
SmbiosEntry->RecordHeader->RecordSize - sizeof (EFI_SMBIOS_RECORD_HEADER) - Record->Length - TargetStrOffset - TargetStrLen - 1 | |
); | |
// | |
// Insert new record | |
// | |
ResizedSmbiosEntry->Signature = EFI_SMBIOS_ENTRY_SIGNATURE; | |
ResizedSmbiosEntry->RecordHeader = InternalRecord; | |
ResizedSmbiosEntry->RecordSize = NewEntrySize; | |
ResizedSmbiosEntry->Smbios32BitTable = SmbiosEntry->Smbios32BitTable; | |
ResizedSmbiosEntry->Smbios64BitTable = SmbiosEntry->Smbios64BitTable; | |
InsertTailList (Link->ForwardLink, &ResizedSmbiosEntry->Link); | |
// | |
// Remove old record | |
// | |
RemoveEntryList (Link); | |
FreePool (SmbiosEntry); | |
// | |
// Some UEFI drivers (such as network) need some information in SMBIOS table. | |
// Here we create SMBIOS table and publish it in | |
// configuration table, so other UEFI drivers can get SMBIOS table from | |
// configuration table without depending on PI SMBIOS protocol. | |
// | |
SmbiosTableConstruction (ResizedSmbiosEntry->Smbios32BitTable, ResizedSmbiosEntry->Smbios64BitTable); | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_SUCCESS; | |
} | |
} | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_INVALID_PARAMETER; | |
} | |
/** | |
Remove an SMBIOS record. | |
@param This The EFI_SMBIOS_PROTOCOL instance. | |
@param SmbiosHandle The handle of the SMBIOS record to remove. | |
@retval EFI_SUCCESS SMBIOS record was removed. | |
@retval EFI_INVALID_PARAMETER SmbiosHandle does not specify a valid SMBIOS record. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmbiosRemove ( | |
IN CONST EFI_SMBIOS_PROTOCOL *This, | |
IN EFI_SMBIOS_HANDLE SmbiosHandle | |
) | |
{ | |
LIST_ENTRY *Link; | |
LIST_ENTRY *Head; | |
EFI_STATUS Status; | |
EFI_SMBIOS_HANDLE MaxSmbiosHandle; | |
SMBIOS_INSTANCE *Private; | |
EFI_SMBIOS_ENTRY *SmbiosEntry; | |
SMBIOS_HANDLE_ENTRY *HandleEntry; | |
EFI_SMBIOS_TABLE_HEADER *Record; | |
// | |
// Check args validity | |
// | |
GetMaxSmbiosHandle (This, &MaxSmbiosHandle); | |
if (SmbiosHandle > MaxSmbiosHandle) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Private = SMBIOS_INSTANCE_FROM_THIS (This); | |
// | |
// Enter into critical section | |
// | |
Status = EfiAcquireLockOrFail (&Private->DataLock); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Head = &Private->DataListHead; | |
for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { | |
SmbiosEntry = SMBIOS_ENTRY_FROM_LINK (Link); | |
Record = (EFI_SMBIOS_TABLE_HEADER *)(SmbiosEntry->RecordHeader + 1); | |
if (Record->Handle == SmbiosHandle) { | |
// | |
// Remove specified smobios record from DataList | |
// | |
RemoveEntryList (Link); | |
// | |
// Remove this handle from AllocatedHandleList | |
// | |
Head = &Private->AllocatedHandleListHead; | |
for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { | |
HandleEntry = SMBIOS_HANDLE_ENTRY_FROM_LINK (Link); | |
if (HandleEntry->SmbiosHandle == SmbiosHandle) { | |
RemoveEntryList (Link); | |
FreePool (HandleEntry); | |
break; | |
} | |
} | |
// | |
// Some UEFI drivers (such as network) need some information in SMBIOS table. | |
// Here we create SMBIOS table and publish it in | |
// configuration table, so other UEFI drivers can get SMBIOS table from | |
// configuration table without depending on PI SMBIOS protocol. | |
// | |
if (SmbiosEntry->Smbios32BitTable) { | |
DEBUG ((DEBUG_INFO, "SmbiosRemove: remove from 32-bit table\n")); | |
} | |
if (SmbiosEntry->Smbios64BitTable) { | |
DEBUG ((DEBUG_INFO, "SmbiosRemove: remove from 64-bit table\n")); | |
} | |
// | |
// Update the whole SMBIOS table again based on which table the removed SMBIOS record is in. | |
// | |
SmbiosTableConstruction (SmbiosEntry->Smbios32BitTable, SmbiosEntry->Smbios64BitTable); | |
FreePool (SmbiosEntry); | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_SUCCESS; | |
} | |
} | |
// | |
// Leave critical section | |
// | |
EfiReleaseLock (&Private->DataLock); | |
return EFI_INVALID_PARAMETER; | |
} | |
/** | |
Allow the caller to discover all or some of the SMBIOS records. | |
@param This The EFI_SMBIOS_PROTOCOL instance. | |
@param SmbiosHandle On entry, points to the previous handle of the SMBIOS record. On exit, points to the | |
next SMBIOS record handle. If it is FFFEh on entry, then the first SMBIOS record | |
handle will be returned. If it returns FFFEh on exit, then there are no more SMBIOS records. | |
@param Type On entry it means return the next SMBIOS record of type Type. If a NULL is passed in | |
this functionally it ignored. Type is not modified by the GetNext() function. | |
@param Record On exit, points to the SMBIOS Record consisting of the formatted area followed by | |
the unformatted area. The unformatted area optionally contains text strings. | |
@param ProducerHandle On exit, points to the ProducerHandle registered by Add(). If no ProducerHandle was passed into Add() NULL is returned. | |
If a NULL pointer is passed in no data will be returned | |
@retval EFI_SUCCESS SMBIOS record information was successfully returned in Record. | |
@retval EFI_NOT_FOUND The SMBIOS record with SmbiosHandle was the last available record. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmbiosGetNext ( | |
IN CONST EFI_SMBIOS_PROTOCOL *This, | |
IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle, | |
IN EFI_SMBIOS_TYPE *Type OPTIONAL, | |
OUT EFI_SMBIOS_TABLE_HEADER **Record, | |
OUT EFI_HANDLE *ProducerHandle OPTIONAL | |
) | |
{ | |
BOOLEAN StartPointFound; | |
LIST_ENTRY *Link; | |
LIST_ENTRY *Head; | |
SMBIOS_INSTANCE *Private; | |
EFI_SMBIOS_ENTRY *SmbiosEntry; | |
EFI_SMBIOS_TABLE_HEADER *SmbiosTableHeader; | |
if (SmbiosHandle == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
StartPointFound = FALSE; | |
Private = SMBIOS_INSTANCE_FROM_THIS (This); | |
Head = &Private->DataListHead; | |
for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { | |
SmbiosEntry = SMBIOS_ENTRY_FROM_LINK (Link); | |
SmbiosTableHeader = (EFI_SMBIOS_TABLE_HEADER *)(SmbiosEntry->RecordHeader + 1); | |
// | |
// If SmbiosHandle is 0xFFFE, the first matched SMBIOS record handle will be returned | |
// | |
if (*SmbiosHandle == SMBIOS_HANDLE_PI_RESERVED) { | |
if ((Type != NULL) && (*Type != SmbiosTableHeader->Type)) { | |
continue; | |
} | |
*SmbiosHandle = SmbiosTableHeader->Handle; | |
*Record = SmbiosTableHeader; | |
if (ProducerHandle != NULL) { | |
*ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle; | |
} | |
return EFI_SUCCESS; | |
} | |
// | |
// Start this round search from the next SMBIOS handle | |
// | |
if (!StartPointFound && (*SmbiosHandle == SmbiosTableHeader->Handle)) { | |
StartPointFound = TRUE; | |
continue; | |
} | |
if (StartPointFound) { | |
if ((Type != NULL) && (*Type != SmbiosTableHeader->Type)) { | |
continue; | |
} | |
*SmbiosHandle = SmbiosTableHeader->Handle; | |
*Record = SmbiosTableHeader; | |
if (ProducerHandle != NULL) { | |
*ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle; | |
} | |
return EFI_SUCCESS; | |
} | |
} | |
*SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED; | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Allow the caller to discover all of the SMBIOS records. | |
@param This The EFI_SMBIOS_PROTOCOL instance. | |
@param CurrentSmbiosEntry On exit, points to the SMBIOS entry on the list which includes the returned SMBIOS record information. | |
If *CurrentSmbiosEntry is NULL on entry, then the first SMBIOS entry on the list will be returned. | |
@param Record On exit, points to the SMBIOS Record consisting of the formatted area followed by | |
the unformatted area. The unformatted area optionally contains text strings. | |
@retval EFI_SUCCESS SMBIOS record information was successfully returned in Record. | |
*CurrentSmbiosEntry points to the SMBIOS entry which includes the returned SMBIOS record information. | |
@retval EFI_NOT_FOUND There is no more SMBIOS entry. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetNextSmbiosRecord ( | |
IN CONST EFI_SMBIOS_PROTOCOL *This, | |
IN OUT EFI_SMBIOS_ENTRY **CurrentSmbiosEntry, | |
OUT EFI_SMBIOS_TABLE_HEADER **Record | |
) | |
{ | |
LIST_ENTRY *Link; | |
LIST_ENTRY *Head; | |
SMBIOS_INSTANCE *Private; | |
EFI_SMBIOS_ENTRY *SmbiosEntry; | |
EFI_SMBIOS_TABLE_HEADER *SmbiosTableHeader; | |
Private = SMBIOS_INSTANCE_FROM_THIS (This); | |
if (*CurrentSmbiosEntry == NULL) { | |
// | |
// Get the beginning of SMBIOS entry. | |
// | |
Head = &Private->DataListHead; | |
} else { | |
// | |
// Get previous SMBIOS entry and make it as start point. | |
// | |
Head = &(*CurrentSmbiosEntry)->Link; | |
} | |
Link = Head->ForwardLink; | |
if (Link == &Private->DataListHead) { | |
// | |
// If no more SMBIOS entry in the list, return not found. | |
// | |
return EFI_NOT_FOUND; | |
} | |
SmbiosEntry = SMBIOS_ENTRY_FROM_LINK (Link); | |
SmbiosTableHeader = (EFI_SMBIOS_TABLE_HEADER *)(SmbiosEntry->RecordHeader + 1); | |
*Record = SmbiosTableHeader; | |
*CurrentSmbiosEntry = SmbiosEntry; | |
return EFI_SUCCESS; | |
} | |
/** | |
Assembles SMBIOS table from the SMBIOS protocol. Produce Table | |
Entry Point and return the pointer to it. | |
@param TableEntryPointStructure On exit, points to the SMBIOS entrypoint structure. | |
@retval EFI_SUCCESS Structure created sucessfully. | |
@retval EFI_OUT_OF_RESOURCES No enough memory. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmbiosCreateTable ( | |
OUT VOID **TableEntryPointStructure | |
) | |
{ | |
UINT8 *BufferPointer; | |
UINTN RecordSize; | |
UINTN NumOfStr; | |
EFI_STATUS Status; | |
EFI_SMBIOS_HANDLE SmbiosHandle; | |
EFI_SMBIOS_PROTOCOL *SmbiosProtocol; | |
EFI_PHYSICAL_ADDRESS PhysicalAddress; | |
EFI_SMBIOS_TABLE_HEADER *SmbiosRecord; | |
EFI_SMBIOS_TABLE_END_STRUCTURE EndStructure; | |
EFI_SMBIOS_ENTRY *CurrentSmbiosEntry; | |
Status = EFI_SUCCESS; | |
BufferPointer = NULL; | |
if (EntryPointStructure == NULL) { | |
// | |
// Initialize the EntryPointStructure with initial values. | |
// It should be done only once. | |
// Allocate memory (below 4GB). | |
// | |
DEBUG ((DEBUG_INFO, "SmbiosCreateTable: Initialize 32-bit entry point structure\n")); | |
EntryPointStructureData.MajorVersion = mPrivateData.Smbios.MajorVersion; | |
EntryPointStructureData.MinorVersion = mPrivateData.Smbios.MinorVersion; | |
EntryPointStructureData.SmbiosBcdRevision = 0; | |
if ((mPrivateData.Smbios.MajorVersion <= 9) && (mPrivateData.Smbios.MinorVersion <= 9)) { | |
EntryPointStructureData.SmbiosBcdRevision = ((mPrivateData.Smbios.MajorVersion & 0x0f) << 4) | (mPrivateData.Smbios.MinorVersion & 0x0f); | |
} | |
PhysicalAddress = 0xffffffff; | |
Status = gBS->AllocatePages ( | |
AllocateMaxAddress, | |
EfiRuntimeServicesData, | |
EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_ENTRY_POINT)), | |
&PhysicalAddress | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "SmbiosCreateTable () could not allocate EntryPointStructure < 4GB\n")); | |
Status = gBS->AllocatePages ( | |
AllocateAnyPages, | |
EfiRuntimeServicesData, | |
EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_ENTRY_POINT)), | |
&PhysicalAddress | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
} | |
EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *)(UINTN)PhysicalAddress; | |
CopyMem ( | |
EntryPointStructure, | |
&EntryPointStructureData, | |
sizeof (SMBIOS_TABLE_ENTRY_POINT) | |
); | |
} | |
// | |
// Get Smbios protocol to traverse SMBIOS records. | |
// | |
SmbiosProtocol = &mPrivateData.Smbios; | |
// | |
// Make some statistics about all the structures | |
// | |
EntryPointStructure->NumberOfSmbiosStructures = 0; | |
EntryPointStructure->TableLength = 0; | |
EntryPointStructure->MaxStructureSize = 0; | |
// | |
// Calculate EPS Table Length | |
// | |
CurrentSmbiosEntry = NULL; | |
do { | |
Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord); | |
if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios32BitTable)) { | |
GetSmbiosStructureSize (SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr); | |
// | |
// Record NumberOfSmbiosStructures, TableLength and MaxStructureSize | |
// | |
EntryPointStructure->NumberOfSmbiosStructures++; | |
EntryPointStructure->TableLength = (UINT16)(EntryPointStructure->TableLength + RecordSize); | |
if (RecordSize > EntryPointStructure->MaxStructureSize) { | |
EntryPointStructure->MaxStructureSize = (UINT16)RecordSize; | |
} | |
} | |
} while (!EFI_ERROR (Status)); | |
// | |
// Create End-Of-Table structure | |
// | |
GetMaxSmbiosHandle (SmbiosProtocol, &SmbiosHandle); | |
EndStructure.Header.Type = SMBIOS_TYPE_END_OF_TABLE; | |
EndStructure.Header.Length = (UINT8)sizeof (EFI_SMBIOS_TABLE_HEADER); | |
EndStructure.Header.Handle = SmbiosHandle; | |
EndStructure.Tailing[0] = 0; | |
EndStructure.Tailing[1] = 0; | |
EntryPointStructure->NumberOfSmbiosStructures++; | |
EntryPointStructure->TableLength = (UINT16)(EntryPointStructure->TableLength + sizeof (EndStructure)); | |
if (sizeof (EndStructure) > EntryPointStructure->MaxStructureSize) { | |
EntryPointStructure->MaxStructureSize = (UINT16)sizeof (EndStructure); | |
} | |
if (EFI_SIZE_TO_PAGES ((UINT32)EntryPointStructure->TableLength) > mPreAllocatedPages) { | |
// | |
// If new SMBIOS table size exceeds the previous allocated page, | |
// it is time to re-allocate memory (below 4GB). | |
// | |
DEBUG (( | |
DEBUG_INFO, | |
"%a() re-allocate SMBIOS 32-bit table\n", | |
__func__ | |
)); | |
if (EntryPointStructure->TableAddress != 0) { | |
// | |
// Free the previous allocated page | |
// | |
FreePages ( | |
(VOID *)(UINTN)EntryPointStructure->TableAddress, | |
mPreAllocatedPages | |
); | |
EntryPointStructure->TableAddress = 0; | |
mPreAllocatedPages = 0; | |
} | |
PhysicalAddress = 0xffffffff; | |
Status = gBS->AllocatePages ( | |
AllocateMaxAddress, | |
EfiRuntimeServicesData, | |
EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength), | |
&PhysicalAddress | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "SmbiosCreateTable() could not allocate SMBIOS table < 4GB\n")); | |
EntryPointStructure->TableAddress = 0; | |
return EFI_OUT_OF_RESOURCES; | |
} else { | |
EntryPointStructure->TableAddress = (UINT32)PhysicalAddress; | |
mPreAllocatedPages = EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength); | |
} | |
} | |
// | |
// Assemble the tables | |
// | |
ASSERT (EntryPointStructure->TableAddress != 0); | |
BufferPointer = (UINT8 *)(UINTN)EntryPointStructure->TableAddress; | |
CurrentSmbiosEntry = NULL; | |
do { | |
Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord); | |
if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios32BitTable)) { | |
GetSmbiosStructureSize (SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr); | |
CopyMem (BufferPointer, SmbiosRecord, RecordSize); | |
BufferPointer = BufferPointer + RecordSize; | |
} | |
} while (!EFI_ERROR (Status)); | |
// | |
// Assemble End-Of-Table structure | |
// | |
CopyMem (BufferPointer, &EndStructure, sizeof (EndStructure)); | |
// | |
// Fixup checksums in the Entry Point Structure | |
// | |
EntryPointStructure->IntermediateChecksum = 0; | |
EntryPointStructure->EntryPointStructureChecksum = 0; | |
EntryPointStructure->IntermediateChecksum = | |
CalculateCheckSum8 ((UINT8 *)EntryPointStructure + 0x10, EntryPointStructure->EntryPointLength - 0x10); | |
EntryPointStructure->EntryPointStructureChecksum = | |
CalculateCheckSum8 ((UINT8 *)EntryPointStructure, EntryPointStructure->EntryPointLength); | |
// | |
// Returns the pointer | |
// | |
*TableEntryPointStructure = EntryPointStructure; | |
return EFI_SUCCESS; | |
} | |
/** | |
Assembles SMBIOS 64-bit table from the SMBIOS protocol. Produce Table | |
Entry Point and return the pointer to it. | |
@param TableEntryPointStructure On exit, points to the SMBIOS entrypoint structure. | |
@retval EFI_SUCCESS Structure created sucessfully. | |
@retval EFI_OUT_OF_RESOURCES No enough memory. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmbiosCreate64BitTable ( | |
OUT VOID **TableEntryPointStructure | |
) | |
{ | |
UINT8 *BufferPointer; | |
UINTN RecordSize; | |
UINTN NumOfStr; | |
EFI_STATUS Status; | |
EFI_SMBIOS_HANDLE SmbiosHandle; | |
EFI_SMBIOS_PROTOCOL *SmbiosProtocol; | |
EFI_PHYSICAL_ADDRESS PhysicalAddress; | |
EFI_SMBIOS_TABLE_HEADER *SmbiosRecord; | |
EFI_SMBIOS_TABLE_END_STRUCTURE EndStructure; | |
EFI_SMBIOS_ENTRY *CurrentSmbiosEntry; | |
Status = EFI_SUCCESS; | |
BufferPointer = NULL; | |
if (Smbios30EntryPointStructure == NULL) { | |
// | |
// Initialize the Smbios30EntryPointStructure with initial values. | |
// It should be done only once. | |
// Allocate memory at any address. | |
// | |
DEBUG ((DEBUG_INFO, "SmbiosCreateTable: Initialize 64-bit entry point structure\n")); | |
Smbios30EntryPointStructureData.MajorVersion = mPrivateData.Smbios.MajorVersion; | |
Smbios30EntryPointStructureData.MinorVersion = mPrivateData.Smbios.MinorVersion; | |
Smbios30EntryPointStructureData.DocRev = PcdGet8 (PcdSmbiosDocRev); | |
Status = gBS->AllocatePages ( | |
AllocateAnyPages, | |
EfiRuntimeServicesData, | |
EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_3_0_ENTRY_POINT)), | |
&PhysicalAddress | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "SmbiosCreate64BitTable() could not allocate Smbios30EntryPointStructure\n")); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Smbios30EntryPointStructure = (SMBIOS_TABLE_3_0_ENTRY_POINT *)(UINTN)PhysicalAddress; | |
CopyMem ( | |
Smbios30EntryPointStructure, | |
&Smbios30EntryPointStructureData, | |
sizeof (SMBIOS_TABLE_3_0_ENTRY_POINT) | |
); | |
} | |
// | |
// Get Smbios protocol to traverse SMBIOS records. | |
// | |
SmbiosProtocol = &mPrivateData.Smbios; | |
Smbios30EntryPointStructure->TableMaximumSize = 0; | |
// | |
// Calculate EPS Table Length | |
// | |
CurrentSmbiosEntry = NULL; | |
do { | |
Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord); | |
if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios64BitTable)) { | |
GetSmbiosStructureSize (SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr); | |
// | |
// Record TableMaximumSize | |
// | |
Smbios30EntryPointStructure->TableMaximumSize = (UINT32)(Smbios30EntryPointStructure->TableMaximumSize + RecordSize); | |
} | |
} while (!EFI_ERROR (Status)); | |
// | |
// Create End-Of-Table structure | |
// | |
GetMaxSmbiosHandle (SmbiosProtocol, &SmbiosHandle); | |
EndStructure.Header.Type = SMBIOS_TYPE_END_OF_TABLE; | |
EndStructure.Header.Length = (UINT8)sizeof (EFI_SMBIOS_TABLE_HEADER); | |
EndStructure.Header.Handle = SmbiosHandle; | |
EndStructure.Tailing[0] = 0; | |
EndStructure.Tailing[1] = 0; | |
Smbios30EntryPointStructure->TableMaximumSize = (UINT32)(Smbios30EntryPointStructure->TableMaximumSize + sizeof (EndStructure)); | |
if (EFI_SIZE_TO_PAGES (Smbios30EntryPointStructure->TableMaximumSize) > mPre64BitAllocatedPages) { | |
// | |
// If new SMBIOS table size exceeds the previous allocated page, | |
// it is time to re-allocate memory at anywhere. | |
// | |
DEBUG (( | |
DEBUG_INFO, | |
"%a() re-allocate SMBIOS 64-bit table\n", | |
__func__ | |
)); | |
if (Smbios30EntryPointStructure->TableAddress != 0) { | |
// | |
// Free the previous allocated page | |
// | |
FreePages ( | |
(VOID *)(UINTN)Smbios30EntryPointStructure->TableAddress, | |
mPre64BitAllocatedPages | |
); | |
Smbios30EntryPointStructure->TableAddress = 0; | |
mPre64BitAllocatedPages = 0; | |
} | |
Status = gBS->AllocatePages ( | |
AllocateAnyPages, | |
EfiRuntimeServicesData, | |
EFI_SIZE_TO_PAGES (Smbios30EntryPointStructure->TableMaximumSize), | |
&PhysicalAddress | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "SmbiosCreateTable() could not allocate SMBIOS 64-bit table\n")); | |
Smbios30EntryPointStructure->TableAddress = 0; | |
return EFI_OUT_OF_RESOURCES; | |
} else { | |
Smbios30EntryPointStructure->TableAddress = PhysicalAddress; | |
mPre64BitAllocatedPages = EFI_SIZE_TO_PAGES (Smbios30EntryPointStructure->TableMaximumSize); | |
} | |
} | |
// | |
// Assemble the tables | |
// | |
ASSERT (Smbios30EntryPointStructure->TableAddress != 0); | |
BufferPointer = (UINT8 *)(UINTN)Smbios30EntryPointStructure->TableAddress; | |
CurrentSmbiosEntry = NULL; | |
do { | |
Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord); | |
if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios64BitTable)) { | |
// | |
// This record can be added to 64-bit table | |
// | |
GetSmbiosStructureSize (SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr); | |
CopyMem (BufferPointer, SmbiosRecord, RecordSize); | |
BufferPointer = BufferPointer + RecordSize; | |
} | |
} while (!EFI_ERROR (Status)); | |
// | |
// Assemble End-Of-Table structure | |
// | |
CopyMem (BufferPointer, &EndStructure, sizeof (EndStructure)); | |
// | |
// Fixup checksums in the Entry Point Structure | |
// | |
Smbios30EntryPointStructure->EntryPointStructureChecksum = 0; | |
Smbios30EntryPointStructure->EntryPointStructureChecksum = | |
CalculateCheckSum8 ((UINT8 *)Smbios30EntryPointStructure, Smbios30EntryPointStructure->EntryPointLength); | |
// | |
// Returns the pointer | |
// | |
*TableEntryPointStructure = Smbios30EntryPointStructure; | |
return EFI_SUCCESS; | |
} | |
/** | |
Create Smbios Table and installs the Smbios Table to the System Table. | |
@param Smbios32BitTable The flag to update 32-bit table. | |
@param Smbios64BitTable The flag to update 64-bit table. | |
**/ | |
VOID | |
EFIAPI | |
SmbiosTableConstruction ( | |
BOOLEAN Smbios32BitTable, | |
BOOLEAN Smbios64BitTable | |
) | |
{ | |
UINT8 *Eps; | |
UINT8 *Eps64Bit; | |
EFI_STATUS Status; | |
if (Smbios32BitTable) { | |
Status = SmbiosCreateTable ((VOID **)&Eps); | |
if (!EFI_ERROR (Status)) { | |
gBS->InstallConfigurationTable (&gEfiSmbiosTableGuid, Eps); | |
} | |
} | |
if (Smbios64BitTable) { | |
Status = SmbiosCreate64BitTable ((VOID **)&Eps64Bit); | |
if (!EFI_ERROR (Status)) { | |
gBS->InstallConfigurationTable (&gEfiSmbios3TableGuid, Eps64Bit); | |
} | |
} | |
} | |
/** | |
Validates a SMBIOS 2.0 table entry point. | |
@param TableEntry The SmBios table entry to validate. | |
@param TableAddress On exit, point to the smbios table addres. | |
@param TableMaximumSize On exit, point to the maximum size of the table. | |
@retval TRUE SMBIOS table entry point is valid. | |
@retval FALSE SMBIOS table entry point is malformed. | |
**/ | |
STATIC | |
BOOLEAN | |
IsValidSmbios20Table ( | |
IN VOID *TableEntry, | |
OUT VOID **TableAddress, | |
OUT UINTN *TableMaximumSize, | |
OUT UINT8 *MajorVersion, | |
OUT UINT8 *MinorVersion | |
) | |
{ | |
UINT8 Checksum; | |
SMBIOS_TABLE_ENTRY_POINT *SmbiosTable; | |
SmbiosTable = (SMBIOS_TABLE_ENTRY_POINT *)TableEntry; | |
if (CompareMem (SmbiosTable->AnchorString, "_SM_", 4) != 0) { | |
return FALSE; | |
} | |
if (CompareMem (SmbiosTable->IntermediateAnchorString, "_DMI_", 5) != 0) { | |
return FALSE; | |
} | |
// | |
// The actual value of the EntryPointLength should be 1Fh. | |
// However, it was incorrectly stated in version 2.1 of smbios specification. | |
// Therefore, 0x1F and 0x1E are both accepted. | |
// | |
if ((SmbiosTable->EntryPointLength != 0x1E) && (SmbiosTable->EntryPointLength != sizeof (SMBIOS_TABLE_ENTRY_POINT))) { | |
return FALSE; | |
} | |
// | |
// MajorVersion should not be less than 2. | |
// | |
if (SmbiosTable->MajorVersion < 2) { | |
return FALSE; | |
} | |
*MajorVersion = SmbiosTable->MajorVersion; | |
*MinorVersion = SmbiosTable->MinorVersion; | |
// | |
// The whole struct check sum should be zero | |
// | |
Checksum = CalculateSum8 ( | |
(UINT8 *)SmbiosTable, | |
SmbiosTable->EntryPointLength | |
); | |
if (Checksum != 0) { | |
return FALSE; | |
} | |
// | |
// The Intermediate Entry Point Structure check sum should be zero. | |
// | |
Checksum = CalculateSum8 ( | |
(UINT8 *)SmbiosTable + OFFSET_OF (SMBIOS_TABLE_ENTRY_POINT, IntermediateAnchorString), | |
SmbiosTable->EntryPointLength - OFFSET_OF (SMBIOS_TABLE_ENTRY_POINT, IntermediateAnchorString) | |
); | |
if (Checksum != 0) { | |
return FALSE; | |
} | |
*TableAddress = (VOID *)(UINTN)SmbiosTable->TableAddress; | |
*TableMaximumSize = SmbiosTable->TableLength; | |
return TRUE; | |
} | |
/** | |
Validates a SMBIOS 3.0 table entry point. | |
@param TableEntry The SmBios table entry to validate. | |
@param TableAddress On exit, point to the smbios table addres. | |
@param TableMaximumSize On exit, point to the maximum size of the table. | |
@retval TRUE SMBIOS table entry point is valid. | |
@retval FALSE SMBIOS table entry point is malformed. | |
**/ | |
STATIC | |
BOOLEAN | |
IsValidSmbios30Table ( | |
IN VOID *TableEntry, | |
OUT VOID **TableAddress, | |
OUT UINTN *TableMaximumSize, | |
OUT UINT8 *MajorVersion, | |
OUT UINT8 *MinorVersion | |
) | |
{ | |
UINT8 Checksum; | |
SMBIOS_TABLE_3_0_ENTRY_POINT *SmbiosTable; | |
SmbiosTable = (SMBIOS_TABLE_3_0_ENTRY_POINT *)TableEntry; | |
if (CompareMem (SmbiosTable->AnchorString, "_SM3_", 5) != 0) { | |
return FALSE; | |
} | |
if (SmbiosTable->EntryPointLength < sizeof (SMBIOS_TABLE_3_0_ENTRY_POINT)) { | |
return FALSE; | |
} | |
if (SmbiosTable->MajorVersion < 3) { | |
return FALSE; | |
} | |
*MajorVersion = SmbiosTable->MajorVersion; | |
*MinorVersion = SmbiosTable->MinorVersion; | |
// | |
// The whole struct check sum should be zero | |
// | |
Checksum = CalculateSum8 ( | |
(UINT8 *)SmbiosTable, | |
SmbiosTable->EntryPointLength | |
); | |
if (Checksum != 0) { | |
return FALSE; | |
} | |
*TableAddress = (VOID *)(UINTN)SmbiosTable->TableAddress; | |
*TableMaximumSize = SmbiosTable->TableMaximumSize; | |
return TRUE; | |
} | |
/** | |
Parse an existing SMBIOS table and insert it using SmbiosAdd. | |
@param ImageHandle The EFI_HANDLE to this driver. | |
@param Smbios The SMBIOS table to parse. | |
@param Length The length of the SMBIOS table. | |
@retval EFI_SUCCESS SMBIOS table was parsed and installed. | |
@retval EFI_OUT_OF_RESOURCES Record was not added due to lack of system resources. | |
@retval EFI_INVALID_PARAMETER Smbios is not a correct smbios table | |
**/ | |
STATIC | |
EFI_STATUS | |
ParseAndAddExistingSmbiosTable ( | |
IN EFI_HANDLE ImageHandle, | |
IN SMBIOS_STRUCTURE_POINTER Smbios, | |
IN UINTN Length, | |
IN UINT8 MajorVersion, | |
IN UINT8 MinorVersion | |
) | |
{ | |
EFI_STATUS Status; | |
CHAR8 *String; | |
EFI_SMBIOS_HANDLE SmbiosHandle; | |
SMBIOS_STRUCTURE_POINTER SmbiosEnd; | |
mPrivateData.Smbios.MajorVersion = MajorVersion; | |
mPrivateData.Smbios.MinorVersion = MinorVersion; | |
SmbiosEnd.Raw = Smbios.Raw + Length; | |
if ((Smbios.Raw >= SmbiosEnd.Raw) || (Smbios.Raw == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
do { | |
// | |
// Make sure not to access memory beyond SmbiosEnd | |
// | |
if ((UINTN)(SmbiosEnd.Raw - Smbios.Raw) < sizeof (SMBIOS_STRUCTURE)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Check for end marker | |
// | |
if (Smbios.Hdr->Type == SMBIOS_TYPE_END_OF_TABLE) { | |
break; | |
} | |
// | |
// Make sure not to access memory beyond SmbiosEnd | |
// Each structure shall be terminated by a double-null (0000h). | |
// | |
if ((UINTN)(SmbiosEnd.Raw - Smbios.Raw) < (Smbios.Hdr->Length + 2U)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Install the table | |
// | |
SmbiosHandle = Smbios.Hdr->Handle; | |
Status = SmbiosAdd ( | |
&mPrivateData.Smbios, | |
ImageHandle, | |
&SmbiosHandle, | |
Smbios.Hdr | |
); | |
ASSERT_EFI_ERROR (Status); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Go to the next SMBIOS structure. Each SMBIOS structure may include 2 parts: | |
// 1. Formatted section; 2. Unformatted string section. So, 2 steps are needed | |
// to skip one SMBIOS structure. | |
// | |
// | |
// Step 1: Skip over formatted section. | |
// | |
String = (CHAR8 *)(Smbios.Raw + Smbios.Hdr->Length); | |
// | |
// Step 2: Skip over unformatted string section. | |
// | |
do { | |
// | |
// Each string is terminated with a NULL(00h) BYTE and the sets of strings | |
// is terminated with an additional NULL(00h) BYTE. | |
// | |
for ( ; *String != 0; String++) { | |
if ((UINTN)String >= (UINTN)SmbiosEnd.Raw - sizeof (UINT8)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
if (*(UINT8 *)++String == 0) { | |
// | |
// Pointer to the next SMBIOS structure. | |
// | |
Smbios.Raw = (UINT8 *)++String; | |
break; | |
} | |
} while (TRUE); | |
} while (Smbios.Raw < SmbiosEnd.Raw); | |
return EFI_SUCCESS; | |
} | |
/** | |
Retrieve SMBIOS from Hob. | |
@param ImageHandle Module's image handle | |
@retval EFI_SUCCESS Smbios from Hob is installed. | |
@return EFI_NOT_FOUND Not found Smbios from Hob. | |
@retval Other No Smbios from Hob is installed. | |
**/ | |
EFI_STATUS | |
RetrieveSmbiosFromHob ( | |
IN EFI_HANDLE ImageHandle | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
SMBIOS_STRUCTURE_POINTER Smbios; | |
EFI_HOB_GUID_TYPE *GuidHob; | |
UNIVERSAL_PAYLOAD_SMBIOS_TABLE *SmBiosTableAdress; | |
UNIVERSAL_PAYLOAD_GENERIC_HEADER *GenericHeader; | |
VOID *TableAddress; | |
UINTN TableMaximumSize; | |
UINT8 MajorVersion; | |
UINT8 MinorVersion; | |
Status = EFI_NOT_FOUND; | |
MajorVersion = 0; | |
MinorVersion = 0; | |
for (Index = 0; Index < ARRAY_SIZE (mIsSmbiosTableValid); Index++) { | |
GuidHob = GetFirstGuidHob (mIsSmbiosTableValid[Index].Guid); | |
if (GuidHob == NULL) { | |
continue; | |
} | |
GenericHeader = (UNIVERSAL_PAYLOAD_GENERIC_HEADER *)GET_GUID_HOB_DATA (GuidHob); | |
if ((sizeof (UNIVERSAL_PAYLOAD_GENERIC_HEADER) <= GET_GUID_HOB_DATA_SIZE (GuidHob)) && (GenericHeader->Length <= GET_GUID_HOB_DATA_SIZE (GuidHob))) { | |
if (GenericHeader->Revision == UNIVERSAL_PAYLOAD_SMBIOS_TABLE_REVISION) { | |
// | |
// UNIVERSAL_PAYLOAD_SMBIOS_TABLE structure is used when Revision equals to UNIVERSAL_PAYLOAD_SMBIOS_TABLE_REVISION | |
// | |
SmBiosTableAdress = (UNIVERSAL_PAYLOAD_SMBIOS_TABLE *)GET_GUID_HOB_DATA (GuidHob); | |
if (GenericHeader->Length >= UNIVERSAL_PAYLOAD_SIZEOF_THROUGH_FIELD (UNIVERSAL_PAYLOAD_SMBIOS_TABLE, SmBiosEntryPoint)) { | |
if (mIsSmbiosTableValid[Index].IsValid ((VOID *)(UINTN)SmBiosTableAdress->SmBiosEntryPoint, &TableAddress, &TableMaximumSize, &MajorVersion, &MinorVersion)) { | |
Smbios.Raw = TableAddress; | |
Status = ParseAndAddExistingSmbiosTable (ImageHandle, Smbios, TableMaximumSize, MajorVersion, MinorVersion); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "RetrieveSmbiosFromHob: Failed to parse preinstalled tables from Guid Hob\n")); | |
Status = EFI_UNSUPPORTED; | |
} else { | |
return EFI_SUCCESS; | |
} | |
} | |
} | |
} | |
} | |
} | |
return Status; | |
} | |
/** | |
Driver to produce Smbios protocol and pre-allocate 1 page for the final SMBIOS table. | |
@param ImageHandle Module's image handle | |
@param SystemTable Pointer of EFI_SYSTEM_TABLE | |
@retval EFI_SUCCESS Smbios protocol installed | |
@retval Other No protocol installed, unload driver. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmbiosDriverEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
mPrivateData.Signature = SMBIOS_INSTANCE_SIGNATURE; | |
mPrivateData.Smbios.Add = SmbiosAdd; | |
mPrivateData.Smbios.UpdateString = SmbiosUpdateString; | |
mPrivateData.Smbios.Remove = SmbiosRemove; | |
mPrivateData.Smbios.GetNext = SmbiosGetNext; | |
mPrivateData.Smbios.MajorVersion = (UINT8)(PcdGet16 (PcdSmbiosVersion) >> 8); | |
mPrivateData.Smbios.MinorVersion = (UINT8)(PcdGet16 (PcdSmbiosVersion) & 0x00ff); | |
InitializeListHead (&mPrivateData.DataListHead); | |
InitializeListHead (&mPrivateData.AllocatedHandleListHead); | |
EfiInitializeLock (&mPrivateData.DataLock, TPL_NOTIFY); | |
// | |
// Make a new handle and install the protocol | |
// | |
mPrivateData.Handle = NULL; | |
Status = gBS->InstallProtocolInterface ( | |
&mPrivateData.Handle, | |
&gEfiSmbiosProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mPrivateData.Smbios | |
); | |
RetrieveSmbiosFromHob (ImageHandle); | |
return Status; | |
} |