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