edk2/DynamicTablesPkg/Library/Common/MetadataHandlerLib/MetadataUid.c
Pierre Gondois d15e48853c DynamicTablesPkg: Add MetadataHandlerLib library
Built on top of the MetadataObjLib, this library aims to
provide functions for each METADATA_ID to:
- Generate new Metadata on the fly: the caller provides
  minimal information for a METADATA_ID, and the library
  generates the missing information.
- Validate all the Metadata objects for a METADATA_ID.
  For instance, _UID must be unique for a _HID/_CID/EISAID.

This patch also adds support for generation/validation of:
- UIDs:
For each EISAID or NameId, UIDs must be unique. The generation
if UIDs is done by a per-EISAID/NameId incrementing counter.
The validation of the Metadata consists in checking for the
uniqueness of the UID per EISAID/NameId.

- ProximityDomains:
Proximity Domain Ids are generated by a counter, starting
from 0.
The validation of the Metadata consists in checking for
the uniqueness of the proximity domain Ids.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
2025-06-11 12:24:35 +00:00

288 lines
7.6 KiB
C

/** @file
Metadata Proximity Domain handlers.
Copyright (c) 2025, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Base.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <ConfigurationManagerObject.h>
#include <Library/MetadataObjLib.h>
#include "MetadataHandler.h"
/** Uid Entry
One entry is allocated for each HID/CID/EISAID. Each entry
contains a counter used for the generation of UID values.
**/
typedef struct MetadataUidEntry {
LIST_ENTRY List;
/// _HID or _CID of the device (NULL-terminated string).
/// This provides a mean to uniquely identify a device type.
/// If not populated, EisaId must be set.
CHAR8 NameId[9];
/// EisaId of the device.
/// This provides a mean to uniquely identify a device type.
/// If not populated, NameId must be set.
UINT32 EisaId;
/// Current Id used for the generation of a HID/CID/EisaId.
UINT32 CurrId;
} METADATA_UID_ENTRY;
// List of METADATA_UID_ENTRY
STATIC LIST_ENTRY mUidList = INITIALIZE_LIST_HEAD_VARIABLE (mUidList);
/** Allocate a METADATA_UID_ENTRY.
@param [in] Metadata Metadata containing the NameId/EisaId to use.
@param [out] OutEntry Allocated Entry.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER A parameter is invalid.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
STATIC
EFI_STATUS
EFIAPI
AllocateUidEntry (
IN METADATA_OBJ_UID *Metadata,
OUT METADATA_UID_ENTRY **OutEntry
)
{
METADATA_UID_ENTRY *Entry;
if ((Metadata == NULL) || (OutEntry == NULL)) {
return EFI_INVALID_PARAMETER;
}
Entry = AllocateZeroPool (sizeof (METADATA_UID_ENTRY));
if (Entry == NULL) {
ASSERT (Entry != NULL);
return EFI_OUT_OF_RESOURCES;
}
InitializeListHead (&Entry->List);
Entry->CurrId = 0;
if (Metadata->NameId[0] != 0) {
AsciiStrCpyS (Entry->NameId, sizeof (Metadata->NameId), Metadata->NameId);
} else {
Entry->EisaId = Metadata->EisaId;
}
*OutEntry = Entry;
return EFI_SUCCESS;
}
/** Find a matching METADATA_UID_ENTRY.
If no Entry is found, allocate one.
@param [in] Metadata Metadata containing the NameId/EisaId to search.
@param [out] OutEntry Matching or allocated Entry.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER A parameter is invalid.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
STATIC
EFI_STATUS
EFIAPI
FindEntry (
IN METADATA_OBJ_UID *Metadata,
OUT METADATA_UID_ENTRY **OutEntry
)
{
EFI_STATUS Status;
LIST_ENTRY *Link;
METADATA_UID_ENTRY *Entry;
if ((Metadata == NULL) || (OutEntry == NULL)) {
ASSERT (Metadata != NULL);
ASSERT (OutEntry != NULL);
return EFI_INVALID_PARAMETER;
}
Link = GetNextNode (&mUidList, &mUidList);
while (Link != &mUidList) {
Entry = (METADATA_UID_ENTRY *)Link;
if (Entry->NameId[0] != 0) {
if (AsciiStrnCmp (Entry->NameId, Metadata->NameId, sizeof (Metadata->NameId)) == 0) {
break;
}
} else if (Metadata->EisaId != 0) {
if (Entry->EisaId == Metadata->EisaId) {
break;
}
} else {
DEBUG ((DEBUG_ERROR, "MetadatUid: Empty NameId and EisaId\n"));
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Link = GetNextNode (&mUidList, Link);
} // while
// No matching METADATA_UID_ENTRY found.
if (Link == &mUidList) {
Status = AllocateUidEntry (Metadata, &Entry);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
InsertTailList (&mUidList, &Entry->List);
}
*OutEntry = Entry;
return EFI_SUCCESS;
}
/** Query the MetadataObjLib for metadata matching the input (Type/Token).
If the metadata exists, return it.
Otherwise:
- Generate a new metadata object
- Add it to the MetadataObjLib
- return it
@param[in] Root Root of the Metadata information.
@param[in] Type METADATA_TYPE of the entry to generate.
@param[in] Token Token uniquely identifying an entry among other
objects with the input METADATA_TYPE.
@param[in] Context Optional context to use during the Metadata generation.
@param[in, out] Metadata On input, can contain METADATA_TYPE-specific information.
On output and if success, contains the generated
Metadata object.
@param[in] MetadataSize Size of the input Metadata.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER A parameter is invalid.
**/
EFI_STATUS
EFIAPI
MetadataGenerateUid (
IN METADATA_ROOT_HANDLE Root,
IN METADATA_TYPE Type,
IN CM_OBJECT_TOKEN Token,
IN VOID *Context,
IN OUT VOID *Metadata,
IN UINT32 MetadataSize
)
{
EFI_STATUS Status;
METADATA_OBJ_UID *Uid;
METADATA_UID_ENTRY *Entry;
if ((Type != MetadataTypeUid) ||
(Token == CM_NULL_TOKEN) ||
(Metadata == NULL))
{
ASSERT (Type == MetadataTypeUid);
ASSERT (Token != CM_NULL_TOKEN);
ASSERT (Metadata != NULL);
return EFI_INVALID_PARAMETER;
}
Status = MetadataGet (Root, Type, Token, Metadata, MetadataSize);
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
ASSERT_EFI_ERROR (Status);
return Status;
} else if (Status == EFI_SUCCESS) {
// Metadata for this (Type/Token) already exists.
return EFI_SUCCESS;
}
// Generate new Metadata (i.e. Status == EFI_NOT_FOUND).
// Get the Current Type for this HID/CID/EISAID
Status = FindEntry (Metadata, &Entry);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
Uid = (METADATA_OBJ_UID *)Metadata;
Uid->Uid = Entry->CurrId++;
Status = MetadataAdd (Root, Type, Token, Metadata, MetadataSize);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
return EFI_SUCCESS;
}
/** Validate the Metadata.
@param[in] Root Root of the Metadata information.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER A parameter is invalid.
**/
EFI_STATUS
EFIAPI
MetadataValidateUid (
IN METADATA_ROOT_HANDLE Root
)
{
METADATA_HANDLE Handle0;
METADATA_HANDLE Handle1;
METADATA_OBJ_UID Metadata0;
METADATA_OBJ_UID Metadata1;
Handle0 = NULL;
while (TRUE) {
Handle0 = MetadataIterate (
Root,
MetadataTypeUid,
Handle0,
&Metadata0,
sizeof (METADATA_OBJ_UID)
);
if (Handle0 == NULL) {
break;
}
// Loop starting from Handle0
Handle1 = Handle0;
while (TRUE) {
Handle1 = MetadataIterate (
Root,
MetadataTypeUid,
Handle1,
&Metadata1,
sizeof (METADATA_OBJ_UID)
);
if (Handle1 == NULL) {
break;
}
if (CompareMem (&Metadata0, &Metadata1, sizeof (METADATA_OBJ_UID)) == 0) {
DEBUG ((
DEBUG_ERROR,
"Metadata: Uid: Same Type: EisaId=%d NameId=%a Uid=%d \n",
Metadata0.EisaId,
Metadata0.NameId,
Metadata0.Uid
));
ASSERT (0);
}
}
}
return EFI_SUCCESS;
}