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