--- /dev/null
+/** @file\r
+ Dynamic Platform Info Repository\r
+\r
+ Copyright (c) 2021, Arm Limited. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+ @par Glossary:\r
+ - Cm or CM - Configuration Manager\r
+ - Obj or OBJ - Object\r
+**/\r
+\r
+#include <Protocol/ConfigurationManagerProtocol.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+\r
+#include "CmObjectTokenFixer.h"\r
+#include "DynamicPlatRepoInternal.h"\r
+#include "TokenGenerator.h"\r
+\r
+/** Allocate a CM_OBJ_NODE.\r
+\r
+ @param [in] CmObjDesc CmObj to wrap in a node.\r
+ All the fields of the CmObj (Data field included),\r
+ are copied.\r
+ @param [in] Token Token to assign to this CmObj/node.\r
+ @param [out] ObjNode Allocated ObjNode.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES An allocation has failed.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+AllocCmObjNode (\r
+ IN CONST CM_OBJ_DESCRIPTOR *CmObjDesc,\r
+ IN CM_OBJECT_TOKEN Token,\r
+ OUT CM_OBJ_NODE **ObjNode\r
+ )\r
+{\r
+ CM_OBJ_NODE *Node;\r
+ CM_OBJ_DESCRIPTOR *Desc;\r
+\r
+ if ((CmObjDesc == NULL) || (ObjNode == NULL)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Node = AllocateZeroPool (sizeof (CM_OBJ_NODE));\r
+ if (Node == NULL) {\r
+ ASSERT (0);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ // Initialise the list head.\r
+ InitializeListHead (&Node->Link);\r
+ Node->Token = Token;\r
+ Desc = &Node->CmObjDesc;\r
+ Desc->ObjectId = CmObjDesc->ObjectId;\r
+ Desc->Size = CmObjDesc->Size;\r
+ Desc->Count = CmObjDesc->Count;\r
+\r
+ // Allocate and copy the CmObject Data.\r
+ Desc->Data = AllocateCopyPool (CmObjDesc->Size, CmObjDesc->Data);\r
+ if (Desc->Data == NULL) {\r
+ FreePool (Node);\r
+ ASSERT (0);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ *ObjNode = Node;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Free a CM_OBJ_NODE.\r
+\r
+ @param [in] ObjNode ObjNode to free.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+FreeCmObjNode (\r
+ IN CM_OBJ_NODE *ObjNode\r
+ )\r
+{\r
+ CM_OBJ_DESCRIPTOR *Desc;\r
+\r
+ if (ObjNode == NULL) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Unlink Node\r
+ RemoveEntryList (&ObjNode->Link);\r
+\r
+ Desc = &ObjNode->CmObjDesc;\r
+ if (Desc->Data != NULL) {\r
+ FreePool (Desc->Data);\r
+ }\r
+\r
+ FreePool (ObjNode);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Add an object to the dynamic platform repository.\r
+\r
+ @param [in] This This dynamic platform repository.\r
+ @param [in] CmObjDesc CmObj to add. The data is copied.\r
+ @param [out] Token If not NULL, token allocated to this CmObj.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES An allocation has failed.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DynPlatRepoAddObject (\r
+ IN DYNAMIC_PLATFORM_REPOSITORY_INFO *This,\r
+ IN CONST CM_OBJ_DESCRIPTOR *CmObjDesc,\r
+ OUT CM_OBJECT_TOKEN *Token OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CM_OBJ_NODE *ObjNode;\r
+ CM_OBJECT_ID ArmNamespaceObjId;\r
+ CM_OBJECT_TOKEN NewToken;\r
+\r
+ // The dynamic repository must be able to receive objects.\r
+ if ((This == NULL) ||\r
+ (CmObjDesc == NULL) ||\r
+ (This->RepoState != DynRepoTransient))\r
+ {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Check the CmObjDesc:\r
+ // - only Arm objects are supported for now.\r
+ // - only EArmObjCmRef objects can be added as arrays.\r
+ ArmNamespaceObjId = GET_CM_OBJECT_ID (CmObjDesc->ObjectId);\r
+ if ((CmObjDesc->Size == 0) ||\r
+ (CmObjDesc->Count == 0) ||\r
+ (ArmNamespaceObjId >= EArmObjMax) ||\r
+ ((CmObjDesc->Count > 1) && (ArmNamespaceObjId != EArmObjCmRef)) ||\r
+ (GET_CM_NAMESPACE_ID (CmObjDesc->ObjectId) != EObjNameSpaceArm))\r
+ {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Generate a token.\r
+ NewToken = GenerateToken ();\r
+\r
+ // Create an ObjNode.\r
+ Status = AllocCmObjNode (CmObjDesc, NewToken, &ObjNode);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT (0);\r
+ return Status;\r
+ }\r
+\r
+ // Fixup self-token if necessary.\r
+ Status = FixupCmObjectSelfToken (&ObjNode->CmObjDesc, NewToken);\r
+ if (EFI_ERROR (Status)) {\r
+ FreeCmObjNode (ObjNode);\r
+ ASSERT (0);\r
+ return Status;\r
+ }\r
+\r
+ // Add to link list.\r
+ InsertTailList (&This->ArmCmObjList[ArmNamespaceObjId], &ObjNode->Link);\r
+ This->ObjectCount += 1;\r
+\r
+ if (Token != NULL) {\r
+ *Token = NewToken;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Group lists of CmObjNode from the ArmNameSpace to one array.\r
+\r
+ @param [in] This This dynamic platform repository.\r
+ @param [in] ArmObjIndex Index in EARM_OBJECT_ID\r
+ (must be < EArmObjMax).\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_BUFFER_TOO_SMALL Buffer too small.\r
+ @retval EFI_OUT_OF_RESOURCES An allocation has failed.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+GroupCmObjNodes (\r
+ IN DYNAMIC_PLATFORM_REPOSITORY_INFO *This,\r
+ IN UINT32 ArmObjIndex\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Count;\r
+ UINTN Size;\r
+ UINT32 CmObjId;\r
+ UINT8 *GroupedData;\r
+ UINT8 *Data;\r
+ CM_OBJ_DESCRIPTOR *CmObjDesc;\r
+ LIST_ENTRY *ListHead;\r
+ LIST_ENTRY *Link;\r
+\r
+ if ((This == NULL) ||\r
+ (ArmObjIndex >= EArmObjMax))\r
+ {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Count = 0;\r
+ Size = 0;\r
+ CmObjId = CREATE_CM_ARM_OBJECT_ID (ArmObjIndex);\r
+ ListHead = &This->ArmCmObjList[ArmObjIndex];\r
+ Link = GetFirstNode (ListHead);\r
+\r
+ // Compute the total count and size of the CmObj in the list.\r
+ while (Link != ListHead) {\r
+ CmObjDesc = &((CM_OBJ_NODE *)Link)->CmObjDesc;\r
+\r
+ if (CmObjDesc->ObjectId != CmObjId) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((CmObjDesc->Count != 1) && (ArmObjIndex != EArmObjCmRef)) {\r
+ // We expect each descriptor to contain an individual object.\r
+ // EArmObjCmRef objects are counted as groups, so +1 as well.\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Count++;\r
+ Size += CmObjDesc->Size;\r
+\r
+ // Next Link\r
+ Link = GetNextNode (ListHead, Link);\r
+ } // while\r
+\r
+ if (Count == 0) {\r
+ // No objects found.\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ GroupedData = AllocateZeroPool (Size);\r
+ if (GroupedData == NULL) {\r
+ ASSERT (0);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ // Copy the Object Data and add to the TokenMapper.\r
+ Data = GroupedData;\r
+ Link = GetFirstNode (ListHead);\r
+ while (Link != ListHead) {\r
+ CmObjDesc = &((CM_OBJ_NODE *)Link)->CmObjDesc;\r
+ CopyMem (Data, CmObjDesc->Data, CmObjDesc->Size);\r
+\r
+ // Add the object to the Token Mapper.\r
+ // Note: The CmObject Data field of objects in the Token Mapper point\r
+ // to the memory in the GroupedData array.\r
+ Status = TokenMapperAddObject (\r
+ &This->TokenMapper,\r
+ ((CM_OBJ_NODE *)Link)->Token,\r
+ CmObjDesc->ObjectId,\r
+ CmObjDesc->Size,\r
+ Data\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (GroupedData);\r
+ return Status;\r
+ }\r
+\r
+ Data += CmObjDesc->Size;\r
+ Link = GetNextNode (ListHead, Link);\r
+ } // while\r
+\r
+ CmObjDesc = &This->ArmCmObjArray[ArmObjIndex];\r
+ CmObjDesc->ObjectId = CmObjId;\r
+ CmObjDesc->Size = Size;\r
+ CmObjDesc->Count = Count;\r
+ CmObjDesc->Data = GroupedData;\r
+\r
+ return Status;\r
+}\r
+\r
+/** Finalise the dynamic repository.\r
+\r
+ Finalising means:\r
+ - Preventing any further objects from being added.\r
+ - Allowing to get objects from the dynamic repository\r
+ (not possible before a call to this function).\r
+\r
+ @param [in] This This dynamic platform repository.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_ALREADY_STARTED Instance already initialised.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_BUFFER_TOO_SMALL Buffer too small.\r
+ @retval EFI_OUT_OF_RESOURCES An allocation has failed.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DynamicPlatRepoFinalise (\r
+ IN DYNAMIC_PLATFORM_REPOSITORY_INFO *This\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN ArmObjIndex;\r
+\r
+ if ((This == NULL) ||\r
+ (This->RepoState != DynRepoTransient))\r
+ {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Prevent any further objects from being added.\r
+ This->RepoState = DynRepoFinalized;\r
+\r
+ // Initialise the token mapper.\r
+ Status = TokenMapperInitialise (&This->TokenMapper, This->ObjectCount);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT (0);\r
+ return Status;\r
+ }\r
+\r
+ // For each CM_OBJECT_ID:\r
+ // - Convert the list of nodes to an array\r
+ // (the array is wrapped in a CmObjDesc).\r
+ // - Add the Token/CmObj binding to the token mapper.\r
+ for (ArmObjIndex = 0; ArmObjIndex < EArmObjMax; ArmObjIndex++) {\r
+ Status = GroupCmObjNodes (This, ArmObjIndex);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT (0);\r
+ // Free the TokenMapper.\r
+ // Ignore the returned Status since we already failed.\r
+ TokenMapperShutdown (&This->TokenMapper);\r
+ return Status;\r
+ }\r
+ } // for\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Get a CmObj from the dynamic repository.\r
+\r
+ @param [in] This Pointer to the Dynamic Platform Repository.\r
+ @param [in] CmObjectId The Configuration Manager Object ID.\r
+ @param [in] Token An optional token identifying the object. If\r
+ unused this must be CM_NULL_TOKEN.\r
+ @param [in, out] CmObjDesc Pointer to the Configuration Manager Object\r
+ descriptor describing the requested Object.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_NOT_FOUND The required object information is not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DynamicPlatRepoGetObject (\r
+ IN DYNAMIC_PLATFORM_REPOSITORY_INFO *This,\r
+ IN CM_OBJECT_ID CmObjectId,\r
+ IN CM_OBJECT_TOKEN Token OPTIONAL,\r
+ IN OUT CM_OBJ_DESCRIPTOR *CmObjDesc\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CM_OBJ_DESCRIPTOR *Desc;\r
+ CM_OBJECT_ID ArmNamespaceObjId;\r
+\r
+ if ((This == NULL) ||\r
+ (CmObjDesc == NULL) ||\r
+ (This->RepoState != DynRepoFinalized))\r
+ {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ArmNamespaceObjId = GET_CM_OBJECT_ID (CmObjectId);\r
+ if (ArmNamespaceObjId >= EArmObjMax) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Token != CM_NULL_TOKEN) {\r
+ // Search in the Token Mapper and return the object.\r
+ Status = TokenMapperGetObject (\r
+ &This->TokenMapper,\r
+ Token,\r
+ CmObjectId,\r
+ CmObjDesc\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+ }\r
+\r
+ if (ArmNamespaceObjId == EArmObjCmRef) {\r
+ // EArmObjCmRef object must be requested using a valid token.\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Desc = &This->ArmCmObjArray[ArmNamespaceObjId];\r
+\r
+ // Nothing here.\r
+ if (Desc->Count == 0) {\r
+ return EFI_NOT_FOUND;\r
+ } else {\r
+ // Return the full array.\r
+ CmObjDesc->ObjectId = Desc->ObjectId;\r
+ CmObjDesc->Size = Desc->Size;\r
+ CmObjDesc->Data = Desc->Data;\r
+ CmObjDesc->Count = Desc->Count;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Initialize the dynamic platform repository.\r
+\r
+ @param [out] DynPlatRepo If success, contains the initialised dynamic\r
+ platform repository.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES An allocation has failed.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DynamicPlatRepoInit (\r
+ OUT DYNAMIC_PLATFORM_REPOSITORY_INFO **DynPlatRepo\r
+ )\r
+{\r
+ UINTN Index;\r
+ DYNAMIC_PLATFORM_REPOSITORY_INFO *Repo;\r
+\r
+ if (DynPlatRepo == NULL) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Repo = AllocateZeroPool (sizeof (DYNAMIC_PLATFORM_REPOSITORY_INFO));\r
+ if (Repo == NULL) {\r
+ ASSERT (0);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ // Initialise the CmObject List.\r
+ for (Index = 0; Index < EArmObjMax; Index++) {\r
+ InitializeListHead (&Repo->ArmCmObjList[Index]);\r
+ }\r
+\r
+ Repo->ObjectCount = 0;\r
+ Repo->RepoState = DynRepoTransient;\r
+\r
+ *DynPlatRepo = Repo;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Shutdown the dynamic platform repository.\r
+\r
+ Free all the memory allocated for the dynamic platform repository.\r
+\r
+ @param [in] DynPlatRepo The dynamic platform repository.\r
+\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_SUCCESS Success.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DynamicPlatRepoShutdown (\r
+ IN DYNAMIC_PLATFORM_REPOSITORY_INFO *DynPlatRepo\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 Index;\r
+ LIST_ENTRY *ListHead;\r
+ CM_OBJ_DESCRIPTOR *CmObjDesc;\r
+ VOID *Data;\r
+\r
+ if (DynPlatRepo == NULL) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Free the list of objects.\r
+ for (Index = 0; Index < EArmObjMax; Index++) {\r
+ // Free all the nodes with this object Id.\r
+ ListHead = &DynPlatRepo->ArmCmObjList[Index];\r
+ while (!IsListEmpty (ListHead)) {\r
+ FreeCmObjNode ((CM_OBJ_NODE *)GetFirstNode (ListHead));\r
+ } // while\r
+ } // for\r
+\r
+ // Free the arrays.\r
+ CmObjDesc = DynPlatRepo->ArmCmObjArray;\r
+ for (Index = 0; Index < EArmObjMax; Index++) {\r
+ Data = CmObjDesc[Index].Data;\r
+ if (Data != NULL) {\r
+ FreePool (Data);\r
+ }\r
+ } // for\r
+\r
+ // Free the TokenMapper\r
+ Status = TokenMapperShutdown (&DynPlatRepo->TokenMapper);\r
+ ASSERT_EFI_ERROR (Status);\r
+ FreePool (DynPlatRepo);\r
+ return Status;\r
+}\r