/*/@file | |
Hardware info parsing functions. | |
Binary data is expected as a consecutive series of header - object pairs. | |
Complete library providing list-like interface to dynamically manipulate | |
hardware info objects and parsing from a generic blob. | |
Copyright 2021 - 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Uefi/UefiBaseType.h> | |
#include <Uefi/UefiSpec.h> | |
#include <Library/DebugLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/UefiLib.h> | |
#include <Library/HardwareInfoLib.h> | |
EFI_STATUS | |
CreateHardwareInfoList ( | |
IN UINT8 *Blob, | |
IN UINTN BlobSize, | |
IN HARDWARE_INFO_TYPE TypeFilter, | |
OUT LIST_ENTRY *ListHead | |
) | |
{ | |
UINT8 *Index; | |
UINT8 *BlobEnd; | |
HARDWARE_INFO *HwComponent; | |
if ((Blob == NULL) || (BlobSize <= 0) || | |
(ListHead == NULL)) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
Index = Blob; | |
BlobEnd = Blob + BlobSize; | |
while (Index < BlobEnd) { | |
HwComponent = AllocateZeroPool (sizeof (HARDWARE_INFO)); | |
if (HwComponent == NULL) { | |
goto FailedAllocate; | |
} | |
HwComponent->Header.Type.Uint64 = *((UINT64 *)Index); | |
Index += sizeof (HwComponent->Header.Type); | |
HwComponent->Header.Size = *((UINT64 *)(Index)); | |
Index += sizeof (HwComponent->Header.Size); | |
if ((HwComponent->Header.Size > MAX_UINTN) || (Index < Blob) || ((Index + HwComponent->Header.Size) > BlobEnd)) { | |
goto FreeResources; | |
} | |
// | |
// Check if optional TypeFilter is set, skip if the current | |
// object is of a different type and release the partially | |
// allocated object | |
// | |
if ((TypeFilter != HardwareInfoTypeUndefined) && | |
(HwComponent->Header.Type.Value != TypeFilter)) | |
{ | |
FreePool (HwComponent); | |
Index += HwComponent->Header.Size; | |
continue; | |
} | |
HwComponent->Data.Raw = AllocateZeroPool ((UINTN)HwComponent->Header.Size); | |
if (HwComponent->Data.Raw == NULL) { | |
goto FreeResources; | |
} | |
CopyMem (HwComponent->Data.Raw, Index, (UINTN)HwComponent->Header.Size); | |
Index += HwComponent->Header.Size; | |
InsertTailList (ListHead, &HwComponent->Link); | |
} | |
return EFI_SUCCESS; | |
FreeResources: | |
// | |
// Clean the resources allocated in the incomplete cycle | |
// | |
FreePool (HwComponent); | |
FailedAllocate: | |
DEBUG (( | |
EFI_D_ERROR, | |
"%a: Failed to allocate memory for hardware info\n", | |
__func__ | |
)); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
VOID | |
FreeHardwareInfoList ( | |
IN OUT LIST_ENTRY *ListHead | |
) | |
{ | |
LIST_ENTRY *CurrentLink; | |
HARDWARE_INFO *HwComponent; | |
if (IsListEmpty (ListHead)) { | |
return; | |
} | |
CurrentLink = ListHead->ForwardLink; | |
while (CurrentLink != NULL && CurrentLink != ListHead) { | |
HwComponent = HARDWARE_INFO_FROM_LINK (CurrentLink); | |
// | |
// Remove item from list before invalidating the pointers | |
// | |
CurrentLink = RemoveEntryList (CurrentLink); | |
FreePool (HwComponent->Data.Raw); | |
FreePool (HwComponent); | |
} | |
} | |
/** | |
Validates if the specified Node has a valid data size and is of | |
specified type. | |
The data size can be less or equal to the provided type size to be | |
regarded as valid and thus accessible with the typed pointer. | |
For future compatibility the size is allowed to be smaller so that | |
different versions interpret fields differently and, particularly, | |
have smaller data structures. However, it cannot be larger than the | |
type size to avoid accessing memory out of bounds. | |
@param[in] Node Hardware Info node to be validated | |
@param[in] TypeSize Size (in bytes) of the data type intended to be | |
used to dereference the data. | |
@retval TRUE Node is valid and can be accessed | |
@retval FALSE Node is not valid | |
/*/ | |
STATIC | |
BOOLEAN | |
IsHardwareInfoNodeValidByType ( | |
IN LIST_ENTRY *ListHead, | |
IN LIST_ENTRY *Link, | |
IN HARDWARE_INFO_TYPE Type, | |
IN UINTN TypeSize | |
) | |
{ | |
HARDWARE_INFO *HwComponent; | |
if (IsNull (ListHead, Link)) { | |
return FALSE; | |
} | |
HwComponent = HARDWARE_INFO_FROM_LINK (Link); | |
// | |
// Verify if the node type is the specified one and the size of | |
// the data allocated to the node is greater than the size of | |
// the type intended to dereference it in order to avoid access | |
// to memory out of bondaries. | |
// | |
if ((HwComponent->Header.Type.Value == Type) && | |
(HwComponent->Header.Size >= TypeSize)) | |
{ | |
return TRUE; | |
} | |
return FALSE; | |
} | |
UINTN | |
GetHardwareInfoCountByType ( | |
IN LIST_ENTRY *ListHead, | |
IN HARDWARE_INFO_TYPE Type, | |
IN UINTN TypeSize | |
) | |
{ | |
UINTN Count; | |
LIST_ENTRY *Link; | |
Count = 0; | |
for (Link = GetFirstHardwareInfoByType (ListHead, Type, TypeSize); | |
!IsNull (ListHead, Link); | |
Link = GetNextHardwareInfoByType (ListHead, Link, Type, TypeSize)) | |
{ | |
if (IsHardwareInfoNodeValidByType (ListHead, Link, Type, TypeSize)) { | |
Count++; | |
} | |
} | |
return Count; | |
} | |
LIST_ENTRY * | |
GetFirstHardwareInfoByType ( | |
IN LIST_ENTRY *ListHead, | |
IN HARDWARE_INFO_TYPE Type, | |
IN UINTN TypeSize | |
) | |
{ | |
LIST_ENTRY *Link; | |
if (IsListEmpty (ListHead)) { | |
return ListHead; | |
} | |
Link = GetFirstNode (ListHead); | |
if (IsHardwareInfoNodeValidByType (ListHead, Link, Type, TypeSize)) { | |
return Link; | |
} | |
return GetNextHardwareInfoByType (ListHead, Link, Type, TypeSize); | |
} | |
LIST_ENTRY * | |
GetNextHardwareInfoByType ( | |
IN LIST_ENTRY *ListHead, | |
IN LIST_ENTRY *Node, | |
IN HARDWARE_INFO_TYPE Type, | |
IN UINTN TypeSize | |
) | |
{ | |
LIST_ENTRY *Link; | |
Link = GetNextNode (ListHead, Node); | |
while (!IsNull (ListHead, Link)) { | |
if (IsHardwareInfoNodeValidByType (ListHead, Link, Type, TypeSize)) { | |
// | |
// Found a node of specified type and with valid size. Break and | |
// return the found node. | |
// | |
break; | |
} | |
Link = GetNextNode (ListHead, Link); | |
} | |
return Link; | |
} | |
BOOLEAN | |
EndOfHardwareInfoList ( | |
IN LIST_ENTRY *ListHead, | |
IN LIST_ENTRY *Node | |
) | |
{ | |
return IsNull (ListHead, Node); | |
} |