--- /dev/null
+/** @file\r
+ AML Serialize.\r
+\r
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <AmlNodeDefines.h>\r
+\r
+#include <AmlCoreInterface.h>\r
+#include <Stream/AmlStream.h>\r
+#include <Tree/AmlNode.h>\r
+#include <Tree/AmlTree.h>\r
+#include <Utils/AmlUtility.h>\r
+\r
+/** Callback function to copy the AML bytecodes contained in a node\r
+ to the Stream stored in the Context.\r
+ The SDT header data contained in the root node is not serialized\r
+ by this function.\r
+\r
+ @param [in] Node Pointer to the node to copy the AML bytecodes\r
+ from.\r
+ @param [in, out] Context Contains a forward Stream to write to.\r
+ (AML_STREAM*)Context.\r
+ @param [in, out] Status At entry, contains the status returned by the\r
+ last call to this exact function during the\r
+ enumeration.\r
+ As exit, contains the returned status of the\r
+ call to this function.\r
+ Optional, can be NULL.\r
+\r
+ @retval TRUE if the enumeration can continue or has finished without\r
+ interruption.\r
+ @retval FALSE if the enumeration needs to stopped or has stopped.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+EFIAPI\r
+AmlSerializeNodeCallback (\r
+ IN AML_NODE_HEADER * Node,\r
+ IN OUT VOID * Context, OPTIONAL\r
+ IN OUT EFI_STATUS * Status OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status1;\r
+\r
+ CONST AML_DATA_NODE * DataNode;\r
+ CONST AML_OBJECT_NODE * ObjectNode;\r
+ AML_STREAM * FStream;\r
+\r
+ // Bytes needed to store OpCode[1] + SubOpcode[1] + MaxPkgLen[4] = 6 bytes.\r
+ UINT8 ObjectNodeInfoArray[6];\r
+ UINT32 Index;\r
+ BOOLEAN ContinueEnum;\r
+\r
+ CONST AML_OBJECT_NODE * ParentNode;\r
+ EAML_PARSE_INDEX IndexPtr;\r
+\r
+ if (!IS_AML_NODE_VALID (Node) ||\r
+ (Context == NULL)) {\r
+ ASSERT (0);\r
+ Status1 = EFI_INVALID_PARAMETER;\r
+ ContinueEnum = FALSE;\r
+ goto error_handler;\r
+ }\r
+\r
+ // Ignore the second fixed argument of method invocation nodes\r
+ // as the information stored there (the argument count) is not in the\r
+ // ACPI specification.\r
+ ParentNode = (CONST AML_OBJECT_NODE*)AmlGetParent ((AML_NODE_HEADER*)Node);\r
+ if (IS_AML_OBJECT_NODE (ParentNode) &&\r
+ AmlNodeCompareOpCode (ParentNode, AML_METHOD_INVOC_OP, 0) &&\r
+ AmlIsNodeFixedArgument (Node, &IndexPtr)) {\r
+ if (IndexPtr == EAmlParseIndexTerm1) {\r
+ if (Status != NULL) {\r
+ *Status = EFI_SUCCESS;\r
+ }\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ Status1 = EFI_SUCCESS;\r
+ ContinueEnum = TRUE;\r
+ FStream = (AML_STREAM*)Context;\r
+\r
+ if (IS_AML_DATA_NODE (Node)) {\r
+ // Copy the content of the Buffer for a DataNode.\r
+ DataNode = (AML_DATA_NODE*)Node;\r
+ Status1 = AmlStreamWrite (\r
+ FStream,\r
+ DataNode->Buffer,\r
+ DataNode->Size\r
+ );\r
+ if (EFI_ERROR (Status1)) {\r
+ ASSERT (0);\r
+ ContinueEnum = FALSE;\r
+ goto error_handler;\r
+ }\r
+\r
+ } else if (IS_AML_OBJECT_NODE (Node) &&\r
+ !AmlNodeHasAttribute (\r
+ (CONST AML_OBJECT_NODE*)Node,\r
+ AML_IS_PSEUDO_OPCODE)) {\r
+ // Ignore pseudo-opcodes as they are not part of the\r
+ // ACPI specification.\r
+\r
+ ObjectNode = (AML_OBJECT_NODE*)Node;\r
+\r
+ Index = 0;\r
+ // Copy the opcode(s).\r
+ ObjectNodeInfoArray[Index++] = ObjectNode->AmlByteEncoding->OpCode;\r
+ if (ObjectNode->AmlByteEncoding->OpCode == AML_EXT_OP) {\r
+ ObjectNodeInfoArray[Index++] = ObjectNode->AmlByteEncoding->SubOpCode;\r
+ }\r
+\r
+ // Copy the PkgLen.\r
+ if (AmlNodeHasAttribute (ObjectNode, AML_HAS_PKG_LENGTH)) {\r
+ Index += AmlSetPkgLength (\r
+ ObjectNode->PkgLen,\r
+ &ObjectNodeInfoArray[Index]\r
+ );\r
+ }\r
+\r
+ Status1 = AmlStreamWrite (\r
+ FStream,\r
+ ObjectNodeInfoArray,\r
+ Index\r
+ );\r
+ if (EFI_ERROR (Status1)) {\r
+ ASSERT (0);\r
+ ContinueEnum = FALSE;\r
+ goto error_handler;\r
+ }\r
+ } // IS_AML_OBJECT_NODE (Node)\r
+\r
+error_handler:\r
+ if (Status != NULL) {\r
+ *Status = Status1;\r
+ }\r
+ return ContinueEnum;\r
+}\r
+\r
+/** Serialize a tree to create an ACPI DSDT/SSDT table.\r
+\r
+ If:\r
+ - the content of BufferSize is >= to the size needed to serialize the\r
+ definition block;\r
+ - Buffer is not NULL;\r
+ first serialize the ACPI DSDT/SSDT header from the root node,\r
+ then serialize the AML blob from the rest of the tree.\r
+\r
+ The content of BufferSize is always updated to the size needed to\r
+ serialize the definition block.\r
+\r
+ @param [in] RootNode Pointer to a root node.\r
+ @param [in] Buffer Buffer to write the DSDT/SSDT table to.\r
+ If Buffer is NULL, the size needed to\r
+ serialize the DSDT/SSDT table is returned\r
+ in BufferSize.\r
+ @param [in, out] BufferSize Pointer holding the size of the Buffer.\r
+ Its content is always updated to the size\r
+ needed to serialize the DSDT/SSDT table.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_BUFFER_TOO_SMALL No space left in the buffer.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlSerializeTree (\r
+ IN AML_ROOT_NODE * RootNode,\r
+ IN UINT8 * Buffer, OPTIONAL\r
+ IN OUT UINT32 * BufferSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ AML_STREAM FStream;\r
+ UINT32 TableSize;\r
+\r
+ if (!IS_AML_ROOT_NODE (RootNode) ||\r
+ (BufferSize == NULL)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Compute the total size of the AML blob.\r
+ Status = AmlComputeSize (\r
+ (CONST AML_NODE_HEADER*)RootNode,\r
+ &TableSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT (0);\r
+ return Status;\r
+ }\r
+\r
+ // Add the size of the ACPI header.\r
+ TableSize += (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER);\r
+\r
+ // Check the size against the SDT header.\r
+ // The Length field in the SDT Header is updated if the tree has\r
+ // been modified.\r
+ if (TableSize != RootNode->SdtHeader->Length) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Buffer is not big enough, or NULL.\r
+ if ((TableSize < *BufferSize) || (Buffer == NULL)) {\r
+ *BufferSize = TableSize;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Initialize the stream to the TableSize that is needed.\r
+ Status = AmlStreamInit (\r
+ &FStream,\r
+ Buffer,\r
+ TableSize,\r
+ EAmlStreamDirectionForward\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT (0);\r
+ return Status;\r
+ }\r
+\r
+ // Serialize the header.\r
+ Status = AmlStreamWrite (\r
+ &FStream,\r
+ (UINT8*)RootNode->SdtHeader,\r
+ sizeof (EFI_ACPI_DESCRIPTION_HEADER)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT (0);\r
+ return Status;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ AmlEnumTree (\r
+ (AML_NODE_HEADER*)RootNode,\r
+ AmlSerializeNodeCallback,\r
+ (VOID*)&FStream,\r
+ &Status\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT (0);\r
+ return Status;\r
+ }\r
+\r
+ // Update the checksum.\r
+ return AcpiPlatformChecksum ((EFI_ACPI_DESCRIPTION_HEADER*)Buffer);\r
+}\r
+\r
+/** Serialize an AML definition block.\r
+\r
+ This functions allocates memory with the "AllocateZeroPool ()"\r
+ function. This memory is used to serialize the AML tree and is\r
+ returned in the Table.\r
+\r
+ @param [in] RootNode Root node of the tree.\r
+ @param [out] Table On return, hold the serialized\r
+ definition block.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlSerializeDefinitionBlock (\r
+ IN AML_ROOT_NODE * RootNode,\r
+ OUT EFI_ACPI_DESCRIPTION_HEADER ** Table\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 * TableBuffer;\r
+ UINT32 TableSize;\r
+\r
+ if (!IS_AML_ROOT_NODE (RootNode) ||\r
+ (Table == NULL)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *Table = NULL;\r
+ TableBuffer = NULL;\r
+ TableSize = 0;\r
+\r
+ // Get the size of the SSDT table.\r
+ Status = AmlSerializeTree (\r
+ RootNode,\r
+ TableBuffer,\r
+ &TableSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT (0);\r
+ return Status;\r
+ }\r
+\r
+ TableBuffer = (UINT8*)AllocateZeroPool (TableSize);\r
+ if (TableBuffer == NULL) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: Failed to allocate memory for Table Buffer."\r
+ ));\r
+ ASSERT (0);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ // Serialize the tree to a SSDT table.\r
+ Status = AmlSerializeTree (\r
+ RootNode,\r
+ TableBuffer,\r
+ &TableSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (TableBuffer);\r
+ ASSERT (0);\r
+ } else {\r
+ // Save the allocated Table buffer in the table list\r
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER*)TableBuffer;\r
+ }\r
+\r
+ return Status;\r
+}\r