/** @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; }