/** @file
AML Code Generation.
Copyright (c) 2020, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
/** Utility function to link a node when returning from a CodeGen function.
@param [in] Node Newly created node.
@param [in] ParentNode If provided, set ParentNode as the parent
of the node created.
@param [out] NewObjectNode If success, contains the created object node.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
LinkNode (
IN AML_OBJECT_NODE * Node,
IN AML_NODE_HEADER * ParentNode,
OUT AML_OBJECT_NODE ** NewObjectNode
)
{
EFI_STATUS Status;
if (NewObjectNode != NULL) {
*NewObjectNode = Node;
}
// Add RdNode as the last element.
if (ParentNode != NULL) {
Status = AmlVarListAddTail (ParentNode, (AML_NODE_HEADER*)Node);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
}
return EFI_SUCCESS;
}
/** AML code generation for DefinitionBlock.
Create a Root Node handle.
It is the caller's responsibility to free the allocated memory
with the AmlDeleteTree function.
AmlCodeGenDefinitionBlock (TableSignature, OemID, TableID, OEMRevision) is
equivalent to the following ASL code:
DefinitionBlock (AMLFileName, TableSignature, ComplianceRevision,
OemID, TableID, OEMRevision) {}
with the ComplianceRevision set to 2 and the AMLFileName is ignored.
@param[in] TableSignature 4-character ACPI signature.
Must be 'DSDT' or 'SSDT'.
@param[in] OemId 6-character string OEM identifier.
@param[in] OemTableId 8-character string OEM table identifier.
@param[in] OemRevision OEM revision number.
@param[out] NewRootNode Pointer to the root node representing a
Definition Block.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
**/
EFI_STATUS
EFIAPI
AmlCodeGenDefinitionBlock (
IN CONST CHAR8 * TableSignature,
IN CONST CHAR8 * OemId,
IN CONST CHAR8 * OemTableId,
IN UINT32 OemRevision,
OUT AML_ROOT_NODE ** NewRootNode
)
{
EFI_STATUS Status;
EFI_ACPI_DESCRIPTION_HEADER AcpiHeader;
if ((TableSignature == NULL) ||
(OemId == NULL) ||
(OemTableId == NULL) ||
(NewRootNode == NULL)) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
CopyMem (&AcpiHeader.Signature, TableSignature, 4);
AcpiHeader.Length = sizeof (EFI_ACPI_DESCRIPTION_HEADER);
AcpiHeader.Revision = 2;
CopyMem (&AcpiHeader.OemId, OemId, 6);
CopyMem (&AcpiHeader.OemTableId, OemTableId, 8);
AcpiHeader.OemRevision = OemRevision;
AcpiHeader.CreatorId = TABLE_GENERATOR_CREATOR_ID_ARM;
AcpiHeader.CreatorRevision = CREATE_REVISION (1, 0);
Status = AmlCreateRootNode (&AcpiHeader, NewRootNode);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** AML code generation for a String object node.
@param [in] String Pointer to a NULL terminated string.
@param [out] NewObjectNode If success, contains the created
String object node.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
**/
STATIC
EFI_STATUS
EFIAPI
AmlCodeGenString (
IN CHAR8 * String,
OUT AML_OBJECT_NODE ** NewObjectNode
)
{
EFI_STATUS Status;
AML_OBJECT_NODE * ObjectNode;
AML_DATA_NODE * DataNode;
if ((String == NULL) ||
(NewObjectNode == NULL)) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
ObjectNode = NULL;
DataNode = NULL;
Status = AmlCreateObjectNode (
AmlGetByteEncodingByOpCode (AML_STRING_PREFIX, 0),
0,
&ObjectNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
Status = AmlCreateDataNode (
EAmlNodeDataTypeString,
(UINT8*)String,
(UINT32)AsciiStrLen (String) + 1,
&DataNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler;
}
Status = AmlSetFixedArgument (
ObjectNode,
EAmlParseIndexTerm0,
(AML_NODE_HEADER*)DataNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
AmlDeleteTree ((AML_NODE_HEADER*)DataNode);
goto error_handler;
}
*NewObjectNode = ObjectNode;
return Status;
error_handler:
if (ObjectNode != NULL) {
AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
}
return Status;
}
/** AML code generation for an Integer object node.
@param [in] Integer Integer of the Integer object node.
@param [out] NewObjectNode If success, contains the created
Integer object node.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
**/
STATIC
EFI_STATUS
EFIAPI
AmlCodeGenInteger (
IN UINT64 Integer,
OUT AML_OBJECT_NODE ** NewObjectNode
)
{
EFI_STATUS Status;
INT8 ValueWidthDiff;
if (NewObjectNode == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Create an object node containing Zero.
Status = AmlCreateObjectNode (
AmlGetByteEncodingByOpCode (AML_ZERO_OP, 0),
0,
NewObjectNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Update the object node with integer value.
Status = AmlNodeSetIntegerValue (*NewObjectNode, Integer, &ValueWidthDiff);
if (EFI_ERROR (Status)) {
ASSERT (0);
AmlDeleteTree ((AML_NODE_HEADER*)*NewObjectNode);
}
return Status;
}
/** AML code generation for a Name object node.
@param [in] NameString The new variable name.
Must be a NULL-terminated ASL NameString
e.g.: "DEV0", "DV15.DEV0", etc.
This input string is copied.
@param [in] Object Object associated to the NameString.
@param [in] ParentNode If provided, set ParentNode as the parent
of the node created.
@param [out] NewObjectNode If success, contains the created node.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
**/
STATIC
EFI_STATUS
EFIAPI
AmlCodeGenName (
IN CONST CHAR8 * NameString,
IN AML_OBJECT_NODE * Object,
IN AML_NODE_HEADER * ParentNode, OPTIONAL
OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL
)
{
EFI_STATUS Status;
AML_OBJECT_NODE * ObjectNode;
AML_DATA_NODE * DataNode;
CHAR8 * AmlNameString;
UINT32 AmlNameStringSize;
if ((NameString == NULL) ||
(Object == NULL) ||
((ParentNode == NULL) && (NewObjectNode == NULL))) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
ObjectNode = NULL;
DataNode = NULL;
AmlNameString = NULL;
Status = ConvertAslNameToAmlName (NameString, &AmlNameString);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
Status = AmlGetNameStringSize (AmlNameString, &AmlNameStringSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler1;
}
Status = AmlCreateObjectNode (
AmlGetByteEncodingByOpCode (AML_NAME_OP, 0),
0,
&ObjectNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler1;
}
Status = AmlCreateDataNode (
EAmlNodeDataTypeNameString,
(UINT8*)AmlNameString,
AmlNameStringSize,
&DataNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler2;
}
Status = AmlSetFixedArgument (
ObjectNode,
EAmlParseIndexTerm0,
(AML_NODE_HEADER*)DataNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
AmlDeleteTree ((AML_NODE_HEADER*)DataNode);
goto error_handler2;
}
Status = AmlSetFixedArgument (
ObjectNode,
EAmlParseIndexTerm1,
(AML_NODE_HEADER*)Object
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler2;
}
Status = LinkNode (
ObjectNode,
ParentNode,
NewObjectNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler2;
}
// Free AmlNameString before returning as it is copied
// in the call to AmlCreateDataNode().
goto error_handler1;
error_handler2:
if (ObjectNode != NULL) {
AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
}
error_handler1:
if (AmlNameString != NULL) {
FreePool (AmlNameString);
}
return Status;
}
/** AML code generation for a Name object node, containing a String.
AmlCodeGenNameString ("_HID", "HID0000", ParentNode, NewObjectNode) is
equivalent of the following ASL code:
Name(_HID, "HID0000")
@param [in] NameString The new variable name.
Must be a NULL-terminated ASL NameString
e.g.: "DEV0", "DV15.DEV0", etc.
The input string is copied.
@param [in] String NULL terminated String to associate to the
NameString.
@param [in] ParentNode If provided, set ParentNode as the parent
of the node created.
@param [out] NewObjectNode If success, contains the created node.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
**/
EFI_STATUS
EFIAPI
AmlCodeGenNameString (
IN CONST CHAR8 * NameString,
IN CHAR8 * String,
IN AML_NODE_HEADER * ParentNode, OPTIONAL
OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL
)
{
EFI_STATUS Status;
AML_OBJECT_NODE * ObjectNode;
if ((NameString == NULL) ||
(String == NULL) ||
((ParentNode == NULL) && (NewObjectNode == NULL))) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Status = AmlCodeGenString (String, &ObjectNode);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
Status = AmlCodeGenName (
NameString,
ObjectNode,
ParentNode,
NewObjectNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
}
return Status;
}
/** AML code generation for a Name object node, containing an Integer.
AmlCodeGenNameInteger ("_UID", 1, ParentNode, NewObjectNode) is
equivalent of the following ASL code:
Name(_UID, One)
@param [in] NameString The new variable name.
Must be a NULL-terminated ASL NameString
e.g.: "DEV0", "DV15.DEV0", etc.
The input string is copied.
@param [in] Integer Integer to associate to the NameString.
@param [in] ParentNode If provided, set ParentNode as the parent
of the node created.
@param [out] NewObjectNode If success, contains the created node.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
**/
EFI_STATUS
EFIAPI
AmlCodeGenNameInteger (
IN CONST CHAR8 * NameString,
IN UINT64 Integer,
IN AML_NODE_HEADER * ParentNode, OPTIONAL
OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL
)
{
EFI_STATUS Status;
AML_OBJECT_NODE * ObjectNode;
if ((NameString == NULL) ||
((ParentNode == NULL) && (NewObjectNode == NULL))) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Status = AmlCodeGenInteger (Integer, &ObjectNode);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
Status = AmlCodeGenName (
NameString,
ObjectNode,
ParentNode,
NewObjectNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
}
return Status;
}
/** AML code generation for a Device object node.
AmlCodeGenDevice ("COM0", ParentNode, NewObjectNode) is
equivalent of the following ASL code:
Device(COM0) {}
@param [in] NameString The new Device's name.
Must be a NULL-terminated ASL NameString
e.g.: "DEV0", "DV15.DEV0", etc.
The input string is copied.
@param [in] ParentNode If provided, set ParentNode as the parent
of the node created.
@param [out] NewObjectNode If success, contains the created node.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
**/
EFI_STATUS
EFIAPI
AmlCodeGenDevice (
IN CONST CHAR8 * NameString,
IN AML_NODE_HEADER * ParentNode, OPTIONAL
OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL
)
{
EFI_STATUS Status;
AML_OBJECT_NODE * ObjectNode;
AML_DATA_NODE * DataNode;
CHAR8 * AmlNameString;
UINT32 AmlNameStringSize;
if ((NameString == NULL) ||
((ParentNode == NULL) && (NewObjectNode == NULL))) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
ObjectNode = NULL;
DataNode = NULL;
AmlNameString = NULL;
Status = ConvertAslNameToAmlName (NameString, &AmlNameString);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
Status = AmlGetNameStringSize (AmlNameString, &AmlNameStringSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler1;
}
Status = AmlCreateObjectNode (
AmlGetByteEncodingByOpCode (AML_EXT_OP, AML_EXT_DEVICE_OP),
AmlNameStringSize + AmlComputePkgLengthWidth (AmlNameStringSize),
&ObjectNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler1;
}
Status = AmlCreateDataNode (
EAmlNodeDataTypeNameString,
(UINT8*)AmlNameString,
AmlNameStringSize,
&DataNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler2;
}
Status = AmlSetFixedArgument (
ObjectNode,
EAmlParseIndexTerm0,
(AML_NODE_HEADER*)DataNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
AmlDeleteTree ((AML_NODE_HEADER*)DataNode);
goto error_handler2;
}
Status = LinkNode (
ObjectNode,
ParentNode,
NewObjectNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler2;
}
// Free AmlNameString before returning as it is copied
// in the call to AmlCreateDataNode().
goto error_handler1;
error_handler2:
if (ObjectNode != NULL) {
AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
}
error_handler1:
if (AmlNameString != NULL) {
FreePool (AmlNameString);
}
return Status;
}
/** AML code generation for a Scope object node.
AmlCodeGenScope ("_SB", ParentNode, NewObjectNode) is
equivalent of the following ASL code:
Scope(_SB) {}
@param [in] NameString The new Scope's name.
Must be a NULL-terminated ASL NameString
e.g.: "DEV0", "DV15.DEV0", etc.
The input string is copied.
@param [in] ParentNode If provided, set ParentNode as the parent
of the node created.
@param [out] NewObjectNode If success, contains the created node.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
**/
EFI_STATUS
EFIAPI
AmlCodeGenScope (
IN CONST CHAR8 * NameString,
IN AML_NODE_HEADER * ParentNode, OPTIONAL
OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL
)
{
EFI_STATUS Status;
AML_OBJECT_NODE * ObjectNode;
AML_DATA_NODE * DataNode;
CHAR8 * AmlNameString;
UINT32 AmlNameStringSize;
if ((NameString == NULL) ||
((ParentNode == NULL) && (NewObjectNode == NULL))) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
ObjectNode = NULL;
DataNode = NULL;
AmlNameString = NULL;
Status = ConvertAslNameToAmlName (NameString, &AmlNameString);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
Status = AmlGetNameStringSize (AmlNameString, &AmlNameStringSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler1;
}
Status = AmlCreateObjectNode (
AmlGetByteEncodingByOpCode (AML_SCOPE_OP, 0),
AmlNameStringSize + AmlComputePkgLengthWidth (AmlNameStringSize),
&ObjectNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler1;
}
Status = AmlCreateDataNode (
EAmlNodeDataTypeNameString,
(UINT8*)AmlNameString,
AmlNameStringSize,
&DataNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler2;
}
Status = AmlSetFixedArgument (
ObjectNode,
EAmlParseIndexTerm0,
(AML_NODE_HEADER*)DataNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
AmlDeleteTree ((AML_NODE_HEADER*)DataNode);
goto error_handler2;
}
Status = LinkNode (
ObjectNode,
ParentNode,
NewObjectNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler2;
}
// Free AmlNameString before returning as it is copied
// in the call to AmlCreateDataNode().
goto error_handler1;
error_handler2:
if (ObjectNode != NULL) {
AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
}
error_handler1:
if (AmlNameString != NULL) {
FreePool (AmlNameString);
}
return Status;
}