From c85ac5245c0c47836fb9b599caff0b949066a412 Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Wed, 5 Aug 2020 15:43:33 +0100 Subject: [PATCH] DynamicTablesPkg: AmlLib APIs AmlLib library implements an AML parser, AML tree interface, serialiser, code generator and other interfaces to generate Definition Block tables. The AmlLib APIs are a collection of interfaces that enable parsing, iterating, modifying, adding, and serialising AML data to generate a Definition Block table. The AmlLib APIs are declared in Include\AmlLib\AmlLib.h Signed-off-by: Pierre Gondois Signed-off-by: Sami Mujawar Reviewed-by: Alexei Fedorov --- .../Include/Library/AmlLib/AmlLib.h | 631 ++++++++++++++++++ .../Library/Common/AmlLib/Api/AmlApi.c | 382 +++++++++++ .../Library/Common/AmlLib/Api/AmlApiHelper.c | 219 ++++++ .../Library/Common/AmlLib/Api/AmlApiHelper.h | 93 +++ .../Common/AmlLib/Api/AmlResourceDataApi.c | 320 +++++++++ 5 files changed, 1645 insertions(+) create mode 100644 DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c diff --git a/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h b/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h new file mode 100644 index 0000000000..1dcb938614 --- /dev/null +++ b/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h @@ -0,0 +1,631 @@ +/** @file + AML Lib. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_LIB_H_ +#define AML_LIB_H_ + +/** + @mainpage Dynamic AML Generation + @{ + @par Summary + @{ + ACPI tables are categorized as data tables and definition block + tables. Dynamic Tables Framework currently supports generation of ACPI + data tables. Generation of definition block tables is difficult as these + tables are encoded in ACPI Machine Language (AML), which has a complex + grammar. + + Dynamic AML Generation is an extension to the Dynamic tables Framework. + One of the techniques used to simplify definition block generation is to + fixup a template SSDT table. + + Dynamic AML aims to provide a framework that allows fixing up of an ACPI + SSDT template with appropriate information about the hardware. + + This framework consists of an: + - AMLLib core that implements a rich set of interfaces to parse, traverse + and update AML data. + - AMLLib library APIs that provides interfaces to search and updates nodes + in the AML namespace. + @} + @} +*/ + +#include + +#ifndef AML_HANDLE + +/** Node handle. +*/ +typedef void* AML_NODE_HANDLE; + +/** Root Node handle. +*/ +typedef void* AML_ROOT_NODE_HANDLE; + +/** Object Node handle. +*/ +typedef void* AML_OBJECT_NODE_HANDLE; + +/** Data Node handle. +*/ +typedef void* AML_DATA_NODE_HANDLE; + +#endif // AML_HANDLE + +/** Parse the definition block. + + The function parses the whole AML blob. It starts with the ACPI DSDT/SSDT + header and then parses the AML bytestream. + A tree structure is returned via the RootPtr. + The tree must be deleted with the AmlDeleteTree function. + + @ingroup UserApis + + @param [in] DefinitionBlock Pointer to the definition block. + @param [out] RootPtr Pointer to the root node of the AML tree. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlParseDefinitionBlock ( + IN CONST EFI_ACPI_DESCRIPTION_HEADER * DefinitionBlock, + OUT AML_ROOT_NODE_HANDLE * RootPtr + ); + +/** Serialize an AML definition block. + + This functions allocates memory with the "AllocateZeroPool ()" + function. This memory is used to serialize the AML tree and is + returned in the Table. + + @ingroup UserApis + + @param [in] RootNode Root node of the tree. + @param [out] Table On return, hold the serialized + definition block. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlSerializeDefinitionBlock ( + IN AML_ROOT_NODE_HANDLE RootNode, + OUT EFI_ACPI_DESCRIPTION_HEADER ** Table + ); + +/** Clone a node and its children (clone a tree branch). + + The cloned branch returned is not attached to any tree. + + @ingroup UserApis + + @param [in] Node Pointer to a node. + Node is the head of the branch to clone. + @param [out] ClonedNode Pointer holding the head of the created cloned + branch. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCloneTree ( + IN AML_NODE_HANDLE Node, + OUT AML_NODE_HANDLE * ClonedNode + ); + +/** Delete a Node and its children. + + The Node must be removed from the tree first, + or must be the root node. + + @ingroup UserApis + + @param [in] Node Pointer to the node to delete. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeleteTree ( + IN AML_NODE_HANDLE Node + ); + +/** Detach the Node from the tree. + + The function will fail if the Node is in its parent's fixed + argument list. + The Node is not deleted. The deletion is done separately + from the removal. + + @ingroup UserApis + + @param [in] Node Pointer to a Node. + Must be a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDetachNode ( + IN AML_NODE_HANDLE Node + ); + +/** Find a node in the AML namespace, given an ASL path and a reference Node. + + - The AslPath can be an absolute path, or a relative path from the + reference Node; + - Node must be a root node or a namespace node; + - A root node is expected to be at the top of the tree. + + E.g.: + For the following AML namespace, with the ReferenceNode being the node with + the name "AAAA": + - the node with the name "BBBB" can be found by looking for the ASL + path "BBBB"; + - the root node can be found by looking for the ASL relative path "^", + or the absolute path "\\". + + AML namespace: + \ + \-AAAA <- ReferenceNode + \-BBBB + + @ingroup NameSpaceApis + + @param [in] ReferenceNode Reference node. + If a relative path is given, the + search is done from this node. If + an absolute path is given, the + search is done from the root node. + Must be a root node or an object + node which is part of the + namespace. + @param [in] AslPath ASL path to the searched node in + the namespace. An ASL path name is + NULL terminated. Can be a relative + or absolute path. + E.g.: "\\_SB.CLU0.CPU0" or "^CPU0" + @param [out] OutNode Pointer to the found node. + Contains NULL if not found. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of memory. +**/ +EFI_STATUS +EFIAPI +AmlFindNode ( + IN AML_NODE_HANDLE ReferenceNode, + IN CHAR8 * AslPath, + OUT AML_NODE_HANDLE * OutNode + ); + +/** + @defgroup UserApis User APIs + @{ + User APIs are implemented to ease most common actions that might be done + using the AmlLib. They allow to find specific objects like "_UID" or + "_CRS" and to update their value. It also shows what can be done using + AmlLib functions. + @} +*/ + +/** Update the name of a DeviceOp object node. + + @ingroup UserApis + + @param [in] DeviceOpNode Object node representing a Device. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + OpCode/SubOpCode. + DeviceOp object nodes are defined in ASL + using the "Device ()" function. + @param [in] NewNameString The new Device's name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeviceOpUpdateName ( + IN AML_OBJECT_NODE_HANDLE DeviceOpNode, + IN CHAR8 * NewNameString + ); + +/** Update an integer value defined by a NameOp object node. + + For compatibility reasons, the NameOpNode must initially + contain an integer. + + @ingroup UserApis + + @param [in] NameOpNode NameOp object node. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] NewInt New Integer value to assign. + Must be a UINT64. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpUpdateInteger ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN UINT64 NewInt + ); + +/** Update a string value defined by a NameOp object node. + + The NameOpNode must initially contain a string. + The EISAID ASL macro converts a string to an integer. This, it is + not accepted. + + @ingroup UserApis + + @param [in] NameOpNode NameOp object node. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] NewName New NULL terminated string to assign to + the NameOpNode. + The input string is copied. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpUpdateString ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CONST CHAR8 * NewName + ); + +/** Get the first Resource Data element contained in a "_CRS" object. + + In the following ASL code, the function will return the Resource Data + node corresponding to the "QWordMemory ()" ASL macro. + Name (_CRS, ResourceTemplate() { + QWordMemory (...) {...}, + Interrupt (...) {...} + } + ) + + Note: + - The "_CRS" object must be declared using ASL "Name (Declare Named Object)". + - "_CRS" declared using ASL "Method (Declare Control Method)" is not + supported. + + @ingroup UserApis + + @param [in] NameOpCrsNode NameOp object node defining a "_CRS" object. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [out] OutRdNode Pointer to the first Resource Data element of + the "_CRS" object. A Resource Data element + is stored in a data node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpCrsGetFirstRdNode ( + IN AML_OBJECT_NODE_HANDLE NameOpCrsNode, + OUT AML_DATA_NODE_HANDLE * OutRdNode + ); + +/** Get the Resource Data element following the CurrRdNode Resource Data. + + In the following ASL code, if CurrRdNode corresponds to the first + "QWordMemory ()" ASL macro, the function will return the Resource Data + node corresponding to the "Interrupt ()" ASL macro. + Name (_CRS, ResourceTemplate() { + QwordMemory (...) {...}, + Interrupt (...) {...} + } + ) + + The CurrRdNode Resource Data node must be defined in an object named "_CRS" + and defined by a "Name ()" ASL function. + + @ingroup UserApis + + @param [in] CurrRdNode Pointer to the current Resource Data element of + the "_CRS" variable. + @param [out] OutRdNode Pointer to the Resource Data element following + the CurrRdNode. + Contain a NULL pointer if CurrRdNode is the + last Resource Data element in the list. + The "End Tag" is not considered as a resource + data element and is not returned. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpCrsGetNextRdNode ( + IN AML_DATA_NODE_HANDLE CurrRdNode, + OUT AML_DATA_NODE_HANDLE * OutRdNode + ); + +/** Update the first interrupt of an Interrupt resource data node. + + The flags of the Interrupt resource data are left unchanged. + + The InterruptRdNode corresponds to the Resource Data created by the + "Interrupt ()" ASL macro. It is an Extended Interrupt Resource Data. + See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + @ingroup UserApis + + @param [in] InterruptRdNode Pointer to the an extended interrupt + resource data node. + @param [in] Irq Interrupt value to update. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdInterrupt ( + IN AML_DATA_NODE_HANDLE InterruptRdNode, + IN UINT32 Irq + ); + +/** Update the base address and length of a QWord resource data node. + + @ingroup UserApis + + @param [in] QWordRdNode Pointer a QWord resource data + node. + @param [in] BaseAddress Base address. + @param [in] BaseAddressLength Base address length. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdQWord ( + IN AML_DATA_NODE_HANDLE QWordRdNode, + IN UINT64 BaseAddress, + IN UINT64 BaseAddressLength + ); + +/** Add an Interrupt Resource Data node. + + This function creates a Resource Data element corresponding to the + "Interrupt ()" ASL function, stores it in an AML Data Node. + + It then adds it after the input CurrRdNode in the list of resource data + element. + + The Resource Data effectively created is an Extended Interrupt Resource + Data. See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + The Extended Interrupt contains one single interrupt. + + This function allocates memory to create a data node. It is the caller's + responsibility to either: + - attach this node to an AML tree; + - delete this node. + + Note: The _CRS node must be defined using the ASL Name () function. + e.g. Name (_CRS, ResourceTemplate () { + ... + } + + @ingroup UserApis + + @param [in] NameOpCrsNode NameOp object node defining a "_CRS" object. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] ResourceConsumer The device consumes the specified interrupt + or produces it for use by a child device. + @param [in] EdgeTriggered The interrupt is edge triggered or + level triggered. + @param [in] ActiveLow The interrupt is active-high or active-low. + @param [in] Shared The interrupt can be shared with other + devices or not (Exclusive). + @param [in] IrqList Interrupt list. Must be non-NULL. + @param [in] IrqCount Interrupt count. Must be non-zero. + + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenCrsAddRdInterrupt ( + IN AML_OBJECT_NODE_HANDLE NameOpCrsNode, + IN BOOLEAN ResourceConsumer, + IN BOOLEAN EdgeTriggered, + IN BOOLEAN ActiveLow, + IN BOOLEAN Shared, + IN UINT32 * IrqList, + IN UINT8 IrqCount + ); + +/** 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. + + @ingroup CodeGenApis + + @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] DefinitionBlockTerm The ASL Term handle 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_HANDLE * NewRootNode + ); + +/** 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") + + @ingroup CodeGenApis + + @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_HANDLE ParentNode, OPTIONAL + OUT AML_OBJECT_NODE_HANDLE * NewObjectNode OPTIONAL + ); + +/** 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) + + @ingroup CodeGenApis + + @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_HANDLE ParentNode, OPTIONAL + OUT AML_OBJECT_NODE_HANDLE * NewObjectNode OPTIONAL + ); + +/** AML code generation for a Device object node. + + AmlCodeGenDevice ("COM0", ParentNode, NewObjectNode) is + equivalent of the following ASL code: + Device(COM0) {} + + @ingroup CodeGenApis + + @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_HANDLE ParentNode, OPTIONAL + OUT AML_OBJECT_NODE_HANDLE * NewObjectNode OPTIONAL + ); + +/** AML code generation for a Scope object node. + + AmlCodeGenScope ("_SB", ParentNode, NewObjectNode) is + equivalent of the following ASL code: + Scope(_SB) {} + + @ingroup CodeGenApis + + @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_HANDLE ParentNode, OPTIONAL + OUT AML_OBJECT_NODE_HANDLE * NewObjectNode OPTIONAL + ); + +#endif // AML_LIB_H_ diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c new file mode 100644 index 0000000000..fdf04acc62 --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c @@ -0,0 +1,382 @@ +/** @file + AML Api. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +/* Even though this file has access to the internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. Only the external node + handle types should be used, i.e. AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, + etc. + Indeed, the functions in the "Api" folder should be implemented only + using the "safe" functions available in the "Include" folder. This + makes the functions available in the "Api" folder easy to export. +*/ +#include + +#include +#include +#include +#include + +/** Update the name of a DeviceOp object node. + + @param [in] DeviceOpNode Object node representing a Device. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + OpCode/SubOpCode. + DeviceOp object nodes are defined in ASL + using the "Device ()" function. + @param [in] NewNameString The new Device's name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeviceOpUpdateName ( + IN AML_OBJECT_NODE_HANDLE DeviceOpNode, + IN CHAR8 * NewNameString + ) +{ + EFI_STATUS Status; + + AML_DATA_NODE_HANDLE DeviceNameDataNode; + CHAR8 * NewAmlNameString; + UINT32 NewAmlNameStringSize; + + // Check the input node is an object node. + if ((DeviceOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)DeviceOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (DeviceOpNode, AML_EXT_OP, AML_EXT_DEVICE_OP)) || + (NewNameString == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the Device's name, being a data node + // which is the 1st fixed argument (i.e. index 0). + DeviceNameDataNode = (AML_DATA_NODE_HANDLE)AmlGetFixedArgument ( + DeviceOpNode, + EAmlParseIndexTerm0 + ); + if ((DeviceNameDataNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)DeviceNameDataNode) != EAmlNodeData) || + (!AmlNodeHasDataType (DeviceNameDataNode, EAmlNodeDataTypeNameString))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = ConvertAslNameToAmlName (NewNameString, &NewAmlNameString); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlGetNameStringSize (NewAmlNameString, &NewAmlNameStringSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Update the Device's name node. + Status = AmlUpdateDataNode ( + DeviceNameDataNode, + EAmlNodeDataTypeNameString, + (UINT8*)NewAmlNameString, + NewAmlNameStringSize + ); + ASSERT_EFI_ERROR (Status); + +exit_handler: + FreePool (NewAmlNameString); + return Status; +} + +/** Update an integer value defined by a NameOp object node. + + For compatibility reasons, the NameOpNode must initially + contain an integer. + + @param [in] NameOpNode NameOp object node. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] NewInt New Integer value to assign. + Must be a UINT64. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpUpdateInteger ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN UINT64 NewInt + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE_HANDLE IntegerOpNode; + + if ((NameOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpNode, AML_NAME_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the Integer object node defined by the "Name ()" function: + // it must have an Integer OpCode (Byte/Word/DWord/QWord). + // It is the 2nd fixed argument (i.e. index 1) of the NameOp node. + // This can also be a ZeroOp or OneOp node. + IntegerOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument ( + NameOpNode, + EAmlParseIndexTerm1 + ); + if ((IntegerOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)IntegerOpNode) != EAmlNodeObject)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the Integer value. + Status = AmlUpdateInteger (IntegerOpNode, NewInt); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Update a string value defined by a NameOp object node. + + The NameOpNode must initially contain a string. + The EISAID ASL macro converts a string to an integer. This, it is + not accepted. + + @param [in] NameOpNode NameOp object node. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] NewName New NULL terminated string to assign to + the NameOpNode. + The input string is copied. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpUpdateString ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CONST CHAR8 * NewName + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE_HANDLE StringOpNode; + AML_DATA_NODE_HANDLE StringDataNode; + + if ((NameOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpNode, AML_NAME_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the String object node defined by the "Name ()" function: + // it must have a string OpCode. + // It is the 2nd fixed argument (i.e. index 1) of the NameOp node. + StringOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument ( + NameOpNode, + EAmlParseIndexTerm1 + ); + if ((StringOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)StringOpNode) != EAmlNodeObject)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the string data node. + // It is the 1st fixed argument (i.e. index 0) of the StringOpNode node. + StringDataNode = (AML_DATA_NODE_HANDLE)AmlGetFixedArgument ( + StringOpNode, + EAmlParseIndexTerm0 + ); + if ((StringDataNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)StringDataNode) != EAmlNodeData)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the string value. + Status = AmlUpdateDataNode ( + StringDataNode, + EAmlNodeDataTypeString, + (UINT8*)NewName, + (UINT32)AsciiStrLen (NewName) + 1 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Get the first Resource Data element contained in a "_CRS" object. + + In the following ASL code, the function will return the Resource Data + node corresponding to the "QWordMemory ()" ASL macro. + Name (_CRS, ResourceTemplate() { + QWordMemory (...) {...}, + Interrupt (...) {...} + } + ) + + Note: + - The "_CRS" object must be declared using ASL "Name (Declare Named Object)". + - "_CRS" declared using ASL "Method (Declare Control Method)" is not + supported. + + @param [in] NameOpCrsNode NameOp object node defining a "_CRS" object. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [out] OutRdNode Pointer to the first Resource Data element of + the "_CRS" object. A Resource Data element + is stored in a data node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpCrsGetFirstRdNode ( + IN AML_OBJECT_NODE_HANDLE NameOpCrsNode, + OUT AML_DATA_NODE_HANDLE * OutRdNode + ) +{ + AML_OBJECT_NODE_HANDLE BufferOpNode; + AML_DATA_NODE_HANDLE FirstRdNode; + + if ((NameOpCrsNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpCrsNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpCrsNode, AML_NAME_OP, 0)) || + (!AmlNameOpCompareName (NameOpCrsNode, "_CRS")) || + (OutRdNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = NULL; + + // Get the _CRS value which is represented as a BufferOp object node + // which is the 2nd fixed argument (i.e. index 1). + BufferOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument ( + NameOpCrsNode, + EAmlParseIndexTerm1 + ); + if ((BufferOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)BufferOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (BufferOpNode, AML_BUFFER_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the first Resource data node in the variable list of + // argument of the BufferOp node. + FirstRdNode = (AML_DATA_NODE_HANDLE)AmlGetNextVariableArgument ( + (AML_NODE_HANDLE)BufferOpNode, + NULL + ); + if ((FirstRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)FirstRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType (FirstRdNode, EAmlNodeDataTypeResourceData))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = FirstRdNode; + return EFI_SUCCESS; +} + +/** Get the Resource Data element following the CurrRdNode Resource Data. + + In the following ASL code, if CurrRdNode corresponds to the first + "QWordMemory ()" ASL macro, the function will return the Resource Data + node corresponding to the "Interrupt ()" ASL macro. + Name (_CRS, ResourceTemplate() { + QwordMemory (...) {...}, + Interrupt (...) {...} + } + ) + + The CurrRdNode Resource Data node must be defined in an object named "_CRS" + and defined by a "Name ()" ASL function. + + @param [in] CurrRdNode Pointer to the current Resource Data element of + the "_CRS" object. + @param [out] OutRdNode Pointer to the Resource Data element following + the CurrRdNode. + Contain a NULL pointer if CurrRdNode is the + last Resource Data element in the list. + The "End Tag" is not considered as a resource + data element and is not returned. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpCrsGetNextRdNode ( + IN AML_DATA_NODE_HANDLE CurrRdNode, + OUT AML_DATA_NODE_HANDLE * OutRdNode + ) +{ + AML_OBJECT_NODE_HANDLE NameOpCrsNode; + AML_OBJECT_NODE_HANDLE BufferOpNode; + + if ((CurrRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)CurrRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType (CurrRdNode, EAmlNodeDataTypeResourceData)) || + (OutRdNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = NULL; + + // The parent of the CurrRdNode must be a BufferOp node. + BufferOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetParent ( + (AML_NODE_HANDLE)CurrRdNode + ); + if ((BufferOpNode == NULL) || + (!AmlNodeHasOpCode (BufferOpNode, AML_BUFFER_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // The parent of the BufferOpNode must be a NameOp node. + NameOpCrsNode = (AML_OBJECT_NODE_HANDLE)AmlGetParent ( + (AML_NODE_HANDLE)BufferOpNode + ); + if ((NameOpCrsNode == NULL) || + (!AmlNodeHasOpCode (NameOpCrsNode, AML_NAME_OP, 0)) || + (!AmlNameOpCompareName (NameOpCrsNode, "_CRS"))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = (AML_DATA_NODE_HANDLE)AmlGetNextVariableArgument ( + (AML_NODE_HANDLE)BufferOpNode, + (AML_NODE_HANDLE)CurrRdNode + ); + + // If the Resource Data is an End Tag, return NULL. + if (AmlNodeHasRdDataType ( + *OutRdNode, + AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) { + *OutRdNode = NULL; + } + + return EFI_SUCCESS; +} diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c new file mode 100644 index 0000000000..9693f28b54 --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c @@ -0,0 +1,219 @@ +/** @file + AML Helper. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +/* Even though this file has access to the internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. Only the external node + handle types should be used, i.e. AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, + etc. + Indeed, the functions in the "Api" folder should be implemented only + using the "safe" functions available in the "Include" folder. This + makes the functions available in the "Api" folder easy to export. +*/ +#include + +#include +#include +#include + +/** Compare the NameString defined by the "Name ()" ASL function, + and stored in the NameOpNode, with the input NameString. + + An ASL NameString is expected to be NULL terminated, and can be composed + of NameSegs that have less that 4 chars, like "DEV". "DEV" will be expanded + as "DEV_". + + An AML NameString is not NULL terminated and is is only composed of + 4 chars long NameSegs. + + @param [in] NameOpNode NameOp object node defining a variable. + Must have an AML_NAME_OP/0 OpCode/SubOpCode. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] AslName ASL NameString to compare the NameOp's name with. + Must be NULL terminated. + + @retval TRUE If the AslName and the AmlName defined by the NameOp node + are similar. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNameOpCompareName ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CHAR8 * AslName + ) +{ + EFI_STATUS Status; + AML_DATA_NODE_HANDLE NameDataNode; + + CHAR8 * AmlName; + UINT32 AmlNameSize; + + BOOLEAN RetVal; + + if ((NameOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpNode, AML_NAME_OP, 0)) || + (AslName == NULL)) { + ASSERT (0); + return FALSE; + } + + // Get the NameOp name, being in a data node + // which is the first fixed argument (i.e. index 0). + NameDataNode = (AML_DATA_NODE_HANDLE)AmlGetFixedArgument ( + NameOpNode, + EAmlParseIndexTerm0 + ); + if ((NameDataNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameDataNode) != EAmlNodeData) || + (!AmlNodeHasDataType (NameDataNode, EAmlNodeDataTypeNameString))) { + ASSERT (0); + return FALSE; + } + + // Get the size of the name. + Status = AmlGetDataNodeBuffer (NameDataNode, NULL, &AmlNameSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Allocate memory to fetch the name. + AmlName = AllocateZeroPool (AmlNameSize); + if (AmlName == NULL) { + ASSERT (0); + return FALSE; + } + + // Fetch the name. + Status = AmlGetDataNodeBuffer (NameDataNode, (UINT8*)AmlName, &AmlNameSize); + if (EFI_ERROR (Status)) { + FreePool (AmlName); + ASSERT (0); + return FALSE; + } + + // Compare the input AslName and the AmlName stored in the NameOp node. + RetVal = CompareAmlWithAslNameString (AmlName, AslName); + + // Free the string buffer. + FreePool (AmlName); + return RetVal; +} + +/** Check whether ObjectNode has the input OpCode/SubOpcode couple. + + @param [in] ObjectNode Pointer to an object node. + @param [in] OpCode OpCode to check + @param [in] SubOpCode SubOpCode to check + + @retval TRUE The node is an object node and + the Opcode and SubOpCode match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasOpCode ( + IN AML_OBJECT_NODE_HANDLE ObjectNode, + IN UINT8 OpCode, + IN UINT8 SubOpCode + ) +{ + EFI_STATUS Status; + UINT8 NodeOpCode; + UINT8 NodeSubOpCode; + + // Get the Node information. + Status = AmlGetObjectNodeInfo ( + ObjectNode, + &NodeOpCode, + &NodeSubOpCode, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Check the OpCode and SubOpCode. + if ((OpCode != NodeOpCode) || + (SubOpCode != NodeSubOpCode)) { + return FALSE; + } + + return TRUE; +} + +/** Check whether DataNode has the input DataType. + + @param [in] DataNode Pointer to a data node. + @param [in] DataType DataType to check. + + @retval TRUE The node is a data node and + the DataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasDataType ( + IN AML_DATA_NODE_HANDLE DataNode, + IN EAML_NODE_DATA_TYPE DataType + ) +{ + EFI_STATUS Status; + EAML_NODE_DATA_TYPE NodeDataType; + + // Get the data type. + Status = AmlGetNodeDataType (DataNode, &NodeDataType); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Check the data type. + if (NodeDataType != DataType) { + return FALSE; + } + + return TRUE; +} + +/** Check whether RdNode has the input RdDataType. + + @param [in] RdNode Pointer to a data node. + @param [in] RdDataType DataType to check. + + @retval TRUE The node is a Resource Data node and + the RdDataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasRdDataType ( + IN AML_DATA_NODE_HANDLE RdNode, + IN AML_RD_HEADER RdDataType + ) +{ + EFI_STATUS Status; + AML_RD_HEADER NodeRdDataType; + + // Get the resource data type. + Status = AmlGetResourceDataType ( + RdNode, + &NodeRdDataType + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Check the RdDataType. + return AmlRdCompareDescId (&NodeRdDataType, RdDataType); +} diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h new file mode 100644 index 0000000000..9872adddc3 --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h @@ -0,0 +1,93 @@ +/** @file + AML Helper. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_HELPER_H_ +#define AML_HELPER_H_ + +#include +#include + +/** Compare the NameString defined by the "Name ()" ASL function, + and stored in the NameOpNode, with the input NameString. + + An ASL NameString is expected to be NULL terminated, and can be composed + of NameSegs that have less that 4 chars, like "DEV". "DEV" will be expanded + as "DEV_". + + An AML NameString is not NULL terminated and is is only composed of + 4 chars long NameSegs. + + @param [in] NameOpNode NameOp object node defining a variable. + Must have an AML_NAME_OP/0 OpCode/SubOpCode. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] AslName ASL NameString to compare the NameOp's name with. + Must be NULL terminated. + + @retval TRUE If the AslName and the AmlName defined by the NameOp node + are similar. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNameOpCompareName ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CHAR8 * AslName + ); + +/** Check whether ObjectNode has the input OpCode/SubOpcode couple. + + @param [in] ObjectNode Pointer to an object node. + @param [in] OpCode OpCode to check + @param [in] SubOpCode SubOpCode to check + + @retval TRUE The node is an object node and + the Opcode and SubOpCode match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasOpCode ( + IN AML_OBJECT_NODE_HANDLE ObjectNode, + IN UINT8 OpCode, + IN UINT8 SubOpCode + ); + +/** Check whether DataNode has the input DataType. + + @param [in] DataNode Pointer to a data node. + @param [in] DataType DataType to check. + + @retval TRUE The node is a data node and + the DataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasDataType ( + IN AML_DATA_NODE_HANDLE DataNode, + IN EAML_NODE_DATA_TYPE DataType + ); + +/** Check whether RdNode has the input RdDataType. + + @param [in] RdNode Pointer to a data node. + @param [in] RdDataType DataType to check. + + @retval TRUE The node is a Resource Data node and + the RdDataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasRdDataType ( + IN AML_DATA_NODE_HANDLE RdNode, + IN AML_RD_HEADER RdDataType + ); + +#endif // AML_HELPER_H_ diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c new file mode 100644 index 0000000000..913c8dcdb0 --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c @@ -0,0 +1,320 @@ +/** @file + AML Update Resource Data. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +/* Even though this file has access to the internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. Only the external node + handle types should be used, i.e. AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, + etc. + Indeed, the functions in the "Api" folder should be implemented only + using the "safe" functions available in the "Include" folder. This + makes the functions available in the "Api" folder easy to export. +*/ +#include + +#include +#include +#include +#include + +/** Update the first interrupt of an Interrupt resource data node. + + The flags of the Interrupt resource data are left unchanged. + + The InterruptRdNode corresponds to the Resource Data created by the + "Interrupt ()" ASL macro. It is an Extended Interrupt Resource Data. + See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + @param [in] InterruptRdNode Pointer to the an extended interrupt + resource data node. + @param [in] Irq Interrupt value to update. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdInterrupt ( + IN AML_DATA_NODE_HANDLE InterruptRdNode, + IN UINT32 Irq + ) +{ + EFI_STATUS Status; + UINT32 * FirstInterrupt; + UINT8 * QueryBuffer; + UINT32 QueryBufferSize; + + if ((InterruptRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)InterruptRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType ( + InterruptRdNode, + EAmlNodeDataTypeResourceData)) || + (!AmlNodeHasRdDataType ( + InterruptRdNode, + AML_RD_BUILD_LARGE_DESC_ID ( + ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME)))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + QueryBuffer = NULL; + + // Get the size of the InterruptRdNode buffer. + Status = AmlGetDataNodeBuffer ( + InterruptRdNode, + NULL, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Check the Buffer is large enough. + if (QueryBufferSize < sizeof (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Allocate a buffer to fetch the data. + QueryBuffer = AllocatePool (QueryBufferSize); + if (QueryBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Get the data. + Status = AmlGetDataNodeBuffer ( + InterruptRdNode, + QueryBuffer, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // Get the address of the first interrupt field. + FirstInterrupt = + ((EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR*)QueryBuffer)->InterruptNumber; + + *FirstInterrupt = Irq; + + // Update the InterruptRdNode buffer. + Status = AmlUpdateDataNode ( + InterruptRdNode, + EAmlNodeDataTypeResourceData, + QueryBuffer, + QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + +error_handler: + if (QueryBuffer != NULL) { + FreePool (QueryBuffer); + } + return Status; +} + +/** Update the interrupt list of an interrupt resource data node. + + The InterruptRdNode corresponds to the Resource Data created by the + "Interrupt ()" ASL function. It is an Extended Interrupt Resource Data. + See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + @param [in] InterruptRdNode Pointer to the an extended interrupt + resource data node. + @param [in] ResourceConsumer The device consumes the specified interrupt + or produces it for use by a child device. + @param [in] EdgeTriggered The interrupt is edge triggered or + level triggered. + @param [in] ActiveLow The interrupt is active-high or active-low. + @param [in] Shared The interrupt can be shared with other + devices or not (Exclusive). + @param [in] IrqList Interrupt list. Must be non-NULL. + @param [in] IrqCount Interrupt count. Must be non-zero. + + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdInterruptEx ( + IN AML_DATA_NODE_HANDLE InterruptRdNode, + IN BOOLEAN ResourceConsumer, + IN BOOLEAN EdgeTriggered, + IN BOOLEAN ActiveLow, + IN BOOLEAN Shared, + IN UINT32 * IrqList, + IN UINT8 IrqCount + ) +{ + EFI_STATUS Status; + + EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR * RdInterrupt; + UINT32 * FirstInterrupt; + UINT8 * UpdateBuffer; + UINT16 UpdateBufferSize; + + if ((InterruptRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)InterruptRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType ( + InterruptRdNode, + EAmlNodeDataTypeResourceData)) || + (!AmlNodeHasRdDataType ( + InterruptRdNode, + AML_RD_BUILD_LARGE_DESC_ID ( + ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME))) || + (IrqList == NULL) || + (IrqCount == 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + UpdateBuffer = NULL; + UpdateBufferSize = sizeof (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR) + + ((IrqCount - 1) * sizeof (UINT32)); + + // Allocate a buffer to update the data. + UpdateBuffer = AllocatePool (UpdateBufferSize); + if (UpdateBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Update the Resource Data information (structure size, interrupt count). + RdInterrupt = (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR*)UpdateBuffer; + RdInterrupt->Header.Header.Byte = + AML_RD_BUILD_LARGE_DESC_ID (ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME); + RdInterrupt->Header.Length = + UpdateBufferSize - sizeof (ACPI_LARGE_RESOURCE_HEADER); + RdInterrupt->InterruptTableLength = IrqCount; + RdInterrupt->InterruptVectorFlags = (ResourceConsumer ? BIT0 : 0) | + (EdgeTriggered ? BIT1 : 0) | + (ActiveLow ? BIT2 : 0) | + (Shared ? BIT3 : 0); + + // Get the address of the first interrupt field. + FirstInterrupt = + ((EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR*)UpdateBuffer)->InterruptNumber; + + // Copy the input list of interrupts. + CopyMem (FirstInterrupt, IrqList, (sizeof (UINT32) * IrqCount)); + + // Update the InterruptRdNode buffer. + Status = AmlUpdateDataNode ( + InterruptRdNode, + EAmlNodeDataTypeResourceData, + UpdateBuffer, + UpdateBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + + // Cleanup + FreePool (UpdateBuffer); + + return Status; +} + +/** Update the base address and length of a QWord resource data node. + + @param [in] QWordRdNode Pointer a QWord resource data + node. + @param [in] BaseAddress Base address. + @param [in] BaseAddressLength Base address length. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdQWord ( + IN AML_DATA_NODE_HANDLE QWordRdNode, + IN UINT64 BaseAddress, + IN UINT64 BaseAddressLength + ) +{ + EFI_STATUS Status; + EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR * RdQWord; + + UINT8 * QueryBuffer; + UINT32 QueryBufferSize; + + if ((QWordRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)QWordRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType (QWordRdNode, EAmlNodeDataTypeResourceData)) || + (!AmlNodeHasRdDataType ( + QWordRdNode, + AML_RD_BUILD_LARGE_DESC_ID ( + ACPI_LARGE_QWORD_ADDRESS_SPACE_DESCRIPTOR_NAME)))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the size of the QWordRdNode's buffer. + Status = AmlGetDataNodeBuffer ( + QWordRdNode, + NULL, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Allocate a buffer to fetch the data. + QueryBuffer = AllocatePool (QueryBufferSize); + if (QueryBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Get the data. + Status = AmlGetDataNodeBuffer ( + QWordRdNode, + QueryBuffer, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + RdQWord = (EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR*)QueryBuffer; + + // Update the Base Address and Length. + RdQWord->AddrRangeMin = BaseAddress; + RdQWord->AddrRangeMax = BaseAddress + BaseAddressLength - 1; + RdQWord->AddrLen = BaseAddressLength; + + // Update Base Address Resource Data node. + Status = AmlUpdateDataNode ( + QWordRdNode, + EAmlNodeDataTypeResourceData, + QueryBuffer, + QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + +error_handler: + if (QueryBuffer != NULL) { + FreePool (QueryBuffer); + } + return Status; +} -- 2.39.2