]> git.proxmox.com Git - mirror_edk2.git/commitdiff
DynamicTablesPkg: AML resource data parser
authorPierre Gondois <pierre.gondois@arm.com>
Tue, 4 Aug 2020 16:09:09 +0000 (17:09 +0100)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Thu, 13 Aug 2020 18:00:06 +0000 (18:00 +0000)
Resource data are defined in the ACPI 6.3 specification,
s6.4 "Resource Data Types for ACPI". They can be created
using the ASL ResourceTemplate () statement, cf s19.3.3
"ASL Resource Templates".

Resource data can be of the small or large type and are
defined by their encoding. The resource data is stored
in the Bytelist of a BufferOp node. The Bytelist of a
BufferOp node is represented by an AML Data node in
the AML tree.

The resource data parser, examines the Bytelist (Data
node buffer) to detect the presence of resource data.
If the Bytelist data matches the encoding for resource
data types, the resource data parser fragments the
Bytelist containing the resource data buffer into
resource data elements represented as individual Data
nodes and stores them in the variable arguments list
of the BufferOp object nodes.

Example: ASL code and the corresponding AML tree
         representation for the resource data.

ASL Code
--------
Name (_CRS, ResourceTemplate() {
  QWordMemory (...)
  Interrupt (...)
}

AML Tree
--------
(NameOp)
  \
   |-[_CRS]-[BufferOp]                    # Fixed Arguments
   |-{NULL}   \                           # Variable Argument
               \                            list
               |-[BuffSize]               # Fixed Arguments
               |-{(Rd1)->(Rd2)->(EndTag)} # Variable Argument
                                            list
 Where:
 Rd1     - QWordMemory resource data element.
 Rd2     - Interrupt resource data element.
 EndTag  - Resource data end tag.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
Reviewed-by: Alexei Fedorov <Alexei.Fedorov@arm.com>
DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c [new file with mode: 0644]
DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h [new file with mode: 0644]

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c
new file mode 100644 (file)
index 0000000..09fb3e7
--- /dev/null
@@ -0,0 +1,328 @@
+/** @file\r
+  AML Resource Data Parser.\r
+\r
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+  @par Glossary:\r
+  - Rd or RD   - Resource Data\r
+  - Rds or RDS - Resource Data Small\r
+  - Rdl or RDL - Resource Data Large\r
+**/\r
+\r
+#include <Parser/AmlResourceDataParser.h>\r
+\r
+#include <AmlCoreInterface.h>\r
+#include <AmlDbgPrint/AmlDbgPrint.h>\r
+#include <Tree/AmlNode.h>\r
+#include <Tree/AmlTree.h>\r
+\r
+/** Get the size of a resource data element using a stream.\r
+\r
+  If the resource data element is of the large type, the Header\r
+  is expected to be at least 3 bytes long.\r
+\r
+  The use of a stream makes this function safer\r
+  than the version without stream.\r
+\r
+  @param  [in]  FStream     Forward stream pointing to a resource data\r
+                            element.\r
+                            The stream must not be at its end.\r
+\r
+  @return The size of the resource data element.\r
+          Zero if error.\r
+**/\r
+UINT32\r
+EFIAPI\r
+AmlRdStreamGetRdSize (\r
+  IN  CONST AML_STREAM    * FStream\r
+  )\r
+{\r
+  CONST AML_RD_HEADER   * CurrRdElement;\r
+\r
+  if (!IS_STREAM (FStream)        ||\r
+      IS_END_OF_STREAM (FStream)  ||\r
+      !IS_STREAM_FORWARD (FStream)) {\r
+    ASSERT (0);\r
+    return 0;\r
+  }\r
+\r
+  CurrRdElement = (CONST AML_RD_HEADER*)AmlStreamGetCurrPos (FStream);\r
+  if (CurrRdElement == NULL) {\r
+    ASSERT (0);\r
+    return 0;\r
+  }\r
+\r
+  // If the resource data element is of the large type, check for overflow.\r
+  if (AML_RD_IS_LARGE (CurrRdElement) &&\r
+      (AmlStreamGetFreeSpace (FStream) <\r
+         sizeof (ACPI_LARGE_RESOURCE_HEADER))) {\r
+    return 0;\r
+  }\r
+\r
+  return AmlRdGetSize (CurrRdElement);\r
+}\r
+\r
+/** Check the nesting of resource data elements that are dependent\r
+    function descriptors.\r
+\r
+  @param  [in]  FStream             Forward stream pointing to a resource data\r
+                                    element. The stream is not\r
+                                    modified/progressing.\r
+                                    The stream must not be at its end.\r
+  @param  [in, out] InFunctionDesc  Pointer holding the nesting of the\r
+                                    resource data buffer.\r
+                                    InFunctionDesc holds TRUE if the resource\r
+                                    data at the address of Buffer is currently\r
+                                    in a dependent function descriptor list.\r
+\r
+  @retval FALSE   The Header being parsed is ending a function descriptor\r
+                  list when none started. This should not be possible for a\r
+                  resource data buffer.\r
+  @retval TRUE    Otherwise.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+EFIAPI\r
+AmlRdCheckFunctionDescNesting (\r
+  IN      CONST AML_STREAM    * FStream,\r
+  IN  OUT       BOOLEAN       * InFunctionDesc\r
+  )\r
+{\r
+  CONST AML_RD_HEADER   * CurrRdElement;\r
+\r
+  if (!IS_STREAM (FStream)        ||\r
+      IS_END_OF_STREAM (FStream)  ||\r
+      (InFunctionDesc == NULL)) {\r
+    ASSERT (0);\r
+    return FALSE;\r
+  }\r
+\r
+  CurrRdElement = AmlStreamGetCurrPos (FStream);\r
+  if (CurrRdElement == NULL) {\r
+    ASSERT (0);\r
+    return FALSE;\r
+  }\r
+\r
+  // Starting a dependent function descriptor.\r
+  // It is possible to start one when one has already started.\r
+  if (AmlRdCompareDescId (\r
+        CurrRdElement,\r
+        AML_RD_BUILD_SMALL_DESC_ID (\r
+          ACPI_SMALL_START_DEPENDENT_DESCRIPTOR_NAME))) {\r
+    *InFunctionDesc = TRUE;\r
+    return TRUE;\r
+  }\r
+\r
+  // Ending a dependent function descriptor.\r
+  if (AmlRdCompareDescId (\r
+        CurrRdElement,\r
+        AML_RD_BUILD_SMALL_DESC_ID (\r
+          ACPI_SMALL_END_DEPENDENT_DESCRIPTOR_NAME))) {\r
+    if (*InFunctionDesc) {\r
+      *InFunctionDesc = FALSE;\r
+      return TRUE;\r
+    }\r
+\r
+    // It should not be possible to end a dependent function descriptor\r
+    // when none started.\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/** Check whether the input stream is pointing to a valid list\r
+    of resource data elements.\r
+\r
+  The check is based on the size of resource data elements.\r
+  This means that a buffer can pass this check with non-existing descriptor Ids\r
+  that have a correct size.\r
+\r
+  A list of resource data elements can contain one unique resource data\r
+  element, without an end tag resource data. This is the case for\r
+  a FieldList.\r
+\r
+  @param  [in]  FStream   Forward stream ideally pointing to a resource\r
+                          data element. The stream is not\r
+                          modified/progressing.\r
+                          The stream must not be at its end.\r
+\r
+  @retval TRUE    The buffer is holding a valid list of resource data elements.\r
+  @retval FALSE   Otherwise.\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+AmlRdIsResourceDataBuffer (\r
+  IN  CONST AML_STREAM    * FStream\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  UINT32                  FreeSpace;\r
+  AML_STREAM              SubStream;\r
+  CONST AML_RD_HEADER   * CurrRdElement;\r
+  UINT32                  CurrRdElementSize;\r
+  BOOLEAN                 InFunctionDesc;\r
+\r
+  if (!IS_STREAM (FStream)        ||\r
+      IS_END_OF_STREAM (FStream)  ||\r
+      !IS_STREAM_FORWARD (FStream)) {\r
+    ASSERT (0);\r
+    return FALSE;\r
+  }\r
+\r
+  // Create a sub-stream from the input stream to leave it untouched.\r
+  Status = AmlStreamInitSubStream (FStream, &SubStream);\r
+  if (EFI_ERROR (Status)) {\r
+    ASSERT (0);\r
+    return FALSE;\r
+  }\r
+\r
+  CurrRdElement = AmlStreamGetCurrPos (&SubStream);\r
+  if (CurrRdElement == NULL) {\r
+    ASSERT (0);\r
+    return FALSE;\r
+  }\r
+\r
+  // The first element cannot be an end tag.\r
+  if (AmlRdCompareDescId (\r
+       CurrRdElement,\r
+       AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) {\r
+    return FALSE;\r
+  }\r
+\r
+  InFunctionDesc = FALSE;\r
+  while (TRUE) {\r
+    FreeSpace = AmlStreamGetFreeSpace (&SubStream);\r
+    CurrRdElement = AmlStreamGetCurrPos (&SubStream);\r
+    CurrRdElementSize = AmlRdStreamGetRdSize (&SubStream);\r
+    if ((FreeSpace == 0)          ||\r
+        (CurrRdElement == NULL)   ||\r
+        (CurrRdElementSize == 0)) {\r
+      return FALSE;\r
+    }\r
+\r
+    if (!AmlRdCheckFunctionDescNesting (&SubStream, &InFunctionDesc)) {\r
+      return FALSE;\r
+    }\r
+\r
+    if (CurrRdElementSize > FreeSpace) {\r
+      return FALSE;\r
+    } else if (CurrRdElementSize == FreeSpace) {\r
+      return TRUE;\r
+    }\r
+\r
+    // TODO Might want to check the CRC when available.\r
+    // An end tag resource data element must be the last element of the list.\r
+    // Thus the function should have already returned.\r
+    if (AmlRdCompareDescId (\r
+          CurrRdElement,\r
+          AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) {\r
+      return FALSE;\r
+    }\r
+\r
+    Status = AmlStreamProgress (&SubStream, CurrRdElementSize);\r
+    if (EFI_ERROR (Status)) {\r
+      ASSERT (0);\r
+      return FALSE;\r
+    }\r
+  } // while\r
+\r
+  return FALSE;\r
+}\r
+\r
+/** Parse a ResourceDataBuffer.\r
+\r
+  For each resource data element, create a data node\r
+  and add them to the variable list of arguments of the BufferNode.\r
+\r
+  The input stream is expected to point to a valid list of resource data\r
+  elements. A function is available to check it for the caller.\r
+\r
+  @param  [in]  BufferNode    Buffer node.\r
+  @param  [in]  FStream       Forward stream pointing to a resource data\r
+                              element.\r
+                              The stream must not be at its end.\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
+AmlParseResourceData (\r
+  IN  AML_OBJECT_NODE   * BufferNode,\r
+  IN  AML_STREAM        * FStream\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  AML_DATA_NODE         * NewNode;\r
+  UINT32                  FreeSpace;\r
+  CONST AML_RD_HEADER   * CurrRdElement;\r
+  UINT32                  CurrRdElementSize;\r
+\r
+  // Check that BufferNode is an ObjectNode and has a ByteList.\r
+  if (!AmlNodeHasAttribute (BufferNode, AML_HAS_BYTE_LIST)  ||\r
+      !IS_STREAM (FStream)                                  ||\r
+      IS_END_OF_STREAM (FStream)                            ||\r
+      !IS_STREAM_FORWARD (FStream)) {\r
+    ASSERT (0);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  // Iterate through the resource data elements and create nodes.\r
+  // We assume the Buffer has already been validated as a list of\r
+  // resource data elements, so less checks are made.\r
+  while (TRUE) {\r
+    FreeSpace = AmlStreamGetFreeSpace (FStream);\r
+    if (FreeSpace == 0) {\r
+      break;\r
+    }\r
+\r
+    CurrRdElement = (CONST AML_RD_HEADER*)AmlStreamGetCurrPos (FStream);\r
+    CurrRdElementSize = AmlRdStreamGetRdSize (FStream);\r
+\r
+    Status = AmlCreateDataNode (\r
+               EAmlNodeDataTypeResourceData,\r
+               (CONST UINT8*)CurrRdElement,\r
+               CurrRdElementSize,\r
+               &NewNode\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      ASSERT (0);\r
+      return Status;\r
+    }\r
+\r
+    Status = AmlVarListAddTailInternal (\r
+               (AML_NODE_HEADER*)BufferNode,\r
+               (AML_NODE_HEADER*)NewNode\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      ASSERT (0);\r
+      AmlDeleteTree ((AML_NODE_HEADER*)NewNode);\r
+      return Status;\r
+    }\r
+\r
+    Status = AmlStreamProgress (FStream, CurrRdElementSize);\r
+    if (EFI_ERROR (Status)) {\r
+      ASSERT (0);\r
+      return Status;\r
+    }\r
+\r
+    DumpRaw (CurrRdElement, CurrRdElementSize);\r
+\r
+    // Exit the loop when finding the resource data end tag.\r
+    if (AmlRdCompareDescId (\r
+          CurrRdElement,\r
+          AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) {\r
+      if (FreeSpace != CurrRdElementSize) {\r
+        ASSERT (0);\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+      break;\r
+    }\r
+  } // while\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h
new file mode 100644 (file)
index 0000000..13dfb35
--- /dev/null
@@ -0,0 +1,71 @@
+/** @file\r
+  AML Resource Data Parser.\r
+\r
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+  @par Glossary:\r
+  - Rd or RD   - Resource Data\r
+  - Rds or RDS - Resource Data Small\r
+  - Rdl or RDL - Resource Data Large\r
+**/\r
+\r
+#ifndef AML_RESOURCE_DATA_PARSER_H_\r
+#define AML_RESOURCE_DATA_PARSER_H_\r
+\r
+#include <AmlNodeDefines.h>\r
+#include <Stream/AmlStream.h>\r
+#include <ResourceData/AmlResourceData.h>\r
+\r
+/** Check whether the input stream is pointing to a valid list\r
+    of resource data elements.\r
+\r
+  The check is based on the size of resource data elements.\r
+  This means that a buffer can pass this check with non-existing descriptor Ids\r
+  that have a correct size.\r
+\r
+  A list of resource data elements can contain one unique resource data\r
+  element, without an end tag resource data. This is the case for\r
+  a FieldList.\r
+\r
+  @param  [in]  FStream   Forward stream ideally pointing to a resource\r
+                          data element. The stream is not\r
+                          modified/progressing.\r
+                          The stream must not be at its end.\r
+\r
+  @retval TRUE    The buffer is holding a valid list of resource data elements.\r
+  @retval FALSE   Otherwise.\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+AmlRdIsResourceDataBuffer (\r
+  IN  CONST AML_STREAM    * FStream\r
+  );\r
+\r
+/** Parse a ResourceDataBuffer.\r
+\r
+  For each resource data element, create a data node\r
+  and add them to the variable list of arguments of the BufferNode.\r
+\r
+  The input stream is expected to point to a valid list of resource data\r
+  elements. A function is available to check it for the caller.\r
+\r
+  @param  [in]  BufferNode    Buffer node.\r
+  @param  [in]  FStream       Forward stream pointing to a resource data\r
+                              element.\r
+                              The stream must not be at its end.\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
+AmlParseResourceData (\r
+  IN  AML_OBJECT_NODE   * BufferNode,\r
+  IN  AML_STREAM        * FStream\r
+  );\r
+\r
+#endif // AML_RESOURCE_DATA_PARSER_H_\r
+\r