522 строки
14 KiB
C
522 строки
14 KiB
C
/** @file
|
|
Dynamic Platform Info Repository
|
|
|
|
Copyright (c) 2021, Arm Limited. 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 [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 = Size;
|
|
CmObjDesc->Count = 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, 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;
|
|
}
|