/** @file
Copyright (C) 2020-2025 Advanced Micro Devices, Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "LocalAmlLib.h"
#include
#define FILECODE LIBRARY_DXEAMLGENERATIONLIB_AMLNAMESTRING_FILECODE
#define MAX_NAME_SEG_COUNT 255
/*
Is character a RootChar
@param[in] TestChar - Character to check
@return TRUE - Character is a RootChar
@return FALSE - Character is not a RootChar
*/
BOOLEAN
InternalIsRootChar (
IN CHAR8 TestChar
)
{
if (TestChar == AML_ROOT_CHAR) {
return TRUE;
}
return FALSE;
}
/*
Is character a ParentPrefixChar
@param[in] TestChar - Character to check
@return TRUE - Character is a ParentPrefixChar
@return FALSE - Character is not a ParentPrefixChar
*/
BOOLEAN
InternalIsParentPrefixChar (
IN CHAR8 TestChar
)
{
if (TestChar == AML_PARENT_PREFIX_CHAR) {
return TRUE;
}
return FALSE;
}
/*
Is character a LeadNameChar = '_', 'A' - 'Z'
@param[in] TestChar - Character to check
@return TRUE - Character is a LeadNameChar
@return FALSE - Character is not a LeadNameChar
*/
BOOLEAN
InternalIsLeadNameChar (
IN CHAR8 TestChar
)
{
if ( // Allowed LeadNameChars '_', 'A'-'Z'
(TestChar == AML_NAME_CHAR__) ||
((TestChar >= AML_NAME_CHAR_A) &&
(TestChar <= AML_NAME_CHAR_Z))
)
{
return TRUE;
}
return FALSE;
}
/*
Is character a DigitChar = '0' - '9'
@param[in] TestChar - Character to check
@return TRUE - Character is a DigitChar
@return FALSE - Character is not a DigitChar
*/
BOOLEAN
InternalIsDigitChar (
IN CHAR8 TestChar
)
{
if ( // Allowed DigitChars '0'-'9'
(TestChar >= AML_DIGIT_CHAR_0) &&
(TestChar <= AML_DIGIT_CHAR_9)
)
{
return TRUE;
}
return FALSE;
}
/*
Is character a NameChar = LeadNameChar | DigitChar
@param[in] TestChar - Character to check
@return TRUE - Character is a NameChar
@return FALSE - Character is not a NameChar
*/
BOOLEAN
InternalIsNameChar (
IN CHAR8 TestChar
)
{
if ( // Allowed LeadNameChar and DigitChars
InternalIsDigitChar (TestChar) ||
InternalIsLeadNameChar (TestChar)
)
{
return TRUE;
}
return FALSE;
}
/*
Is character a NameSeg separator
@param[in] TestChar - Character to check
@return TRUE - Character is a NameChar
@return FALSE - Character is not a NameChar
*/
BOOLEAN
InternalIsNameSegSeparator (
IN CHAR8 TestChar
)
{
if (TestChar == '.') {
return TRUE;
}
return FALSE;
}
/**
Creates a NameSeg AML object and inserts it into the List
NameSeg :=
NameSegs shorter than 4 characters are filled with trailing underscores
@param[in] Name - NameSeg
@param[in,out] ListHead - Linked list has NameSeg after call
@retval EFI_SUCCESS
@retval Error status
**/
EFI_STATUS
EFIAPI
InternalAmlNameSeg (
IN CHAR8 *Name,
IN OUT LIST_ENTRY *ListHead
)
{
AML_OBJECT_INSTANCE *Object;
UINT8 *NameSeg;
UINTN NameLen;
EFI_STATUS Status;
if (Name == NULL) {
return EFI_INVALID_PARAMETER;
}
NameLen = AsciiStrLen (Name);
Status = EFI_DEVICE_ERROR;
Object = NULL;
NameSeg = NULL;
// parameter validation
if ((NameLen == 0) || (NameLen > 4)) {
return EFI_INVALID_PARAMETER;
}
if (!InternalIsLeadNameChar (Name[0])) {
return EFI_INVALID_PARAMETER;
}
for (UINT8 i = 1; NameLen > 1 && i < NameLen; i++) {
if (!InternalIsNameChar (Name[i])) {
return EFI_INVALID_PARAMETER;
}
}
NameSeg = AllocateZeroPool (4);
if (NameSeg == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (NameSeg, Name, NameLen);
if (NameLen < 4) {
SetMem (&NameSeg[NameLen], 4 - NameLen, '_');
}
Status = InternalAppendNewAmlObjectNoData (&Object, ListHead);
if (Object != NULL) {
if (!EFI_ERROR (Status)) {
Object->Data = NameSeg;
Object->DataSize = 4;
Object->Completed = TRUE;
} else {
InternalFreeAmlObject (&Object, ListHead);
FreePool (NameSeg);
}
}
return Status;
}
/**
Creates a Namestring AML Object and inserts it into the linked list
LeadNameChar := 'A'-'Z' | '_'
DigitChar := '0'-'9'
NameChar := DigitChar | LeadNameChar
RootChar := '\'
ParentPrefixChar := '^'
'A'-'Z' := 0x41 - 0x5A
'_' := 0x5F
'0'-'9' := 0x30 - 0x39
'\' := 0x5C
'^' := 0x5E
NameSeg :=
// Notice that NameSegs shorter than 4 characters are filled with
// trailing underscores ('_'s).
NameString := |
PrefixPath := Nothing | <'^' PrefixPath>
NamePath := NameSeg | DualNamePath | MultiNamePath | NullName
DualNamePath := DualNamePrefix NameSeg NameSeg
DualNamePrefix := 0x2E
MultiNamePath := MultiNamePrefix SegCount NameSeg(SegCount)
MultiNamePrefix := 0x2F
SegCount := ByteData
Note:SegCount can be from 1 to 255. For example: MultiNamePrefix(35) is
encoded as 0x2f 0x23 and followed by 35 NameSegs. So, the total encoding
length will be 1 + 1 + 35*4 = 142. Notice that: DualNamePrefix NameSeg
NameSeg has a smaller encoding than the encoding of: MultiNamePrefix(2)
NameSeg NameSeg
SimpleName := NameString | ArgObj | LocalObj
SuperName := SimpleName | DebugObj | Type6Opcode
NullName := 0x00
Target := SuperName | NullName
@param[in] String - Null Terminated NameString Representation
@param[in,out] ListHead - Head of Linked List of all AML Objects
@return EFI_SUCCESS - Success
@return all others - Fail
**/
EFI_STATUS
EFIAPI
AmlOPNameString (
IN CHAR8 *String,
IN OUT LIST_ENTRY *ListHead
)
{
EFI_STATUS Status;
AML_OBJECT_INSTANCE *Object;
CHAR8 *NameString;
CHAR8 *NameStringPrefix;
UINTN NameStringBufferSize;
UINTN NameStringSize;
UINTN NameStringPrefixSize;
UINTN NameSegCount;
UINTN StringIndex;
UINTN StringLength;
UINTN NameSegIndex;
BOOLEAN FoundRootChar;
BOOLEAN FoundParentPrefixChar;
BOOLEAN FoundParenthesisOpenChar;
BOOLEAN FoundParenthesisCloseChar;
if ((String == NULL) || (ListHead == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_DEVICE_ERROR;
Object = NULL;
NameString = NULL;
FoundRootChar = FALSE;
FoundParentPrefixChar = FALSE;
NameStringBufferSize = 0;
FoundParenthesisOpenChar = FALSE;
FoundParenthesisCloseChar = FALSE;
Status = InternalAppendNewAmlObjectNoData (&Object, ListHead);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Start NameString %a object\n", __func__, String));
goto Done;
}
// Create a buffer to fit NameSeg [4] * max NameSegCount [255]
NameStringBufferSize = 4 * MAX_NAME_SEG_COUNT;
NameString = AllocateZeroPool (NameStringBufferSize);
// Create arbitrarily large RootChar\ParentPrefixChar buffer
NameStringPrefix = AllocateZeroPool (NameStringBufferSize);
// Calculate length of required space
StringLength = AsciiStrLen (String);
NameStringSize = 0;
NameStringPrefixSize = 0;
NameSegIndex = 0;
NameSegCount = 0;
for (StringIndex = 0; StringIndex < StringLength; StringIndex++) {
if (NameStringPrefixSize >= NameStringBufferSize) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: Exceeded ParentPrefixChar support at offset=%d of String=%a\n",
__func__,
StringIndex,
String
));
goto Done;
}
if (InternalIsRootChar (String[StringIndex])) {
if (NameSegCount != 0) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((DEBUG_ERROR, "%a: ERROR: RootChar at offset=%d of String=%a\n", __func__, StringIndex, String));
goto Done;
}
if (FoundRootChar) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((DEBUG_ERROR, "%a: ERROR: NameString=%a contains more than 1 RootChar.\n", __func__, String));
goto Done;
}
if (FoundParentPrefixChar) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((DEBUG_ERROR, "%a: ERROR: NameString=%a contains RootChar and ParentPrefixChar.\n", __func__, String));
goto Done;
}
// RootChar; increment NameStringSize
NameStringPrefix[NameStringPrefixSize] = String[StringIndex];
NameStringPrefixSize++;
FoundRootChar = TRUE;
} else if (InternalIsParentPrefixChar (String[StringIndex])) {
if (NameSegCount != 0) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((DEBUG_ERROR, "%a: ERROR: ParentPrefixChar at offset=%d of String=%a\n", __func__, StringIndex, String));
goto Done;
}
if (FoundRootChar) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((DEBUG_ERROR, "%a: ERROR: NameString=%a contains RootChar and ParentPrefixChar.\n", __func__, String));
goto Done;
}
// ParentPrefixChar; increment NameStringSize
NameStringPrefix[NameStringPrefixSize] = String[StringIndex];
NameStringPrefixSize++;
FoundParentPrefixChar = TRUE;
} else if (!InternalIsNameChar (String[StringIndex])) {
if (InternalIsNameSegSeparator (String[StringIndex])) {
if (NameSegIndex == 0) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: Invalid NameSeg separator at offset=%d of String=%a\n",
__func__,
StringIndex,
String
));
goto Done;
} else {
NameSegIndex = 0;
}
} else if (String[StringIndex] == '(') {
if (FoundParenthesisOpenChar) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: Invalid Parenthesis at offset=%d of String=%a\n",
__func__,
StringIndex,
String
));
goto Done;
}
FoundParenthesisOpenChar = TRUE;
} else if (String[StringIndex] == ')') {
if (FoundParenthesisCloseChar) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: Invalid Parenthesis at offset=%d of String=%a\n",
__func__,
StringIndex,
String
));
goto Done;
} else if (!FoundParenthesisOpenChar) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: No Open Parenthesis before offset=%d of String=%a\n",
__func__,
StringIndex,
String
));
goto Done;
}
FoundParenthesisCloseChar = TRUE;
} else {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: Unsupported character at offset=%d of String=%a\n",
__func__,
StringIndex,
String
));
goto Done;
}
} else {
// Must be NameChar
if (FoundParenthesisOpenChar || FoundParenthesisCloseChar) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: NameChar after Parenthesis at offset=%d of String=%a\n",
__func__,
StringIndex,
String
));
goto Done;
} else if ((NameSegIndex == 0) && InternalIsDigitChar (String[StringIndex])) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: must be LeadNameChar at offset=%d of String=%a'\n",
__func__,
StringIndex,
String
));
goto Done;
}
if (NameSegIndex >= 4) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: NameSeg > 4 characters at offset=%d of String=%a'\n",
__func__,
StringIndex,
String
));
goto Done;
} else {
if (NameSegIndex == 0) {
NameSegCount++;
if (NameSegCount > MAX_NAME_SEG_COUNT) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: Max NameSegCount=%d reached at offset=%d of String=%a'\n",
__func__,
MAX_NAME_SEG_COUNT,
StringIndex,
String
));
goto Done;
}
}
NameString[NameStringSize] = String[StringIndex];
NameStringSize++;
NameSegIndex++;
if ((StringIndex + 1 >= StringLength) ||
!InternalIsNameChar (String[StringIndex + 1]))
{
// Extend in progress NameSeg with '_'s
if (NameSegIndex < 4) {
SetMem (&NameString[NameStringSize], 4 - NameSegIndex, '_');
NameStringSize += 4 - NameSegIndex;
}
}
}
}
}
// Create AML Record with NameString contents from above
// Copy in RootChar or ParentPrefixChar(s)
if (NameStringPrefixSize != 0) {
Object->Data = ReallocatePool (
Object->DataSize,
NameStringPrefixSize,
Object->Data
);
CopyMem (
&Object->Data[Object->DataSize],
NameStringPrefix,
NameStringPrefixSize
);
Object->DataSize += NameStringPrefixSize;
FreePool (NameStringPrefix);
}
// Set up for Dual/MultiName Prefix
if (NameSegCount > MAX_NAME_SEG_COUNT) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((DEBUG_ERROR, "%a: ERROR: Exceeded MaxNameSegCount in NameString=%a\n", __func__, String));
goto Done;
} else if (NameSegCount == 0) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((DEBUG_ERROR, "%a: ERROR: Must be at least one NameSeg in NameString=%a\n", __func__, String));
goto Done;
} else if (NameSegCount == 1) {
// Single NameSeg
Object->Data = ReallocatePool (
Object->DataSize,
Object->DataSize + NameStringSize,
Object->Data
);
} else if (NameSegCount == 2) {
Object->Data = ReallocatePool (
Object->DataSize,
Object->DataSize + NameStringSize + 1,
Object->Data
);
Object->Data[Object->DataSize] = AML_DUAL_NAME_PREFIX;
Object->DataSize += 1;
} else {
Object->Data = ReallocatePool (
Object->DataSize,
Object->DataSize + NameStringSize + 2,
Object->Data
);
Object->Data[Object->DataSize] = AML_MULTI_NAME_PREFIX;
Object->Data[Object->DataSize + 1] = NameSegCount & 0xFF;
Object->DataSize += 2;
}
// Copy NameString data over. From above must be at least one NameSeg
CopyMem (&Object->Data[Object->DataSize], NameString, NameStringSize);
Object->DataSize += NameStringSize;
FreePool (NameString);
Object->Completed = TRUE;
Status = EFI_SUCCESS;
Done:
if (EFI_ERROR (Status)) {
InternalFreeAmlObject (&Object, ListHead);
if (NameString != NULL) {
FreePool (NameString);
}
}
return Status;
}