/** @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_AMLDATAOBJECTS_FILECODE
/*
Creates an allocated buffer with sized data and no Op Code
ByteData := 0x00 - 0xFF
WordData := ByteData[0:7] ByteData[8:15] // 0x0000-0xFFFF
DWordData := WordData[0:15] WordData[16:31] // 0x00000000-0xFFFFFFFF
QWordData := DWordData[0:31] DWordData[32:63] // 0x0000000000000000- 0xFFFFFFFFFFFFFFFF
Forces max integer size UINT64
Caller is responsible for freeing returned buffer.
@param[in] Integer - Integer value to encode
@param[in] IntegerSize - Size of integer in bytes
@param[out] ReturnData - Allocated DataBuffer with encoded integer
@param[out] ReturnDataSize - Size of ReturnData
@return EFI_SUCCESS - Successful completion
@return EFI_OUT_OF_RESOURCES - Failed to allocate ReturnDataBuffer
*/
EFI_STATUS
EFIAPI
InternalAmlSizedDataBuffer (
IN UINT64 Integer,
IN UINTN IntegerSize,
OUT VOID **ReturnData
)
{
UINT8 *Data;
if ((IntegerSize != sizeof (UINT8)) &&
(IntegerSize != sizeof (UINT16)) &&
(IntegerSize != sizeof (UINT32)) &&
(IntegerSize != sizeof (UINT64)))
{
DEBUG ((DEBUG_ERROR, "%a: ERROR: Incorrect integer size=%d requested.\n", __func__, IntegerSize));
return EFI_INVALID_PARAMETER;
}
if ((IntegerSize < sizeof (UINT64)) && (Integer >= LShiftU64 (1, IntegerSize * 8))) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Integer is larger than requestd size.\n", __func__));
return EFI_INVALID_PARAMETER;
}
// Max Data Size is 64 bit. Plus one Opcode byte
Data = AllocateZeroPool (sizeof (UINT64));
if (Data == NULL) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Integer Space Alloc Failed\n", __func__));
return EFI_OUT_OF_RESOURCES;
}
// Already established we only have supported sizes above
switch (IntegerSize) {
case sizeof (UINT8):
*(UINT8 *)Data = (UINT8)Integer;
break;
case sizeof (UINT16):
*(UINT16 *)Data = (UINT16)Integer;
break;
case sizeof (UINT32):
*(UINT32 *)Data = (UINT32)Integer;
break;
case sizeof (UINT64):
*(UINT64 *)Data = (UINT64)Integer;
break;
}
*ReturnData = (VOID *)Data;
return EFI_SUCCESS;
}
/*
Calculates the optimized integer value used by AmlOPDataInteger and others
Forces max integer size UINT64
@param[in] Integer - Integer value to encode
@param[out] ReturnData - Allocated DataBuffer with encoded integer
@param[out] ReturnDataSize - Size of ReturnData
@return EFI_SUCCESS - Successful completion
@return EFI_OUT_OF_RESOURCES - Failed to allocate ReturnDataBuffer
*/
EFI_STATUS
EFIAPI
InternalAmlDataIntegerBuffer (
IN UINT64 Integer,
OUT VOID **ReturnData,
OUT UINTN *ReturnDataSize
)
{
UINT8 *IntegerData;
UINTN IntegerDataSize;
UINT8 *Data = NULL;
UINTN DataSize;
// Max Data Size is 64 bit. Plus one Opcode byte
IntegerData = AllocateZeroPool (sizeof (UINT64) + 1);
if (IntegerData == NULL) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Integer Space Alloc Failed\n", __func__));
return EFI_OUT_OF_RESOURCES;
}
if (Integer == 0) {
// ZeroOp
IntegerDataSize = 1;
IntegerData[0] = AML_ZERO_OP;
} else if (Integer == 1) {
// OneOp
IntegerDataSize = 1;
IntegerData[0] = AML_ONE_OP;
} else if (Integer == (UINT64) ~0x0) {
// OnesOp
IntegerDataSize = 1;
IntegerData[0] = AML_ONES_OP;
} else {
if (Integer >= 0x100000000) {
// QWordConst
IntegerDataSize = sizeof (UINT64) + 1;
IntegerData[0] = AML_QWORD_PREFIX;
} else if (Integer >= 0x10000) {
// DWordConst
IntegerDataSize = sizeof (UINT32) + 1;
IntegerData[0] = AML_DWORD_PREFIX;
} else if (Integer >= 0x100) {
// WordConst
IntegerDataSize = sizeof (UINT16) + 1;
IntegerData[0] = AML_WORD_PREFIX;
} else {
// ByteConst
IntegerDataSize = sizeof (UINT8) + 1;
IntegerData[0] = AML_BYTE_PREFIX;
}
DataSize = IntegerDataSize - 1;
InternalAmlSizedDataBuffer (Integer, DataSize, (VOID **)&Data);
if (Data == NULL) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Integer Data Space Alloc Failed\n", __func__));
FreePool (IntegerData);
return EFI_OUT_OF_RESOURCES;
}
CopyMem (&IntegerData[1], Data, DataSize);
FreePool (Data);
}
// Reallocate the pool so size is exact
*ReturnData = (VOID *)IntegerData;
*ReturnDataSize = IntegerDataSize;
return EFI_SUCCESS;
}
/**
Creates an optimized integer object
Forces max integer size UINT64
ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | String |
ConstObj | RevisionOp | DefBuffer
DataObject := ComputationalData | DefPackage | DefVarPackage
DataRefObject := DataObject | ObjectReference | DDBHandle
ByteConst := BytePrefix ByteData
BytePrefix := 0x0A
WordConst := WordPrefix WordData
WordPrefix := 0x0B
DWordConst := DWordPrefix DWordData
DWordPrefix := 0x0C
QWordConst := QWordPrefix QWordData
QWordPrefix := 0x0E
ConstObj := ZeroOp | OneOp | OnesOp
ByteData := 0x00 - 0xFF
WordData := ByteData[0:7] ByteData[8:15]
// 0x0000-0xFFFF
DWordData := WordData[0:15] WordData[16:31]
// 0x00000000-0xFFFFFFFF
QWordData := DWordData[0:31] DWordData[32:63]
// 0x0000000000000000-0xFFFFFFFFFFFFFFFF
ZeroOp := 0x00
OneOp := 0x01
OnesOp := 0xFF
@param[in] Integer - Number to be optimized and encoded
@param[in,out] ListHead - Head of Linked List of all AML Objects
@return EFI_SUCCESS - Success
@return all others - Fail
**/
EFI_STATUS
EFIAPI
AmlOPDataInteger (
IN UINT64 Integer,
IN OUT LIST_ENTRY *ListHead
)
{
EFI_STATUS Status;
AML_OBJECT_INSTANCE *Object;
if (ListHead == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_DEVICE_ERROR;
Object = NULL;
Status = InternalAppendNewAmlObjectNoData (&Object, ListHead);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Start %a object\n", __func__, "DATA_INTEGER"));
goto Done;
}
Status = InternalAmlDataIntegerBuffer (
Integer,
(VOID **)&(Object->Data),
&(Object->DataSize)
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: ACPI Integer 0x%X object\n", __func__, Integer));
goto Done;
}
Object->Completed = TRUE;
Status = EFI_SUCCESS;
Done:
if (EFI_ERROR (Status)) {
InternalFreeAmlObject (&Object, ListHead);
}
return Status;
}
/**
Creates an Sized Data integer object for use in Buffer objects. Does not
include opcode.
ByteData := 0x00 - 0xFF
WordData := ByteData[0:7] ByteData[8:15]
// 0x0000-0xFFFF
DWordData := WordData[0:15] WordData[16:31]
// 0x00000000-0xFFFFFFFF
QWordData := DWordData[0:31] DWordData[32:63]
// 0x0000000000000000-0xFFFFFFFFFFFFFFFF
@param[in] Integer - Number to be optimized and encoded
@param[in,out] ListHead - Head of Linked List of all AML Objects
@return EFI_SUCCESS - Success
@return all others - Fail
**/
EFI_STATUS
EFIAPI
InternalAmlOPSizedData (
IN UINT64 Integer,
IN UINTN IntegerSize,
IN OUT LIST_ENTRY *ListHead
)
{
EFI_STATUS Status;
AML_OBJECT_INSTANCE *Object;
if (ListHead == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_DEVICE_ERROR;
Object = NULL;
Status = InternalAppendNewAmlObjectNoData (&Object, ListHead);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Start %a object\n", __func__, "SIZED_DATA_INTEGER"));
goto Done;
}
Object->DataSize = IntegerSize;
Status = InternalAmlSizedDataBuffer (
Integer,
Object->DataSize,
(VOID **)&(Object->Data)
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: ACPI Integer 0x%X object\n", __func__, Integer));
goto Done;
}
Object->Completed = TRUE;
Status = EFI_SUCCESS;
Done:
if (EFI_ERROR (Status)) {
InternalFreeAmlObject (&Object, ListHead);
}
return Status;
}
/**
Creates a ByteData integer object for use in Buffer objects. Does not
include opcode.
ByteData := 0x00 - 0xFF
@param[in] Integer - Number to be placed in object
@param[in,out] ListHead - Head of Linked List of all AML Objects
@return EFI_SUCCESS - Success
@return all others - Fail
**/
EFI_STATUS
EFIAPI
AmlOPByteData (
IN UINT8 Integer,
IN OUT LIST_ENTRY *ListHead
)
{
return InternalAmlOPSizedData (Integer, sizeof (UINT8), ListHead);
}
/**
Creates a WordData integer object for use in Buffer objects. Does not
include opcode.
WordData := 0x0000 - 0xFFFF
@param[in] Integer - Number to be placed in object
@param[in,out] ListHead - Head of Linked List of all AML Objects
@return EFI_SUCCESS - Success
@return all others - Fail
**/
EFI_STATUS
EFIAPI
AmlOPWordData (
IN UINT16 Integer,
IN OUT LIST_ENTRY *ListHead
)
{
return InternalAmlOPSizedData (Integer, sizeof (UINT16), ListHead);
}
/**
Creates a DWordData integer object for use in Buffer objects. Does not
include opcode.
DWordData := 0x00000000 - 0xFFFFFFFF
@param[in] Integer - Number to be placed in object
@param[in,out] ListHead - Head of Linked List of all AML Objects
@return EFI_SUCCESS - Success
@return all others - Fail
**/
EFI_STATUS
EFIAPI
AmlOPDWordData (
IN UINT32 Integer,
IN OUT LIST_ENTRY *ListHead
)
{
return InternalAmlOPSizedData (Integer, sizeof (UINT32), ListHead);
}
/**
Creates a QWordData integer object for use in Buffer objects. Does not
include opcode.
QWordData := 0x00000000_00000000 - 0xFFFFFFFF_FFFFFFFF
@param[in] Integer - Number to be placed in object
@param[in,out] ListHead - Head of Linked List of all AML Objects
@return EFI_SUCCESS - Success
@return all others - Fail
**/
EFI_STATUS
EFIAPI
AmlOPQWordData (
IN UINT64 Integer,
IN OUT LIST_ENTRY *ListHead
)
{
return InternalAmlOPSizedData (Integer, sizeof (UINT64), ListHead);
}
/**
Creates a data string object
ComputationalData := String
String := StringPrefix AsciiCharList NullChar
StringPrefix := 0x0D
AsciiCharList := Nothing |
AsciiChar := 0x01 - 0x7F
NullChar := 0x00
@param[in] String - String to be encoded
@param[in,out] ListHead - Head of Linked List of all AML Objects
@return EFI_SUCCESS - Success
@return all others - Fail
**/
EFI_STATUS
EFIAPI
AmlOPDataString (
IN CHAR8 *String,
IN OUT LIST_ENTRY *ListHead
)
{
EFI_STATUS Status;
AML_OBJECT_INSTANCE *Object;
UINT8 *Data;
UINTN DataSize;
UINTN Index;
if ((String == NULL) || (ListHead == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_DEVICE_ERROR;
Object = NULL;
// Validate all characters
DataSize = AsciiStrLen (String);
for (Index = 0; Index < DataSize; Index++) {
if (String[Index] < 0x01) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: Invalid character String[%d] : %a\n",
__func__,
Index,
String
));
return Status;
}
}
Status = InternalAppendNewAmlObjectNoData (&Object, ListHead);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Start %a object\n", __func__, String));
goto Done;
}
// AML_STRING_PREFIX + String + NULL Terminator
DataSize += 2;
Data = AllocatePool (DataSize);
if (Data == NULL) {
Status = EFI_OUT_OF_RESOURCES;
DEBUG ((
DEBUG_ERROR,
"%a: ERROR: String Space Allocation %a\n",
__func__,
String
));
goto Done;
}
Data[0] = AML_STRING_PREFIX;
CopyMem (&Data[1], String, DataSize - 1);
// DataString Complete, Put into Object
Object->Data = Data;
Object->DataSize = DataSize;
Object->Completed = TRUE;
Status = EFI_SUCCESS;
Done:
if (EFI_ERROR (Status)) {
InternalFreeAmlObject (&Object, ListHead);
}
return Status;
}
/**
Creates a data buffer AML object from an array
This will take the passed in buffer and generate an AML Object from that
buffer
@param[in] Buffer - Buffer to be placed in AML Object
@param[in] BufferSize - Size of Buffer to be copied into Object
@param[in,out] ListHead - Head of Linked List of all AML Objects
@return EFI_SUCCESS - Success
@return all others - Fail
**/
EFI_STATUS
EFIAPI
AmlOPDataBufferFromArray (
IN VOID *Buffer,
IN UINTN BufferSize,
IN OUT LIST_ENTRY *ListHead
)
{
EFI_STATUS Status;
AML_OBJECT_INSTANCE *Object;
if ((Buffer == NULL) || (BufferSize == 0) || (ListHead == NULL)) {
return EFI_INVALID_PARAMETER;
}
Object = NULL;
Status = InternalAppendNewAmlObjectNoData (&Object, ListHead);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Start Data Buffer object\n", __func__));
goto Done;
}
Object->Data = AllocatePool (BufferSize);
Object->DataSize = BufferSize;
if (Object->Data == NULL) {
Status = EFI_OUT_OF_RESOURCES;
DEBUG ((DEBUG_ERROR, "%a: ERROR: Data Buffer allocate failed\n", __func__));
goto Done;
}
CopyMem (Object->Data, Buffer, BufferSize);
Object->Completed = TRUE;
Status = EFI_SUCCESS;
Done:
if (EFI_ERROR (Status)) {
InternalFreeAmlObject (&Object, ListHead);
}
return Status;
}
/**
19.6.36 EISAID (EISA ID String To Integer Conversion Macro)
Syntax:
EISAID (EisaIdString) => DWordConst
Arguments:
The EisaIdString must be a String object of the form "UUUNNNN", where "U"
is an uppercase letter and "N" is a hexadecimal digit. No asterisks or other
characters are allowed in the string.
Description:
Converts EisaIdString, a 7-character text string argument, into its
corresponding 4-byte numeric EISA ID encoding. It can be used when declaring
IDs for devices that have EISA IDs.
Encoded EISA ID Definition - 32-bits
bits[15:0] - three character compressed ASCII EISA ID. *
bits[31:16] - binary number
* Compressed ASCII is 5 bits per character 0b00001 = 'A' 0b11010 = 'Z'
Example:
EISAID ("PNP0C09") // This is a valid invocation of the macro.
@param[in] String - EISA ID string.
@param[in,out] ListHead - Head of Linked List of all AML Objects
**/
EFI_STATUS
EFIAPI
AmlOPEisaId (
IN CHAR8 *String,
IN OUT LIST_ENTRY *ListHead
)
{
EFI_STATUS Status;
UINT32 EncodedEisaId;
UINT8 i;
EncodedEisaId = 0;
if ((String == NULL) || (ListHead == NULL)) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Invalid parameter, inputs cannot == NULL.\n", __func__));
return EFI_INVALID_PARAMETER;
}
if (AsciiStrLen (String) != 0x7) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Invalid length for 'String' parameter.\n", __func__));
return EFI_INVALID_PARAMETER;
}
//
// Verify String is formatted as "UUUNNNN".
//
for (i = 0; i <= 0x6; i++) {
//
// If first 3 characters are not uppercase alpha or last 4 characters are not hexadecimal
//
if (((i <= 0x2) && (!IS_ASCII_UPPER_ALPHA (String[i]))) ||
((i >= 0x3) && (!IS_ASCII_HEX_DIGIT (String[i]))))
{
DEBUG ((DEBUG_ERROR, "%a: ERROR: Invalid EISA ID string format!\n", __func__));
DEBUG ((DEBUG_ERROR, " Input String must be formatted as 'UUUNNNN'.\n"));
return EFI_INVALID_PARAMETER;
}
}
//
// Convert string to 4-byte EISA ID encoding.
// Ex: 'PNP0A03' encodes to '0x30AD041'
//
EncodedEisaId = ((((String[0] - AML_NAME_CHAR_A + 1) & 0x1f) << 10)
+ (((String[1] - AML_NAME_CHAR_A + 1) & 0x1f) << 5)
+ (((String[2] - AML_NAME_CHAR_A + 1) & 0x1f) << 0)
+ (UINT32)(AsciiStrHexToUint64 (&String[3]) << 16));
//
// Swap bytes of upper and lower WORD to format EISA ID with proper endian-ness.
//
EncodedEisaId = Swap4Bytes (EncodedEisaId);
//
// Insert DWordPrefix into list.
// Note: EncodedEisaId will always be 32-bits, resulting in DWordConst.
//
Status = AmlOPDataInteger (EncodedEisaId, ListHead);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: ERROR: Unable to create ACPI DWordConst from Encoded EISA ID.\n", __func__));
return Status;
}
return Status;
}