/** @file Dynamic Platform Info Repository Copyright (c) 2021, Arm Limited. All rights reserved.
Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @par Glossary: - Cm or CM - Configuration Manager - Obj or OBJ - Object **/ #include #include #include #include #include #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 [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, OUT CM_OBJECT_TOKEN *Token OPTIONAL ) { EFI_STATUS Status; CM_OBJ_NODE *ObjNode; CM_OBJECT_ID ArmNamespaceObjId; CM_OBJECT_TOKEN NewToken; // 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 are supported for now. // - only EArmObjCmRef objects can be added as arrays. ArmNamespaceObjId = GET_CM_OBJECT_ID (CmObjDesc->ObjectId); if ((CmObjDesc->Size == 0) || (CmObjDesc->Count == 0) || (ArmNamespaceObjId >= EArmObjMax) || ((CmObjDesc->Count > 1) && (ArmNamespaceObjId != EArmObjCmRef)) || (GET_CM_NAMESPACE_ID (CmObjDesc->ObjectId) != EObjNameSpaceArm)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Generate a token. NewToken = GenerateToken (); // Create an ObjNode. Status = AllocCmObjNode (CmObjDesc, NewToken, &ObjNode); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Fixup self-token if necessary. Status = FixupCmObjectSelfToken (&ObjNode->CmObjDesc, NewToken); if (EFI_ERROR (Status)) { FreeCmObjNode (ObjNode); ASSERT (0); return Status; } // Add to link list. InsertTailList (&This->ArmCmObjList[ArmNamespaceObjId], &ObjNode->Link); This->ObjectCount += 1; if (Token != NULL) { *Token = NewToken; } return EFI_SUCCESS; } /** Group lists of CmObjNode from the ArmNameSpace to one array. @param [in] This This dynamic platform repository. @param [in] ArmObjIndex Index in EARM_OBJECT_ID (must be < EArmObjMax). @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 UINT32 ArmObjIndex ) { EFI_STATUS Status; UINTN Count; UINTN Size; UINT32 CmObjId; UINT8 *GroupedData; UINT8 *Data; CM_OBJ_DESCRIPTOR *CmObjDesc; LIST_ENTRY *ListHead; LIST_ENTRY *Link; if ((This == NULL) || (ArmObjIndex >= EArmObjMax)) { ASSERT (0); return EFI_INVALID_PARAMETER; } Count = 0; Size = 0; CmObjId = CREATE_CM_ARM_OBJECT_ID (ArmObjIndex); ListHead = &This->ArmCmObjList[ArmObjIndex]; 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) && (ArmObjIndex != EArmObjCmRef)) { // We expect each descriptor to contain an individual object. // EArmObjCmRef 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 = &This->ArmCmObjArray[ArmObjIndex]; 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 ArmObjIndex; 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 (ArmObjIndex = 0; ArmObjIndex < EArmObjMax; ArmObjIndex++) { Status = GroupCmObjNodes (This, (UINT32)ArmObjIndex); if (EFI_ERROR (Status)) { ASSERT (0); // Free the TokenMapper. // Ignore the returned Status since we already failed. TokenMapperShutdown (&This->TokenMapper); return Status; } } // for return EFI_SUCCESS; } /** 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 ArmNamespaceObjId; if ((This == NULL) || (CmObjDesc == NULL) || (This->RepoState != DynRepoFinalized)) { ASSERT (0); return EFI_INVALID_PARAMETER; } ArmNamespaceObjId = GET_CM_OBJECT_ID (CmObjectId); if (ArmNamespaceObjId >= EArmObjMax) { 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; } if (ArmNamespaceObjId == EArmObjCmRef) { // EArmObjCmRef object must be requested using a valid token. ASSERT (0); return EFI_INVALID_PARAMETER; } Desc = &This->ArmCmObjArray[ArmNamespaceObjId]; // 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]); } Repo->ObjectCount = 0; Repo->RepoState = DynRepoTransient; *DynPlatRepo = Repo; return EFI_SUCCESS; } /** 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; UINT32 Index; LIST_ENTRY *ListHead; CM_OBJ_DESCRIPTOR *CmObjDesc; VOID *Data; if (DynPlatRepo == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } // 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 the TokenMapper Status = TokenMapperShutdown (&DynPlatRepo->TokenMapper); ASSERT_EFI_ERROR (Status); FreePool (DynPlatRepo); return Status; }