public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH v1 00/30] Add Dynamic AML generation support
@ 2020-08-12 15:22 Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 01/30] DynamicTablesPkg: Introduction to Dynamic AML Sami Mujawar
                   ` (30 more replies)
  0 siblings, 31 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	leif, Matteo.Carlini, Ben.Adderson, Hemendra.Dassanayake,
	Samer.El-Haj-Mahmoud, nd

ACPI tables are categorised as Data tables (e.g. MADT, GTDT, etc.) or
Definition Block tables (DSDT or SSDT). The Dynamic Tables Framework
already supports generation of Data tables. Definition Block tables
are written in ACPI Source Language (ASL) and compiled to ACPI Machine
Language (AML). This AML bytecode is processed by the AML interpreter
that runs as part of the Operating System (OS). 

AML has a complex grammar, making runtime modifications on an AML byte
stream difficult. Dynamic AML is a solution to simplify the generation
of Definition Block tables. 

Dynamic AML introduces an AmlLib library that implements an AML parser
that parses the AML bytecode and represent it as a tree data structure.
This allows to easily modify the AML code by updating/adding/removing
the tree nodes. The modified AML tree is then serialised to a buffer
for installing as a Definition block table.

AmlLib Library provides a rich set of APIs to
  - parse AML bytecode to generate an AML tree.
  - search, update, traverse and serialise an AML tree.
  - enumerate, add, delete nodes of an AML tree.
  - generate small segments of AML code in the form of
    AML nodes that can be used to form a branch/tree.

Dynamic AML introduces the following techniques:
  * AML Fixup
  * AML Codegen
  * AML Fixup + Codegen

  AML Fixup is a technique that involves run-time fixup of sections
  of an AML template bytecode. An ASL template file is compiled to
  AML bytecode at build time. At runtime, this AML template bytecode
  is parsed using the AmlLib library APIs and is represented as an
  AML tree. The AML tree nodes can be searched to locate a node that
  needs updating and can be fixed up. The AML tree is then serialised
  to a buffer representing the Definition Block table.

  AML Codegen employs generating small segments of AML code and
  populating a small AML tree that can be serialised.

  AML Fixup + Codegen is a combination of Fixup and Codegen. Here the
  AML template is parsed to generate an AML Tree. Sections of AML code
  can be fixed-up as well as new AML nodes/branches can be attached to
  the AML tree using the AML Codegen APIs.


A SSDT Serial Port generator has also been introduced to generate
definition block tables describing the serial ports on a platform. 

A SSDT Serial Port Fixup library has been provided to generate
description of a serial port in a Definition Block table. This
library is shared by SPCR, DBG2 and the SSDT Serial Port generator
to describe the respective serial ports.

The SPCR and DBG2 table generators have also been updated to
generate the SSDT serial port tables describing the respective
serial ports.

The changes can be seen at:
https://github.com/samimujawar/edk2/tree/675_dynamic_aml_v1

Pierre Gondois (27):
  DynamicTablesPkg: AmlLib definitions
  DynamicTablesPkg: AML grammar definition
  DynamicTablesPkg: AML node definitions
  DynamicTablesPkg: AML tree interface
  DynamicTablesPkg: AML tree enumerator
  DynamicTablesPkg: AML tree traversal
  DynamicTablesPkg: AML tree iterator
  DynamicTablesPkg: AML tree/node cloning
  DynamicTablesPkg: AML utility interfaces
  DynamicTablesPkg: AML and ASL string helper
  DynamicTablesPkg: AML serialise interface
  DynamicTablesPkg: AML debug logging
  DynamicTablesPkg: AML ACPI Namespace interface
  DynamicTablesPkg: AML Parser
  DynamicTablesPkg: AML resource data helper
  DynamicTablesPkg: AML resource data parser
  DynamicTablesPkg: AML Method parser
  DynamicTablesPkg: AML Field list parser
  DynamicTablesPkg: AML Codegen
  DynamicTablesPkg: AML Resource Data Codegen
  DynamicTablesPkg: AML Core interface
  DynamicTablesPkg: AmlLib APIs
  DynamicTablesPkg: Dynamic AML: Add AmlLib library
  DynamicTablesPkg: SSDT Serial Port Fixup library
  DynamicTablesPkg: SSDT Serial Port generator
  DynamicTablesPkg: Add SSDT Serial port for SPCR
  DynamicTablesPkg: Add SSDT Serial port for DBG2

Sami Mujawar (3):
  DynamicTablesPkg: Introduction to Dynamic AML
  DynamicTablesPkg: AML stream interface
  DynamicTablesPkg: Add AsciiFromHex helper function

 DynamicTablesPkg/DynamicTables.dsc.inc                                               |    8 +
 DynamicTablesPkg/DynamicTablesPkg.ci.yaml                                            |    9 +-
 DynamicTablesPkg/DynamicTablesPkg.dec                                                |    8 +-
 DynamicTablesPkg/DynamicTablesPkg.dsc                                                |    4 +-
 DynamicTablesPkg/Include/AcpiTableGenerator.h                                        |    7 +-
 DynamicTablesPkg/Include/ArmNameSpaceObjects.h                                       |    9 +-
 DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h                                     |  631 ++++++++
 DynamicTablesPkg/Include/Library/SsdtSerialPortFixupLib.h                            |   68 +
 DynamicTablesPkg/Include/Library/TableHelperLib.h                                    |   15 +-
 DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf                  |    3 +-
 DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c                     |  228 ++-
 DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf                  |    3 +-
 DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c                     |  219 ++-
 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c |  367 +++++
 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf  |   33 +
 DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h                            |  767 ++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c                     |  546 +++++++
 DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h                     |  154 ++
 DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h                                  |  188 +++
 DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c                             |  805 +++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h                             |  330 +++++
 DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h                                  |   18 +
 DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf                                    |   76 +
 DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h                              |  183 +++
 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c                                  |  382 +++++
 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c                            |  219 +++
 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h                            |   93 ++
 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c                      |  320 +++++
 DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c                          |  701 +++++++++
 DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c              |  256 ++++
 DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h              |   59 +
 DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c                      | 1501 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h                      |   74 +
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c                   |  375 +++++
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h                   |   77 +
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c                      | 1458 +++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h                      |  188 +++
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c                            | 1448 +++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h                            |   72 +
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c                |  328 +++++
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h                |   71 +
 DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c                |  103 ++
 DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h                |  174 +++
 DynamicTablesPkg/Library/Common/AmlLib/Serialize/AmlSerialize.c                      |  324 +++++
 DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c                            |  665 +++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h                            |  451 ++++++
 DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c                            | 1022 +++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h                            |  401 ++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c                               |  205 +++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c                                |  673 +++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h                                |  212 +++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c                       |  566 ++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c                                | 1047 ++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h                                |  127 ++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c                      |   98 ++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c                        |  353 +++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h                        |  220 +++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c                       |  548 +++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h                       |  138 ++
 DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c                            |  906 ++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h                            |   95 ++
 DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c      |  524 +++++++
 DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf    |   30 +
 DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortTemplate.asl    |   60 +
 DynamicTablesPkg/Library/Common/TableHelperLib/TableHelper.c                         |   30 +-
 DynamicTablesPkg/Readme.md                                                           |  128 +-
 66 files changed, 21247 insertions(+), 154 deletions(-)
 create mode 100644 DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h
 create mode 100644 DynamicTablesPkg/Include/Library/SsdtSerialPortFixupLib.h
 create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c
 create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.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
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Serialize/AmlSerialize.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c
 create mode 100644 DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h
 create mode 100644 DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c
 create mode 100644 DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf
 create mode 100644 DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortTemplate.asl

-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v1 01/30] DynamicTablesPkg: Introduction to Dynamic AML
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 02/30] DynamicTablesPkg: AmlLib definitions Sami Mujawar
                   ` (29 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

ACPI Definition block (e.g. DSDT or SSDT) tables are implemented
using ACPI source language (ASL) and compiled to ACPI Machine
language (AML). The AML bytecode runs in the OS ACPI Interpreter.
AML has a complex grammar which makes generation of ACPI Definition
block tables difficult.

Dynamic Tables Framework introduces a new feature 'Dynamic AML' that
aims at simplifying the generation of ACPI Definition block tables.

Dynamic AML provides the following techniques for generating ACPI
Definition blocks.
  - AML Fixup
  - AML Codegen
  - AML Fixup + Codegen

AML Fixup involves patching an AML template code at runtime and then
installing the fixed-up AML code as an ACPI table.

AML Codegen provides APIs to generate small segments of AML code that
can be serialised for installation as an ACPI table.

AML Fixup + Codegen is an approach where parts of an AML template are
fixed-up at runtime as well as the AML Codegen APIs are used to insert
small segments of AML code in the AML template. This AML code is then
serialised for installation as an ACPI table.

To assist Dynamic AML generation an AmlLib library is introduced that
provides a rich set of APIs that can be used to parse, traverse, fixup,
codegen and serialise AML definition blocks.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Readme.md | 128 +++++++++++++++++---
 1 file changed, 111 insertions(+), 17 deletions(-)

diff --git a/DynamicTablesPkg/Readme.md b/DynamicTablesPkg/Readme.md
index 34000cf73bc7da5fce2e520d41318af908353969..5f92d4c6cb2efd77bbc3306f827088216b7a32e0 100644
--- a/DynamicTablesPkg/Readme.md
+++ b/DynamicTablesPkg/Readme.md
@@ -1,5 +1,4 @@
-Dynamic Tables Framework
-------------------------
+# Dynamic Tables Framework
 
 Dynamic Tables Framework provides mechanisms to reduce the amount
 of effort required in porting firmware to new platforms. The aim is
@@ -10,8 +9,8 @@ be generated from the system construction.  This initial release
 does not fully implement that - the configuration is held in local
 UEFI modules.
 
-Feature Summary
----------------
+# Feature Summary
+
 The dynamic tables framework is designed to generate standardised
 firmware tables that describe the hardware information at
 run-time. A goal of standardised firmware is to have a common
@@ -58,8 +57,105 @@ The framework currently supports the following table generators for ARM:
 * SSDT - Secondary System Description Table. This is essentially
          a RAW table generator.
 
-Roadmap
--------
+## Dynamic AML
+
+ACPI Definition block (e.g. DSDT or SSDT) tables are used to describe system
+devices along with other control and power management information. These tables
+are written using ACPI Source Language (ASL). The ASL code is compiled using an
+ASL compiler (e.g. Intel iASL compiler) to generate ACPI Machine Language (AML)
+bytecode.
+
+Since, definition blocks are represented using AML grammar, run-time generation
+of definition blocks is complex. Dynamic AML is a feature of Dynamic Tables
+framework that provides a solution for dynamic generation of ACPI Definition
+block tables.
+
+Dynamic AML introduces the following techniques:
+* AML Fixup
+* AML Codegen
+* AML Fixup + Codegen
+
+### AML Fixup
+AML fixup is a technique that involves compiling an ASL template file to
+generate AML bytecode. This template AML bytecode can be parsed at run-time
+and a fixup code can update the required fields in the AML template.
+
+To simplify AML Fixup, the Dynamic Tables Framework provides an *AmlLib*
+library with a rich set of APIs that can be used to fixup the AML code.
+
+### AML Codegen
+AML Codegen employs generating small segments of AML code. The *AmlLib*
+library provides AML Codegen APIs that generate the AML code segments.
+
+    Example: The following table depicts the AML Codegen APIs and the
+             corresponding ASL code that would be generated.
+
+    | AML Codegen API                | ASL Code                       |
+    |--------------------------------|--------------------------------|
+    |  AmlCodeGenDefinitionBlock (   |  DefinitionBlock (             |
+    |    ..,                         |    ...                         |
+    |    &RootNode);                 |  ) {                           |
+    |  AmlCodeGenScope (             |    Scope (_SB) {               |
+    |    "\_SB",                     |                                |
+    |    RootNode,                   |                                |
+    |    &ScopeNode);                |                                |
+    |  AmlCodeGenDevice (            |    Device (CPU0) {             |
+    |    "CPU0",                     |                                |
+    |    ScopeNode,                  |                                |
+    |    &CpuNode);                  |                                |
+    |  AmlCodeGenNameString (        |      Name (_HID, "ACPI0007")   |
+    |    "_HID",                     |                                |
+    |    "ACPI0007",                 |                                |
+    |    CpuNode,                    |                                |
+    |    &HidNode);                  |                                |
+    |  AmlCodeGenNameInteger (       |      Name (_UID, Zero)         |
+    |    "_UID",                     |                                |
+    |    0,                          |                                |
+    |    CpuNode,                    |                                |
+    |    &UidNode);                  |                                |
+    |                                |      } // Device               |
+    |                                |    } // Scope                  |
+    |                                |  } // DefinitionBlock          |
+
+### AML Fixup + Codegen
+A combination of AML Fixup and AML Codegen could be used for generating
+Definition Blocks. For example the AML Fixup could be used to fixup certain
+parts of the AML template while the AML Codegen APIs could be used to inserted
+small fragments of AML code in the AML template.
+
+### AmlLib Library
+Since, AML bytecode represents complex AML grammar, an **AmlLib** library is
+introduced to assist parsing and traversing of the AML bytecode at run-time.
+
+The AmlLib library parses a definition block and represents it as an AML
+tree. This tree representation is based on the AML grammar defined by the
+ACPI 6.3 specification, section - 20 'ACPI Machine Language (AML)
+Specification'.
+
+AML objects, methods and data are represented as tree nodes. Since the AML
+data is represented as tree nodes, it is possible to traverse the tree, locate
+a node and modify the node data. The tree can then be serialized to a buffer
+(that represents the definition block). This definition block containing
+the fixed up AML code can then be installed as an ACPI table (DSDT/SSDT).
+
+AmlLib provides a rich API to operate on AML data. For example it provides
+APIs to update a device's name, the value of a "_UID" object, and the memory
+and interrupt number stored in a "_CRS" node.
+
+Although the AmlLib performs checks to a reasonable extent while modifying a
+definition block, these checks may not cover all aspects due to the complexity
+of the ASL/AML language. It is therefore recommended to review any operation
+performed, and validate the generated output.
+
+    Example: The serialized AML code could be validated by
+     - Saving the generated AML to a file and comparing with
+       a reference output.
+     or
+     - Disassemble the generated AML using the iASL compiler
+       and verifying the output.
+
+# Roadmap
+
 The current implementation of the Configuration Manager populates the
 platform information statically as a C structure. Further enhancements
 to introduce runtime loading of platform information from a platform
@@ -68,13 +164,13 @@ information file is planned.
 Also support for generating SMBIOS tables is planned and will be added
 subsequently.
 
-Supported Platforms
--------------------
+# Supported Platforms
+
 1. Juno
 2. FVP Models
 
-Build Instructions
-------------------
+# Build Instructions
+
 1. Set path for the iASL compiler with support for generating a C header
    file as output.
 
@@ -102,15 +198,14 @@ or
 >build -a AARCH64 -p Platform\ARM\VExpressPkg\ArmVExpress-FVP-AArch64.dsc
    -t GCC5 **-D DYNAMIC_TABLES_FRAMEWORK**
 
-Prerequisites
--------------
+# Prerequisites
+
 Ensure that the latest ACPICA iASL compiler is used for building *Dynamic Tables Framework*.
 *Dynamic Tables Framework* has been tested using the following iASL compiler version:
-[Version 20200717](https://www.acpica.org/node/183)*, dated 17 July, 2020.
+[Version 20200717](https://www.acpica.org/node/183), dated 17 July, 2020.
 
 
-Running CI builds locally
--------------------------
+#Running CI builds locally
 
 The TianoCore EDKII project has introduced Core CI infrastructure using TianoCore EDKII Tools PIP modules:
 
@@ -178,8 +273,7 @@ The instructions to setup the CI environment are in *'edk2\\.pytool\\Readme.md'*
     - use `stuart_build -c .pytool/CISettings.py -h` option to see help on additional options.
 
 
-Documentation
--------------
+# Documentation
 
 Refer to the following presentation from *UEFI Plugfest Seattle 2018*:
 
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 02/30] DynamicTablesPkg: AmlLib definitions
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 01/30] DynamicTablesPkg: Introduction to Dynamic AML Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 03/30] DynamicTablesPkg: AML grammar definition Sami Mujawar
                   ` (28 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

Dynamic AML is a solution to generate Definition Block tables
at runtime. Dynamic AML provides the following techniques for
generating AML tables.
  - AML Fixup
  - AML Codegen
  - AML Fixup + Codegen

AML fixup involves patching small sections of a template AML
code at runtime, while AML Codegen provides APIs to generate
small sections of AML code at runtime. A combination of
Fixup and Codegen can also be used.

AML has a complex grammar. To simplify the generation of
AML tables, Dynamic AML introduces AmlLib that provides a
rich set of APIs for parsing, traversing, fixup, codegen
and serialisation of AML byte code.

This patch introduces the definitions used by AmlLib.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h | 188 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h |  18 ++
 2 files changed, 206 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h b/DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h
new file mode 100644
index 0000000000000000000000000000000000000000..cbae14d78802005c9b7610e8085a484610e8b18f
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h
@@ -0,0 +1,188 @@
+/** @file
+  AML Defines.
+
+  Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_DEFINES_H_
+#define AML_DEFINES_H_
+
+/**
+  @defgroup TreeStructures Tree structures
+  @ingroup AMLLib
+  @{
+    The AML tree created by the AMLLib relies on enum/define values and
+    structures defined here.
+  @}
+*/
+
+/** AML tree node types.
+
+  Data nodes are tagged with the data type they contain.
+  Some data types cannot be used for data nodes (None, Object).
+  EAmlUIntX types are converted to the EAML_NODE_DATA_TYPE enum type.
+  These types are accessible externally.
+
+  @ingroup TreeStructures
+*/
+typedef enum EAmlNodeDataType {
+  EAmlNodeDataTypeNone = 0,         ///< EAmlNone,   not accessible.
+  EAmlNodeDataTypeReserved1,        ///< EAmlUInt8,  converted to the UInt type.
+  EAmlNodeDataTypeReserved2,        ///< EAmlUInt16, converted to the UInt type.
+  EAmlNodeDataTypeReserved3,        ///< EAmlUInt32, converted to the UInt type.
+  EAmlNodeDataTypeReserved4,        ///< EAmlUInt64, converted to the UInt type.
+  EAmlNodeDataTypeReserved5,        ///< EAmlObject, not accessible.
+  EAmlNodeDataTypeNameString,       ///< EAmlName, name corresponding to the
+                                    ///  NameString keyword in the ACPI
+                                    ///  specification. E.g.: "\_SB_.DEV0"
+  EAmlNodeDataTypeString,           ///< EAmlString, NULL terminated string.
+  EAmlNodeDataTypeUInt,             ///< Integer data of any length, EAmlUIntX
+                                    ///  are converted to this type.
+  EAmlNodeDataTypeRaw,              ///< Raw bytes contained in a buffer.
+  EAmlNodeDataTypeResourceData,     ///< Resource data element.
+  EAmlNodeDataTypeFieldPkgLen,      ///< FieldPkgLen data element.
+                                    ///  PkgLen are usually stored as
+                                    ///  part of object node structures.
+                                    ///  However, they can be found
+                                    ///  standalone in a FieldList.
+  EAmlNodeDataTypeMax               ///< Max enum.
+} EAML_NODE_DATA_TYPE;
+
+/** Indexes of fixed arguments.
+
+  AML objects defined the ACPI 6.3 specification,
+  s20.3 "AML Byte Stream Byte Values" can have at most 6 fixed arguments.
+
+  Method and functions can have at most 7 arguments, cf
+  s19.6.83 "Method (Declare Control Method)". The enum goes to 8 to store the
+  name of the method invocation.
+
+  @ingroup TreeStructures
+*/
+typedef enum EAmlParseIndex {
+  EAmlParseIndexTerm0  = 0,     ///< First fixed argument index.
+  EAmlParseIndexTerm1,          ///< Second fixed argument index.
+  EAmlParseIndexTerm2,          ///< Third fixed argument index.
+  EAmlParseIndexTerm3,          ///< Fourth fixed argument index.
+  EAmlParseIndexTerm4,          ///< Fifth fixed argument index.
+  EAmlParseIndexTerm5,          ///< Sixth fixed argument index.
+  EAmlParseIndexMax             ///< Maximum fixed argument index (=6).
+} EAML_PARSE_INDEX;
+
+/** Maximum size of an AML NameString.
+
+ An AML NameString can be at most (255 * 4) + 255 + 2 = 1277 bytes long.
+ Indeed, according to ACPI 6.3 specification, s20.2.2,
+ an AML NameString can be resolved as a MultiNamePath.
+
+ The encoding of this MultiNamePath can be made of at most:
+  - 255 carets ('^'), one for each level in the namespace;
+  - 255 NameSeg of 4 bytes;
+  - 2 bytes for the MultiNamePrefix and SegCount.
+
+  @ingroup TreeStructures
+*/
+#define MAX_AML_NAMESTRING_SIZE       1277U
+
+/** Maximum size of an ASL NameString.
+
+ An ASL NameString can be at most (255 * 4) + 255 + 254 = 1529 bytes long.
+ Cf the ASL grammar available in ACPI 6.3 specification, 19.2.2.
+
+ The encoding of an ASL NameString can be made of at most:
+  - 255 carets ('^'), one for each level in the namespace;
+  - 255 NameSeg of 4 bytes;
+  - 254 NameSeg separators ('.').
+
+  @ingroup TreeStructures
+*/
+#define MAX_ASL_NAMESTRING_SIZE       1529U
+
+/** Pseudo OpCode for method invocations.
+
+  The AML grammar does not attribute an OpCode/SubOpCode couple for
+  method invocations. This library is representing method invocations
+  as if they had one.
+
+  The AML encoding for method invocations in the ACPI specification 6.3 is:
+    MethodInvocation := NameString TermArgList
+  In this library, it is:
+    MethodInvocation := MethodInvocationOp NameString ArgumentCount TermArgList
+    ArgumentCount    := ByteData
+
+  When computing the size of a tree or serializing it, the additional data is
+  not taken into account (i.e. the MethodInvocationOp and the ArgumentCount).
+
+  @ingroup TreeStructures
+*/
+#define AML_METHOD_INVOC_OP           0xD0
+
+/** Pseudo OpCode for NamedField field elements.
+
+  The AML grammar does not attribute an OpCode/SubOpCode couple for
+  the NamedField field element. This library is representing NamedField field
+  elements as if they had one.
+
+  The AML encoding for NamedField field elements in the ACPI specification 6.3
+  is:
+    NamedField      := NameSeg PkgLength
+  In this library, it is:
+    NamedField      := NamedFieldOp NameSeg PkgLength
+
+  When computing the size of a tree or serializing it, the additional data is
+  not taken into account (i.e. the NamedFieldOp).
+
+  @ingroup TreeStructures
+*/
+#define AML_FIELD_NAMED_OP            0x04
+
+/** Size of a NameSeg.
+    Cf. ACPI 6.3 specification, s20.2.
+
+  @ingroup TreeStructures
+*/
+ #define AML_NAME_SEG_SIZE            4U
+
+/** AML object types.
+
+  The ACPI specification defines several object types. They are listed
+  with the definition of ObjectTypeKeyword.
+
+  @ingroup TreeStructures
+*/
+typedef enum EAmlObjType {
+  EAmlObjTypeUnknown        = 0x0,
+  EAmlObjTypeInt,
+  EAmlObjTypeStrObj,
+  EAmlObjTypeBuffObj,
+  EAmlObjTypePkgObj,
+  EAmlObjTypeFieldUnitObj,
+  EAmlObjTypeDeviceObj,
+  EAmlObjTypeEventObj,
+  EAmlObjTypeMethodObj,
+  EAmlObjTypeMutexObj,
+  EAmlObjTypeOpRegionObj,
+  EAmlObjTypePowerResObj,
+  EAmlObjTypeProcessorObj,
+  EAmlObjTypeThermalZoneObj,
+  EAmlObjTypeBuffFieldObj,
+  EAmlObjTypeDDBHandleObj,
+} EAML_OBJ_TYPE;
+
+/** Node types.
+
+  @ingroup TreeStructures
+*/
+typedef enum EAmlNodeType {
+  EAmlNodeUnknown,  ///< Unknown/Invalid AML Node Type.
+  EAmlNodeRoot,     ///< AML Root Node, typically represents a DefinitionBlock.
+  EAmlNodeObject,   ///< AML Object Node, typically represents an ASL statement
+                    ///  or its arguments.
+  EAmlNodeData,     ///< AML Data Node, typically represents arguments for an
+                    ///  ASL statement.
+  EAmlNodeMax       ///< Max enum.
+} EAML_NODE_TYPE;
+
+#endif // AML_DEFINES_H_
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h b/DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h
new file mode 100644
index 0000000000000000000000000000000000000000..274482f0d1244cdabc5436c5542a2b829083ae93
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h
@@ -0,0 +1,18 @@
+/** @file
+  AML Include file
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_INCLUDE_H_
+#define AML_INCLUDE_H_
+
+#include <Base.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#endif // AML_INCLUDE_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 03/30] DynamicTablesPkg: AML grammar definition
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 01/30] DynamicTablesPkg: Introduction to Dynamic AML Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 02/30] DynamicTablesPkg: AmlLib definitions Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 04/30] DynamicTablesPkg: AML node definitions Sami Mujawar
                   ` (27 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

ASL is a source language for defining ACPI objects including
writing ACPI control methods. An ASL file is compiled using
an ASL compiler tool to generate ACPI Machine Language (AML).
This AML bytecode is processed by the ACPI AML interpreter
that runs as part of an Operating System (OS).

Both ASL and AML are declarative languages. Although they
are closely related they are different languages.

ASL statements declare objects. Each object has three parts,
two of which can be NULL:
  Object := ObjectType FixedList VariableList

The AML grammar defines corresponding encodings that makes
up the AML byte stream.

This patch introduces the AML grammar definitions used by
AmlLib for encoding/decoding AML byte streams.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c | 805 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h | 330 ++++++++
 2 files changed, 1135 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c b/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c
new file mode 100644
index 0000000000000000000000000000000000000000..780bdea67f6035023f0ee995095e50f0ae35f810
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c
@@ -0,0 +1,805 @@
+/** @file
+  AML grammar definitions.
+
+  Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved. <BR>
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlEncoding/Aml.h>
+
+/** AML grammar encoding table.
+
+  The ASL language is a description language, used to define abstract
+  objects, like devices, thermal zones, etc. and their place in a hierarchical
+  tree. The following table stores the AML grammar definition. It can be used
+  to parse an AML bytestream. Each line corresponds to the definition of an
+  opcode and what is expected to be found with this opcode.
+  See table 20-440 in the ACPI 6.3 specification s20.3, and the AML
+  grammar definitions in s20.2.
+
+  - OpCode/SubOpCode:
+  An OpCode/SubOpCode couple allows to identify an object type.
+  The OpCode and SubOpCode are one byte each. The SubOpCode is
+  used when the Opcode value is 0x5B (extended OpCode). Otherwise
+  the SubOpcode is set to 0. If the SubOpCode is 0 in the table
+  below, there is no SubOpCode in the AML bytestream, only the
+  OpCode is used to identify the object.
+
+  - Fixed arguments:
+  The fixed arguments follow the OpCode and SubOpCode. Their number
+  and type can be found in the table below. There can be at the most
+  6 fixed arguments for an object.
+  Fixed arguments's type allow to know what is expected in the AML bytestream.
+  Knowing the size of the incoming element, AML bytes can be packed and parsed
+  accordingly. These types can be found in the same table 20-440 in the
+  ACPI 6.3, s20.3 specification.
+  E.g.: An AML object, a UINT8, a NULL terminated string, etc.
+
+  -Attributes:
+  The attribute field gives additional information on each object. This can
+  be the presence of a variable list of arguments, the presence of a PkgLen,
+  etc.
+
+  In summary, an AML object is described as:
+    OpCode [SubOpcode] [PkgLen] [FixedArgs] [VarArgs]
+
+  OpCode                        {1 byte}
+  [SubOpCode]                   {1 byte.
+                                 Only relevant if the OpCode value is
+                                 0x5B (extended OpCode prefix).
+                                 Otherwise 0. Most objects don't have one.}
+  [PkgLen]                      {Size of the object.
+                                 It has a special encoding, cf. ACPI 6.3
+                                 specification, s20.2.4 "Package Length
+                                 Encoding".
+                                 Most objects don't have one.}
+  [FixedArgs[0..X]]             {Fixed list of arguments.
+    (where X <= 5)               Can be other objects or data (a byte,
+                                 a string, etc.). They belong to the
+                                 current AML object.
+                                 The number of fixed arguments varies according
+                                 to the object, but it is fixed for each kind of
+                                 object.}
+  [VarArgs]                     {Variable list of arguments.
+                                 They also belong to the current object and can
+                                 be objects or data.
+                                 Most objects don't have one.}
+    [ByteList]                  {This is a sub-type of a variable list of
+                                 arguments. It can only be found in buffer
+                                 objects.
+                                 A ByteList is either a list of bytes or
+                                 a list of resource data elements. Resource
+                                 data elements have specific opcodes.}
+    [FieldList]                 {This is a sub-type of a variable list of
+                                 arguments. It can only be found in Fields,
+                                 IndexFields and BankFields.
+                                 A FieldList is made of FieldElements.
+                                 FieldElements have specific opcodes.}
+*/
+GLOBAL_REMOVE_IF_UNREFERENCED
+STATIC
+CONST
+AML_BYTE_ENCODING mAmlByteEncoding[] = {
+  // Comment       Str                                   OpCode                     SubOpCode               MaxIndex  NameIndex   0                 1                 2                 3                 4                 5                 Attribute
+  /* 0x00 */      {AML_DEBUG_STR ("ZeroOp")              AML_ZERO_OP,               0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x01 */      {AML_DEBUG_STR ("OneOp")               AML_ONE_OP,                0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x06 */      {AML_DEBUG_STR ("AliasOp")             AML_ALIAS_OP,              0,                      2,        1,         {EAmlName,         EAmlName,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x08 */      {AML_DEBUG_STR ("NameOp")              AML_NAME_OP,               0,                      2,        0,         {EAmlName,         EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x0A */      {AML_DEBUG_STR ("BytePrefix")          AML_BYTE_PREFIX,           0,                      1,        0,         {EAmlUInt8,        EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x0B */      {AML_DEBUG_STR ("WordPrefix")          AML_WORD_PREFIX,           0,                      1,        0,         {EAmlUInt16,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x0C */      {AML_DEBUG_STR ("DWordPrefix")         AML_DWORD_PREFIX,          0,                      1,        0,         {EAmlUInt32,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x0D */      {AML_DEBUG_STR ("StringPrefix")        AML_STRING_PREFIX,         0,                      1,        0,         {EAmlString,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x0E */      {AML_DEBUG_STR ("QWordPrefix")         AML_QWORD_PREFIX,          0,                      1,        0,         {EAmlUInt64,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x10 */      {AML_DEBUG_STR ("ScopeOp")             AML_SCOPE_OP,              0,                      1,        0,         {EAmlName,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE},
+  /* 0x11 */      {AML_DEBUG_STR ("BufferOp")            AML_BUFFER_OP,             0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_BYTE_LIST},
+  /* 0x12 */      {AML_DEBUG_STR ("PackageOp")           AML_PACKAGE_OP,            0,                      1,        0,         {EAmlUInt8,        EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ},
+  /* 0x13 */      {AML_DEBUG_STR ("VarPackageOp")        AML_VAR_PACKAGE_OP,        0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ},
+  /* 0x14 */      {AML_DEBUG_STR ("MethodOp")            AML_METHOD_OP,             0,                      2,        0,         {EAmlName,         EAmlUInt8,        EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE},
+  /* 0x15 */      {AML_DEBUG_STR ("ExternalOp")          AML_EXTERNAL_OP,           0,                      3,        0,         {EAmlName,         EAmlUInt8,        EAmlUInt8,        EAmlNone,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x2E */      {AML_DEBUG_STR ("DualNamePrefix")      AML_DUAL_NAME_PREFIX,      0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x2F */      {AML_DEBUG_STR ("MultiNamePrefix")     AML_MULTI_NAME_PREFIX,     0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x41 */      {AML_DEBUG_STR ("NameChar_A")          'A',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x42 */      {AML_DEBUG_STR ("NameChar_B")          'B',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x43 */      {AML_DEBUG_STR ("NameChar_C")          'C',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x44 */      {AML_DEBUG_STR ("NameChar_D")          'D',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x45 */      {AML_DEBUG_STR ("NameChar_E")          'E',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x46 */      {AML_DEBUG_STR ("NameChar_F")          'F',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x47 */      {AML_DEBUG_STR ("NameChar_G")          'G',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x48 */      {AML_DEBUG_STR ("NameChar_H")          'H',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x49 */      {AML_DEBUG_STR ("NameChar_I")          'I',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x4A */      {AML_DEBUG_STR ("NameChar_J")          'J',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x4B */      {AML_DEBUG_STR ("NameChar_K")          'K',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x4C */      {AML_DEBUG_STR ("NameChar_L")          'L',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x4D */      {AML_DEBUG_STR ("NameChar_M")          'M',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x4E */      {AML_DEBUG_STR ("NameChar_N")          'N',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x4F */      {AML_DEBUG_STR ("NameChar_O")          'O',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x50 */      {AML_DEBUG_STR ("NameChar_P")          'P',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x51 */      {AML_DEBUG_STR ("NameChar_Q")          'Q',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x52 */      {AML_DEBUG_STR ("NameChar_R")          'R',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x53 */      {AML_DEBUG_STR ("NameChar_S")          'S',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x54 */      {AML_DEBUG_STR ("NameChar_T")          'T',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x55 */      {AML_DEBUG_STR ("NameChar_U")          'U',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x56 */      {AML_DEBUG_STR ("NameChar_V")          'V',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x57 */      {AML_DEBUG_STR ("NameChar_W")          'W',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x58 */      {AML_DEBUG_STR ("NameChar_X")          'X',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x59 */      {AML_DEBUG_STR ("NameChar_Y")          'Y',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x5A */      {AML_DEBUG_STR ("NameChar_Z")          'Z',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x5B 0x01 */ {AML_DEBUG_STR ("MutexOp")             AML_EXT_OP,                AML_EXT_MUTEX_OP,       2,        0,         {EAmlName,         EAmlUInt8,        EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x5B 0x02 */ {AML_DEBUG_STR ("EventOp")             AML_EXT_OP,                AML_EXT_EVENT_OP,       1,        0,         {EAmlName,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x5B 0x12 */ {AML_DEBUG_STR ("CondRefOfOp")         AML_EXT_OP,                AML_EXT_COND_REF_OF_OP, 2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x13 */ {AML_DEBUG_STR ("CreateFieldOp")       AML_EXT_OP,                AML_EXT_CREATE_FIELD_OP,4,        3,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlName,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x5B 0x1F */ {AML_DEBUG_STR ("LoadTableOp")         AML_EXT_OP,                AML_EXT_LOAD_TABLE_OP,  6,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlObject,       EAmlObject,       EAmlObject},      0},
+  /* 0x5B 0x20 */ {AML_DEBUG_STR ("LoadOp")              AML_EXT_OP,                AML_EXT_LOAD_OP,        2,        0,         {EAmlName,         EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x21 */ {AML_DEBUG_STR ("StallOp")             AML_EXT_OP,                AML_EXT_STALL_OP,       1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x22 */ {AML_DEBUG_STR ("SleepOp")             AML_EXT_OP,                AML_EXT_SLEEP_OP,       1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x23 */ {AML_DEBUG_STR ("AcquireOp")           AML_EXT_OP,                AML_EXT_ACQUIRE_OP,     2,        0,         {EAmlObject,       EAmlUInt16,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x24 */ {AML_DEBUG_STR ("SignalOp")            AML_EXT_OP,                AML_EXT_SIGNAL_OP,      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x25 */ {AML_DEBUG_STR ("WaitOp")              AML_EXT_OP,                AML_EXT_WAIT_OP,        2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x26 */ {AML_DEBUG_STR ("ResetOp")             AML_EXT_OP,                AML_EXT_RESET_OP,       1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x27 */ {AML_DEBUG_STR ("ReleaseOp")           AML_EXT_OP,                AML_EXT_RELEASE_OP,     1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x28 */ {AML_DEBUG_STR ("FromBCDOp")           AML_EXT_OP,                AML_EXT_FROM_BCD_OP,    2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x29 */ {AML_DEBUG_STR ("ToBCDOp")             AML_EXT_OP,                AML_EXT_TO_BCD_OP,      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x2A */ {AML_DEBUG_STR ("UnloadOp")            AML_EXT_OP,                AML_EXT_UNLOAD_OP,      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x30 */ {AML_DEBUG_STR ("RevisionOp")          AML_EXT_OP,                AML_EXT_REVISION_OP,    0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x31 */ {AML_DEBUG_STR ("DebugOp")             AML_EXT_OP,                AML_EXT_DEBUG_OP,       0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x32 */ {AML_DEBUG_STR ("FatalOp")             AML_EXT_OP,                AML_EXT_FATAL_OP,       3,        0,         {EAmlUInt8,        EAmlUInt32,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x33 */ {AML_DEBUG_STR ("TimerOp")             AML_EXT_OP,                AML_EXT_TIMER_OP,       0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x5B 0x80 */ {AML_DEBUG_STR ("OpRegionOp")          AML_EXT_OP,                AML_EXT_REGION_OP,      4,        0,         {EAmlName,         EAmlUInt8,        EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x5B 0x81 */ {AML_DEBUG_STR ("FieldOp")             AML_EXT_OP,                AML_EXT_FIELD_OP,       2,        0,         {EAmlName,         EAmlUInt8,        EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_FIELD_LIST},
+  /* 0x5B 0x82 */ {AML_DEBUG_STR ("DeviceOp")            AML_EXT_OP,                AML_EXT_DEVICE_OP,      1,        0,         {EAmlName,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE},
+  /* 0x5B 0x83 */ {AML_DEBUG_STR ("ProcessorOp")         AML_EXT_OP,                AML_EXT_PROCESSOR_OP,   4,        0,         {EAmlName,         EAmlUInt8,        EAmlUInt32,       EAmlUInt8,        EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE},
+  /* 0x5B 0x84 */ {AML_DEBUG_STR ("PowerResOp")          AML_EXT_OP,                AML_EXT_POWER_RES_OP,   3,        0,         {EAmlName,         EAmlUInt8,        EAmlUInt16,       EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE},
+  /* 0x5B 0x85 */ {AML_DEBUG_STR ("ThermalZoneOp")       AML_EXT_OP,                AML_EXT_THERMAL_ZONE_OP,1,        0,         {EAmlName,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE},
+  /* 0x5B 0x86 */ {AML_DEBUG_STR ("IndexFieldOp")        AML_EXT_OP,                AML_EXT_INDEX_FIELD_OP, 3,        0,         {EAmlName,         EAmlName,         EAmlUInt8,        EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_FIELD_LIST},
+  /* 0x5B 0x87 */ {AML_DEBUG_STR ("BankFieldOp")         AML_EXT_OP,                AML_EXT_BANK_FIELD_OP,  4,        0,         {EAmlName,         EAmlName,         EAmlObject,       EAmlUInt8,        EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_FIELD_LIST},
+  /* 0x5B 0x88 */ {AML_DEBUG_STR ("DataRegionOp")        AML_EXT_OP,                AML_EXT_DATA_REGION_OP, 4,        0,         {EAmlName,         EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x5C */      {AML_DEBUG_STR ("RootChar")            AML_ROOT_CHAR,             0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x5E */      {AML_DEBUG_STR ("ParentPrefixChar")    AML_PARENT_PREFIX_CHAR,    0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x5F */      {AML_DEBUG_STR ("NameChar")            '_',                       0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_NAME_CHAR},
+  /* 0x60 */      {AML_DEBUG_STR ("Local0Op")            AML_LOCAL0,                0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x61 */      {AML_DEBUG_STR ("Local1Op")            AML_LOCAL1,                0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x62 */      {AML_DEBUG_STR ("Local2Op")            AML_LOCAL2,                0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x63 */      {AML_DEBUG_STR ("Local3Op")            AML_LOCAL3,                0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x64 */      {AML_DEBUG_STR ("Local4Op")            AML_LOCAL4,                0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x65 */      {AML_DEBUG_STR ("Local5Op")            AML_LOCAL5,                0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x66 */      {AML_DEBUG_STR ("Local6Op")            AML_LOCAL6,                0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x67 */      {AML_DEBUG_STR ("Local7Op")            AML_LOCAL7,                0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x68 */      {AML_DEBUG_STR ("Arg0Op")              AML_ARG0,                  0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x69 */      {AML_DEBUG_STR ("Arg1Op")              AML_ARG1,                  0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x6A */      {AML_DEBUG_STR ("Arg2Op")              AML_ARG2,                  0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x6B */      {AML_DEBUG_STR ("Arg3Op")              AML_ARG3,                  0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x6C */      {AML_DEBUG_STR ("Arg4Op")              AML_ARG4,                  0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x6D */      {AML_DEBUG_STR ("Arg5Op")              AML_ARG5,                  0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x6E */      {AML_DEBUG_STR ("Arg6Op")              AML_ARG6,                  0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x70 */      {AML_DEBUG_STR ("StoreOp")             AML_STORE_OP,              0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x71 */      {AML_DEBUG_STR ("RefOfOp")             AML_REF_OF_OP,             0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x72 */      {AML_DEBUG_STR ("AddOp")               AML_ADD_OP,                0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x73 */      {AML_DEBUG_STR ("ConcatOp")            AML_CONCAT_OP,             0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x74 */      {AML_DEBUG_STR ("SubtractOp")          AML_SUBTRACT_OP,           0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x75 */      {AML_DEBUG_STR ("IncrementOp")         AML_INCREMENT_OP,          0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x76 */      {AML_DEBUG_STR ("DecrementOp")         AML_DECREMENT_OP,          0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x77 */      {AML_DEBUG_STR ("MultiplyOp")          AML_MULTIPLY_OP,           0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x78 */      {AML_DEBUG_STR ("DivideOp")            AML_DIVIDE_OP,             0,                      4,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone},        0},
+  /* 0x79 */      {AML_DEBUG_STR ("ShiftLeftOp")         AML_SHIFT_LEFT_OP,         0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x7A */      {AML_DEBUG_STR ("ShiftRightOp")        AML_SHIFT_RIGHT_OP,        0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x7B */      {AML_DEBUG_STR ("AndOp")               AML_AND_OP,                0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x7C */      {AML_DEBUG_STR ("NAndOp")              AML_NAND_OP,               0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x7D */      {AML_DEBUG_STR ("OrOp")                AML_OR_OP,                 0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x7E */      {AML_DEBUG_STR ("NorOp")               AML_NOR_OP,                0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x7F */      {AML_DEBUG_STR ("XOrOp")               AML_XOR_OP,                0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x80 */      {AML_DEBUG_STR ("NotOp")               AML_NOT_OP,                0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x81 */      {AML_DEBUG_STR ("FindSetLeftBitOp")    AML_FIND_SET_LEFT_BIT_OP,  0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x82 */      {AML_DEBUG_STR ("FindSetRightBitOp")   AML_FIND_SET_RIGHT_BIT_OP, 0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x83 */      {AML_DEBUG_STR ("DerefOfOp")           AML_DEREF_OF_OP,           0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x84 */      {AML_DEBUG_STR ("ConcatResOp")         AML_CONCAT_RES_OP,         0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x85 */      {AML_DEBUG_STR ("ModOp")               AML_MOD_OP,                0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x86 */      {AML_DEBUG_STR ("NotifyOp")            AML_NOTIFY_OP,             0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x87 */      {AML_DEBUG_STR ("SizeOfOp")            AML_SIZE_OF_OP,            0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x88 */      {AML_DEBUG_STR ("IndexOp")             AML_INDEX_OP,              0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x89 */      {AML_DEBUG_STR ("MatchOp")             AML_MATCH_OP,              0,                      6,        0,         {EAmlObject,       EAmlUInt8,        EAmlObject,       EAmlUInt8,        EAmlObject,       EAmlObject},      0},
+  /* 0x8A */      {AML_DEBUG_STR ("CreateDWordFieldOp")  AML_CREATE_DWORD_FIELD_OP, 0,                      3,        2,         {EAmlObject,       EAmlObject,       EAmlName,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x8B */      {AML_DEBUG_STR ("CreateWordFieldOp")   AML_CREATE_WORD_FIELD_OP,  0,                      3,        2,         {EAmlObject,       EAmlObject,       EAmlName,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x8C */      {AML_DEBUG_STR ("CreateByteFieldOp")   AML_CREATE_BYTE_FIELD_OP,  0,                      3,        2,         {EAmlObject,       EAmlObject,       EAmlName,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x8D */      {AML_DEBUG_STR ("CreateBitFieldOp")    AML_CREATE_BIT_FIELD_OP,   0,                      3,        2,         {EAmlObject,       EAmlObject,       EAmlName,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x8E */      {AML_DEBUG_STR ("ObjectTypeOp")        AML_OBJECT_TYPE_OP,        0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x8F */      {AML_DEBUG_STR ("CreateQWordFieldOp")  AML_CREATE_QWORD_FIELD_OP, 0,                      3,        2,         {EAmlObject,       EAmlObject,       EAmlName,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IN_NAMESPACE},
+  /* 0x90 */      {AML_DEBUG_STR ("LAndOp")              AML_LAND_OP,               0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x91 */      {AML_DEBUG_STR ("LOrOp")               AML_LOR_OP,                0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x92 */      {AML_DEBUG_STR ("LNotOp")              AML_LNOT_OP,               0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x93 */      {AML_DEBUG_STR ("LEqualOp")            AML_LEQUAL_OP,             0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x94 */      {AML_DEBUG_STR ("LGreaterOp")          AML_LGREATER_OP,           0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x95 */      {AML_DEBUG_STR ("LLessOp")             AML_LLESS_OP,              0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x96 */      {AML_DEBUG_STR ("ToBufferOp")          AML_TO_BUFFER_OP,          0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x97 */      {AML_DEBUG_STR ("ToDecimalStringOp")   AML_TO_DEC_STRING_OP,      0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x98 */      {AML_DEBUG_STR ("ToHexStringOp")       AML_TO_HEX_STRING_OP,      0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x99 */      {AML_DEBUG_STR ("ToIntegerOp")         AML_TO_INTEGER_OP,         0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x9C */      {AML_DEBUG_STR ("ToStringOp")          AML_TO_STRING_OP,          0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x9D */      {AML_DEBUG_STR ("CopyObjectOp")        AML_COPY_OBJECT_OP,        0,                      2,        0,         {EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x9E */      {AML_DEBUG_STR ("MidOp")               AML_MID_OP,                0,                      3,        0,         {EAmlObject,       EAmlObject,       EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0x9F */      {AML_DEBUG_STR ("ContinueOp")          AML_CONTINUE_OP,           0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0xA0 */      {AML_DEBUG_STR ("IfOp")                AML_IF_OP,                 0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ},
+  /* 0xA1 */      {AML_DEBUG_STR ("ElseOp")              AML_ELSE_OP,               0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ},
+  /* 0xA2 */      {AML_DEBUG_STR ("WhileOp")             AML_WHILE_OP,              0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ},
+  /* 0xA3 */      {AML_DEBUG_STR ("NoopOp")              AML_NOOP_OP,               0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0xA4 */      {AML_DEBUG_STR ("ReturnOp")            AML_RETURN_OP,             0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0xA5 */      {AML_DEBUG_STR ("BreakOp")             AML_BREAK_OP,              0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0xCC */      {AML_DEBUG_STR ("BreakPointOp")        AML_BREAK_POINT_OP,        0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+  /* 0xD0 */      {AML_DEBUG_STR ("MethodInvocOp")       AML_METHOD_INVOC_OP,       0,                      2,        0,         {EAmlName,         EAmlUInt8,        EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_PSEUDO_OPCODE | AML_HAS_CHILD_OBJ},
+  /* 0xFF */      {AML_DEBUG_STR ("OnesOp")              AML_ONES_OP,               0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        0},
+};
+
+/** AML grammar encoding for field elements.
+
+  Some AML objects are expecting a FieldList. They are referred in this library
+  as field nodes. These objects have the following opcodes:
+   - FieldOp;
+   - IndexFieldOp;
+   - BankFieldOp.
+  In the AML grammar encoding table, they have the AML_HAS_FIELD_LIST
+  attribute.
+
+  A field list is made of field elements.
+  According to the ACPI 6.3 specification, s20.2.5.2 "Named Objects Encoding",
+  field elements can be:
+   - NamedField           := NameSeg PkgLength;
+   - ReservedField        := 0x00 PkgLength;
+   - AccessField          := 0x01 AccessType AccessAttrib;
+   - ConnectField         := <0x02 NameString> | <0x02 BufferData>;
+   - ExtendedAccessField  := 0x03 AccessType ExtendedAccessAttrib AccessLength.
+
+  A small set of opcodes describes field elements. They are referred in this
+  library as field opcodes.
+  The NamedField field element doesn't have a field opcode. A pseudo
+  OpCode/SubOpCode couple has been created for it.
+
+  Field elements:
+   - don't have a SubOpCode;
+   - have at most 3 fixed arguments (6 for object opcodes,
+     8 for method invocations);
+   - don't have variable list of arguments;
+   - are not part of the AML namespace, except NamedField field elements.
+*/
+GLOBAL_REMOVE_IF_UNREFERENCED
+STATIC
+CONST
+AML_BYTE_ENCODING mAmlFieldEncoding[] = {
+  // Comment       Str                                   OpCode                     SubOpCode               MaxIndex  NameIndex   0                 1                 2                 3                 4                 5                 Attribute
+  /* 0x00 */      {AML_DEBUG_STR ("FieldReservedOp")     AML_FIELD_RESERVED_OP,     0,                      0,        0,         {EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_FIELD_ELEMENT | AML_HAS_PKG_LENGTH},
+  /* 0x01 */      {AML_DEBUG_STR ("FieldAccessOp")       AML_FIELD_ACCESS_OP,       0,                      2,        0,         {EAmlUInt8,        EAmlUInt8,        EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_FIELD_ELEMENT},
+  /* 0x02 */      {AML_DEBUG_STR ("FieldConnectionOp")   AML_FIELD_CONNECTION_OP,   0,                      1,        0,         {EAmlObject,       EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_FIELD_ELEMENT},
+  /* 0x03 */      {AML_DEBUG_STR ("FieldExtAccessOp")    AML_FIELD_EXT_ACCESS_OP,   0,                      3,        0,         {EAmlUInt8,        EAmlUInt8,        EAmlUInt8,        EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_FIELD_ELEMENT},
+  /* 0x04 */      {AML_DEBUG_STR ("FieldNamed")          AML_FIELD_NAMED_OP,        0,                      2,        0,         {EAmlName,         EAmlFieldPkgLen,  EAmlNone,         EAmlNone,         EAmlNone,         EAmlNone},        AML_IS_FIELD_ELEMENT | AML_IS_PSEUDO_OPCODE | AML_IN_NAMESPACE}
+};
+
+/** Get the AML_BYTE_ENCODING entry in the AML encoding table.
+
+  Note: For Pseudo OpCodes this function returns NULL.
+
+  @param  [in]  Buffer    Pointer to an OpCode/SubOpCode couple.
+                          If *Buffer = 0x5b (extended OpCode),
+                          Buffer must be at least two bytes long.
+
+  @return The corresponding AML_BYTE_ENCODING entry.
+          NULL if not found.
+**/
+CONST
+AML_BYTE_ENCODING *
+EFIAPI
+AmlGetByteEncoding (
+  IN  CONST UINT8   * Buffer
+  )
+{
+  UINT8     OpCode;
+  UINT8     SubOpCode;
+  UINT32    Index;
+
+  if (Buffer == NULL) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  // Get OpCode and SubOpCode.
+  OpCode = Buffer[0];
+  if (OpCode == AML_EXT_OP) {
+    SubOpCode = Buffer[1];
+  } else {
+    SubOpCode = 0;
+  }
+
+  // Search the table.
+  for (Index = 0;
+       Index < (sizeof (mAmlByteEncoding) / sizeof (mAmlByteEncoding[0]));
+       Index++) {
+    if ((mAmlByteEncoding[Index].OpCode == OpCode) &&
+        (mAmlByteEncoding[Index].SubOpCode == SubOpCode)) {
+       if ((mAmlByteEncoding[Index].Attribute & AML_IS_PSEUDO_OPCODE) ==
+            AML_IS_PSEUDO_OPCODE) {
+        // A pseudo OpCode cannot be parsed as it is internal to this library.
+        // The MethodInvocation encoding can be detected by NameSpace lookup.
+        ASSERT (0);
+        return NULL;
+      }
+      return &mAmlByteEncoding[Index];
+    }
+  }
+
+  return NULL;
+}
+
+/** Get the AML_BYTE_ENCODING entry in the AML encoding table
+    by providing an OpCode/SubOpCode couple.
+
+  @param  [in]  OpCode     OpCode.
+  @param  [in]  SubOpCode  SubOpCode.
+
+  @return The corresponding AML_BYTE_ENCODING entry.
+          NULL if not found.
+**/
+CONST
+AML_BYTE_ENCODING *
+EFIAPI
+AmlGetByteEncodingByOpCode (
+  IN  UINT8   OpCode,
+  IN  UINT8   SubOpCode
+  )
+{
+  UINT32    Index;
+
+  // Search the table.
+  for (Index = 0;
+       Index < (sizeof (mAmlByteEncoding) / sizeof (mAmlByteEncoding[0]));
+       Index++) {
+    if ((mAmlByteEncoding[Index].OpCode == OpCode) &&
+        (mAmlByteEncoding[Index].SubOpCode == SubOpCode)) {
+      return &mAmlByteEncoding[Index];
+    }
+  }
+  return NULL;
+}
+
+/** Get the AML_BYTE_ENCODING entry in the field encoding table.
+
+  Note: For Pseudo OpCodes this function returns NULL.
+
+  @param  [in]  Buffer  Pointer to a field OpCode.
+                        No SubOpCode is expected.
+
+  @return The corresponding AML_BYTE_ENCODING entry
+          in the field encoding table.
+          NULL if not found.
+**/
+CONST
+AML_BYTE_ENCODING *
+EFIAPI
+AmlGetFieldEncoding (
+  IN  CONST UINT8   * Buffer
+  )
+{
+  UINT8     OpCode;
+  UINT32    Index;
+
+  if (Buffer == NULL) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  // Get OpCode.
+  OpCode = *Buffer;
+
+  // Search in the table.
+  for (Index = 0;
+       Index < (sizeof (mAmlFieldEncoding) / sizeof (mAmlFieldEncoding[0]));
+       Index++) {
+    if (mAmlFieldEncoding[Index].OpCode == OpCode) {
+      if ((mAmlFieldEncoding[Index].Attribute & AML_IS_PSEUDO_OPCODE) ==
+             AML_IS_PSEUDO_OPCODE) {
+        // A pseudo OpCode cannot be parsed as it is internal to this library.
+        // The NamedField encoding can be detected because it begins with a
+        // char.
+        ASSERT (0);
+        return NULL;
+      }
+      return &mAmlFieldEncoding[Index];
+    }
+  }
+
+  return NULL;
+}
+
+/** Get the AML_BYTE_ENCODING entry in the field encoding table
+    by providing an OpCode/SubOpCode couple.
+
+  @param  [in]  OpCode     OpCode.
+  @param  [in]  SubOpCode  SubOpCode.
+
+  @return The corresponding AML_BYTE_ENCODING entry
+          in the field encoding table.
+          NULL if not found.
+**/
+CONST
+AML_BYTE_ENCODING *
+EFIAPI
+AmlGetFieldEncodingByOpCode (
+  IN  UINT8   OpCode,
+  IN  UINT8   SubOpCode
+  )
+{
+  UINT32    Index;
+
+  // Search the table.
+  for (Index = 0;
+       Index < (sizeof (mAmlFieldEncoding) / sizeof (mAmlFieldEncoding[0]));
+       Index++) {
+    if ((mAmlFieldEncoding[Index].OpCode == OpCode) &&
+        (mAmlFieldEncoding[Index].SubOpCode == SubOpCode)) {
+      return &mAmlFieldEncoding[Index];
+    }
+  }
+  return NULL;
+}
+
+// Enable this function for debug.
+#if !defined (MDEPKG_NDEBUG)
+/** Look for an OpCode/SubOpCode couple in the AML grammar,
+    and return a corresponding string.
+
+  @param  [in]  OpCode      The OpCode.
+  @param  [in]  SubOpCode   The SubOpCode.
+
+  @return A string describing the OpCode/SubOpCode couple.
+          NULL if not found.
+**/
+CONST
+CHAR8 *
+AmlGetOpCodeStr (
+  IN  UINT8   OpCode,
+  IN  UINT8   SubOpCode
+  )
+{
+  EAML_PARSE_INDEX  Index;
+
+  // Search the table.
+  for (Index = 0;
+       Index < (sizeof (mAmlByteEncoding) / sizeof (mAmlByteEncoding[0]));
+       Index++) {
+    if ((mAmlByteEncoding[Index].OpCode == OpCode) &&
+        (mAmlByteEncoding[Index].SubOpCode == SubOpCode)) {
+      return mAmlByteEncoding[Index].Str;
+    }
+  }
+
+  ASSERT (0);
+  return NULL;
+}
+
+/** Look for an OpCode/SubOpCode couple in the AML field element grammar,
+    and return a corresponding string.
+
+  @param  [in]  OpCode      The OpCode.
+  @param  [in]  SubOpCode   The SubOpCode. Must be zero.
+
+  @return A string describing the OpCode/SubOpCode couple.
+          NULL if not found.
+**/
+CONST
+CHAR8 *
+AmlGetFieldOpCodeStr (
+  IN  UINT8   OpCode,
+  IN  UINT8   SubOpCode
+  )
+{
+  EAML_PARSE_INDEX  Index;
+
+  if (SubOpCode != 0) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  // Search the table.
+  for (Index = 0;
+       Index < (sizeof (mAmlFieldEncoding) / sizeof (mAmlFieldEncoding[0]));
+       Index++) {
+    if ((mAmlFieldEncoding[Index].OpCode == OpCode)) {
+      return mAmlFieldEncoding[Index].Str;
+    }
+  }
+
+  ASSERT (0);
+  return NULL;
+}
+#endif // MDEPKG_NDEBUG
+
+/** Check whether the OpCode/SubOpcode couple is a valid entry
+    in the AML grammar encoding table.
+
+  @param  [in]  OpCode     OpCode to check.
+  @param  [in]  SubOpCode  SubOpCode to check.
+
+  @retval TRUE    The OpCode/SubOpCode couple is valid.
+  @retval FALSE   Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlIsOpCodeValid (
+  IN  UINT8   OpCode,
+  IN  UINT8   SubOpCode
+  )
+{
+  EAML_PARSE_INDEX  Index;
+
+  // Search the table.
+  for (Index = 0;
+       Index < (sizeof (mAmlByteEncoding) / sizeof (mAmlByteEncoding[0]));
+       Index++) {
+    if ((mAmlByteEncoding[Index].OpCode == OpCode) &&
+        (mAmlByteEncoding[Index].SubOpCode == SubOpCode)) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+/** AML_PARSE_FORMAT to EAML_NODE_DATA_TYPE translation table.
+
+  AML_PARSE_FORMAT describes an internal set of values identifying the types
+  that can be found while parsing an AML bytestream.
+  EAML_NODE_DATA_TYPE describes an external set of values allowing to identify
+  what type of data can be found in data nodes.
+*/
+GLOBAL_REMOVE_IF_UNREFERENCED
+STATIC
+CONST
+EAML_NODE_DATA_TYPE mAmlTypeToNodeDataType[] = {
+  EAmlNodeDataTypeNone,         // EAmlNone
+  EAmlNodeDataTypeUInt,         // EAmlUInt8
+  EAmlNodeDataTypeUInt,         // EAmlUInt16
+  EAmlNodeDataTypeUInt,         // EAmlUInt32
+  EAmlNodeDataTypeUInt,         // EAmlUInt64
+  EAmlNodeDataTypeReserved5,    // EAmlObject
+  EAmlNodeDataTypeNameString,   // EAmlName
+  EAmlNodeDataTypeString,       // EAmlString
+  EAmlNodeDataTypeFieldPkgLen   // EAmlFieldPkgLen
+};
+
+/** Convert an AML_PARSE_FORMAT to its corresponding EAML_NODE_DATA_TYPE.
+
+  @param  [in]  AmlType   Input AML Type.
+
+  @return The corresponding EAML_NODE_DATA_TYPE.
+          EAmlNodeDataTypeNone if not found.
+**/
+EAML_NODE_DATA_TYPE
+EFIAPI
+AmlTypeToNodeDataType (
+  IN  AML_PARSE_FORMAT  AmlType
+  )
+{
+  if (AmlType >=
+      (sizeof (mAmlTypeToNodeDataType) / sizeof (mAmlTypeToNodeDataType[0]))) {
+    ASSERT (0);
+    return EAmlNodeDataTypeNone;
+  }
+
+  return mAmlTypeToNodeDataType[AmlType];
+}
+
+/** Get the package length from the buffer.
+
+  @param  [in]  Buffer      AML buffer.
+  @param  [out] PkgLength   The interpreted PkgLen value.
+                            Length cannot exceed 2^28.
+
+  @return The number of bytes to represent the package length.
+          0 if an issue occurred.
+**/
+UINT32
+EFIAPI
+AmlGetPkgLength (
+  IN  CONST UINT8   * Buffer,
+  OUT       UINT32  * PkgLength
+  )
+{
+  UINT8     LeadByte;
+  UINT8     ByteCount;
+  UINT32    RealLength;
+  UINT32    Offset;
+
+  if ((Buffer == NULL)  ||
+      (PkgLength == NULL)) {
+    ASSERT (0);
+    return 0;
+  }
+
+  /* From ACPI 6.3 specification, s20.2.4 "Package Length Encoding":
+
+  PkgLength := PkgLeadByte |
+               <PkgLeadByte ByteData> |
+               <PkgLeadByte ByteData ByteData> |
+               <PkgLeadByte ByteData ByteData ByteData>
+
+  PkgLeadByte := <bit 7-6: ByteData count that follows (0-3)>
+                 <bit 5-4: Only used if PkgLength < 63>
+                 <bit 3-0: Least significant package length nibble>
+
+  Note:
+    The high 2 bits of the first byte reveal how many
+    follow bytes are in the PkgLength. If the
+    PkgLength has only one byte, bit 0 through 5 are
+    used to encode the package length (in other
+    words, values 0-63). If the package length value
+    is more than 63, more than one byte must be
+    used for the encoding in which case bit 4 and 5 of
+    the PkgLeadByte are reserved and must be zero.
+    If the multiple bytes encoding is used, bits 0-3 of
+    the PkgLeadByte become the least significant 4
+    bits of the resulting package length value. The next
+    ByteData will become the next least
+    significant 8 bits of the resulting value and so on,
+    up to 3 ByteData bytes. Thus, the maximum
+    package length is 2**28.
+  */
+
+  LeadByte = *Buffer;
+  ByteCount = (LeadByte >> 6) & 0x03U;
+  Offset = ByteCount + 1U;
+  RealLength = 0;
+
+  // Switch on the number of bytes used to store the PkgLen.
+  switch (ByteCount) {
+    case 0:
+    {
+      RealLength = LeadByte;
+      break;
+    }
+    case 1:
+    {
+      RealLength = *(Buffer + 1);
+      RealLength = (RealLength << 4) | (LeadByte & 0xF);
+      break;
+    }
+    case 2:
+    {
+      RealLength = *(Buffer + 1);
+      RealLength |= ((UINT32)(*(Buffer + 2))) << 8;
+      RealLength = (RealLength << 4) | (LeadByte & 0xF);
+      break;
+    }
+    case 3:
+    {
+      RealLength = *(Buffer + 1);
+      RealLength |= ((UINT32)(*(Buffer + 2))) << 8;
+      RealLength |= ((UINT32)(*(Buffer + 3))) << 16;
+      RealLength = (RealLength << 4) | (LeadByte & 0xF);
+      break;
+    }
+    default:
+    {
+      ASSERT (0);
+      Offset = 0;
+      break;
+    }
+  } // switch
+
+  *PkgLength = RealLength;
+
+  return Offset;
+}
+
+/** Convert the Length to the AML PkgLen encoding,
+    then and write it in the Buffer.
+
+  @param  [in]    Length  Length to convert.
+                          Length cannot exceed 2^28.
+  @param  [out]   Buffer  Write the result in this Buffer.
+
+  @return The number of bytes used to write the Length.
+**/
+UINT8
+EFIAPI
+AmlSetPkgLength (
+  IN  UINT32    Length,
+  OUT UINT8   * Buffer
+  )
+{
+  UINT8   LeadByte;
+  UINT8   Offset;
+  UINT8   CurrentOffset;
+  UINT8   CurrentShift;
+  UINT32  ComputedLength;
+
+  if (Buffer == NULL) {
+    ASSERT (0);
+    return 0;
+  }
+
+  LeadByte = 0;
+  Offset = 0;
+
+  if ((Length < (1 << 6))) {
+    // Length < 2^6, only need one byte to encode it.
+    LeadByte = (UINT8)Length;
+
+  } else {
+    // Need more than one byte to encode it.
+    // Test Length to find how many bytes are needed.
+
+    if (Length >= (1 << 28)) {
+      // Length >= 2^28, should not be possible.
+      ASSERT (0);
+      return 0;
+
+    } else if (Length >= (1 << 20)) {
+      // Length >= 2^20
+      Offset = 3;
+
+    } else if (Length >= (1 << 12)) {
+      // Length >= 2^12
+      Offset = 2;
+
+    } else if (Length >= (1 << 6)) {
+      // Length >= 2^6
+      Offset = 1;
+
+    } else {
+      // Should not be possible.
+      ASSERT (0);
+      return 0;
+    }
+
+    // Set the LeadByte.
+    LeadByte = (UINT8)(Offset << 6);
+    LeadByte = (UINT8)(LeadByte | (Length & 0xF));
+  }
+
+  // Write to the Buffer.
+  *Buffer = LeadByte;
+  CurrentOffset = 1;
+  while (CurrentOffset < (Offset + 1)) {
+    CurrentShift = (UINT8)((CurrentOffset - 1) * 8);
+    ComputedLength = Length & (UINT32)(0x00000FF0 << CurrentShift);
+    ComputedLength = (ComputedLength) >> (4 + CurrentShift);
+    LeadByte = (UINT8)(ComputedLength & 0xFF);
+    *(Buffer + CurrentOffset) = LeadByte;
+    CurrentOffset++;
+  }
+
+  return ++Offset;
+}
+
+/** Compute the number of bytes required to write a package length.
+
+  @param  [in]  Length  The length to convert in the AML package length
+                        encoding style.
+                        Length cannot exceed 2^28.
+
+  @return The number of bytes required to write the Length.
+**/
+UINT8
+EFIAPI
+AmlComputePkgLengthWidth (
+  IN  UINT32  Length
+  )
+{
+  // Length >= 2^28, should not be possible.
+  if (Length >= (1 << 28)) {
+    ASSERT (0);
+    return 0;
+
+  } else if (Length >= (1 << 20)) {
+    // Length >= 2^20
+    return 4;
+
+  } else if (Length >= (1 << 12)) {
+    // Length >= 2^12
+    return 3;
+
+  } else if (Length >= (1 << 6)) {
+    // Length >= 2^6
+    return 2;
+  }
+
+  // Length < 2^6
+  return 1;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h b/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h
new file mode 100644
index 0000000000000000000000000000000000000000..ba1228621303f5208f94891d46594c50637195db
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h
@@ -0,0 +1,330 @@
+/** @file
+  AML grammar definitions.
+
+  Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved. <BR>
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_H_
+#define AML_H_
+
+#include <AmlDefines.h>
+#include <AmlInclude.h>
+#include <IndustryStandard/AcpiAml.h>
+
+#if !defined (MDEPKG_NDEBUG)
+#define AML_DEBUG_STR(str) str,
+#else
+#define AML_DEBUG_STR(x)
+#endif // MDEPKG_NDEBUG
+
+/** AML types.
+
+  In the AML bytestream, data is represented using one of the following types.
+  These types are used in the parsing logic to know what kind of data is
+  expected next in the bytestream. This allows to parse data according
+  to the AML_PARSE_FORMAT type.
+  E.g.: A string will not be parsed in the same way as a UINT8.
+
+  These are internal types.
+*/
+typedef enum EAmlParseFormat {
+  EAmlNone   = 0,     ///< No data expected.
+  EAmlUInt8,          ///< One byte value evaluated as a UINT8.
+  EAmlUInt16,         ///< Two byte value evaluated as a UINT16.
+  EAmlUInt32,         ///< Four byte value evaluated as a UINT32.
+  EAmlUInt64,         ///< Eight byte value evaluated as a UINT64.
+  EAmlObject,         ///< AML object, starting with an OpCode/SubOpCode
+                      ///  couple, potentially followed by package length.
+                      ///  EAmlName is a subtype of an EAmlObject.
+                      ///  Indeed, an EAmlName can also be evaluated as
+                      ///  an EAmlObject in the parsing.
+  EAmlName,           ///< Name corresponding to the NameString keyword
+                      ///  in the ACPI specification. E.g.: "\_SB_.DEV0"
+  EAmlString,         ///< NULL terminated string.
+  EAmlFieldPkgLen,    ///< A field package length (PkgLen). A data node of this
+                      ///  type can only be found in a field list, in a
+                      ///  NamedField statement. The PkgLen is otherwise
+                      /// part of the object node structure.
+  EAmlParseFormatMax  ///< Max enum.
+} AML_PARSE_FORMAT;
+
+/** AML attributes
+
+  To add some more information to the byte encoding, it is possible to add
+  these attributes.
+*/
+typedef UINT32 AML_OP_ATTRIBUTE;
+
+/** A PkgLength is expected between the OpCode/SubOpCode couple and the first
+    fixed argument of the object.
+*/
+#define AML_HAS_PKG_LENGTH      0x00001U
+
+/** The object's OpCode is actually a character. Encodings with this attribute
+    don't describe objects. The dual/multi name prefix have this attribute,
+    indicating the start of a longer NameString.
+*/
+#define AML_IS_NAME_CHAR        0x00002U
+
+/** A variable list of arguments is following the last fixed argument. Each
+    argument is evaluated as an EAmlObject.
+*/
+#define AML_HAS_CHILD_OBJ       0x00004U
+
+/** This is a sub-type of a variable list of arguments. It can only be
+    found in buffer objects. A ByteList is either a list of
+    bytes or a list of resource data elements. Resource data elements
+    have specific opcodes.
+*/
+#define AML_HAS_BYTE_LIST       0x00008U
+
+/** This is a sub-type of a variable list of arguments. It can only be
+    found in Fields, IndexFields and BankFields.
+    A FieldList is made of FieldElements. FieldElements have specific opcodes.
+*/
+#define AML_HAS_FIELD_LIST      0x00010U
+
+/** This object node is a field element. Its opcode is to be fetched from
+    the field encoding table.
+*/
+#define AML_IS_FIELD_ELEMENT    0x00020U
+
+/** The object has a name and which is part of the AML namespace. The name
+    can be found in the fixed argument list at the NameIndex.
+*/
+#define AML_IN_NAMESPACE        0x10000U
+
+/** Some OpCodes have been created in this library. They are called
+    pseudo opcodes and must stay internal to this library.
+*/
+#define AML_IS_PSEUDO_OPCODE    0x20000U
+
+/** Encoding of an AML object.
+
+  Every AML object has a specific encoding. This encoding information
+  is used to parse AML objects. A table of AML_BYTE_ENCODING entries
+  allows to parse an AML bytestream.
+  This structure is also used to describe field objects.
+
+  Cf. ACPI 6.3 specification, s20.2.
+*/
+typedef struct _AML_BYTE_ENCODING {
+// Enable this field for debug.
+#if !defined (MDEPKG_NDEBUG)
+  /// String field allowing to print the AML object.
+  CONST CHAR8         * Str;
+#endif // MDEPKG_NDEBUG
+
+  /// OpCode of the AML object.
+  UINT8                 OpCode;
+
+  /// SubOpCode of the AML object.
+  /// The SubOpcode field has a valid value when the OpCode is 0x5B,
+  /// otherwise this field must be zero.
+  /// For field objects, the SubOpCode is not used.
+  UINT8                 SubOpCode;
+
+  /// Number of fixed arguments for the AML statement represented
+  /// by the OpCode & SubOpcode.
+  /// Maximum is 6 for AML objects.
+  /// Maximum is 3 for field objects.
+  EAML_PARSE_INDEX      MaxIndex;
+
+  /// If the encoding has the AML_IN_NAMESPACE attribute (cf Attribute
+  /// field below), indicate where to find the name in the fixed list
+  /// of arguments.
+  EAML_PARSE_INDEX      NameIndex;
+
+  /// Type of each fixed argument.
+  AML_PARSE_FORMAT      Format[EAmlParseIndexMax];
+
+  /// Additional information on the AML object.
+  AML_OP_ATTRIBUTE      Attribute;
+} AML_BYTE_ENCODING;
+
+/** Get the AML_BYTE_ENCODING entry in the AML encoding table.
+
+  Note: For Pseudo OpCodes this function returns NULL.
+
+  @param  [in]  Buffer    Pointer to an OpCode/SubOpCode couple.
+                          If *Buffer = 0x5b (extended OpCode),
+                          Buffer must be at least two bytes long.
+
+  @return The corresponding AML_BYTE_ENCODING entry.
+          NULL if not found.
+**/
+CONST
+AML_BYTE_ENCODING *
+EFIAPI
+AmlGetByteEncoding (
+  IN  CONST UINT8   * Buffer
+  );
+
+/** Get the AML_BYTE_ENCODING entry in the AML encoding table
+    by providing an OpCode/SubOpCode couple.
+
+  @param  [in]  OpCode     OpCode.
+  @param  [in]  SubOpCode  SubOpCode.
+
+  @return The corresponding AML_BYTE_ENCODING entry.
+          NULL if not found.
+**/
+CONST
+AML_BYTE_ENCODING *
+EFIAPI
+AmlGetByteEncodingByOpCode (
+  IN  UINT8   OpCode,
+  IN  UINT8   SubOpCode
+  );
+
+/** Get the AML_BYTE_ENCODING entry in the field encoding table.
+
+  Note: For Pseudo OpCodes this function returns NULL.
+
+  @param  [in]  Buffer  Pointer to a field OpCode.
+                        No SubOpCode is expected.
+
+  @return The corresponding AML_BYTE_ENCODING entry
+          in the field encoding table.
+          NULL if not found.
+**/
+CONST
+AML_BYTE_ENCODING *
+EFIAPI
+AmlGetFieldEncoding (
+  IN  CONST UINT8   * Buffer
+  );
+
+/** Get the AML_BYTE_ENCODING entry in the field encoding table
+    by providing an OpCode/SubOpCode couple.
+
+  @param  [in]  OpCode     OpCode.
+  @param  [in]  SubOpCode  SubOpCode.
+
+  @return The corresponding AML_BYTE_ENCODING entry
+          in the field encoding table.
+          NULL if not found.
+**/
+CONST
+AML_BYTE_ENCODING *
+EFIAPI
+AmlGetFieldEncodingByOpCode (
+  IN  UINT8   OpCode,
+  IN  UINT8   SubOpCode
+  );
+
+// Enable this function for debug.
+#if !defined (MDEPKG_NDEBUG)
+/** Look for an OpCode/SubOpCode couple in the AML grammar,
+    and return a corresponding string.
+
+  @param  [in]  OpCode      The OpCode.
+  @param  [in]  SubOpCode   The SubOpCode.
+
+  @return A string describing the OpCode/SubOpCode couple.
+          NULL if not found.
+**/
+CONST
+CHAR8 *
+AmlGetOpCodeStr (
+  IN  UINT8   OpCode,
+  IN  UINT8   SubOpCode
+  );
+
+/** Look for an OpCode/SubOpCode couple in the AML field element grammar,
+    and return a corresponding string.
+
+  @param  [in]  OpCode      The OpCode.
+  @param  [in]  SubOpCode   The SubOpCode. Must be zero.
+
+  @return A string describing the OpCode/SubOpCode couple.
+          NULL if not found.
+**/
+CONST
+CHAR8 *
+AmlGetFieldOpCodeStr (
+  IN  UINT8   OpCode,
+  IN  UINT8   SubOpCode
+  );
+#endif // MDEPKG_NDEBUG
+
+/** Check whether the OpCode/SubOpcode couple is a valid entry
+    in the AML grammar encoding table.
+
+  @param  [in]  OpCode     OpCode to check.
+  @param  [in]  SubOpCode  SubOpCode to check.
+
+  @retval TRUE    The OpCode/SubOpCode couple is valid.
+  @retval FALSE   Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlIsOpCodeValid (
+  IN  UINT8   OpCode,
+  IN  UINT8   SubOpCode
+  );
+
+/** Convert an AML_PARSE_FORMAT to its corresponding EAML_NODE_DATA_TYPE.
+
+  @param  [in]  AmlType   Input AML Type.
+
+  @return The corresponding EAML_NODE_DATA_TYPE.
+          EAmlNodeDataTypeNone if not found.
+**/
+EAML_NODE_DATA_TYPE
+EFIAPI
+AmlTypeToNodeDataType (
+  IN  AML_PARSE_FORMAT  AmlType
+  );
+
+/** Get the package length from the buffer.
+
+  @param  [in]  Buffer      AML buffer.
+  @param  [out] PkgLength   The interpreted PkgLen value.
+                            Length cannot exceed 2^28.
+
+  @return The number of bytes to represent the package length.
+          0 if an issue occurred.
+**/
+UINT32
+EFIAPI
+AmlGetPkgLength (
+  IN  CONST UINT8   * Buffer,
+  OUT       UINT32  * PkgLength
+  );
+
+/** Convert the Length to the AML PkgLen encoding,
+    then and write it in the Buffer.
+
+  @param  [in]    Length  Length to convert.
+                          Length cannot exceed 2^28.
+  @param  [out]   Buffer  Write the result in this Buffer.
+
+  @return The number of bytes used to write the Length.
+**/
+UINT8
+EFIAPI
+AmlSetPkgLength (
+  IN  UINT32    Length,
+  OUT UINT8   * Buffer
+  );
+
+/** Compute the number of bytes required to write a package length.
+
+  @param  [in]  Length  The length to convert in the AML package length
+                        encoding style.
+                        Length cannot exceed 2^28.
+
+  @return The number of bytes required to write the Length.
+**/
+UINT8
+EFIAPI
+AmlComputePkgLengthWidth (
+  IN  UINT32  Length
+  );
+
+#endif // AML_H_
+
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 04/30] DynamicTablesPkg: AML node definitions
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (2 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 03/30] DynamicTablesPkg: AML grammar definition Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 05/30] DynamicTablesPkg: AML tree interface Sami Mujawar
                   ` (26 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

AML has a complex grammar, and this makes runtime modifications
on an AML byte stream difficult. A solution is to parse the AML
bytecode and represent it in a tree data structure, henceforth
called the AML tree.

The AML tree is composite in the sense it has the following node
types:
 - A 'Root node' that represents the root of the AML tree.
 - An 'Object node' that contains the OP Code (AML Encoding).
 - A 'Data node' that contains a data buffer.

The Root node contains the Definition block header (ACPI header)
and a Variable Argument list.
The Object node is composed of an array of Fixed Arguments and
a Variable Argument list.

Fixed arguments can be either Object Nodes or Data nodes. Their
placement (index) in the Fixed Argument array is defined by the
AML encoding of the enclosing Object Node.

Variable arguments can be Object nodes or Data nodes.

Following is a depiction of a typical AML tree:

 (/)                         # Root Node
   \
   |-{(N1)->...}             # Variable Argument list, N1 is
        \                    # an Object Node
         \         /-i       # Child of fixed argument b
          \       /
          |- [a][b][c][d]    # Fixed Arguments
          |- {(e)->(f)->(g)} # Variable Arguments
                \
                 \-h         # Child of variable argument e

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h        | 183 ++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c          | 673 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h          | 212 ++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c | 566 ++++++++++++++++
 4 files changed, 1634 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h b/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h
new file mode 100644
index 0000000000000000000000000000000000000000..fffba6d54b64e8f37ca33d99465ec22eb81b5a99
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h
@@ -0,0 +1,183 @@
+/** @file
+  AML Node Definition.
+
+  Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_NODE_DEFINES_H_
+#define AML_NODE_DEFINES_H_
+
+#include <AmlEncoding/Aml.h>
+#include <IndustryStandard/Acpi.h>
+
+/** AML header node.
+
+  This abstract class represents either a root/object/data node.
+  All the enumerated nodes have this same common header.
+*/
+typedef struct AmlNodeHeader {
+  /// This must be the first field in this structure.
+  LIST_ENTRY              Link;
+
+  /// Parent of this node. NULL for the root node.
+  struct AmlNodeHeader  * Parent;
+
+  /// Node type allowing to identify a root/object/data node.
+  EAML_NODE_TYPE          NodeType;
+} AML_NODE_HEADER;
+
+/** Node handle.
+*/
+typedef AML_NODE_HEADER* AML_NODE_HANDLE;
+
+/** AML root node.
+
+  The root node is unique and at the head of of tree. It is a fake node used
+  to maintain the list of AML statements (stored as object nodes) which are
+  at the first scope level.
+*/
+typedef struct AmlRootNode {
+  /// Header information. Must be the first field of the struct.
+  AML_NODE_HEADER                NodeHeader;
+
+  /// List of object nodes being at the first scope level.
+  /// These are children and can only be object nodes.
+  LIST_ENTRY                     VariableArgs;
+
+  /// ACPI DSDT/SSDT header.
+  EFI_ACPI_DESCRIPTION_HEADER  * SdtHeader;
+} AML_ROOT_NODE;
+
+/** Root Node handle.
+*/
+typedef AML_ROOT_NODE* AML_ROOT_NODE_HANDLE;
+
+/** AML object node.
+
+  Object nodes match AML statements. They are associated with an
+  OpCode/SubOpCode, and can have children.
+*/
+typedef struct AmlObjectNode {
+  /// Header information. Must be the first field of the struct.
+  AML_NODE_HEADER            NodeHeader;
+
+  /// Some object nodes have a variable list of arguments.
+  /// These are children and can only be object/data nodes.
+  /// Cf ACPI specification, s20.3.
+  LIST_ENTRY                 VariableArgs;
+
+  /// Fixed arguments of this object node.
+  /// These are children and can be object/data nodes.
+  /// Cf ACPI specification, s20.3.
+  AML_NODE_HEADER          * FixedArgs[EAmlParseIndexMax];
+
+  /// AML byte encoding. Stores the encoding information:
+  /// (OpCode/SubOpCode/number of fixed arguments/ attributes).
+  CONST AML_BYTE_ENCODING  * AmlByteEncoding;
+
+  /// Some nodes have a PkgLen following their OpCode/SubOpCode in the
+  /// AML bytestream. This field stores the decoded value of the PkgLen.
+  UINT32                     PkgLen;
+} AML_OBJECT_NODE;
+
+/** Object Node handle.
+*/
+typedef AML_OBJECT_NODE* AML_OBJECT_NODE_HANDLE;
+
+/** AML data node.
+
+  Data nodes store the smallest pieces of information.
+  E.g.: UINT8, UINT64, NULL terminated string, etc.
+  Data node don't have children nodes.
+*/
+typedef struct AmlDataNode {
+  /// Header information. Must be the first field of the struct.
+  AML_NODE_HEADER       NodeHeader;
+
+  /// Tag identifying what data is stored in this node.
+  /// E.g. UINT, NULL terminated string, resource data element, etc.
+  EAML_NODE_DATA_TYPE   DataType;
+
+  /// Buffer containing the data stored by this node.
+  UINT8               * Buffer;
+
+  /// Size of the Buffer.
+  UINT32                Size;
+} AML_DATA_NODE;
+
+/** Data Node handle.
+*/
+typedef AML_DATA_NODE* AML_DATA_NODE_HANDLE;
+
+/** Check whether a Node has a valid NodeType.
+
+  @param  [in]  Node  The node to check.
+
+  @retval TRUE  The Node has a valid NodeType.
+  @retval FALSE Otherwise.
+*/
+#define IS_AML_NODE_VALID(Node)                                               \
+          ((Node != NULL)                                                  && \
+            ((((CONST AML_NODE_HEADER*)Node)->NodeType > EAmlNodeUnknown)  || \
+            (((CONST AML_NODE_HEADER*)Node)->NodeType < EAmlNodeMax)))
+
+/** Check whether a Node is a root node.
+
+  @param  [in]  Node  The node to check.
+
+  @retval TRUE  The Node is a root node.
+  @retval FALSE Otherwise.
+*/
+#define IS_AML_ROOT_NODE(Node)                                                \
+          ((Node != NULL)                                                  && \
+           (((CONST AML_NODE_HEADER*)Node)->NodeType == EAmlNodeRoot))
+
+/** Check whether a Node is an object node.
+
+  @param  [in]  Node  The node to check.
+
+  @retval TRUE  The Node is an object node.
+  @retval FALSE Otherwise.
+*/
+#define IS_AML_OBJECT_NODE(Node)                                              \
+          ((Node != NULL)                                                  && \
+           (((CONST AML_NODE_HEADER*)Node)->NodeType == EAmlNodeObject))
+
+/** Check whether a Node is a data node.
+
+  @param  [in]  Node  The node to check.
+
+  @retval TRUE  The Node is a data node.
+  @retval FALSE Otherwise.
+*/
+#define IS_AML_DATA_NODE(Node)                                                \
+          ((Node != NULL)                                                  && \
+           (((CONST AML_NODE_HEADER*)Node)->NodeType == EAmlNodeData))
+
+/** Check whether a Node has a parent.
+
+  @param  [in]  Node  The node to check.
+
+  @retval TRUE  The Node is a data node.
+  @retval FALSE Otherwise.
+*/
+#define AML_NODE_HAS_PARENT(Node)                                             \
+          (IS_AML_NODE_VALID (Node)                                        && \
+           (((CONST AML_NODE_HEADER*)Node)->Parent != NULL))
+
+/** Check that the Node is not attached somewhere.
+    This doesn't mean the node cannot have children.
+
+  @param  [in]  Node  The node to check.
+
+  @retval TRUE  The Node has been detached.
+  @retval FALSE Otherwise.
+*/
+#define AML_NODE_IS_DETACHED(Node)                                            \
+          (IS_AML_NODE_VALID (Node)                                        && \
+           IsListEmpty ((CONST LIST_ENTRY*)Node)                           && \
+           (((CONST AML_NODE_HEADER*)Node)->Parent == NULL))
+
+#endif // AML_NODE_DEFINES_H_
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c
new file mode 100644
index 0000000000000000000000000000000000000000..2c80440a447ad261290a2cf84a8edd3434cf9e5e
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c
@@ -0,0 +1,673 @@
+/** @file
+  AML Node.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Tree/AmlNode.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlTree.h>
+
+/** Initialize an AML_NODE_HEADER structure.
+
+  @param  [in]  Node      Pointer to a node header.
+  @param  [in]  NodeType  NodeType to initialize the Node with.
+                          Must be an EAML_NODE_TYPE.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlInitializeNodeHeader (
+  IN  AML_NODE_HEADER   * Node,
+  IN  EAML_NODE_TYPE      NodeType
+  )
+{
+  if (Node == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  InitializeListHead (&Node->Link);
+
+  Node->Parent = NULL;
+  Node->NodeType = NodeType;
+
+  return EFI_SUCCESS;
+}
+
+/** Delete a root node and its ACPI DSDT/SSDT header.
+
+  It is the caller's responsibility to check the RootNode has been removed
+  from the tree and is not referencing any other node in the tree.
+
+  @param  [in]  RootNode  Pointer to a root node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlDeleteRootNode (
+  IN  AML_ROOT_NODE  * RootNode
+  )
+{
+  if (!IS_AML_ROOT_NODE (RootNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((RootNode->SdtHeader != NULL)) {
+    FreePool (RootNode->SdtHeader);
+  } else {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  FreePool (RootNode);
+  return EFI_SUCCESS;
+}
+
+/** Create an AML_ROOT_NODE.
+    This node will be the root of the tree.
+
+  @param  [in]  SdtHeader       Pointer to an ACPI DSDT/SSDT header to copy
+                                the data from.
+  @param  [out] NewRootNodePtr  The created AML_ROOT_NODE.
+
+  @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
+AmlCreateRootNode (
+  IN  CONST EFI_ACPI_DESCRIPTION_HEADER   * SdtHeader,
+  OUT       AML_ROOT_NODE                ** NewRootNodePtr
+  )
+{
+  EFI_STATUS        Status;
+  AML_ROOT_NODE   * RootNode;
+
+  if ((SdtHeader == NULL) ||
+      (NewRootNodePtr == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  RootNode = AllocateZeroPool (sizeof (AML_ROOT_NODE));
+  if (RootNode == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Status = AmlInitializeNodeHeader (&RootNode->NodeHeader, EAmlNodeRoot);
+  if (EFI_ERROR (Status)) {
+    FreePool (RootNode);
+    ASSERT (0);
+    return Status;
+  }
+
+  InitializeListHead (&RootNode->VariableArgs);
+
+  RootNode->SdtHeader = AllocateCopyPool (
+                          sizeof (EFI_ACPI_DESCRIPTION_HEADER),
+                          SdtHeader
+                          );
+  if (RootNode->SdtHeader == NULL) {
+    ASSERT (0);
+    AmlDeleteRootNode (RootNode);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  *NewRootNodePtr = RootNode;
+
+  return EFI_SUCCESS;
+}
+
+/** Delete an object node.
+
+  It is the caller's responsibility to check the ObjectNode has been removed
+  from the tree and is not referencing any other node in the tree.
+
+  @param  [in]  ObjectNode  Pointer to an object node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlDeleteObjectNode (
+  IN  AML_OBJECT_NODE   * ObjectNode
+  )
+{
+  if (!IS_AML_OBJECT_NODE (ObjectNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  FreePool (ObjectNode);
+  return EFI_SUCCESS;
+}
+
+/** Create an AML_OBJECT_NODE.
+
+  @param  [in]  AmlByteEncoding   Byte encoding entry.
+  @param  [in]  PkgLength         PkgLength of the node if the AmlByteEncoding
+                                  has the PkgLen attribute.
+                                  0 otherwise.
+  @param  [out] NewObjectNodePtr  The created AML_OBJECT_NODE.
+
+  @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
+AmlCreateObjectNode (
+  IN  CONST  AML_BYTE_ENCODING   * AmlByteEncoding,
+  IN         UINT32                PkgLength,
+  OUT        AML_OBJECT_NODE    ** NewObjectNodePtr
+  )
+{
+  EFI_STATUS            Status;
+  AML_OBJECT_NODE     * ObjectNode;
+
+  if ((AmlByteEncoding == NULL)  ||
+      (NewObjectNodePtr == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ObjectNode = AllocateZeroPool (sizeof (AML_OBJECT_NODE));
+  if (ObjectNode == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Status = AmlInitializeNodeHeader (&ObjectNode->NodeHeader, EAmlNodeObject);
+  if (EFI_ERROR (Status)) {
+    FreePool (ObjectNode);
+    ASSERT (0);
+    return Status;
+  }
+
+  InitializeListHead (&ObjectNode->VariableArgs);
+
+  // ObjectNode->FixedArgs[...] is already initialised to NULL as the
+  // ObjectNode is Zero allocated.
+  ObjectNode->AmlByteEncoding = AmlByteEncoding;
+  ObjectNode->PkgLen = PkgLength;
+
+  *NewObjectNodePtr = ObjectNode;
+
+  return EFI_SUCCESS;
+}
+
+/** Delete a data node and its buffer.
+
+  It is the caller's responsibility to check the DataNode has been removed
+  from the tree and is not referencing any other node in the tree.
+
+  @param  [in]  DataNode  Pointer to a data node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Could not allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlDeleteDataNode (
+  IN  AML_DATA_NODE   * DataNode
+  )
+{
+  if (!IS_AML_DATA_NODE (DataNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (DataNode->Buffer != NULL) {
+    FreePool (DataNode->Buffer);
+  } else {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  FreePool (DataNode);
+  return EFI_SUCCESS;
+}
+
+/** Create an AML_DATA_NODE.
+
+  @param  [in]  DataType        DataType of the node.
+  @param  [in]  Data            Pointer to the AML bytecode corresponding to
+                                this node. Data is copied from there.
+  @param  [in]  DataSize        Number of bytes to consider at the address
+                                pointed by Data.
+  @param  [out] NewDataNodePtr  The created AML_DATA_NODE.
+
+  @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
+AmlCreateDataNode (
+  IN        EAML_NODE_DATA_TYPE     DataType,
+  IN  CONST UINT8                 * Data,
+  IN        UINT32                  DataSize,
+  OUT       AML_DATA_NODE        ** NewDataNodePtr
+  )
+{
+  EFI_STATUS        Status;
+  AML_DATA_NODE   * DataNode;
+
+  // A data node must not be created for certain data types.
+  if ((DataType == EAmlNodeDataTypeNone)       ||
+      (DataType == EAmlNodeDataTypeReserved1)  ||
+      (DataType == EAmlNodeDataTypeReserved2)  ||
+      (DataType == EAmlNodeDataTypeReserved3)  ||
+      (DataType == EAmlNodeDataTypeReserved4)  ||
+      (DataType == EAmlNodeDataTypeReserved5)  ||
+      (Data == NULL)                           ||
+      (DataSize == 0)                          ||
+      (NewDataNodePtr == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DataNode = AllocateZeroPool (sizeof (AML_DATA_NODE));
+  if (DataNode == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Status = AmlInitializeNodeHeader (&DataNode->NodeHeader, EAmlNodeData);
+  if (EFI_ERROR (Status)) {
+    FreePool (DataNode);
+    ASSERT (0);
+    return Status;
+  }
+
+  DataNode->Buffer = AllocateCopyPool (DataSize, Data);
+  if (DataNode->Buffer == NULL) {
+    AmlDeleteDataNode (DataNode);
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  DataNode->DataType = DataType;
+  DataNode->Size = DataSize;
+
+  *NewDataNodePtr = DataNode;
+
+  return EFI_SUCCESS;
+}
+
+/** Delete a Node.
+
+  @param  [in]  Node  Pointer to a Node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteNode (
+  IN  AML_NODE_HEADER   * Node
+  )
+{
+  EFI_STATUS          Status;
+  EAML_PARSE_INDEX    Index;
+
+  // Check that the node being deleted is unlinked.
+  // When removing the node, its parent and list are reset
+  // with InitializeListHead. Thus it must be empty.
+  if (!IS_AML_NODE_VALID (Node) ||
+      !AML_NODE_IS_DETACHED (Node)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  switch (Node->NodeType) {
+    case EAmlNodeRoot:
+    {
+      // Check the variable list of arguments has been cleaned.
+      if (!IsListEmpty (AmlNodeGetVariableArgList (Node))) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      Status = AmlDeleteRootNode ((AML_ROOT_NODE*)Node);
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+      }
+      break;
+    }
+
+    case EAmlNodeObject:
+    {
+      // Check the variable list of arguments has been cleaned.
+      if (!IsListEmpty (AmlNodeGetVariableArgList (Node))) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      // Check the fixed argument list has been cleaned.
+      for (Index = EAmlParseIndexTerm0; Index < EAmlParseIndexMax; Index++) {
+        if (((AML_OBJECT_NODE*)Node)->FixedArgs[Index] != NULL) {
+          ASSERT (0);
+          return EFI_INVALID_PARAMETER;
+        }
+      }
+
+      Status = AmlDeleteObjectNode ((AML_OBJECT_NODE*)Node);
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+      }
+      break;
+    }
+
+    case EAmlNodeData:
+    {
+      Status = AmlDeleteDataNode ((AML_DATA_NODE*)Node);
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+      }
+      break;
+    }
+
+    default:
+    {
+      ASSERT (0);
+      Status = EFI_INVALID_PARAMETER;
+      break;
+    }
+  } // switch
+
+  return Status;
+}
+
+/** Check whether ObjectNode has the input attribute.
+    This function can be used to check ObjectNode is an object node
+    at the same time.
+
+  @param  [in]  ObjectNode  Pointer to an object node.
+  @param  [in]  Attribute   Attribute to check for.
+
+  @retval TRUE    The node is an AML object and the attribute is present.
+  @retval FALSE   Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlNodeHasAttribute (
+  IN  CONST AML_OBJECT_NODE   * ObjectNode,
+  IN        AML_OP_ATTRIBUTE    Attribute
+  )
+{
+  if (!IS_AML_OBJECT_NODE (ObjectNode) ||
+      (ObjectNode->AmlByteEncoding == NULL)) {
+    return FALSE;
+  }
+
+  return ((ObjectNode->AmlByteEncoding->Attribute &
+           Attribute) == 0 ? FALSE : TRUE);
+}
+
+/** 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 AML object and
+                  the Opcode and the SubOpCode match.
+  @retval FALSE   Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlNodeCompareOpCode (
+  IN  CONST  AML_OBJECT_NODE  * ObjectNode,
+  IN         UINT8              OpCode,
+  IN         UINT8              SubOpCode
+  )
+{
+  if (!IS_AML_OBJECT_NODE (ObjectNode) ||
+      (ObjectNode->AmlByteEncoding == NULL)) {
+    return FALSE;
+  }
+
+  ASSERT (AmlIsOpCodeValid (OpCode, SubOpCode));
+
+  return ((ObjectNode->AmlByteEncoding->OpCode == OpCode) &&
+           (ObjectNode->AmlByteEncoding->SubOpCode == SubOpCode)) ?
+           TRUE : FALSE;
+}
+
+/** Check whether a Node is an integer node.
+
+  By integer node we mean an object node having one of the following opcode:
+   - AML_BYTE_PREFIX;
+   - AML_WORD_PREFIX;
+   - AML_DWORD_PREFIX;
+   - AML_QWORD_PREFIX.
+
+  @param  [in]  Node  The node to check.
+
+  @retval TRUE  The Node is an integer node.
+  @retval FALSE Otherwise.
+*/
+BOOLEAN
+EFIAPI
+IsIntegerNode (
+  IN  AML_OBJECT_NODE   * Node
+  )
+{
+  UINT8   OpCode;
+
+  if (!IS_AML_OBJECT_NODE (Node)  ||
+      (Node->AmlByteEncoding == NULL)) {
+    return FALSE;
+  }
+
+  // Check Node is an integer node.
+  OpCode = Node->AmlByteEncoding->OpCode;
+  if ((OpCode != AML_BYTE_PREFIX)   &&
+      (OpCode != AML_WORD_PREFIX)   &&
+      (OpCode != AML_DWORD_PREFIX)  &&
+      (OpCode != AML_QWORD_PREFIX)) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/** Check whether a Node is a ZeroOp, a OneOp or a OnesOp.
+
+  These two objects don't have a data node holding
+  a value. This require special handling.
+
+  @param  [in]  Node  The node to check.
+
+  @retval TRUE  The Node is a ZeroOp or OneOp.
+  @retval FALSE Otherwise.
+*/
+BOOLEAN
+EFIAPI
+IsSpecialIntegerNode (
+  IN  AML_OBJECT_NODE   * Node
+  )
+{
+  UINT8   OpCode;
+
+  if (!IS_AML_OBJECT_NODE (Node)  ||
+      (Node->AmlByteEncoding == NULL)) {
+    return FALSE;
+  }
+
+  OpCode = Node->AmlByteEncoding->OpCode;
+
+  if ((OpCode != AML_ZERO_OP) &&
+      (OpCode != AML_ONE_OP)  &&
+      (OpCode != AML_ONES_OP)) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/** Check whether Node corresponds to a method definition.
+
+  A method definition can be introduced:
+   - By a method object, having an AML_METHOD_OP OpCode;
+   - By an external definition of a method, having an AML_EXTERNAL_OP OpCode
+     and an ObjectType byte set to the MethodObj.
+
+  Note:
+  An alias node, having an AML_ALIAS_OP, can be resolved to a method
+  definition. This function doesn't handle this case.
+
+  @param [in] Node    Node to check whether it is a method definition.
+
+  @retval TRUE  The Node is a method definition.
+  @retval FALSE Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlIsMethodDefinitionNode (
+  IN  CONST AML_OBJECT_NODE   * Node
+  )
+{
+  AML_DATA_NODE   * ObjectType;
+
+  // Node is checked to be an object node aswell.
+  if (AmlNodeCompareOpCode (Node, AML_METHOD_OP, 0)) {
+    return TRUE;
+  } else if (AmlNodeCompareOpCode (Node, AML_EXTERNAL_OP, 0)) {
+    // If the node is an external definition, check this is a method.
+    // DefExternal := ExternalOp NameString ObjectType ArgumentCount
+    // ExternalOp := 0x15
+    // ObjectType := ByteData
+    // ArgumentCount := ByteData (0 – 7)
+    ObjectType = (AML_DATA_NODE*)AmlGetFixedArgument (
+                                   (AML_OBJECT_NODE*)Node,
+                                   EAmlParseIndexTerm1
+                                   );
+    if (IS_AML_DATA_NODE (ObjectType)                   &&
+        (ObjectType->DataType == EAmlNodeDataTypeUInt)  &&
+        ((ObjectType->Size == 1))) {
+      if (*((UINT8*)ObjectType->Buffer) == (UINT8)EAmlObjTypeMethodObj) {
+        // The external definition is a method.
+        return TRUE;
+      } else {
+        // The external definition is not a method.
+        return FALSE;
+      }
+    } else {
+      // The tree is inconsistent.
+      ASSERT (0);
+      return FALSE;
+    }
+  }
+
+  // This is not a method definition.
+  return FALSE;
+}
+
+/** Get the index at which the name of the node is stored.
+
+  @param  [in]  ObjectNode  Pointer to an object node.
+                            Must have the AML_IN_NAMESPACE attribute.
+  @param  [out] Index       Index of the name in the fixed list of arguments.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+AmlNodeGetNameIndex (
+  IN  CONST AML_OBJECT_NODE     * ObjectNode,
+  OUT       EAML_PARSE_INDEX    * Index
+  )
+{
+  EAML_PARSE_INDEX    NameIndex;
+
+  if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE)   ||
+      (ObjectNode->AmlByteEncoding == NULL)                 ||
+      (Index == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  NameIndex = ObjectNode->AmlByteEncoding->NameIndex;
+
+  if ((NameIndex > ObjectNode->AmlByteEncoding->MaxIndex)   ||
+      (ObjectNode->AmlByteEncoding->Format[NameIndex] != EAmlName)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *Index = NameIndex;
+
+  return EFI_SUCCESS;
+}
+
+/** Get the name of the Node.
+
+  Node must be part of the namespace.
+
+  @param [in] ObjectNode    Pointer to an object node,
+                            which is part of the namespace.
+
+  @return A pointer to the name.
+          NULL otherwise.
+          Return NULL for the root node.
+**/
+CHAR8 *
+EFIAPI
+AmlNodeGetName (
+  IN  CONST AML_OBJECT_NODE   * ObjectNode
+  )
+{
+  EFI_STATUS          Status;
+  EAML_PARSE_INDEX    NameIndex;
+  AML_DATA_NODE     * DataNode;
+
+  if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE)) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  // Get the index at which the name is stored in the fixed arguments list.
+  Status = AmlNodeGetNameIndex (ObjectNode, &NameIndex);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  // The name is stored in a Data node.
+  DataNode = (AML_DATA_NODE*)ObjectNode->FixedArgs[NameIndex];
+  if (IS_AML_DATA_NODE (DataNode) &&
+      (DataNode->DataType == EAmlNodeDataTypeNameString)) {
+    return (CHAR8*)DataNode->Buffer;
+  }
+
+  /* Return NULL if no name is found.
+     This can occur if the name of a node is defined as a further
+     fixed argument.
+     E.g.:  CreateField (BD03, 0x28, Add (ID03 + 0x08), BF33)
+                                     ^
+                             The parser is here.
+     The parent of the Add statement is the CreateField statement. This
+     statement defines a name in the AML namespace. This name defined as
+     the fourth fixed argument. It hasn't been parsed yet.
+  */
+  return NULL;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h
new file mode 100644
index 0000000000000000000000000000000000000000..3584b572baae5d48f5016c0b889f557c5ba94d07
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h
@@ -0,0 +1,212 @@
+/** @file
+  AML Node.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_NODE_H_
+#define AML_NODE_H_
+
+#include <AmlNodeDefines.h>
+#include <IndustryStandard/Acpi.h>
+
+/** Create an AML_ROOT_NODE.
+    This node will be the root of the tree.
+
+  @param  [in]  SdtHeader       Pointer to an ACPI DSDT/SSDT header to copy
+                                the data from.
+  @param  [out] NewRootNodePtr  The created AML_ROOT_NODE.
+
+  @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
+AmlCreateRootNode (
+  IN  CONST EFI_ACPI_DESCRIPTION_HEADER   * SdtHeader,
+  OUT       AML_ROOT_NODE                ** NewRootNodePtr
+  );
+
+/** Create an AML_OBJECT_NODE.
+
+  @param  [in]  AmlByteEncoding   Byte encoding entry.
+  @param  [in]  PkgLength         PkgLength of the node if the AmlByteEncoding
+                                  has the PkgLen attribute.
+                                  0 otherwise.
+  @param  [out] NewObjectNodePtr  The created AML_OBJECT_NODE.
+
+  @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
+AmlCreateObjectNode (
+  IN  CONST  AML_BYTE_ENCODING   * AmlByteEncoding,
+  IN         UINT32                PkgLength,
+  OUT        AML_OBJECT_NODE    ** NewObjectNodePtr
+  );
+
+/** Create an AML_DATA_NODE.
+
+  @param  [in]  DataType        DataType of the node.
+  @param  [in]  Data            Pointer to the AML bytecode corresponding to
+                                this node. Data is copied from there.
+  @param  [in]  DataSize        Number of bytes to consider at the address
+                                pointed by Data.
+  @param  [out] NewDataNodePtr  The created AML_DATA_NODE.
+
+  @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
+AmlCreateDataNode (
+  IN        EAML_NODE_DATA_TYPE     DataType,
+  IN  CONST UINT8                 * Data,
+  IN        UINT32                  DataSize,
+  OUT       AML_DATA_NODE        ** NewDataNodePtr
+  );
+
+/** Delete a Node.
+
+  @param  [in]  Node  Pointer to a Node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteNode (
+  IN  AML_NODE_HEADER   * Node
+  );
+
+/** Check whether ObjectNode has the input attribute.
+    This function can be used to check ObjectNode is an object node
+    at the same time.
+
+  @param  [in]  ObjectNode  Pointer to an object node.
+  @param  [in]  Attribute   Attribute to check for.
+
+  @retval TRUE    The node is an AML object and the attribute is present.
+  @retval FALSE   Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlNodeHasAttribute (
+  IN  CONST AML_OBJECT_NODE   * ObjectNode,
+  IN        AML_OP_ATTRIBUTE    Attribute
+  );
+
+/** 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 AML object and
+                  the Opcode and the SubOpCode match.
+  @retval FALSE   Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlNodeCompareOpCode (
+  IN  CONST  AML_OBJECT_NODE  * ObjectNode,
+  IN         UINT8              OpCode,
+  IN         UINT8              SubOpCode
+  );
+
+/** Check whether a Node is an integer node.
+
+  By integer node we mean an object node having one of the following opcode:
+   - AML_BYTE_PREFIX;
+   - AML_WORD_PREFIX;
+   - AML_DWORD_PREFIX;
+   - AML_QWORD_PREFIX.
+
+  @param  [in]  Node  The node to check.
+
+  @retval TRUE  The Node is an integer node.
+  @retval FALSE Otherwise.
+*/
+BOOLEAN
+EFIAPI
+IsIntegerNode (
+  IN  AML_OBJECT_NODE   * Node
+  );
+
+/** Check whether a Node is a ZeroOp, a OneOp or a OnesOp.
+
+  These two objects don't have a data node holding
+  a value. This require special handling.
+
+  @param  [in]  Node  The node to check.
+
+  @retval TRUE  The Node is a ZeroOp or OneOp.
+  @retval FALSE Otherwise.
+*/
+BOOLEAN
+EFIAPI
+IsSpecialIntegerNode (
+  IN  AML_OBJECT_NODE   * Node
+  );
+
+/** Check whether Node corresponds to a method definition.
+
+  A method definition can be introduced:
+   - By a method object, having an AML_METHOD_OP OpCode;
+   - By an external definition of a method, having an AML_EXTERNAL_OP OpCode
+     and an ObjectType byte set to the MethodObj.
+
+  Note:
+  An alias node, having an AML_ALIAS_OP, can be resolved to a method
+  definition. This function doesn't handle this case.
+
+  @param [in] Node    Node to check whether it is a method definition.
+
+  @retval TRUE  The Node is a method definition.
+  @retval FALSE Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlIsMethodDefinitionNode (
+  IN  CONST AML_OBJECT_NODE   * Node
+  );
+
+/** Get the index at which the name of the node is stored.
+
+  @param  [in]  ObjectNode  Pointer to an object node.
+                            Must have the AML_IN_NAMESPACE attribute.
+  @param  [out] Index       Index of the name in the fixed list of arguments.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+AmlNodeGetNameIndex (
+  IN  CONST AML_OBJECT_NODE     * ObjectNode,
+  OUT       EAML_PARSE_INDEX    * Index
+  );
+
+/** Get the name of the Node.
+
+  Node must be part of the namespace.
+
+  @param [in] ObjectNode    Pointer to an object node,
+                            which is part of the namespace.
+
+  @return A pointer to the name.
+          NULL otherwise.
+          Return NULL for the root node.
+**/
+CHAR8 *
+EFIAPI
+AmlNodeGetName (
+  IN  CONST AML_OBJECT_NODE   * ObjectNode
+  );
+
+#endif // AML_NODE_H_
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c
new file mode 100644
index 0000000000000000000000000000000000000000..870346c40a5d9df44d01953035c760671817b960
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c
@@ -0,0 +1,566 @@
+/** @file
+  AML Node Interface.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+
+#include <AmlCoreInterface.h>
+#include <ResourceData/AmlResourceData.h>
+#include <String/AmlString.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+#include <Utils/AmlUtility.h>
+
+/** Returns the tree node type (Root/Object/Data).
+
+  @param [in] Node  Pointer to a Node.
+
+  @return The node type.
+           EAmlNodeUnknown if invalid parameter.
+**/
+EAML_NODE_TYPE
+EFIAPI
+AmlGetNodeType (
+  IN  AML_NODE_HEADER   * Node
+  )
+{
+  if (!IS_AML_NODE_VALID (Node)) {
+    ASSERT (0);
+    return EAmlNodeUnknown;
+  }
+
+  return Node->NodeType;
+}
+
+/** Get the RootNode information.
+    The Node must be a root node.
+
+  @param  [in]  RootNode          Pointer to a root node.
+  @param  [out] SdtHeaderBuffer   Buffer to copy the ACPI DSDT/SSDT header to.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetRootNodeInfo (
+  IN  AML_ROOT_NODE                 * RootNode,
+  OUT EFI_ACPI_DESCRIPTION_HEADER   * SdtHeaderBuffer
+  )
+{
+  if (!IS_AML_ROOT_NODE (RootNode)  ||
+      (SdtHeaderBuffer == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CopyMem (
+    SdtHeaderBuffer,
+    RootNode->SdtHeader,
+    sizeof (EFI_ACPI_DESCRIPTION_HEADER)
+    );
+
+  return EFI_SUCCESS;
+}
+
+/** Get the ObjectNode information.
+    The Node must be an object node.
+
+  @ingroup NodeInterfaceApi
+
+  @param  [in]  ObjectNode        Pointer to an object node.
+  @param  [out] OpCode            Pointer holding the OpCode.
+                                  Optional, can be NULL.
+  @param  [out] SubOpCode         Pointer holding the SubOpCode.
+                                  Optional, can be NULL.
+  @param  [out] PkgLen            Pointer holding the PkgLen.
+                                  The PkgLen is 0 for nodes
+                                  not having the Pkglen attribute.
+                                  Optional, can be NULL.
+  @param  [out] IsNameSpaceNode   Pointer holding TRUE if the node is defining
+                                  or changing the NameSpace scope.
+                                  E.g.: The "Name ()" and "Scope ()" ASL
+                                  statements add/modify the NameSpace scope.
+                                  Their corresponding node are NameSpace nodes.
+                                  Optional, can be NULL.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetObjectNodeInfo (
+  IN  AML_OBJECT_NODE   * ObjectNode,
+  OUT UINT8             * OpCode,           OPTIONAL
+  OUT UINT8             * SubOpCode,        OPTIONAL
+  OUT UINT32            * PkgLen,           OPTIONAL
+  OUT BOOLEAN           * IsNameSpaceNode   OPTIONAL
+  )
+{
+  if (!IS_AML_OBJECT_NODE (ObjectNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (OpCode != NULL) {
+    *OpCode = ObjectNode->AmlByteEncoding->OpCode;
+  }
+  if (SubOpCode != NULL) {
+    *SubOpCode = ObjectNode->AmlByteEncoding->SubOpCode;
+  }
+  if (PkgLen != NULL) {
+    *PkgLen = ObjectNode->PkgLen;
+  }
+  if (IsNameSpaceNode != NULL) {
+    *IsNameSpaceNode = AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Returns the count of the fixed arguments for the input Node.
+
+  @param  [in]  Node  Pointer to an object node.
+
+  @return Number of fixed arguments of the object node.
+          Return 0 if the node is not an object node.
+**/
+UINT8
+AmlGetFixedArgumentCount (
+  IN  AML_OBJECT_NODE   * Node
+  )
+{
+  if (IS_AML_OBJECT_NODE (Node) &&
+      (Node->AmlByteEncoding != NULL)) {
+    return (UINT8)Node->AmlByteEncoding->MaxIndex;
+  }
+
+  return 0;
+}
+
+/** Get the data type of the DataNode.
+    The Node must be a data node.
+
+  @param  [in]  DataNode  Pointer to a data node.
+  @param  [out] DataType  Pointer holding the data type of the data buffer.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetNodeDataType (
+  IN  AML_DATA_NODE       * DataNode,
+  OUT EAML_NODE_DATA_TYPE * DataType
+  )
+{
+  if (!IS_AML_DATA_NODE (DataNode)  ||
+      (DataType == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *DataType = DataNode->DataType;
+
+  return EFI_SUCCESS;
+}
+
+/** Get the descriptor Id of the resource data element
+    contained in the DataNode.
+
+  The Node must be a data node.
+  The Node must have the resource data type, i.e. have the
+  EAmlNodeDataTypeResourceData data type.
+
+  @param  [in]  DataNode          Pointer to a data node containing a
+                                  resource data element.
+  @param  [out] ResourceDataType  Pointer holding the descriptor Id of
+                                  the resource data.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetResourceDataType (
+  IN  AML_DATA_NODE   * DataNode,
+  OUT AML_RD_HEADER   * ResourceDataType
+  )
+{
+  if (!IS_AML_DATA_NODE (DataNode)  ||
+      (ResourceDataType == NULL)    ||
+      (DataNode->DataType != EAmlNodeDataTypeResourceData)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *ResourceDataType = AmlRdGetDescId (DataNode->Buffer);
+
+  return EFI_SUCCESS;
+}
+
+/** Get the data buffer and size of the DataNode.
+    The Node must be a data node.
+
+  BufferSize is always updated to the size of buffer of the DataNode.
+
+  If:
+   - the content of BufferSize is >= to the DataNode's buffer size;
+   - Buffer is not NULL;
+  then copy the content of the DataNode's buffer in Buffer.
+
+  @param  [in]      DataNode      Pointer to a data node.
+  @param  [out]     Buffer        Buffer to write the data to.
+                                  Optional, if NULL, only update BufferSize.
+  @param  [in, out] BufferSize    Pointer holding:
+                                   - At entry, the size of the Buffer;
+                                   - At exit, the size of the DataNode's
+                                     buffer size.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+  @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetDataNodeBuffer (
+  IN      AML_DATA_NODE   * DataNode,
+      OUT UINT8           * Buffer,       OPTIONAL
+  IN  OUT UINT32          * BufferSize
+  )
+{
+  if (!IS_AML_DATA_NODE (DataNode) ||
+      (BufferSize == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((*BufferSize >= DataNode->Size)  &&
+      (Buffer != NULL)) {
+    CopyMem (Buffer, DataNode->Buffer, DataNode->Size);
+  }
+
+  *BufferSize = DataNode->Size;
+
+  return EFI_SUCCESS;
+}
+
+/** Update the ACPI DSDT/SSDT table header.
+
+  The input SdtHeader information is copied to the tree RootNode.
+  The table Length field is automatically updated.
+  The checksum field is only updated when serializing the tree.
+
+  @param  [in]  RootNode    Pointer to a root node.
+  @param  [in]  SdtHeader   Pointer to an ACPI DSDT/SSDT table header.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpdateRootNode (
+  IN        AML_ROOT_NODE                 * RootNode,
+  IN  CONST EFI_ACPI_DESCRIPTION_HEADER   * SdtHeader
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Length;
+
+  if (!IS_AML_ROOT_NODE (RootNode)  ||
+      (SdtHeader == NULL)           ||
+      ((SdtHeader->Signature !=
+        EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) &&
+       (SdtHeader->Signature !=
+        EFI_ACPI_6_3_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE))) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CopyMem (
+    RootNode->SdtHeader,
+    SdtHeader,
+    sizeof (EFI_ACPI_DESCRIPTION_HEADER)
+    );
+
+  // Update the Length field.
+  Status = AmlComputeSize ((AML_NODE_HEADER*)RootNode, &Length);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  RootNode->SdtHeader->Length = Length +
+                                  (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER);
+
+  return Status;
+}
+
+/** Update an object node representing an integer with a new value.
+
+  The object node must have one of the following OpCodes:
+   - AML_BYTE_PREFIX
+   - AML_WORD_PREFIX
+   - AML_DWORD_PREFIX
+   - AML_QWORD_PREFIX
+   - AML_ZERO_OP
+   - AML_ONE_OP
+
+  The following OpCode is not supported:
+   - AML_ONES_OP
+
+  @param  [in] IntegerOpNode   Pointer an object node containing an integer.
+                               Must not be an object node with an AML_ONES_OP
+                               OpCode.
+  @param  [in] NewInteger      New integer value to set.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpdateInteger (
+  IN  AML_OBJECT_NODE   * IntegerOpNode,
+  IN  UINT64              NewInteger
+  )
+{
+  EFI_STATUS   Status;
+
+  INT8         ValueWidthDiff;
+
+  if (!IS_AML_OBJECT_NODE (IntegerOpNode)     ||
+      (!IsIntegerNode (IntegerOpNode)         &&
+       !IsSpecialIntegerNode (IntegerOpNode)) ||
+      AmlNodeCompareOpCode (IntegerOpNode, AML_ONES_OP, 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = AmlNodeSetIntegerValue (IntegerOpNode, NewInteger, &ValueWidthDiff);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // If the new size is different from the old size, propagate the new size.
+  if (ValueWidthDiff != 0) {
+    // Propagate the information.
+    Status = AmlPropagateInformation (
+               (AML_NODE_HEADER*)IntegerOpNode,
+               (ValueWidthDiff > 0) ? TRUE : FALSE,
+               ABS (ValueWidthDiff),
+               0
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+  }
+
+  return Status;
+}
+
+/** Update the buffer of a data node.
+
+  Note: The data type of the buffer's content must match the data type of the
+        DataNode. This is a hard restriction to prevent undesired behaviour.
+
+  @param  [in]  DataNode  Pointer to a data node.
+  @param  [in]  DataType  Data type of the Buffer's content.
+  @param  [in]  Buffer    Buffer containing the new data. The content of
+                          the Buffer is copied.
+  @param  [in]  Size      Size of the Buffer.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_UNSUPPORTED         Operation not supporter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpdateDataNode (
+  IN  AML_DATA_NODE         * DataNode,
+  IN  EAML_NODE_DATA_TYPE     DataType,
+  IN  UINT8                 * Buffer,
+  IN  UINT32                  Size
+  )
+{
+  EFI_STATUS              Status;
+
+  UINT32                  ExpectedSize;
+  AML_OBJECT_NODE       * ParentNode;
+  EAML_NODE_DATA_TYPE     ExpectedArgType;
+  EAML_PARSE_INDEX        Index;
+
+  if (!IS_AML_DATA_NODE (DataNode)      ||
+      (DataType > EAmlNodeDataTypeMax)  ||
+      (Buffer == NULL)                  ||
+      (Size == 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ParentNode = (AML_OBJECT_NODE*)AmlGetParent ((AML_NODE_HEADER*)DataNode);
+  if (!IS_AML_OBJECT_NODE (ParentNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // The NewNode and OldNode must have the same type.
+  // We do not allow to change the argument type of a data node.
+  // If required, the initial ASL template should be modified
+  // accordingly.
+  // It is however possible to interchange a raw buffer and a
+  // resource data element, since raw data can be misinterpreted
+  // as a resource data element.
+  ExpectedArgType = DataNode->DataType;
+  if ((ExpectedArgType != DataType)                         &&
+      (((ExpectedArgType != EAmlNodeDataTypeRaw)            &&
+        (ExpectedArgType != EAmlNodeDataTypeResourceData))  ||
+       ((DataType != EAmlNodeDataTypeRaw)                   &&
+        (DataType != EAmlNodeDataTypeResourceData)))) {
+    ASSERT (0);
+    return EFI_UNSUPPORTED;
+  }
+
+  // Perform some compatibility checks.
+  switch (DataType) {
+    case EAmlNodeDataTypeNameString:
+    {
+      // Check the name contained in the Buffer is an AML name
+      // with the right size.
+      Status = AmlGetNameStringSize ((CONST CHAR8*)Buffer, &ExpectedSize);
+      if (EFI_ERROR (Status)  ||
+          (Size != ExpectedSize)) {
+        ASSERT (0);
+        return Status;
+      }
+      break;
+    }
+    case EAmlNodeDataTypeString:
+    {
+      ExpectedSize = 0;
+      while (ExpectedSize < Size) {
+        // Cf ACPI 6.3 specification 20.2.3 Data Objects Encoding.
+        // AsciiCharList := Nothing | <AsciiChar AsciiCharList>
+        // AsciiChar := 0x01 - 0x7F
+        // NullChar := 0x00
+        if (Buffer[ExpectedSize] > 0x7F) {
+          ASSERT (0);
+          return EFI_INVALID_PARAMETER;
+        }
+        ExpectedSize++;
+      }
+
+      if (ExpectedSize != Size) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+      break;
+    }
+    case EAmlNodeDataTypeUInt:
+    {
+      if (AmlIsNodeFixedArgument ((CONST AML_NODE_HEADER*)DataNode, &Index)) {
+        if ((ParentNode->AmlByteEncoding == NULL) ||
+            (ParentNode->AmlByteEncoding->Format == NULL)) {
+          ASSERT (0);
+          return EFI_INVALID_PARAMETER;
+        }
+
+        // It is not possible to change the size of a fixed length UintX.
+        // E.g. for PackageOp the first fixed argument is of type EAmlUInt8
+        // and represents the count of elements. This type cannot be changed.
+        if ((ParentNode->AmlByteEncoding->Format[Index] != EAmlObject) &&
+            (DataNode->Size != Size)) {
+          ASSERT (0);
+          return EFI_UNSUPPORTED;
+        }
+      }
+      break;
+    }
+    case EAmlNodeDataTypeRaw:
+    {
+      // Check if the parent node has the byte list flag set.
+      if (!AmlNodeHasAttribute (ParentNode, AML_HAS_BYTE_LIST)) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+      break;
+    }
+    case EAmlNodeDataTypeResourceData:
+    {
+      // The resource data can be either small or large resource data.
+      // Small resource data must be at least 1 byte.
+      // Large resource data must be at least as long as the header
+      // of a large resource data.
+      if (AML_RD_IS_LARGE (Buffer)  &&
+          (Size < sizeof (ACPI_LARGE_RESOURCE_HEADER))) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      // Check if the parent node has the byte list flag set.
+      if (!AmlNodeHasAttribute (ParentNode, AML_HAS_BYTE_LIST)) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      // Check the size of the buffer is equal to the resource data size
+      // encoded in the input buffer.
+      ExpectedSize = AmlRdGetSize (Buffer);
+      if (ExpectedSize != Size) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+      break;
+    }
+    case EAmlNodeDataTypeFieldPkgLen:
+    {
+      // Check the parent is a FieldNamed field element.
+      if (!AmlNodeCompareOpCode (ParentNode, AML_FIELD_NAMED_OP, 0)) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+      break;
+    }
+    // None and reserved types.
+    default:
+    {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+      break;
+    }
+  } // switch
+
+  // If the new size is different from the old size, propagate the new size.
+  if (DataNode->Size != Size) {
+    // Propagate the information.
+    Status = AmlPropagateInformation (
+               DataNode->NodeHeader.Parent,
+               (Size > DataNode->Size) ? TRUE : FALSE,
+               (Size > DataNode->Size) ?
+                (Size - DataNode->Size) :
+                (DataNode->Size - Size),
+               0
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Free the old DataNode buffer and allocate a new buffer to store the
+    // new data.
+    FreePool (DataNode->Buffer);
+    DataNode->Buffer = AllocateZeroPool (Size);
+    if (DataNode->Buffer == NULL) {
+      ASSERT (0);
+      return EFI_OUT_OF_RESOURCES;
+    }
+    DataNode->Size = Size;
+  }
+
+  CopyMem (DataNode->Buffer, Buffer, Size);
+
+  return EFI_SUCCESS;
+}
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 05/30] DynamicTablesPkg: AML tree interface
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (3 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 04/30] DynamicTablesPkg: AML node definitions Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 06/30] DynamicTablesPkg: AML tree enumerator Sami Mujawar
                   ` (25 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

The AML tree is composite and has the following node types:
  - Root node.
  - Object node.
  - Data node.

These nodes are part of the Fixed Arguments or the Variable
arguments list in the AML tree.

The AML tree interface provides functions to manage the fixed
and the variable argument nodes in the AML tree.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c | 1047 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h |  127 +++
 2 files changed, 1174 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c
new file mode 100644
index 0000000000000000000000000000000000000000..65dad95da2760c512563a5152ba4acf42d555019
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c
@@ -0,0 +1,1047 @@
+/** @file
+  AML Tree.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Tree/AmlTree.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTreeTraversal.h>
+#include <Utils/AmlUtility.h>
+
+/** Get the parent node of the input Node.
+
+  @param [in] Node  Pointer to a node.
+
+  @return The parent node of the input Node.
+          NULL otherwise.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetParent (
+  IN  AML_NODE_HEADER   * Node
+  )
+{
+  if (IS_AML_DATA_NODE (Node) ||
+      IS_AML_OBJECT_NODE (Node)) {
+    return Node->Parent;
+  }
+
+  return NULL;
+}
+
+/** Get the root node from any node of the tree.
+    This is done by climbing up the tree until the root node is reached.
+
+  @param  [in]  Node    Pointer to a node.
+
+  @return The root node of the tree.
+          NULL if error.
+*/
+AML_ROOT_NODE *
+EFIAPI
+AmlGetRootNode (
+  IN  CONST AML_NODE_HEADER   * Node
+  )
+{
+  if (!IS_AML_NODE_VALID (Node)) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  while (!IS_AML_ROOT_NODE (Node)) {
+    Node = Node->Parent;
+    if (!IS_AML_NODE_VALID (Node)) {
+      ASSERT (0);
+      return NULL;
+    }
+  }
+  return (AML_ROOT_NODE*)Node;
+}
+
+/** Get the node at the input Index in the fixed argument list of the input
+    ObjectNode.
+
+  @param  [in]  ObjectNode  Pointer to an object node.
+  @param  [in]  Index       The Index of the fixed argument to get.
+
+  @return The node at the input Index in the fixed argument list
+          of the input ObjectNode.
+          NULL otherwise, e.g. if the node is not an object node, or no
+          node is available at this Index.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetFixedArgument (
+  IN  AML_OBJECT_NODE     * ObjectNode,
+  IN  EAML_PARSE_INDEX      Index
+  )
+{
+  if (IS_AML_OBJECT_NODE (ObjectNode)) {
+    if (Index < (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (ObjectNode)) {
+      return ObjectNode->FixedArgs[Index];
+    }
+  }
+
+  return NULL;
+}
+
+/** Check whether the input Node is in the fixed argument list of its parent
+    node.
+
+  If so, IndexPtr contains this Index.
+
+  @param  [in]  Node          Pointer to a Node.
+  @param  [out] IndexPtr      Pointer holding the Index of the Node in
+                              its parent's fixed argument list.
+
+  @retval TRUE   The node is a fixed argument and the index
+                 in IndexPtr is valid.
+  @retval FALSE  The node is not a fixed argument.
+**/
+BOOLEAN
+EFIAPI
+AmlIsNodeFixedArgument (
+  IN  CONST  AML_NODE_HEADER     * Node,
+  OUT        EAML_PARSE_INDEX    * IndexPtr
+  )
+{
+  AML_NODE_HEADER         * ParentNode;
+
+  EAML_PARSE_INDEX        Index;
+  EAML_PARSE_INDEX        MaxIndex;
+
+  if ((IndexPtr == NULL)               ||
+      (!IS_AML_DATA_NODE (Node)        &&
+       !IS_AML_OBJECT_NODE (Node))) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node);
+  if (IS_AML_ROOT_NODE (ParentNode)) {
+    return FALSE;
+  } else if (IS_AML_DATA_NODE (ParentNode)) {
+    // Tree is inconsistent.
+    ASSERT (0);
+    return FALSE;
+  }
+
+  // Check whether the Node is in the fixed argument list.
+  MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
+                                 (AML_OBJECT_NODE*)ParentNode
+                                 );
+  for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) {
+    if (AmlGetFixedArgument ((AML_OBJECT_NODE*)ParentNode, Index) == Node) {
+      *IndexPtr = Index;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/** Set the fixed argument of the ObjectNode at the Index to the NewNode.
+
+  It is the caller's responsibility to save the old node, if desired,
+  otherwise the reference to the old node will be lost.
+  If NewNode is not NULL, set its parent to ObjectNode.
+
+  @param  [in]  ObjectNode    Pointer to an object node.
+  @param  [in]  Index         Index in the fixed argument list of
+                              the ObjectNode to set.
+  @param  [in]  NewNode       Pointer to the NewNode.
+                              Can be NULL, a data node or an object node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlSetFixedArgument (
+  IN  AML_OBJECT_NODE   * ObjectNode,
+  IN  EAML_PARSE_INDEX    Index,
+  IN  AML_NODE_HEADER   * NewNode
+  )
+{
+  if (IS_AML_OBJECT_NODE (ObjectNode)                                     &&
+      (Index <= (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (ObjectNode))  &&
+      ((NewNode == NULL)                                                  ||
+       IS_AML_OBJECT_NODE (NewNode)                                       ||
+       IS_AML_DATA_NODE (NewNode))) {
+    ObjectNode->FixedArgs[Index] = NewNode;
+
+    // If NewNode is a data node or an object node, set its parent.
+    if (NewNode != NULL) {
+      NewNode->Parent = (AML_NODE_HEADER*)ObjectNode;
+    }
+
+    return EFI_SUCCESS;
+  }
+
+  ASSERT (0);
+  return EFI_INVALID_PARAMETER;
+}
+
+/** If the given AML_NODE_HEADER has a variable list of arguments,
+    return a pointer to this list.
+    Return NULL otherwise.
+
+  @param  [in]  Node  Pointer to the AML_NODE_HEADER to check.
+
+  @return The list of variable arguments if there is one.
+          NULL otherwise.
+**/
+LIST_ENTRY *
+EFIAPI
+AmlNodeGetVariableArgList (
+  IN  CONST AML_NODE_HEADER   * Node
+  )
+{
+  if (IS_AML_ROOT_NODE (Node)) {
+    return &(((AML_ROOT_NODE*)Node)->VariableArgs);
+  } else if (IS_AML_OBJECT_NODE (Node)) {
+    return &(((AML_OBJECT_NODE*)Node)->VariableArgs);
+  }
+  return NULL;
+}
+
+/** Remove the Node from its parent's variable list of arguments.
+
+  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.
+
+  @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
+AmlRemoveNodeFromVarArgList (
+  IN  AML_NODE_HEADER   * Node
+  )
+{
+  EFI_STATUS          Status;
+  AML_NODE_HEADER   * ParentNode;
+  UINT32              Size;
+
+  if ((!IS_AML_DATA_NODE (Node) &&
+       !IS_AML_OBJECT_NODE (Node))) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ParentNode = AmlGetParent (Node);
+  if (!IS_AML_ROOT_NODE (ParentNode)  &&
+      !IS_AML_OBJECT_NODE (ParentNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check the node is in its parent variable list of arguments.
+  if (!IsNodeInList (
+         AmlNodeGetVariableArgList (ParentNode),
+         &Node->Link)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Unlink Node from the tree.
+  RemoveEntryList (&Node->Link);
+  InitializeListHead (&Node->Link);
+  Node->Parent = NULL;
+
+  // Get the size of the node removed.
+  Status = AmlComputeSize (Node, &Size);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Propagate the information.
+  Status = AmlPropagateInformation (ParentNode, FALSE, Size, 1);
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** 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.
+
+  @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_HEADER   * Node
+  )
+{
+  return AmlRemoveNodeFromVarArgList (Node);
+}
+
+/** Add the NewNode to the head of the variable list of arguments
+    of the ParentNode.
+
+  @param  [in]  ParentNode  Pointer to the parent node.
+                            Must be a root or an object node.
+  @param  [in]  NewNode     Pointer to the node to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlVarListAddHead (
+  IN  AML_NODE_HEADER  * ParentNode,
+  IN  AML_NODE_HEADER  * NewNode
+  )
+{
+  EFI_STATUS    Status;
+  UINT32        NewSize;
+  LIST_ENTRY  * ChildrenList;
+
+  // Check arguments and that NewNode is not already attached to a tree.
+  // ParentNode != Data Node AND NewNode != Root Node AND NewNode != attached.
+  if ((!IS_AML_ROOT_NODE (ParentNode)     &&
+       !IS_AML_OBJECT_NODE (ParentNode))  ||
+      (!IS_AML_DATA_NODE (NewNode)        &&
+       !IS_AML_OBJECT_NODE (NewNode))     ||
+      !AML_NODE_IS_DETACHED (NewNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Insert it at the head of the list.
+  ChildrenList = AmlNodeGetVariableArgList (ParentNode);
+  if (ChildrenList == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  InsertHeadList (ChildrenList, &NewNode->Link);
+  NewNode->Parent = ParentNode;
+
+  // Get the size of the NewNode.
+  Status = AmlComputeSize (NewNode, &NewSize);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Propagate the new information.
+  Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1);
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Add the NewNode to the tail of the variable list of arguments
+    of the ParentNode.
+
+  NOTE: This is an internal function which does not propagate the size
+        when a new node is added.
+
+  @param  [in]  ParentNode  Pointer to the parent node.
+                            Must be a root or an object node.
+  @param  [in]  NewNode     Pointer to the node to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlVarListAddTailInternal (
+  IN  AML_NODE_HEADER  * ParentNode,
+  IN  AML_NODE_HEADER  * NewNode
+  )
+{
+  LIST_ENTRY  * ChildrenList;
+
+  // Check arguments and that NewNode is not already attached to a tree.
+  // ParentNode != Data Node AND NewNode != Root Node AND NewNode != attached.
+  if ((!IS_AML_ROOT_NODE (ParentNode)     &&
+       !IS_AML_OBJECT_NODE (ParentNode))  ||
+      (!IS_AML_DATA_NODE (NewNode)        &&
+       !IS_AML_OBJECT_NODE (NewNode))     ||
+      !AML_NODE_IS_DETACHED (NewNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Insert it at the tail of the list.
+  ChildrenList = AmlNodeGetVariableArgList (ParentNode);
+  if (ChildrenList == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  InsertTailList (ChildrenList, &NewNode->Link);
+  NewNode->Parent = ParentNode;
+
+  return EFI_SUCCESS;
+}
+
+/** Add the NewNode to the tail of the variable list of arguments
+    of the ParentNode.
+
+  @param  [in]  ParentNode  Pointer to the parent node.
+                            Must be a root or an object node.
+  @param  [in]  NewNode     Pointer to the node to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlVarListAddTail (
+  IN  AML_NODE_HEADER   * ParentNode,
+  IN  AML_NODE_HEADER   * NewNode
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      NewSize;
+
+  // Add the NewNode and check arguments.
+  Status = AmlVarListAddTailInternal (ParentNode, NewNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Get the size of the NewNode.
+  Status = AmlComputeSize (NewNode, &NewSize);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Propagate the new information.
+  Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1);
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Add the NewNode before the Node in the list of variable
+    arguments of the Node's parent.
+
+  @param  [in]  Node      Pointer to a node.
+                          Must be a root or an object node.
+  @param  [in]  NewNode   Pointer to the node to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlVarListAddBefore (
+  IN  AML_NODE_HEADER  * Node,
+  IN  AML_NODE_HEADER  * NewNode
+  )
+{
+  EFI_STATUS         Status;
+  AML_NODE_HEADER  * ParentNode;
+  UINT32             NewSize;
+
+  // Check arguments and that NewNode is not already attached to a tree.
+  if ((!IS_AML_DATA_NODE (NewNode)        &&
+       !IS_AML_OBJECT_NODE (NewNode))     ||
+      !AML_NODE_IS_DETACHED (NewNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ParentNode = AmlGetParent (Node);
+  if (!IS_AML_ROOT_NODE (ParentNode)    &&
+      !IS_AML_OBJECT_NODE (ParentNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Insert it before the input Node.
+  InsertTailList (&Node->Link, &NewNode->Link);
+  NewNode->Parent = ParentNode;
+
+  // Get the size of the NewNode.
+  Status = AmlComputeSize (NewNode, &NewSize);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Propagate the new information.
+  Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1);
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Add the NewNode after the Node in the variable list of arguments
+    of the Node's parent.
+
+  @param  [in]  Node      Pointer to a node.
+                          Must be a root or an object node.
+  @param  [in]  NewNode   Pointer to the node to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlVarListAddAfter (
+  IN  AML_NODE_HEADER   * Node,
+  IN  AML_NODE_HEADER   * NewNode
+  )
+{
+  EFI_STATUS          Status;
+  AML_NODE_HEADER   * ParentNode;
+  UINT32              NewSize;
+
+  // Check arguments and that NewNode is not already attached to a tree.
+  if ((!IS_AML_DATA_NODE (NewNode)        &&
+       !IS_AML_OBJECT_NODE (NewNode))     ||
+      !AML_NODE_IS_DETACHED (NewNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ParentNode = AmlGetParent (Node);
+  if (!IS_AML_ROOT_NODE (ParentNode)    &&
+      !IS_AML_OBJECT_NODE (ParentNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Insert the new node after the input Node.
+  InsertHeadList (&Node->Link, &NewNode->Link);
+  NewNode->Parent = ParentNode;
+
+  // Get the size of the NewNode.
+  Status = AmlComputeSize (NewNode, &NewSize);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Propagate the new information.
+  Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1);
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Append a Resource Data node to the BufferOpNode.
+
+  The Resource Data node is added at the end of the variable
+  list of arguments of the BufferOpNode, but before the End Tag.
+  If no End Tag is found, the function returns an error.
+
+  @param  [in]  BufferOpNode  Buffer node containing resource data elements.
+  @param  [in]  NewRdNode     The new Resource Data node to add.
+
+  @retval  EFI_SUCCESS            The function completed successfully.
+  @retval  EFI_INVALID_PARAMETER  Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlAppendRdNode (
+  IN  AML_OBJECT_NODE   * BufferOpNode,
+  IN  AML_DATA_NODE     * NewRdNode
+  )
+{
+  EFI_STATUS        Status;
+  AML_DATA_NODE   * CurrRdNode;
+  AML_RD_HEADER     RdDataType;
+
+  if (!AmlNodeCompareOpCode (BufferOpNode, AML_BUFFER_OP, 0)  ||
+      !IS_AML_DATA_NODE (NewRdNode)                           ||
+      (NewRdNode->DataType != EAmlNodeDataTypeResourceData)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the first Resource data node in the variable list of
+  // argument of the BufferOp node.
+  CurrRdNode = (AML_DATA_NODE*)AmlGetNextVariableArgument (
+                                 (AML_NODE_HEADER*)BufferOpNode,
+                                 NULL
+                                 );
+  if ((CurrRdNode == NULL)             ||
+      !IS_AML_DATA_NODE (CurrRdNode)   ||
+      (CurrRdNode->DataType != EAmlNodeDataTypeResourceData)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Iterate through the Resource Data nodes to find the End Tag.
+  while (TRUE) {
+    Status = AmlGetResourceDataType (CurrRdNode, &RdDataType);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // If the Resource Data is an End Tag,
+    // add the new node before and return.
+    if (AmlRdCompareDescId (
+          &RdDataType,
+          AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) {
+      Status = AmlVarListAddBefore (
+                 (AML_NODE_HEADER*)CurrRdNode,
+                 (AML_NODE_HEADER*)NewRdNode)
+                 ;
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+      }
+      return Status;
+    }
+
+    // Get the next Resource Data node.
+    // If this was the last node and no End Tag was found, return error.
+    // It is possible to have only one Resource Data in a BufferOp,
+    // but it should not be possible to add a new Resource Data in the list
+    // in this case.
+    CurrRdNode = (AML_DATA_NODE*)AmlGetSiblingVariableArgument (
+                                   (AML_NODE_HEADER*)CurrRdNode
+                                   );
+    if (!IS_AML_DATA_NODE (CurrRdNode)  ||
+        (CurrRdNode->DataType != EAmlNodeDataTypeResourceData)) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+  } // while
+}
+
+/** Replace the fixed argument at the Index of the ParentNode with the NewNode.
+
+  Note: This function unlinks the OldNode from the tree. It is the callers
+        responsibility to delete the OldNode if needed.
+
+  @param  [in]  ParentNode  Pointer to the parent node.
+                            Must be an object node.
+  @param  [in]  Index       Index of the fixed argument to replace.
+  @param  [in]  NewNode     The new node to insert.
+                            Must be an object node or a data node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlReplaceFixedArgument (
+  IN  AML_OBJECT_NODE      * ParentNode,
+  IN  EAML_PARSE_INDEX       Index,
+  IN  AML_NODE_HEADER      * NewNode
+  )
+{
+  EFI_STATUS              Status;
+
+  AML_NODE_HEADER       * OldNode;
+  UINT32                  NewSize;
+  UINT32                  OldSize;
+  AML_PARSE_FORMAT        FixedArgType;
+
+  // Check arguments and that NewNode is not already attached to a tree.
+  if (!IS_AML_OBJECT_NODE (ParentNode)  ||
+      (!IS_AML_DATA_NODE (NewNode)      &&
+       !IS_AML_OBJECT_NODE (NewNode))   ||
+      !AML_NODE_IS_DETACHED (NewNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Perform some compatibility checks between NewNode and OldNode.
+  FixedArgType = ParentNode->AmlByteEncoding->Format[Index];
+  switch (FixedArgType) {
+    case EAmlFieldPkgLen:
+    {
+      // A FieldPkgLen can only have a parent node with the
+      // AML_IS_FIELD_ELEMENT flag.
+      if (!AmlNodeHasAttribute (
+             (AML_OBJECT_NODE*)ParentNode,
+             AML_HAS_FIELD_LIST)) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+      // Fall through.
+    }
+
+    case EAmlUInt8:
+    case EAmlUInt16:
+    case EAmlUInt32:
+    case EAmlUInt64:
+    case EAmlName:
+    case EAmlString:
+    {
+      // A uint, a name, a string and a FieldPkgLen can only be replaced by a
+      // data node of the same type.
+      // Note: This condition might be too strict, but safer.
+      if (!IS_AML_DATA_NODE (NewNode) ||
+          (((AML_DATA_NODE*)NewNode)->DataType !=
+            AmlTypeToNodeDataType (FixedArgType))) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+      break;
+    }
+
+    case EAmlObject:
+    {
+      // If it's an object node, the grammar is too complex to do any check.
+      break;
+    }
+
+    case EAmlNone:
+    default:
+    {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+      break;
+    }
+  } // switch
+
+  // Replace the OldNode with the NewNode.
+  OldNode = AmlGetFixedArgument (ParentNode, Index);
+  if (!IS_AML_NODE_VALID (OldNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Unlink the old node.
+  // Note: This function unlinks the OldNode from the tree. It is the callers
+  //       responsibility to delete the OldNode if needed.
+  OldNode->Parent = NULL;
+
+  Status = AmlSetFixedArgument (ParentNode, Index, NewNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Get the size of the OldNode.
+  Status = AmlComputeSize (OldNode, &OldSize);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Get the size of the NewNode.
+  Status = AmlComputeSize (NewNode, &NewSize);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Propagate the new information.
+  Status = AmlPropagateInformation (
+            (AML_NODE_HEADER*)ParentNode,
+            (NewSize > OldSize) ? TRUE : FALSE,
+            (NewSize > OldSize) ? (NewSize - OldSize) : (OldSize - NewSize),
+            0
+            );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Replace the OldNode, which is in a variable list of arguments,
+    with the NewNode.
+
+  Note: This function unlinks the OldNode from the tree. It is the callers
+        responsibility to delete the OldNode if needed.
+
+  @param  [in]  OldNode   Pointer to the node to replace.
+                          Must be a data node or an object node.
+  @param  [in]  NewNode   The new node to insert.
+                          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
+AmlReplaceVariableArgument (
+  IN  AML_NODE_HEADER   * OldNode,
+  IN  AML_NODE_HEADER   * NewNode
+  )
+{
+  EFI_STATUS          Status;
+  UINT32              NewSize;
+  UINT32              OldSize;
+  EAML_PARSE_INDEX    Index;
+
+  AML_DATA_NODE     * NewDataNode;
+  AML_NODE_HEADER   * ParentNode;
+  LIST_ENTRY        * NextLink;
+
+  // Check arguments, that NewNode is not already attached to a tree,
+  // and that OldNode is attached and not in a fixed list of arguments.
+  if ((!IS_AML_DATA_NODE (OldNode)      &&
+       !IS_AML_OBJECT_NODE (OldNode))   ||
+      (!IS_AML_DATA_NODE (NewNode)      &&
+       !IS_AML_OBJECT_NODE (NewNode))   ||
+      !AML_NODE_IS_DETACHED (NewNode)   ||
+      AML_NODE_IS_DETACHED (OldNode)    ||
+      AmlIsNodeFixedArgument (OldNode, &Index)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ParentNode = AmlGetParent (OldNode);
+    if (!IS_AML_ROOT_NODE (ParentNode)    &&
+        !IS_AML_OBJECT_NODE (ParentNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  NewDataNode = (AML_DATA_NODE*)NewNode;
+
+  // Check attributes if the parent node is an object node.
+  if (IS_AML_OBJECT_NODE (ParentNode)) {
+    // A child node of a node with the HAS_CHILD flag must be either a
+    // data node or an object node. This has already been checked. So,
+    // check for other cases.
+
+    if (AmlNodeHasAttribute ((AML_OBJECT_NODE*)ParentNode, AML_HAS_BYTE_LIST)) {
+      if (!IS_AML_DATA_NODE (NewNode)                       ||
+          ((NewDataNode->DataType != EAmlNodeDataTypeRaw)   &&
+           (NewDataNode->DataType != EAmlNodeDataTypeResourceData))) {
+        // A child node of a node with the BYTE_LIST flag must be a data node,
+        // containing raw data or a resource data.
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+    } else if (AmlNodeHasAttribute (
+                (AML_OBJECT_NODE*)ParentNode,
+                AML_HAS_FIELD_LIST)) {
+      if (!AmlNodeHasAttribute (
+             (CONST AML_OBJECT_NODE*)NewNode,
+              AML_IS_FIELD_ELEMENT)) {
+        // A child node of a node with the FIELD_LIST flag must be an object
+        // node with AML_IS_FIELD_ELEMENT flag.
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+    }
+  } else {
+    // Parent node is a root node.
+    // A root node cannot have a data node as its child.
+    if (!IS_AML_DATA_NODE (NewNode)) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  // Unlink OldNode from the tree.
+  NextLink = RemoveEntryList (&OldNode->Link);
+  InitializeListHead (&OldNode->Link);
+  OldNode->Parent = NULL;
+
+  // Add the NewNode.
+  InsertHeadList (NextLink, &NewNode->Link);
+  NewNode->Parent = ParentNode;
+
+  // Get the size of the OldNode.
+  Status = AmlComputeSize (OldNode, &OldSize);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Get the size of the NewNode.
+  Status = AmlComputeSize (NewNode, &NewSize);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Propagate the new information.
+  Status = AmlPropagateInformation (
+             ParentNode,
+             (NewSize > OldSize) ? TRUE : FALSE,
+             (NewSize > OldSize) ? (NewSize - OldSize) : (OldSize - NewSize),
+             0
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Replace the OldNode by the NewNode.
+
+  Note: This function unlinks the OldNode from the tree. It is the callers
+        responsibility to delete the OldNode if needed.
+
+  @param  [in]  OldNode   Pointer to the node to replace.
+                          Must be a data node or an object node.
+  @param  [in]  NewNode   The new node to insert.
+                          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
+AmlReplaceArgument (
+  IN  AML_NODE_HEADER   * OldNode,
+  IN  AML_NODE_HEADER   * NewNode
+  )
+{
+  EFI_STATUS            Status;
+  AML_NODE_HEADER     * ParentNode;
+  EAML_PARSE_INDEX      Index;
+
+  // Check arguments and that NewNode is not already attached to a tree.
+  if ((!IS_AML_DATA_NODE (OldNode)      &&
+       !IS_AML_OBJECT_NODE (OldNode))   ||
+      (!IS_AML_DATA_NODE (NewNode)      &&
+       !IS_AML_OBJECT_NODE (NewNode))   ||
+      !AML_NODE_IS_DETACHED (NewNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // ParentNode can be a root node or an object node.
+  ParentNode = AmlGetParent (OldNode);
+  if (!IS_AML_ROOT_NODE (ParentNode)  &&
+      !IS_AML_OBJECT_NODE (ParentNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (AmlIsNodeFixedArgument (OldNode, &Index)) {
+    // OldNode is in its parent's fixed argument list at the Index.
+    Status = AmlReplaceFixedArgument (
+               (AML_OBJECT_NODE*)ParentNode,
+               Index,
+               NewNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  } else {
+    // OldNode is not in its parent's fixed argument list.
+    // It must be in its variable list of arguments.
+    Status = AmlReplaceVariableArgument (OldNode, NewNode);
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  return Status;
+}
+
+/** Delete a Node and its children.
+
+  The Node must be removed from the tree first,
+  or must be the root node.
+
+  @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_HEADER  * Node
+  )
+{
+  EFI_STATUS            Status;
+
+  EAML_PARSE_INDEX      Index;
+  EAML_PARSE_INDEX      MaxIndex;
+
+  AML_NODE_HEADER     * Arg;
+  LIST_ENTRY          * StartLink;
+  LIST_ENTRY          * CurrentLink;
+  LIST_ENTRY          * NextLink;
+
+  // Check that the node being deleted is unlinked.
+  // When removing the node, its parent pointer and
+  // its lists data structure are reset with
+  // InitializeListHead. Thus it must be detached
+  // from the tree to avoid memory leaks.
+  if (!IS_AML_NODE_VALID (Node)  ||
+      !AML_NODE_IS_DETACHED (Node)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // 1. Recursively detach and delete the fixed arguments.
+  //    Iterate through the fixed list of arguments.
+  if (IS_AML_OBJECT_NODE (Node)) {
+    MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
+                                   (AML_OBJECT_NODE*)Node
+                                   );
+    for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) {
+      Arg = AmlGetFixedArgument ((AML_OBJECT_NODE*)Node, Index);
+      if (Arg == NULL) {
+        // A fixed argument is missing. The tree is inconsistent.
+        // Note: During CodeGeneration, the fixed arguments should be set
+        //       with an incrementing index, and then the variable arguments
+        //       should be added. This allows to free as many nodes as
+        //       possible if a crash occurs.
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      // Remove the node from the fixed argument list.
+      Arg->Parent = NULL;
+      Status = AmlSetFixedArgument ((AML_OBJECT_NODE*)Node, Index, NULL);
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+
+      Status = AmlDeleteTree (Arg);
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+    }
+  }
+
+  // 2. Recursively detach and delete the variable arguments.
+  //    Iterate through the variable list of arguments.
+  StartLink = AmlNodeGetVariableArgList (Node);
+  if (StartLink != NULL) {
+    NextLink = StartLink->ForwardLink;
+    while (NextLink != StartLink) {
+      CurrentLink = NextLink;
+
+      // Unlink the node from the tree.
+      NextLink = RemoveEntryList (CurrentLink);
+      InitializeListHead (CurrentLink);
+      ((AML_NODE_HEADER*)CurrentLink)->Parent = NULL;
+
+      Status = AmlDeleteTree ((AML_NODE_HEADER*)CurrentLink);
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+    } // while
+  }
+
+  // 3. Delete the node.
+  Status = AmlDeleteNode (Node);
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b3803c47c7f468ca248d717e72d161812d485d8
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h
@@ -0,0 +1,127 @@
+/** @file
+  AML Tree.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_TREE_H_
+#define AML_TREE_H_
+
+#include <AmlNodeDefines.h>
+
+/** Get the root node from any node of the tree.
+    This is done by climbing up the tree until the root node is reached.
+
+  @param  [in]  Node    Pointer to a node.
+
+  @return The root node of the tree.
+          NULL if error.
+*/
+AML_ROOT_NODE *
+EFIAPI
+AmlGetRootNode (
+  IN  CONST AML_NODE_HEADER   * Node
+  );
+
+/** Check whether the input Node is in the fixed argument list of its parent
+    node.
+
+  If so, IndexPtr contains this Index.
+
+  @param  [in]  Node          Pointer to a Node.
+  @param  [out] IndexPtr      Pointer holding the Index of the Node in
+                              its parent's fixed argument list.
+
+  @retval TRUE   The node is a fixed argument and the index
+                 in IndexPtr is valid.
+  @retval FALSE  The node is not a fixed argument.
+**/
+BOOLEAN
+EFIAPI
+AmlIsNodeFixedArgument (
+  IN  CONST  AML_NODE_HEADER     * Node,
+  OUT        EAML_PARSE_INDEX    * IndexPtr
+  );
+
+/** Set the fixed argument of the ObjectNode at the Index to the NewNode.
+
+  It is the caller's responsibility to save the old node, if desired,
+  otherwise the reference to the old node will be lost.
+  If NewNode is not NULL, set its parent to ObjectNode.
+
+  @param  [in]  ObjectNode    Pointer to an object node.
+  @param  [in]  Index         Index in the fixed argument list of
+                              the ObjectNode to set.
+  @param  [in]  NewNode       Pointer to the NewNode.
+                              Can be NULL, a data node or an object node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlSetFixedArgument (
+  IN  AML_OBJECT_NODE   * ObjectNode,
+  IN  EAML_PARSE_INDEX    Index,
+  IN  AML_NODE_HEADER   * NewNode
+  );
+
+/** If the given AML_NODE_HEADER has a variable list of arguments,
+    return a pointer to this list.
+    Return NULL otherwise.
+
+  @param  [in]  Node  Pointer to the AML_NODE_HEADER to check.
+
+  @return The list of variable arguments if there is one.
+          NULL otherwise.
+**/
+LIST_ENTRY *
+EFIAPI
+AmlNodeGetVariableArgList (
+  IN  CONST AML_NODE_HEADER   * Node
+  );
+
+/** Add the NewNode to the tail of the variable list of arguments
+    of the ParentNode.
+
+  NOTE: This is an internal function which does not propagate the size
+        when a new node is added.
+
+  @param  [in]  ParentNode  Pointer to the parent node.
+                            Must be a root or an object node.
+  @param  [in]  NewNode     Pointer to the node to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlVarListAddTailInternal (
+  IN  AML_NODE_HEADER  * ParentNode,
+  IN  AML_NODE_HEADER  * NewNode
+  );
+
+/** Replace the OldNode by the NewNode.
+
+  Note: This function unlinks the OldNode from the tree. It is the callers
+        responsibility to delete the OldNode if needed.
+
+  @param  [in]  OldNode   Pointer to the node to replace.
+                          Must be a data node or an object node.
+  @param  [in]  NewNode   The new node to insert.
+                          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
+AmlReplaceArgument (
+  IN  AML_NODE_HEADER   * OldNode,
+  IN  AML_NODE_HEADER   * NewNode
+  );
+
+#endif // AML_TREE_H_
+
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 06/30] DynamicTablesPkg: AML tree enumerator
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (4 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 05/30] DynamicTablesPkg: AML tree interface Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 07/30] DynamicTablesPkg: AML tree traversal Sami Mujawar
                   ` (24 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

The AML tree enumerator interface allows enumeration of the
nodes in the AML tree. The enumerator interface can be useful
to search, serialise, print etc. the nodes in the AML tree.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c | 98 ++++++++++++++++++++
 1 file changed, 98 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c
new file mode 100644
index 0000000000000000000000000000000000000000..3eeb7253bfb647f5430a89a87e8a196c453abe71
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c
@@ -0,0 +1,98 @@
+/** @file
+  AML Tree Enumerator.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlTree.h>
+
+/** Enumerate all nodes of the subtree under the input Node in the AML
+    bytestream order (i.e. in a depth first order), and call the CallBack
+    function with the input Context.
+    The prototype of the Callback function is EDKII_AML_TREE_ENUM_CALLBACK.
+
+  @param  [in]      Node      Enumerate nodes of the subtree under this Node.
+                              Must be a valid node.
+  @param  [in]      CallBack  Callback function to call on each node.
+  @param  [in, out] Context   Void pointer used to pass some information
+                              to the Callback function.
+                              Optional, can be NULL.
+  @param  [out]     Status    Optional parameter that can be used to get
+                              the status of the Callback function.
+                              If used, need to be init to EFI_SUCCESS.
+
+  @retval TRUE if the enumeration can continue or has finished without
+          interruption.
+  @retval FALSE if the enumeration needs to stopped or has stopped.
+**/
+BOOLEAN
+EFIAPI
+AmlEnumTree (
+  IN      AML_NODE_HEADER               * Node,
+  IN      EDKII_AML_TREE_ENUM_CALLBACK    CallBack,
+  IN  OUT VOID                          * Context,  OPTIONAL
+      OUT EFI_STATUS                    * Status    OPTIONAL
+  )
+{
+  BOOLEAN               ContinueEnum;
+
+  EAML_PARSE_INDEX      Index;
+  EAML_PARSE_INDEX      MaxIndex;
+
+  LIST_ENTRY          * StartLink;
+  LIST_ENTRY          * CurrentLink;
+
+  if (!IS_AML_NODE_VALID (Node) || (CallBack == NULL)) {
+    ASSERT (0);
+    if (Status != NULL) {
+      *Status = EFI_INVALID_PARAMETER;
+    }
+    return FALSE;
+  }
+
+  ContinueEnum = (*CallBack)(Node, Context, Status);
+  if (ContinueEnum == FALSE) {
+    return ContinueEnum;
+  }
+
+  // Iterate through the fixed list of arguments.
+  MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
+                                 (AML_OBJECT_NODE*)Node
+                                 );
+  for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) {
+    ContinueEnum = AmlEnumTree (
+                     AmlGetFixedArgument ((AML_OBJECT_NODE*)Node, Index),
+                     CallBack,
+                     Context,
+                     Status
+                     );
+    if (ContinueEnum == FALSE) {
+      return ContinueEnum;
+    }
+  }
+
+  // Iterate through the variable list of arguments.
+  StartLink = AmlNodeGetVariableArgList (Node);
+  if (StartLink != NULL) {
+    CurrentLink = StartLink->ForwardLink;
+    while (CurrentLink != StartLink) {
+      ContinueEnum = AmlEnumTree (
+                       (AML_NODE_HEADER*)CurrentLink,
+                       CallBack,
+                       Context,
+                       Status
+                       );
+      if (ContinueEnum == FALSE) {
+        return ContinueEnum;
+      }
+      CurrentLink = CurrentLink->ForwardLink;
+    } // while
+  }
+
+  return ContinueEnum;
+}
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 07/30] DynamicTablesPkg: AML tree traversal
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (5 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 06/30] DynamicTablesPkg: AML tree enumerator Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 08/30] DynamicTablesPkg: AML tree iterator Sami Mujawar
                   ` (23 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

The AML tree traversal provides interfaces to traverse the
nodes in the AML tree.

It provides interfaces to traverse the AML tree in the
following order:

  - Traverse sibling nodes.

    (Node)        /-i           # Child of fixed argument b
        \        /
         |- [a][b][c][d]        # Fixed Arguments
         |- {(e)->(f)->(g)}     # Variable Arguments
               \
                \-h             # Child of variable argument e

    Traversal Order:
      - AmlGetNextSibling() : a, b, c, d, e, f, g, NULL
      - AmlGetPreviousSibling(): g, f, e, d, c, b, a, NULL

  - Iterate depth-first path (follow AML byte stream).
    (Node)        /-i           # Child of fixed argument b
        \        /
         |- [a][b][c][d]        # Fixed Arguments
         |- {(e)->(f)->(g)}     # Variable Arguments
               \
                \-h             # Child of variable argument e

    Traversal Order:
      - AmlGetNextNode(): a, b, i, c, d, e, h, f, g, NULL
      - AmlGetPreviousNode() g, f, h, e, d, c, i, b, a, NULL
        Note: The branch i and h will be traversed if it has
              any children.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c | 548 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h | 138 +++++
 2 files changed, 686 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c
new file mode 100644
index 0000000000000000000000000000000000000000..9d0c794dbe773061233a4f88e18cb55431bfbf4c
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c
@@ -0,0 +1,548 @@
+/** @file
+  AML Tree Traversal.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Tree/AmlTreeTraversal.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlTree.h>
+
+/** Get the sibling node among the nodes being in
+    the same variable argument list.
+
+  (ParentNode)  /-i                 # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]              # Fixed Arguments
+       |- {(VarArgNode)->(f)->(g)}  # Variable Arguments
+             \
+              \-h                   # Child of variable argument e
+
+  Node must be in a variable list of arguments.
+  Traversal Order: VarArgNode, f, g, NULL
+
+  @ingroup CoreNavigationApis
+
+  @param  [in]  VarArgNode  Pointer to a node.
+                            Must be in a variable list of arguments.
+
+  @return The next node after VarArgNode in the variable list of arguments.
+          Return NULL if
+          - VarArgNode is the last node of the list, or
+          - VarArgNode is not part of a variable list of arguments.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetSiblingVariableArgument (
+  IN  AML_NODE_HEADER   * VarArgNode
+  )
+{
+  EAML_PARSE_INDEX    Index;
+  AML_NODE_HEADER   * ParentNode;
+
+  // VarArgNode must be an object node or a data node,
+  // and be in a variable list of arguments.
+  if ((!IS_AML_OBJECT_NODE (VarArgNode) &&
+       !IS_AML_DATA_NODE (VarArgNode))  ||
+      AmlIsNodeFixedArgument (VarArgNode, &Index)) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  ParentNode = AmlGetParent (VarArgNode);
+  if (!IS_AML_NODE_VALID (ParentNode)) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  return AmlGetNextVariableArgument (ParentNode, VarArgNode);
+}
+
+/** Get the next variable argument.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: e, f, g, NULL
+
+  @param  [in]  Node        Pointer to a Root node or Object Node.
+  @param  [in]  CurrVarArg  Pointer to the Current Variable Argument.
+
+  @return The node after the CurrVarArg in the variable list of arguments.
+          If CurrVarArg is NULL, return the first node of the
+          variable argument list.
+          Return NULL if
+          - CurrVarArg is the last node of the list, or
+          - Node does not have a variable list of arguments.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetNextVariableArgument (
+  IN  AML_NODE_HEADER  * Node,
+  IN  AML_NODE_HEADER  * CurrVarArg
+  )
+{
+  CONST LIST_ENTRY       * StartLink;
+  CONST LIST_ENTRY       * NextLink;
+
+  // Node must be a RootNode or an Object Node
+  // and the CurrVarArg must not be a Root Node.
+  if ((!IS_AML_ROOT_NODE (Node)           &&
+       !IS_AML_OBJECT_NODE (Node))        ||
+      ((CurrVarArg != NULL)               &&
+       (!IS_AML_OBJECT_NODE (CurrVarArg)  &&
+        !IS_AML_DATA_NODE (CurrVarArg)))) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  StartLink = AmlNodeGetVariableArgList (Node);
+  if (StartLink == NULL) {
+    return NULL;
+  }
+
+  // Get the first child of the variable list of arguments.
+  if (CurrVarArg == NULL) {
+    NextLink = StartLink->ForwardLink;
+    if (NextLink != StartLink) {
+      return (AML_NODE_HEADER*)NextLink;
+    }
+    // List is empty.
+    return NULL;
+  }
+
+  // Check if CurrVarArg is in the VariableArgument List.
+  if (!IsNodeInList (StartLink, &CurrVarArg->Link)) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  // Get the node following the CurrVarArg.
+  NextLink = CurrVarArg->Link.ForwardLink;
+  if (NextLink != StartLink) {
+    return (AML_NODE_HEADER*)NextLink;
+  }
+
+  // End of the list has been reached.
+  return NULL;
+}
+
+/** Get the previous variable argument.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: g, f, e, NULL
+
+  @param  [in]  Node        Pointer to a root node or an object node.
+  @param  [in]  CurrVarArg  Pointer to the Current Variable Argument.
+
+  @return The node before the CurrVarArg in the variable list of
+          arguments.
+          If CurrVarArg is NULL, return the last node of the
+          variable list of arguments.
+          Return NULL if:
+          - CurrVarArg is the first node of the list, or
+          - Node doesn't have a variable list of arguments.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetPreviousVariableArgument (
+  IN  AML_NODE_HEADER  * Node,
+  IN  AML_NODE_HEADER  * CurrVarArg
+  )
+{
+  CONST LIST_ENTRY       * StartLink;
+  CONST LIST_ENTRY       * PreviousLink;
+
+  // Node must be a RootNode or an Object Node
+  // and the CurrVarArg must not be a Root Node.
+  if ((!IS_AML_ROOT_NODE (Node)           &&
+       !IS_AML_OBJECT_NODE (Node))        ||
+      ((CurrVarArg != NULL)               &&
+       (!IS_AML_OBJECT_NODE (CurrVarArg)  &&
+        !IS_AML_DATA_NODE (CurrVarArg)))) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  StartLink = AmlNodeGetVariableArgList (Node);
+  if (StartLink == NULL) {
+    return NULL;
+  }
+
+  // Get the last child of the variable list of arguments.
+  if (CurrVarArg == NULL) {
+    PreviousLink = StartLink->BackLink;
+    if (PreviousLink != StartLink) {
+      return (AML_NODE_HEADER*)PreviousLink;
+    }
+    // List is empty.
+    return NULL;
+  }
+
+  // Check if CurrVarArg is in the VariableArgument List.
+  if (!IsNodeInList (StartLink, &CurrVarArg->Link)) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  // Get the node before the CurrVarArg.
+  PreviousLink = CurrVarArg->Link.BackLink;
+  if (PreviousLink != StartLink) {
+    return (AML_NODE_HEADER*)PreviousLink;
+  }
+
+  // We have reached the beginning of the list.
+  return NULL;
+}
+
+/** Get the next sibling node among the children of the input Node.
+
+  This function traverses the FixedArguments followed by the
+  VariableArguments at the same level in the hierarchy.
+
+  Fixed arguments are before variable arguments.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: a, b, c, d, e, f, g, NULL
+
+
+  @param  [in]  Node        Pointer to a root node or an object node.
+  @param  [in]  ChildNode   Get the node after the ChildNode.
+
+  @return The node after the ChildNode among the children of the input Node.
+           - If ChildNode is NULL, return the first available node among
+             the fixed argument list then variable list of arguments;
+           - If ChildNode is the last node of the fixed argument list,
+             return the first argument of the variable list of arguments;
+           - If ChildNode is the last node of the variable list of arguments,
+             return NULL.
+
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetNextSibling (
+  IN  CONST AML_NODE_HEADER   * Node,
+  IN  CONST AML_NODE_HEADER   * ChildNode
+  )
+{
+  EAML_PARSE_INDEX    Index;
+  AML_NODE_HEADER   * CandidateNode;
+
+  // Node must be a RootNode or an Object Node
+  // and the CurrVarArg must not be a Root Node.
+  if ((!IS_AML_ROOT_NODE (Node)           &&
+       !IS_AML_OBJECT_NODE (Node))        ||
+      ((ChildNode != NULL)                &&
+       (!IS_AML_OBJECT_NODE (ChildNode)   &&
+        !IS_AML_DATA_NODE (ChildNode)))) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  if (IS_AML_OBJECT_NODE (Node)) {
+    if (ChildNode == NULL) {
+      // Get the fixed argument at index 0 of the ChildNode.
+      CandidateNode = AmlGetFixedArgument (
+                        (AML_OBJECT_NODE*)Node,
+                        EAmlParseIndexTerm0
+                        );
+      if (CandidateNode != NULL) {
+        return CandidateNode;
+      }
+    } else {
+      // (ChildNode != NULL)
+      if (AmlIsNodeFixedArgument (ChildNode, &Index)) {
+        // Increment index to point to the next fixed argument.
+        Index++;
+        // The node is part of the list of fixed arguments.
+        if (Index == (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
+                                         (AML_OBJECT_NODE*)Node)
+                                         ) {
+        // It is at the last argument of the fixed argument list.
+        // Get the first argument of the variable list of arguments.
+          ChildNode = NULL;
+        } else {
+          // Else return the next node in the list of fixed arguments.
+          return AmlGetFixedArgument ((AML_OBJECT_NODE*)Node, Index);
+        }
+      }
+    }
+  } // IS_AML_OBJECT_NODE (Node)
+
+  // Else, get the next node in the variable list of arguments.
+  return AmlGetNextVariableArgument (
+           (AML_NODE_HEADER*)Node,
+           (AML_NODE_HEADER*)ChildNode
+           );
+}
+
+/** Get the previous sibling node among the children of the input Node.
+
+  This function traverses the FixedArguments followed by the
+  VariableArguments at the same level in the hierarchy.
+
+  Fixed arguments are before variable arguments.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: g, f, e, d, c, b, a, NULL
+
+  @param  [in]  Node        The node to get the fixed argument from.
+  @param  [in]  ChildNode   Get the node before the ChildNode.
+
+  @return The node before the ChildNode among the children of the input Node.
+           - If ChildNode is NULL, return the last available node among
+             the variable list of arguments then fixed argument list;
+           - If ChildNode is the first node of the variable list of arguments,
+             return the last argument of the fixed argument list;
+           - If ChildNode is the first node of the fixed argument list,
+             return NULL.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetPreviousSibling (
+  IN  CONST  AML_NODE_HEADER  * Node,
+  IN  CONST  AML_NODE_HEADER  * ChildNode
+  )
+{
+  EAML_PARSE_INDEX    Index;
+  EAML_PARSE_INDEX    MaxIndex;
+
+  AML_NODE_HEADER   * CandidateNode;
+
+  // Node must be a Root Node or an Object Node
+  // and the ChildNode must not be a Root Node.
+  if ((!IS_AML_ROOT_NODE (Node)           &&
+       !IS_AML_OBJECT_NODE (Node))        ||
+      ((ChildNode != NULL)                &&
+       (!IS_AML_OBJECT_NODE (ChildNode)   &&
+        !IS_AML_DATA_NODE (ChildNode)))) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
+                                 (AML_OBJECT_NODE*)Node
+                                 );
+
+  // Get the last variable argument if no ChildNode.
+  // Otherwise the fixed argument list is checked first.
+  if ((ChildNode != NULL)         &&
+      IS_AML_OBJECT_NODE (Node)   &&
+      (MaxIndex != EAmlParseIndexTerm0)) {
+    if (AmlIsNodeFixedArgument (ChildNode, &Index)) {
+      // The node is part of the list of fixed arguments.
+      if (Index == EAmlParseIndexTerm0) {
+        // The node is the first fixed argument, return NULL.
+        return NULL;
+      } else {
+        // Return the previous node in the fixed argument list.
+        return AmlGetFixedArgument (
+                 (AML_OBJECT_NODE*)Node,
+                 (EAML_PARSE_INDEX)(Index - 1)
+                 );
+      }
+    }
+  }
+
+  // ChildNode is in the variable list of arguments.
+  CandidateNode = AmlGetPreviousVariableArgument (
+                    (AML_NODE_HEADER*)Node,
+                    (AML_NODE_HEADER*)ChildNode
+                    );
+  if (CandidateNode != NULL) {
+    if (!IS_AML_NODE_VALID (CandidateNode)) {
+      ASSERT (0);
+      return NULL;
+    }
+    // A Node has been found
+    return CandidateNode;
+  } else if (MaxIndex != EAmlParseIndexTerm0) {
+    // ChildNode was the first node of the variable list of arguments.
+    return AmlGetFixedArgument (
+             (AML_OBJECT_NODE*)Node,
+             (EAML_PARSE_INDEX)(MaxIndex - 1)
+             );
+  } else {
+    // No fixed arguments or variable arguments.
+    return NULL;
+  }
+}
+
+/** Iterate through the nodes in the same order as the AML bytestream.
+
+  The iteration is similar to a depth-first path.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: a, b, i, c, d, e, h, f, g, NULL
+  Note: The branch i and h will be traversed if it has any children.
+
+  @param  [in]  Node  Pointer to a node.
+
+  @return The next node in the AML bytestream order.
+          Return NULL if Node is the Node corresponding to the last
+          bytecode of the tree.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetNextNode (
+  IN  CONST AML_NODE_HEADER   * Node
+  )
+{
+  AML_NODE_HEADER   * ParentNode;
+  AML_NODE_HEADER   * CandidateNode;
+
+  if (!IS_AML_NODE_VALID (Node)) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  if (IS_AML_ROOT_NODE (Node) || IS_AML_OBJECT_NODE (Node)) {
+    // The node has children. Get the first child.
+    CandidateNode = AmlGetNextSibling (Node, NULL);
+    if (CandidateNode != NULL) {
+      if (!IS_AML_NODE_VALID (CandidateNode)) {
+        ASSERT (0);
+        return NULL;
+      }
+      // A Node has been found
+      return CandidateNode;
+    } else if (IS_AML_ROOT_NODE (Node)) {
+      // The node is the root node and it doesn't have children.
+      return NULL;
+    }
+  }
+
+  // We have traversed the current branch, go to the parent node
+  // and start traversing the next branch.
+  // Keep going up the tree until you reach the root node.
+  while (1) {
+    if (IS_AML_ROOT_NODE (Node)) {
+      // This is the last node of the tree.
+      return NULL;
+    }
+
+    ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node);
+    if (!IS_AML_NODE_VALID (ParentNode)) {
+      ASSERT (0);
+      return NULL;
+    }
+
+    CandidateNode = AmlGetNextSibling (ParentNode, Node);
+    if (CandidateNode != NULL) {
+      if (!IS_AML_NODE_VALID (CandidateNode)) {
+        ASSERT (0);
+        return NULL;
+      }
+      // A Node has been found
+      return CandidateNode;
+    }
+
+    Node = ParentNode;
+  } // while
+
+  return NULL;
+}
+
+/** Iterate through the nodes in the reverse order of the AML bytestream.
+
+  The iteration is similar to a depth-first path,
+  but done in a reverse order.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: g, f, h, e, d, c, i, b, a, NULL
+  Note: The branch i and h will be traversed if it has any children.
+
+  @param  [in]  Node  Pointer to a node.
+
+  @return The previous node in the AML bytestream order.
+          Return NULL if Node is the Node corresponding to the last
+          bytecode of the tree.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetPreviousNode (
+  IN  CONST  AML_NODE_HEADER * Node
+  )
+{
+  AML_NODE_HEADER  * ParentNode;
+  AML_NODE_HEADER  * CandidateNode;
+  AML_NODE_HEADER  * PreviousNode;
+
+  if (!IS_AML_NODE_VALID (Node)) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  while (1) {
+
+    if (IS_AML_ROOT_NODE (Node)) {
+      // This is the root node.
+      return NULL;
+    }
+
+    ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node);
+    CandidateNode = AmlGetPreviousSibling (ParentNode, Node);
+
+    if (CandidateNode == NULL) {
+      // Node is the first child of its parent.
+      return ParentNode;
+    } else if (IS_AML_DATA_NODE (CandidateNode)) {
+      // CandidateNode is a data node, thus it has no children.
+      return CandidateNode;
+    } else if (IS_AML_OBJECT_NODE (CandidateNode)) {
+      // Get the previous node in the list of children of ParentNode,
+      // then get the last child of this node.
+      // If this node has children, get its last child, etc.
+      while (1) {
+        PreviousNode = CandidateNode;
+        CandidateNode = AmlGetPreviousSibling (PreviousNode, NULL);
+        if (CandidateNode == NULL) {
+          return PreviousNode;
+        } else if (IS_AML_DATA_NODE (CandidateNode)) {
+          return CandidateNode;
+        }
+      } // while
+
+    } else {
+      ASSERT (0);
+      return NULL;
+    }
+  } // while
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h
new file mode 100644
index 0000000000000000000000000000000000000000..a4980b716d7542cd64d39094327e21fa2f164a4c
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h
@@ -0,0 +1,138 @@
+/** @file
+  AML Tree Traversal.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_TREE_TRAVERSAL_H_
+#define AML_TREE_TRAVERSAL_H_
+
+#include <AmlNodeDefines.h>
+
+/** Get the next sibling node among the children of the input Node.
+
+  This function traverses the FixedArguments followed by the
+  VariableArguments at the same level in the hierarchy.
+
+  Fixed arguments are before variable arguments.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: a, b, c, d, e, f, g, NULL
+
+
+  @param  [in]  Node        Pointer to a root node or an object node.
+  @param  [in]  ChildNode   Get the node after the ChildNode.
+
+  @return The node after the ChildNode among the children of the input Node.
+           - If ChildNode is NULL, return the first available node among
+             the fixed argument list then variable list of arguments;
+           - If ChildNode is the last node of the fixed argument list,
+             return the first argument of the variable list of arguments;
+           - If ChildNode is the last node of the variable list of arguments,
+             return NULL.
+
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetNextSibling (
+  IN  CONST AML_NODE_HEADER   * Node,
+  IN  CONST AML_NODE_HEADER   * ChildNode
+  );
+
+/** Get the previous sibling node among the children of the input Node.
+
+  This function traverses the FixedArguments followed by the
+  VariableArguments at the same level in the hierarchy.
+
+  Fixed arguments are before variable arguments.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: g, f, e, d, c, b, a, NULL
+
+  @param  [in]  Node        The node to get the fixed argument from.
+  @param  [in]  ChildNode   Get the node before the ChildNode.
+
+  @return The node before the ChildNode among the children of the input Node.
+           - If ChildNode is NULL, return the last available node among
+             the variable list of arguments then fixed argument list;
+           - If ChildNode is the first node of the variable list of arguments,
+             return the last argument of the fixed argument list;
+           - If ChildNode is the first node of the fixed argument list,
+             return NULL.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetPreviousSibling (
+  IN  CONST  AML_NODE_HEADER  * Node,
+  IN  CONST  AML_NODE_HEADER  * ChildNode
+  );
+
+/** Iterate through the nodes in the same order as the AML bytestream.
+
+  The iteration is similar to a depth-first path.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: a, b, i, c, d, e, h, f, g, NULL
+  Note: The branch i and h will be traversed if it has any children.
+
+  @param  [in]  Node  Pointer to a node.
+
+  @return The next node in the AML bytestream order.
+          Return NULL if Node is the Node corresponding to the last
+          bytecode of the tree.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetNextNode (
+  IN  CONST AML_NODE_HEADER   * Node
+  );
+
+/** Iterate through the nodes in the reverse order of the AML bytestream.
+
+  The iteration is similar to a depth-first path,
+  but done in a reverse order.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: g, f, h, e, d, c, i, b, a, NULL
+  Note: The branch i and h will be traversed if it has any children.
+
+  @param  [in]  Node  Pointer to a node.
+
+  @return The previous node in the AML bytestream order.
+          Return NULL if Node is the Node corresponding to the last
+          bytecode of the tree.
+**/
+AML_NODE_HEADER *
+EFIAPI
+AmlGetPreviousNode (
+  IN  CONST  AML_NODE_HEADER * Node
+  );
+
+#endif // AML_TREE_TRAVERSAL_H_
+
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 08/30] DynamicTablesPkg: AML tree iterator
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (6 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 07/30] DynamicTablesPkg: AML tree traversal Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 09/30] DynamicTablesPkg: AML tree/node cloning Sami Mujawar
                   ` (22 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

The AML tree iterator provides interfaces to traverse the nodes
in the AML tree. The iterator can traverse the AML tree nodes in
the following order:
  - Linear progression: Iterate following the AML byte stream
                        order (depth first).
  - Branch progression: Iterate following the AML byte stream
                        order (depth first), but stop iterating
                        at the end of the branch.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c | 353 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h | 220 ++++++++++++
 2 files changed, 573 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c
new file mode 100644
index 0000000000000000000000000000000000000000..e99c234990c669c89767d922aad75627adb2c350
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c
@@ -0,0 +1,353 @@
+/** @file
+  AML Tree Iterator.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+#include <Tree/AmlTreeIterator.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlTreeTraversal.h>
+
+/** Iterator to traverse the tree.
+
+  This is an internal structure.
+*/
+typedef struct AmlTreeInternalIterator {
+  /// External iterator structure, containing the external APIs.
+  /// Must be the first field.
+  AML_TREE_ITERATOR         Iterator;
+
+  // Note: The following members of this structure are opaque to the users
+  //       of the Tree iterator APIs.
+
+  /// Pointer to the node on which the iterator has been initialized.
+  CONST  AML_NODE_HEADER  * InitialNode;
+
+  /// Pointer to the current node.
+  CONST  AML_NODE_HEADER  * CurrentNode;
+
+  /// Iteration mode.
+  /// Allow to choose how to traverse the tree/choose which node is next.
+  EAML_ITERATOR_MODE        Mode;
+} AML_TREE_ITERATOR_INTERNAL;
+
+/** Get the current node of an iterator.
+
+  @param  [in]  Iterator  Pointer to an iterator.
+  @param  [out] OutNode   Pointer holding the current node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlIteratorGetNode (
+  IN  AML_TREE_ITERATOR   * Iterator,
+  OUT AML_NODE_HEADER    ** OutNode
+  )
+{
+  AML_TREE_ITERATOR_INTERNAL  * InternalIterator;
+
+  InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator;
+
+  // CurrentNode can be NULL, but InitialNode cannot.
+  if ((OutNode == NULL)                                       ||
+      (InternalIterator == NULL)                              ||
+      (InternalIterator->Mode <= EAmlIteratorUnknown)         ||
+      (InternalIterator->Mode >= EAmlIteratorModeMax)         ||
+      !IS_AML_NODE_VALID (InternalIterator->InitialNode)      ||
+      ((InternalIterator->CurrentNode != NULL)                &&
+        !IS_AML_NODE_VALID (InternalIterator->CurrentNode))) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *OutNode = (AML_NODE_HEADER*)InternalIterator->CurrentNode;
+
+  return EFI_SUCCESS;
+}
+
+/** Move the current node of the iterator to the next node,
+    according to the iteration mode selected.
+
+  If NextNode is not NULL, return the next node.
+
+  @param  [in]  Iterator    Pointer to an iterator.
+  @param  [out] NextNode    If not NULL, updated to the next node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlIteratorGetNextLinear (
+  IN  AML_TREE_ITERATOR  * Iterator,
+  OUT AML_NODE_HEADER   ** NextNode
+  )
+{
+  AML_TREE_ITERATOR_INTERNAL  * InternalIterator;
+
+  InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator;
+
+  // CurrentNode can be NULL, but InitialNode cannot.
+  if ((InternalIterator == NULL)                              ||
+      (InternalIterator->Mode != EAmlIteratorLinear)          ||
+      !IS_AML_NODE_VALID (InternalIterator->InitialNode)      ||
+      !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the next node according to the iteration mode.
+  InternalIterator->CurrentNode = AmlGetNextNode (
+                                    InternalIterator->CurrentNode
+                                    );
+
+  if (NextNode != NULL) {
+    *NextNode = (AML_NODE_HEADER*)InternalIterator->CurrentNode;
+  }
+  return EFI_SUCCESS;
+}
+
+/** Move the current node of the iterator to the previous node,
+    according to the iteration mode selected.
+
+  If PrevNode is not NULL, return the previous node.
+
+  @param  [in]  Iterator    Pointer to an iterator.
+  @param  [out] PrevNode    If not NULL, updated to the previous node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlIteratorGetPreviousLinear (
+  IN  AML_TREE_ITERATOR  * Iterator,
+  OUT AML_NODE_HEADER   ** PrevNode
+  )
+{
+  AML_TREE_ITERATOR_INTERNAL  * InternalIterator;
+
+  InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator;
+
+  // CurrentNode can be NULL, but InitialNode cannot.
+  if ((InternalIterator == NULL)                              ||
+      (InternalIterator->Mode != EAmlIteratorLinear)          ||
+      !IS_AML_NODE_VALID (InternalIterator->InitialNode)      ||
+      !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the previous node according to the iteration mode.
+  InternalIterator->CurrentNode = AmlGetPreviousNode (
+                                    InternalIterator->CurrentNode
+                                    );
+  if (PrevNode != NULL) {
+    *PrevNode = (AML_NODE_HEADER*)InternalIterator->CurrentNode;
+  }
+  return EFI_SUCCESS;
+}
+
+/** Move the current node of the iterator to the next node,
+    according to the iteration mode selected.
+
+  If NextNode is not NULL, return the next node.
+
+  @param  [in]  Iterator    Pointer to an iterator.
+  @param  [out] NextNode    If not NULL, updated to the next node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlIteratorGetNextBranch (
+  IN  AML_TREE_ITERATOR  * Iterator,
+  OUT AML_NODE_HEADER   ** NextNode
+  )
+{
+  AML_TREE_ITERATOR_INTERNAL  * InternalIterator;
+  AML_NODE_HEADER             * Node;
+
+  InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator;
+
+  // CurrentNode can be NULL, but InitialNode cannot.
+  if ((InternalIterator == NULL)                              ||
+      (InternalIterator->Mode != EAmlIteratorBranch)          ||
+      !IS_AML_NODE_VALID (InternalIterator->InitialNode)      ||
+      !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Node = AmlGetNextNode (InternalIterator->CurrentNode);
+  // Check whether NextNode is a sibling of InitialNode.
+  if (AmlGetParent (Node) ==
+        AmlGetParent ((AML_NODE_HEADER*)InternalIterator->InitialNode)) {
+    Node = NULL;
+  }
+
+  InternalIterator->CurrentNode = Node;
+
+  if (NextNode != NULL) {
+    *NextNode = Node;
+  }
+  return EFI_SUCCESS;
+}
+
+/** Move the current node of the iterator to the previous node,
+    according to the iteration mode selected.
+
+  If PrevNode is not NULL, return the previous node.
+
+  @param  [in]  Iterator    Pointer to an iterator.
+  @param  [out] PrevNode    If not NULL, updated to the previous node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlIteratorGetPreviousBranch (
+  IN  AML_TREE_ITERATOR  * Iterator,
+  OUT AML_NODE_HEADER   ** PrevNode
+  )
+{
+  AML_TREE_ITERATOR_INTERNAL  * InternalIterator;
+  AML_NODE_HEADER             * Node;
+
+  InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator;
+
+  // CurrentNode can be NULL, but InitialNode cannot.
+  if ((InternalIterator == NULL)                              ||
+      (InternalIterator->Mode != EAmlIteratorBranch)          ||
+      !IS_AML_NODE_VALID (InternalIterator->InitialNode)      ||
+      !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Node = AmlGetPreviousNode (InternalIterator->CurrentNode);
+  // Check whether PreviousNode is a sibling of InitialNode.
+  if (AmlGetParent (Node) ==
+        AmlGetParent ((AML_NODE_HEADER*)InternalIterator->InitialNode)) {
+    Node = NULL;
+  }
+
+  InternalIterator->CurrentNode = Node;
+
+  if (PrevNode != NULL) {
+    *PrevNode = Node;
+  }
+  return EFI_SUCCESS;
+}
+
+/** Initialize an iterator.
+
+  Note: The caller must call AmlDeleteIterator () to free the memory
+        allocated for the iterator.
+
+  @param  [in]  Node          Pointer to the node.
+  @param  [in]  IteratorMode  Selected mode to traverse the tree.
+  @param  [out] IteratorPtr   Pointer holding the created iterator.
+
+  @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
+AmlInitializeIterator (
+  IN   AML_NODE_HEADER      * Node,
+  IN   EAML_ITERATOR_MODE     IteratorMode,
+  OUT  AML_TREE_ITERATOR   ** IteratorPtr
+  )
+{
+  AML_TREE_ITERATOR_INTERNAL * InternalIterator;
+
+  if (!IS_AML_NODE_VALID (Node)             ||
+      (IteratorMode <= EAmlIteratorUnknown) ||
+      (IteratorMode >= EAmlIteratorModeMax) ||
+      (IteratorPtr == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *IteratorPtr = NULL;
+  InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)AllocateZeroPool (
+                                                    sizeof (
+                                                      AML_TREE_ITERATOR_INTERNAL
+                                                      )
+                                                    );
+  if (InternalIterator == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  InternalIterator->InitialNode = Node;
+  InternalIterator->CurrentNode = Node;
+  InternalIterator->Mode = IteratorMode;
+  InternalIterator->Iterator.GetNode = AmlIteratorGetNode;
+
+  switch (InternalIterator->Mode) {
+    case EAmlIteratorLinear:
+    {
+      InternalIterator->Iterator.GetNext = AmlIteratorGetNextLinear;
+      InternalIterator->Iterator.GetPrevious = AmlIteratorGetPreviousLinear;
+      break;
+    }
+    case EAmlIteratorBranch:
+    {
+      InternalIterator->Iterator.GetNext = AmlIteratorGetNextBranch;
+      InternalIterator->Iterator.GetPrevious = AmlIteratorGetPreviousBranch;
+      break;
+    }
+    default:
+    {
+      ASSERT (0);
+      FreePool (InternalIterator);
+      return EFI_INVALID_PARAMETER;
+    }
+  } // switch
+
+  *IteratorPtr = &InternalIterator->Iterator;
+
+  return EFI_SUCCESS;
+}
+
+/** Delete an iterator.
+
+  Note: The caller must have first initialized the iterator with the
+        AmlInitializeIterator () function.
+
+  @param  [in]  Iterator  Pointer to an iterator.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteIterator (
+  IN  AML_TREE_ITERATOR   * Iterator
+  )
+{
+  if (Iterator == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  FreePool (Iterator);
+
+  return EFI_SUCCESS;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h
new file mode 100644
index 0000000000000000000000000000000000000000..d96eecab916818ceb0ab46a25e5151916cd4451d
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h
@@ -0,0 +1,220 @@
+/** @file
+  AML Iterator.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_ITERATOR_H_
+#define AML_ITERATOR_H_
+
+/* This header file does not include internal Node definition,
+   i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions
+   must be included by the caller file. The function prototypes must
+   only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node
+   definitions.
+   This allows to keep the functions defined here both internal and
+   potentially external. If necessary, any function of this file can
+   be exposed externally.
+   The Api folder is internal to the AmlLib, but should only use these
+   functions. They provide a "safe" way to interact with the AmlLib.
+*/
+
+/**
+  @defgroup IteratorLibrary Iterator library
+  @ingroup NavigationApis
+  @{
+    The iterator library allow to navigate in the AML tree using an iterator.
+    It is possible to initialize/delete an iterator.
+
+    This iterator can progress in the tree by different orders:
+     - Linear progression: Iterate following the AML bytestream order
+                           (depth first).
+     - Branch progression: Iterate following the AML bytestream order
+                           (depth first), but stop iterating at the
+                           end of the branch.
+
+    An iterator has the following features:
+     - GetNode:       Get the current node pointed by the iterator.
+     - GetNext:       Move the current node of the iterator to the next
+                      node, according to the iteration mode selected.
+     - GetPrevious:   Move the current node of the iterator to the previous
+                      node, according to the iteration mode selected.
+  @}
+*/
+
+/**
+  @defgroup IteratorApis Iterator APIs
+  @ingroup IteratorLibrary
+  @{
+    Iterator APIs defines the action that can be done on an iterator:
+     - Initialization;
+     - Deletion;
+     - Getting the node currently pointed by the iterator;
+     - Moving to the next node;
+     - Moving to the previous node.
+  @}
+*/
+
+/**
+  @defgroup IteratorStructures Iterator structures
+  @ingroup IteratorLibrary
+  @{
+    Iterator structures define the enum/define values and structures related
+    to iterators.
+  @}
+*/
+
+/** Iterator mode.
+
+  Modes to choose how the iterator is progressing in the tree.
+  A
+  \-B    <- Iterator initialized with this node.
+  | \-C
+  | | \-D
+  | \-E
+  |   \-F
+  |   \-G
+  \-H
+    \-I
+
+  @ingroup IteratorStructures
+*/
+typedef enum EAmlIteratorMode {
+  EAmlIteratorUnknown,        ///< Unknown/Invalid AML IteratorMode
+  EAmlIteratorLinear,         ///< Iterate following the AML bytestream order
+                              ///  (depth first).
+                              ///  The order followed by the iterator would be:
+                              ///  B, C, D, E, F, G, H, I, NULL.
+  EAmlIteratorBranch,         ///< Iterate through the node of a branch.
+                              ///  The iteration follows the AML bytestream
+                              ///  order but within the branch B.
+                              ///  The order followed by the iterator would be:
+                              ///  B, C, D, E, F, G, NULL.
+  EAmlIteratorModeMax         ///< Max enum.
+} EAML_ITERATOR_MODE;
+
+/** Iterator.
+
+  Allows to traverse the tree in different orders.
+
+  @ingroup IteratorStructures
+*/
+typedef struct AmlTreeIterator AML_TREE_ITERATOR;
+
+/** Function pointer to a get the current node of the iterator.
+
+  @ingroup IteratorApis
+
+  @param  [in]  Iterator  Pointer to an iterator.
+  @param  [out] OutNode   Pointer holding the current node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+*/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_AML_TREE_ITERATOR_GET_NODE) (
+  IN  AML_TREE_ITERATOR  * Iterator,
+  OUT AML_NODE_HANDLE    * OutNode
+  );
+
+/** Function pointer to move the current node of the iterator to the
+    next node, according to the iteration mode selected.
+
+  If NextNode is not NULL, return the next node.
+
+  @ingroup IteratorApis
+
+  @param  [in]  Iterator    Pointer to an iterator.
+  @param  [out] NextNode    If not NULL, updated to the next node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+*/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_AML_TREE_ITERATOR_GET_NEXT) (
+  IN  AML_TREE_ITERATOR  * Iterator,
+  OUT AML_NODE_HANDLE    * NextNode
+  );
+
+/** Function pointer to move the current node of the iterator to the
+    previous node, according to the iteration mode selected.
+
+  If PrevNode is not NULL, return the previous node.
+
+  @ingroup IteratorApis
+
+  @param  [in]  Iterator    Pointer to an iterator.
+  @param  [out] PrevNode    If not NULL, updated to the previous node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+*/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_AML_TREE_ITERATOR_GET_PREVIOUS) (
+  IN  AML_TREE_ITERATOR  * Iterator,
+  OUT AML_NODE_HANDLE    * PrevNode
+  );
+
+/**  Iterator structure to traverse the tree.
+
+  @ingroup IteratorStructures
+*/
+typedef struct AmlTreeIterator {
+  /// Get the current node of the iterator.
+  EDKII_AML_TREE_ITERATOR_GET_NODE      GetNode;
+
+  /// Update the current node of the iterator with the next node.
+  EDKII_AML_TREE_ITERATOR_GET_NEXT      GetNext;
+
+  /// Update the current node of the iterator with the previous node.
+  EDKII_AML_TREE_ITERATOR_GET_PREVIOUS  GetPrevious;
+} AML_TREE_ITERATOR;
+
+
+/** Initialize an iterator.
+
+  Note: The caller must call AmlDeleteIterator () to free the memory
+        allocated for the iterator.
+
+  @ingroup IteratorApis
+
+  @param  [in]  Node          Pointer to the node.
+  @param  [in]  IteratorMode  Selected mode to traverse the tree.
+  @param  [out] IteratorPtr   Pointer holding the created iterator.
+
+  @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
+AmlInitializeIterator (
+  IN   AML_NODE_HANDLE        Node,
+  IN   EAML_ITERATOR_MODE     IteratorMode,
+  OUT  AML_TREE_ITERATOR   ** IteratorPtr
+  );
+
+/** Delete an iterator.
+
+  Note: The caller must have first initialized the iterator with the
+        AmlInitializeIterator () function.
+
+  @ingroup IteratorApis
+
+  @param  [in]  Iterator  Pointer to an iterator.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteIterator (
+  IN  AML_TREE_ITERATOR   * Iterator
+  );
+
+#endif // AML_ITERATOR_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 09/30] DynamicTablesPkg: AML tree/node cloning
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (7 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 08/30] DynamicTablesPkg: AML tree iterator Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 10/30] DynamicTablesPkg: AML utility interfaces Sami Mujawar
                   ` (21 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

It is often desirable to clone an AML branch/tree
or an AML node. An example of could be to clone
an AML template before fixup so that the original
AML template remains unmodified. Another example
would be replicating a device branch in the AML
tree and fixing up the device information.

To facilitate such scenarios the AmlLib library
provides functions that can be used to clone an
AML branch/tree or an AML node.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c | 205 ++++++++++++++++++++
 1 file changed, 205 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c
new file mode 100644
index 0000000000000000000000000000000000000000..e09372b039f1f232de500effae7d403d56c989cd
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c
@@ -0,0 +1,205 @@
+/** @file
+  AML Clone.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+
+/** Clone a node.
+
+  This function does not clone the children nodes.
+  The cloned node returned is not attached to any tree.
+
+  @param  [in]  Node        Pointer to a node.
+  @param  [out] ClonedNode  Pointer holding the cloned node.
+
+  @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
+AmlCloneNode (
+  IN  AML_NODE_HEADER   * Node,
+  OUT AML_NODE_HEADER  ** ClonedNode
+  )
+{
+  EFI_STATUS              Status;
+
+  AML_OBJECT_NODE       * ObjectNode;
+  AML_DATA_NODE         * DataNode;
+  AML_ROOT_NODE         * RootNode;
+
+  if (!IS_AML_NODE_VALID (Node) ||
+      (ClonedNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *ClonedNode = NULL;
+
+  if (IS_AML_DATA_NODE (Node)) {
+    DataNode = (AML_DATA_NODE*)Node;
+    Status = AmlCreateDataNode (
+                DataNode->DataType,
+                DataNode->Buffer,
+                DataNode->Size,
+                (AML_DATA_NODE**)ClonedNode
+                );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+  } else if (IS_AML_OBJECT_NODE (Node)) {
+    ObjectNode = (AML_OBJECT_NODE*)Node;
+
+    Status = AmlCreateObjectNode (
+                ObjectNode->AmlByteEncoding,
+                ObjectNode->PkgLen,
+                (AML_OBJECT_NODE**)ClonedNode
+                );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+  } else if (IS_AML_ROOT_NODE (Node)) {
+    RootNode = (AML_ROOT_NODE*)Node;
+
+    Status = AmlCreateRootNode (
+               RootNode->SdtHeader,
+               (AML_ROOT_NODE**)ClonedNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+  } else {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return Status;
+}
+
+/** Clone a node and its children (clone a tree branch).
+
+  The cloned branch returned is not attached to any tree.
+
+  @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_HEADER   * Node,
+  OUT AML_NODE_HEADER  ** ClonedNode
+  )
+{
+  EFI_STATUS              Status;
+
+  AML_NODE_HEADER       * HeadNode;
+  AML_NODE_HEADER       * ClonedChildNode;
+  AML_NODE_HEADER       * FixedArgNode;
+
+  EAML_PARSE_INDEX        Index;
+  EAML_PARSE_INDEX        MaxIndex;
+
+  LIST_ENTRY            * StartLink;
+  LIST_ENTRY            * CurrentLink;
+
+  if (!IS_AML_NODE_VALID (Node) ||
+      (ClonedNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = AmlCloneNode (Node, &HeadNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Clone the fixed arguments and bind them to their parent.
+  MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
+                                 (AML_OBJECT_NODE*)Node
+                                 );
+  for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) {
+    FixedArgNode = AmlGetFixedArgument ((AML_OBJECT_NODE*)Node, Index);
+    if (FixedArgNode == NULL) {
+      Status = EFI_INVALID_PARAMETER;
+      ASSERT (0);
+      goto error_handler;
+    }
+
+    // Clone child.
+    Status = AmlCloneTree (
+               FixedArgNode,
+               &ClonedChildNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      goto error_handler;
+    }
+
+    // Bind child.
+    Status = AmlSetFixedArgument (
+               (AML_OBJECT_NODE*)HeadNode,
+               Index,
+               ClonedChildNode
+               );
+    if (EFI_ERROR (Status)) {
+      AmlDeleteTree (ClonedChildNode);
+      ASSERT (0);
+      goto error_handler;
+    }
+  } // for
+
+  // Clone the variable arguments and bind them to their parent.
+  StartLink = AmlNodeGetVariableArgList (Node);
+  if (StartLink != NULL) {
+    CurrentLink = StartLink->ForwardLink;
+    while (CurrentLink != StartLink) {
+      // Clone child.
+      Status = AmlCloneTree ((AML_NODE_HEADER*)CurrentLink, &ClonedChildNode);
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        goto error_handler;
+      }
+
+      // Bind child.
+      Status = AmlVarListAddTailInternal (
+                 HeadNode,
+                 ClonedChildNode
+                 );
+      if (EFI_ERROR (Status)) {
+        AmlDeleteTree (ClonedChildNode);
+        ASSERT (0);
+        goto error_handler;
+      }
+
+      CurrentLink = CurrentLink->ForwardLink;
+    } // while
+  }
+
+  *ClonedNode = HeadNode;
+  return Status;
+
+error_handler:
+  *ClonedNode = NULL;
+
+  if (HeadNode != NULL) {
+    AmlDeleteTree (HeadNode);
+  }
+
+  return Status;
+}
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 10/30] DynamicTablesPkg: AML utility interfaces
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (8 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 09/30] DynamicTablesPkg: AML tree/node cloning Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 11/30] DynamicTablesPkg: AML and ASL string helper Sami Mujawar
                   ` (20 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

The AML utility interfaces are a collection of helper functions
that assist in computing the checksum, size and to propagate the
node information as a result of addition or update of AML nodes.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c | 906 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h |  95 ++
 2 files changed, 1001 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c
new file mode 100644
index 0000000000000000000000000000000000000000..7ebd08f945c0c5ae87a3408152aefa6cc1e78234
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c
@@ -0,0 +1,906 @@
+/** @file
+  AML Utility.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Utils/AmlUtility.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+
+/** This function computes and updates the ACPI table checksum.
+
+  @param  [in]  AcpiTable   Pointer to an Acpi table.
+
+  @retval EFI_SUCCESS   The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AcpiPlatformChecksum (
+  IN  EFI_ACPI_DESCRIPTION_HEADER  * AcpiTable
+  )
+{
+  UINT8   * Ptr;
+  UINT8     Sum;
+  UINT32    Size;
+
+  if (AcpiTable == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Ptr = (UINT8*)AcpiTable;
+  Size = AcpiTable->Length;
+  Sum = 0;
+
+  // Set the checksum field to 0 first.
+  AcpiTable->Checksum = 0;
+
+  // Compute the checksum.
+  while ((Size--) != 0) {
+    Sum = (UINT8)(Sum + (*Ptr++));
+  }
+
+  // Set the checksum.
+  AcpiTable->Checksum = (UINT8)(0xFF - Sum + 1);
+
+  return EFI_SUCCESS;
+}
+
+/** A callback function that computes the size of a Node and adds it to the
+    Size pointer stored in the Context.
+    Calling this function on the root node will compute the total size of the
+    AML bytestream.
+
+  @param  [in]      Node      Node to compute the size.
+  @param  [in, out] Context   Pointer holding the computed size.
+                              (UINT32 *) Context.
+  @param  [in, out] Status    Pointer holding:
+                               - At entry, the Status returned by the
+                                 last call to this exact function during
+                                 the enumeration;
+                               - At exit, he returned status of the
+                                 call to this function.
+                              Optional, can be NULL.
+
+  @retval TRUE if the enumeration can continue or has finished without
+          interruption.
+  @retval FALSE if the enumeration needs to stopped or has stopped.
+**/
+STATIC
+BOOLEAN
+EFIAPI
+AmlComputeSizeCallback (
+  IN      AML_NODE_HEADER  * Node,
+  IN  OUT VOID             * Context,
+  IN  OUT EFI_STATUS       * Status   OPTIONAL
+  )
+{
+  UINT32                    Size;
+  EAML_PARSE_INDEX          IndexPtr;
+  CONST AML_OBJECT_NODE   * ParentNode;
+
+  if (!IS_AML_NODE_VALID (Node) ||
+      (Context == NULL)) {
+    ASSERT (0);
+    if (Status != NULL) {
+      *Status = EFI_INVALID_PARAMETER;
+    }
+    return FALSE;
+  }
+
+  // Ignore the second fixed argument of method invocation nodes
+  // as the information stored there (the argument count) is not in the
+  // ACPI specification.
+  ParentNode = (CONST AML_OBJECT_NODE*)AmlGetParent (Node);
+  if (IS_AML_OBJECT_NODE (ParentNode)                             &&
+      AmlNodeCompareOpCode (ParentNode, AML_METHOD_INVOC_OP, 0)   &&
+      AmlIsNodeFixedArgument (Node, &IndexPtr)) {
+    if (IndexPtr == EAmlParseIndexTerm1) {
+      if (Status != NULL) {
+        *Status = EFI_SUCCESS;
+      }
+      return TRUE;
+    }
+  }
+
+  Size = *((UINT32*)Context);
+
+  if (IS_AML_DATA_NODE (Node)) {
+    Size += ((AML_DATA_NODE*)Node)->Size;
+  } else if (IS_AML_OBJECT_NODE (Node)  &&
+             !AmlNodeHasAttribute (
+                (CONST AML_OBJECT_NODE*)Node,
+                AML_IS_PSEUDO_OPCODE)) {
+    // Ignore pseudo-opcodes as they are not part of the
+    // ACPI specification.
+
+    Size += (((AML_OBJECT_NODE*)Node)->AmlByteEncoding->OpCode ==
+               AML_EXT_OP) ? 2 : 1;
+
+    // Add the size of the PkgLen.
+    if (AmlNodeHasAttribute (
+          (AML_OBJECT_NODE*)Node,
+          AML_HAS_PKG_LENGTH)) {
+      Size += AmlComputePkgLengthWidth (((AML_OBJECT_NODE*)Node)->PkgLen);
+    }
+  }
+
+  // Check for overflow.
+  // The root node has a null size, thus the strict comparison.
+  if (*((UINT32*)Context) > Size) {
+    ASSERT (0);
+    *Status = EFI_INVALID_PARAMETER;
+    return FALSE;
+  }
+
+  *((UINT32*)Context) = Size;
+
+  if (Status != NULL) {
+    *Status = EFI_SUCCESS;
+  }
+
+  return TRUE;
+}
+
+/** Compute the size of a tree/sub-tree.
+
+  @param  [in]      Node      Node to compute the size.
+  @param  [in, out] Size      Pointer holding the computed size.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlComputeSize (
+  IN      CONST AML_NODE_HEADER   * Node,
+  IN  OUT       UINT32            * Size
+  )
+{
+  EFI_STATUS  Status;
+
+  if (!IS_AML_NODE_VALID (Node) ||
+      (Size == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *Size = 0;
+
+  AmlEnumTree (
+    (AML_NODE_HEADER*)Node,
+    AmlComputeSizeCallback,
+    (VOID*)Size,
+    &Status
+    );
+
+  return Status;
+}
+
+/** Get the value contained in an integer node.
+
+  @param  [in]  Node    Pointer to an integer node.
+                        Must be an object node.
+  @param  [out] Value   Value contained in the integer node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlNodeGetIntegerValue (
+  IN  AML_OBJECT_NODE   * Node,
+  OUT UINT64            * Value
+  )
+{
+  AML_DATA_NODE  * DataNode;
+
+  if ((!IsIntegerNode (Node)            &&
+       !IsSpecialIntegerNode (Node))    ||
+      (Value == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // For ZeroOp and OneOp, there is no data node.
+  if (IsSpecialIntegerNode (Node)) {
+    if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) {
+      *Value = 0;
+    } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) {
+      *Value = 1;
+    } else {
+      // OnesOp cannot be handled: it represents a maximum value.
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+    return EFI_SUCCESS;
+  }
+
+  // For integer nodes, the value is in the first fixed argument.
+  DataNode = (AML_DATA_NODE*)Node->FixedArgs[EAmlParseIndexTerm0];
+  if (!IS_AML_DATA_NODE (DataNode) ||
+      (DataNode->DataType != EAmlNodeDataTypeUInt)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  switch (DataNode->Size) {
+    case 1:
+    {
+      *Value = *((UINT8*)(DataNode->Buffer));
+      break;
+    }
+    case 2:
+    {
+      *Value = *((UINT16*)(DataNode->Buffer));
+      break;
+    }
+    case 4:
+    {
+      *Value = *((UINT32*)(DataNode->Buffer));
+      break;
+    }
+    case 8:
+    {
+      *Value = *((UINT64*)(DataNode->Buffer));
+      break;
+    }
+    default:
+    {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+  } // switch
+
+  return EFI_SUCCESS;
+}
+
+/** Replace a Zero (AML_ZERO_OP) or One (AML_ONE_OP) object node
+    with a byte integer (AML_BYTE_PREFIX) object node having the same value.
+
+  @param  [in]  Node    Pointer to an integer node.
+                        Must be an object node having ZeroOp or OneOp.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlUnwindSpecialInteger (
+  IN  AML_OBJECT_NODE   * Node
+  )
+{
+  EFI_STATUS                  Status;
+
+  AML_DATA_NODE             * NewDataNode;
+  UINT8                       Value;
+  CONST AML_BYTE_ENCODING   * ByteEncoding;
+
+  if (!IsSpecialIntegerNode (Node)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Find the value.
+  if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) {
+    Value = 0;
+  } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) {
+    Value = 1;
+  } else {
+    // OnesOp cannot be handled: it represents a maximum value.
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = AmlCreateDataNode (
+              EAmlNodeDataTypeUInt,
+              &Value,
+              sizeof (UINT8),
+              (AML_DATA_NODE**)&NewDataNode
+              );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Change the encoding of the special node to a ByteOp encoding.
+  ByteEncoding = AmlGetByteEncodingByOpCode (AML_BYTE_PREFIX, 0);
+  if (ByteEncoding == NULL) {
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+    goto error_handler;
+  }
+
+  // Update the ByteEncoding from ZERO_OP/ONE_OP to AML_BYTE_PREFIX.
+  Node->AmlByteEncoding = ByteEncoding;
+
+  // Add the data node as the first fixed argument of the ByteOp object.
+  Status = AmlSetFixedArgument (
+              (AML_OBJECT_NODE*)Node,
+              EAmlParseIndexTerm0,
+              (AML_NODE_HEADER*)NewDataNode
+              );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto error_handler;
+  }
+
+  return Status;
+
+error_handler:
+  AmlDeleteTree ((AML_NODE_HEADER*)NewDataNode);
+  return Status;
+}
+
+/** Set the value contained in an integer node.
+
+  The OpCode is updated accordingly to the new value
+  (e.g.: If the original value was a UINT8 value, then the OpCode
+         would be AML_BYTE_PREFIX. If it the new value is a UINT16
+         value then the OpCode will be updated to AML_WORD_PREFIX).
+
+  @param  [in]  Node            Pointer to an integer node.
+                                Must be an object node.
+  @param  [in]  NewValue        New value to write in the integer node.
+  @param  [out] ValueWidthDiff  Difference in number of bytes used to store
+                                the new value.
+                                Can be negative.
+
+  @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
+AmlNodeSetIntegerValue (
+  IN  AML_OBJECT_NODE   * Node,
+  IN  UINT64              NewValue,
+  OUT INT8              * ValueWidthDiff
+  )
+{
+  EFI_STATUS        Status;
+  AML_DATA_NODE   * DataNode;
+
+  UINT8             NewOpCode;
+  UINT8             NumberOfBytes;
+
+  if ((!IsIntegerNode (Node)            &&
+       !IsSpecialIntegerNode (Node))    ||
+      (ValueWidthDiff == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *ValueWidthDiff = 0;
+  // For ZeroOp and OneOp, there is no data node.
+  // Thus the object node is converted to a byte object node holding 0 or 1.
+  if (IsSpecialIntegerNode (Node)) {
+    switch (NewValue) {
+      case AML_ZERO_OP:
+        Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ZERO_OP, 0);
+        return EFI_SUCCESS;
+      case AML_ONE_OP:
+        Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ONE_OP, 0);
+        return EFI_SUCCESS;
+      default:
+      {
+        Status = AmlUnwindSpecialInteger (Node);
+        if (EFI_ERROR (Status)) {
+          ASSERT (0);
+          return Status;
+        }
+        // The AmlUnwindSpecialInteger functions converts a special integer
+        // node to a UInt8/Byte data node. Thus, the size increments by one:
+        // special integer are encoded as one byte (the opcode only) while byte
+        // integers are encoded as two bytes (the opcode + the value).
+        *ValueWidthDiff += sizeof (UINT8);
+      }
+    } // switch
+  } // IsSpecialIntegerNode (Node)
+
+  // For integer nodes, the value is in the first fixed argument.
+  DataNode = (AML_DATA_NODE*)Node->FixedArgs[EAmlParseIndexTerm0];
+  if (!IS_AML_DATA_NODE (DataNode) ||
+      (DataNode->DataType != EAmlNodeDataTypeUInt)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // The value can be encoded with a special 0 or 1 OpCode.
+  // The AML_ONES_OP is not handled.
+  if (NewValue <= 1) {
+    NewOpCode = (NewValue == 0) ? AML_ZERO_OP : AML_ONE_OP;
+    Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0);
+
+    // The value is encoded with a AML_ZERO_OP or AML_ONE_OP.
+    // This means there is no need for a DataNode containing the value.
+    // The change in size is equal to the size of the DataNode's buffer.
+    *ValueWidthDiff = -((INT8)DataNode->Size);
+
+    // Detach and free the DataNode containing the integer value.
+    DataNode->NodeHeader.Parent = NULL;
+    Node->FixedArgs[EAmlParseIndexTerm0] = NULL;
+    Status = AmlDeleteNode ((AML_NODE_HEADER*)DataNode);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    return EFI_SUCCESS;
+  }
+
+  // Check the number of bits needed to represent the value.
+  if (NewValue > MAX_UINT32) {
+    // Value is 64 bits.
+    NewOpCode = AML_QWORD_PREFIX;
+    NumberOfBytes = 8;
+  } else if (NewValue > MAX_UINT16) {
+    // Value is 32 bits.
+    NewOpCode = AML_DWORD_PREFIX;
+    NumberOfBytes = 4;
+  } else if (NewValue > MAX_UINT8) {
+    // Value is 16 bits.
+    NewOpCode = AML_WORD_PREFIX;
+    NumberOfBytes = 2;
+  } else {
+    // Value is 8 bits.
+    NewOpCode = AML_BYTE_PREFIX;
+    NumberOfBytes = 1;
+  }
+
+  *ValueWidthDiff += (INT8)(NumberOfBytes - DataNode->Size);
+
+  // Update the ByteEncoding as it may have changed between [8 .. 64] bits.
+  Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0);
+  if (Node->AmlByteEncoding == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Free the old DataNode buffer and allocate a buffer with the right size
+  // to store the new data.
+  if (*ValueWidthDiff != 0) {
+    FreePool (DataNode->Buffer);
+    DataNode->Buffer = AllocateZeroPool (NumberOfBytes);
+    if (DataNode->Buffer == NULL) {
+      ASSERT (0);
+      return EFI_OUT_OF_RESOURCES;
+    }
+    DataNode->Size = NumberOfBytes;
+  }
+
+  // Write the new value.
+  CopyMem (DataNode->Buffer, &NewValue, NumberOfBytes);
+
+  return EFI_SUCCESS;
+}
+
+/** Increment/decrement the value contained in the IntegerNode.
+
+  @param  [in]  IntegerNode     Pointer to an object node containing
+                                an integer.
+  @param  [in]  IsIncrement     Choose the operation to do:
+                                 - TRUE:  Increment the Node's size and
+                                          the Node's count;
+                                 - FALSE: Decrement the Node's size and
+                                          the Node's count.
+  @param  [in]  Diff            Value to add/subtract to the integer.
+  @param  [out] ValueWidthDiff  When modifying the integer, it can be
+                                promoted/demoted, e.g. from UINT8 to UINT16.
+                                Stores the change in width.
+                                Can be negative.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlNodeUpdateIntegerValue (
+  IN  AML_OBJECT_NODE       * IntegerNode,
+  IN  BOOLEAN                 IsIncrement,
+  IN  UINT64                  Diff,
+  OUT INT8                  * ValueWidthDiff
+  )
+{
+  EFI_STATUS       Status;
+  UINT64           Value;
+
+  if (ValueWidthDiff == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the current value.
+  // Checks on the IntegerNode are done in the call.
+  Status = AmlNodeGetIntegerValue (IntegerNode, &Value);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Check for UINT64 over/underflow.
+  if ((IsIncrement && (Value > (MAX_UINT64 - Diff))) ||
+      (!IsIncrement && (Value < Diff))) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Compute the new value.
+  if (IsIncrement) {
+    Value += Diff;
+  } else {
+    Value -= Diff;
+  }
+
+  Status = AmlNodeSetIntegerValue (
+             IntegerNode,
+             Value,
+             ValueWidthDiff
+             );
+  ASSERT_EFI_ERROR (Status);
+  return Status;
+}
+
+/** Propagate the size information up the tree.
+
+  The length of the ACPI table is updated in the RootNode,
+  but not the checksum.
+
+  @param  [in]  Node          Pointer to a node.
+                              Must be a root node or an object node.
+  @param  [in]  IsIncrement   Choose the operation to do:
+                               - TRUE:  Increment the Node's size and
+                                        the Node's count;
+                               - FALSE: Decrement the Node's size and
+                                        the Node's count.
+  @param  [in]  Diff          Value to add/subtract to the Node's size.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlPropagateSize (
+  IN  AML_NODE_HEADER   * Node,
+  IN  BOOLEAN             IsIncrement,
+  IN  UINT32            * Diff
+  )
+{
+  EFI_STATUS         Status;
+  AML_OBJECT_NODE  * ObjectNode;
+  AML_NODE_HEADER  * ParentNode;
+
+  UINT32             Value;
+  UINT32             InitialPkgLenWidth;
+  UINT32             NewPkgLenWidth;
+  UINT32             ReComputedPkgLenWidth;
+  INT8               FieldWidthChange;
+
+  if (!IS_AML_OBJECT_NODE (Node) &&
+      !IS_AML_ROOT_NODE (Node)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (IS_AML_OBJECT_NODE (Node)) {
+    ObjectNode = (AML_OBJECT_NODE*)Node;
+
+    // For BufferOp, the buffer size is stored in BufferSize. Therefore,
+    // BufferOp needs special handling to update the BufferSize.
+    // BufferSize must be updated before the PkgLen to accommodate any
+    // increment resulting from the update of the BufferSize.
+    // DefBuffer := BufferOp PkgLength BufferSize ByteList
+    // BufferOp := 0x11
+    // BufferSize := TermArg => Integer
+    if (AmlNodeCompareOpCode (ObjectNode, AML_BUFFER_OP, 0)) {
+      // First fixed argument of BufferOp is an integer (BufferSize)
+      // (can be a BYTE, WORD, DWORD or QWORD).
+      // BufferSize is an object node.
+      Status = AmlNodeUpdateIntegerValue (
+                 (AML_OBJECT_NODE*)AmlGetFixedArgument (
+                                     ObjectNode,
+                                     EAmlParseIndexTerm0
+                                     ),
+                 IsIncrement,
+                 (UINT64)(*Diff),
+                 &FieldWidthChange
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+
+      // FieldWidthChange is an integer.
+      // It must be positive if IsIncrement is TRUE, negative otherwise.
+      if ((IsIncrement              &&
+           (FieldWidthChange < 0))  ||
+          (!IsIncrement             &&
+           (FieldWidthChange > 0))) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      // Check for UINT32 overflow.
+      if (*Diff > (MAX_UINT32 - (UINT32)ABS (FieldWidthChange))) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      // Update Diff if the field width changed.
+      *Diff = (UINT32)(*Diff + ABS (FieldWidthChange));
+    } // AML_BUFFER_OP node.
+
+    // Update the PgkLen.
+    // Needs to be done at last to reflect the potential field width changes.
+    if (AmlNodeHasAttribute (ObjectNode, AML_HAS_PKG_LENGTH)) {
+      Value = ObjectNode->PkgLen;
+
+      // Subtract the size of the PkgLen encoding. The size of the PkgLen
+      // encoding must be computed after having updated Value.
+      InitialPkgLenWidth = AmlComputePkgLengthWidth (Value);
+      Value -= InitialPkgLenWidth;
+
+      // Check for an over/underflows.
+      // PkgLen is a 28 bit value, cf 20.2.4 Package Length Encoding
+      // i.e. the maximum value is (2^28 - 1) = ((BIT0 << 28) - 1).
+      if ((IsIncrement && ((((BIT0 << 28) - 1) - Value) < *Diff))   ||
+          (!IsIncrement && (Value < *Diff))) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      // Update the size.
+      if (IsIncrement) {
+        Value += *Diff;
+      } else {
+        Value -= *Diff;
+      }
+
+      // Compute the new PkgLenWidth.
+      NewPkgLenWidth = AmlComputePkgLengthWidth (Value);
+      if (NewPkgLenWidth == 0) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      // Add it to the Value.
+      Value += NewPkgLenWidth;
+
+      // Check that adding the PkgLenWidth didn't trigger a domino effect,
+      // increasing the encoding width of the PkgLen again.
+      // The PkgLen is encoded on at most 4 bytes. It is possible to increase
+      // the PkgLen width if its encoding is on less than 3 bytes.
+      ReComputedPkgLenWidth = AmlComputePkgLengthWidth (Value);
+      if (ReComputedPkgLenWidth != NewPkgLenWidth) {
+        if ((ReComputedPkgLenWidth != 0)   &&
+            (ReComputedPkgLenWidth < 4)) {
+          // No need to recompute the PkgLen since a new threshold cannot
+          // be reached by incrementing the value by one.
+          Value += 1;
+        } else {
+          ASSERT (0);
+          return EFI_INVALID_PARAMETER;
+        }
+      }
+
+      *Diff += (InitialPkgLenWidth > ReComputedPkgLenWidth) ?
+                 (InitialPkgLenWidth - ReComputedPkgLenWidth) :
+                 (ReComputedPkgLenWidth - InitialPkgLenWidth);
+      ObjectNode->PkgLen = Value;
+    } // PkgLen update.
+
+    // During CodeGeneration, the tree is incomplete and
+    // there is no root node at the top of the tree. Stop
+    // propagating the new size when finding a root node
+    // OR when a NULL parent is found.
+    ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node);
+    if (ParentNode != NULL) {
+      // Propagate the size up the tree.
+      Status = AmlPropagateSize (
+                 Node->Parent,
+                 IsIncrement,
+                 Diff
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+    }
+
+  } else if (IS_AML_ROOT_NODE (Node)) {
+    // Update the length field in the SDT header.
+    Value = ((AML_ROOT_NODE*)Node)->SdtHeader->Length;
+
+    // Check for an over/underflows.
+    if ((IsIncrement && (Value > (MAX_UINT32 - *Diff))) ||
+        (!IsIncrement && (Value < *Diff))) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+
+    // Update the size.
+    if (IsIncrement) {
+      Value += *Diff;
+    } else {
+      Value -= *Diff;
+    }
+
+    ((AML_ROOT_NODE*)Node)->SdtHeader->Length = Value;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Propagate the node count information up the tree.
+
+  @param  [in]  ObjectNode        Pointer to an object node.
+  @param  [in]  IsIncrement       Choose the operation to do:
+                                   - TRUE:  Increment the Node's size and
+                                            the Node's count;
+                                   - FALSE: Decrement the Node's size and
+                                           the Node's count.
+  @param  [in]  NodeCount         Number of nodes added/removed (depends on the
+                                  value of Operation).
+  @param  [out] FieldWidthChange  When modifying the integer, it can be
+                                  promoted/demoted, e.g. from UINT8 to UINT16.
+                                  Stores the change in width.
+                                  Can be negative.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlPropagateNodeCount (
+  IN  AML_OBJECT_NODE   * ObjectNode,
+  IN  BOOLEAN             IsIncrement,
+  IN  UINT8               NodeCount,
+  OUT INT8              * FieldWidthChange
+  )
+{
+  EFI_STATUS         Status;
+
+  AML_NODE_HEADER  * NodeCountArg;
+  UINT8              CurrNodeCount;
+
+  // Currently there is no use case where (NodeCount > 1).
+  if (!IS_AML_OBJECT_NODE (ObjectNode)  ||
+      (FieldWidthChange == NULL)        ||
+      (NodeCount > 1)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *FieldWidthChange = 0;
+
+  // Update the number of elements stored in PackageOp and VarPackageOp.
+  // The number of elements is stored as the first fixed argument.
+  // DefPackage := PackageOp PkgLength NumElements PackageElementList
+  // PackageOp := 0x12
+  // DefVarPackage := VarPackageOp PkgLength VarNumElements PackageElementList
+  // VarPackageOp := 0x13
+  // NumElements := ByteData
+  // VarNumElements := TermArg => Integer
+  NodeCountArg = AmlGetFixedArgument (ObjectNode, EAmlParseIndexTerm0);
+  if (AmlNodeCompareOpCode (ObjectNode, AML_PACKAGE_OP, 0)) {
+    // First fixed argument of PackageOp stores the number of elements
+    // in the package. It is an UINT8.
+
+    // Check for over/underflow.
+    CurrNodeCount = *(((AML_DATA_NODE*)NodeCountArg)->Buffer);
+    if ((IsIncrement && (CurrNodeCount == MAX_UINT8)) ||
+        (!IsIncrement && (CurrNodeCount == 0))) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+
+    // Update the node count in the DataNode.
+    CurrNodeCount = IsIncrement ? (CurrNodeCount + 1) : (CurrNodeCount - 1);
+    *(((AML_DATA_NODE*)NodeCountArg)->Buffer) =  CurrNodeCount;
+  } else if (AmlNodeCompareOpCode (ObjectNode, AML_VAR_PACKAGE_OP, 0)) {
+    // First fixed argument of PackageOp stores the number of elements
+    // in the package. It is an integer (can be a BYTE, WORD, DWORD, QWORD).
+    Status = AmlNodeUpdateIntegerValue (
+                (AML_OBJECT_NODE*)NodeCountArg,
+                IsIncrement,
+                NodeCount,
+                FieldWidthChange
+                );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Propagate information up the tree.
+
+  The information can be a new size, a new number of arguments.
+
+  @param  [in]  Node          Pointer to a node.
+                              Must be a root node or an object node.
+  @param  [in]  IsIncrement   Choose the operation to do:
+                               - TRUE:  Increment the Node's size and
+                                        the Node's count;
+                               - FALSE: Decrement the Node's size and
+                                        the Node's count.
+  @param  [in]  Diff          Value to add/subtract to the Node's size.
+  @param  [in]  NodeCount     Number of nodes added/removed.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlPropagateInformation (
+  IN  AML_NODE_HEADER   * Node,
+  IN  BOOLEAN             IsIncrement,
+  IN  UINT32              Diff,
+  IN  UINT8               NodeCount
+  )
+{
+  EFI_STATUS  Status;
+  INT8        FieldWidthChange;
+
+  // Currently there is no use case where (NodeCount > 1).
+  if ((!IS_AML_ROOT_NODE (Node)     &&
+       !IS_AML_OBJECT_NODE (Node))  ||
+      (NodeCount > 1)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Propagate the node count first as it may change the number of bytes
+  // needed to store the node count, and then impact FieldWidthChange.
+  if ((NodeCount != 0) &&
+      IS_AML_OBJECT_NODE (Node)) {
+    Status = AmlPropagateNodeCount (
+               (AML_OBJECT_NODE*)Node,
+               IsIncrement,
+               NodeCount,
+               &FieldWidthChange
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Propagate the potential field width change.
+    // Maximum change is between UINT8/UINT64: 8 bytes.
+    if ((ABS (FieldWidthChange) > 8)                          ||
+        (IsIncrement                                          &&
+          ((FieldWidthChange < 0)                             ||
+           ((Diff + (UINT8)FieldWidthChange) > MAX_UINT32)))  ||
+        (!IsIncrement                                         &&
+          ((FieldWidthChange > 0)                             ||
+           (Diff < (UINT32)ABS (FieldWidthChange))))) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+    Diff = (UINT32)(Diff + (UINT8)ABS (FieldWidthChange));
+  }
+
+  // Diff can be zero if some data is updated without modifying the data size.
+  if (Diff != 0) {
+    Status = AmlPropagateSize (Node, IsIncrement, &Diff);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h
new file mode 100644
index 0000000000000000000000000000000000000000..c57d780140d4f053a225032fb6fc4e7b9991896d
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h
@@ -0,0 +1,95 @@
+/** @file
+  AML Utility.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_UTILITY_H_
+#define AML_UTILITY_H_
+
+#include <AmlNodeDefines.h>
+
+/** This function computes and updates the ACPI table checksum.
+
+  @param  [in]  AcpiTable   Pointer to an Acpi table.
+
+  @retval EFI_SUCCESS   The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AcpiPlatformChecksum (
+  IN  EFI_ACPI_DESCRIPTION_HEADER  * AcpiTable
+  );
+
+/** Compute the size of a tree/sub-tree.
+
+  @param  [in]      Node      Node to compute the size.
+  @param  [in, out] Size      Pointer holding the computed size.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlComputeSize (
+  IN      CONST AML_NODE_HEADER   * Node,
+  IN  OUT       UINT32            * Size
+  );
+
+/** Set the value contained in an integer node.
+
+  The OpCode is updated accordingly to the new value
+  (e.g.: If the original value was a UINT8 value, then the OpCode
+         would be AML_BYTE_PREFIX. If it the new value is a UINT16
+         value then the OpCode will be updated to AML_WORD_PREFIX).
+
+  @param  [in]  Node            Pointer to an integer node.
+                                Must be an object node.
+  @param  [in]  NewValue        New value to write in the integer node.
+  @param  [out] ValueWidthDiff  Difference in number of bytes used to store
+                                the new value.
+                                Can be negative.
+
+  @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
+AmlNodeSetIntegerValue (
+  IN  AML_OBJECT_NODE   * Node,
+  IN  UINT64              NewValue,
+  OUT INT8              * ValueWidthDiff
+  );
+
+/** Propagate information up the tree.
+
+  The information can be a new size, a new number of arguments.
+
+  @param  [in]  Node          Pointer to a node.
+                              Must be a root node or an object node.
+  @param  [in]  IsIncrement   Choose the operation to do:
+                               - TRUE:  Increment the Node's size and
+                                        the Node's count;
+                               - FALSE: Decrement the Node's size and
+                                        the Node's count.
+  @param  [in]  Diff          Value to add/subtract to the Node's size.
+  @param  [in]  NodeCount     Number of nodes added/removed.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlPropagateInformation (
+  IN  AML_NODE_HEADER   * Node,
+  IN  BOOLEAN             IsIncrement,
+  IN  UINT32              Diff,
+  IN  UINT8               NodeCount
+  );
+
+#endif // AML_UTILITY_H_
+
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 11/30] DynamicTablesPkg: AML and ASL string helper
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (9 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 10/30] DynamicTablesPkg: AML utility interfaces Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 12/30] DynamicTablesPkg: AML stream interface Sami Mujawar
                   ` (19 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

Dynamic AML requires encoding/decoding and conversion of
AML and ASL strings. A collection of helper functions
have been provided for internal use in the AmlLib Library.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c | 1022 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h |  401 ++++++++
 2 files changed, 1423 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c b/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c
new file mode 100644
index 0000000000000000000000000000000000000000..e9d36f4b6c710cc9a22968d234bb4be720523bb4
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c
@@ -0,0 +1,1022 @@
+/** @file
+  AML String.
+
+  Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved. <BR>
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <String/AmlString.h>
+
+#include <AmlDefines.h>
+#include <IndustryStandard/AcpiAml.h>
+
+/** Check NameString/path information is valid.
+
+  Root, ParentPrefix and SegCount cannot be 0 at the same time.
+  This function works for ASL and AML name strings.
+
+  @param [in]   Root          Number of root char.
+                              Must be 0 or 1.
+  @param [in]   ParentPrefix  Number of carets char ('^').
+                              Must be [0-255].
+  @param [in]   SegCount      Number of NameSeg (s).
+                              Must be [0-255].
+
+  @retval TRUE id the input information is in the right boundaries.
+          FALSE otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlIsNameString (
+  IN  UINT32    Root,
+  IN  UINT32    ParentPrefix,
+  IN  UINT32    SegCount
+  )
+{
+  if (((Root == 0) || (Root == 1))            &&
+      (ParentPrefix <= MAX_UINT8)             &&
+      (!((ParentPrefix != 0) && (Root != 0))) &&
+      (SegCount <= MAX_UINT8)                 &&
+      ((SegCount + Root + ParentPrefix) != 0)) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/** Copy bytes from SrcBuffer to DstBuffer and convert to upper case.
+    Don't copy more than MaxDstBufferSize bytes.
+
+  @param  [out] DstBuffer         Destination buffer.
+  @param  [in]  MaxDstBufferSize  Maximum size of DstBuffer.
+                                  Must be non-zero.
+  @param  [in]  SrcBuffer         Source buffer.
+  @param  [in]  Count             Count of bytes to copy from SrcBuffer.
+                                  Return success if 0.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpperCaseMemCpyS (
+  OUT       CHAR8   * DstBuffer,
+  IN        UINT32    MaxDstBufferSize,
+  IN  CONST CHAR8   * SrcBuffer,
+  IN        UINT32    Count
+  )
+{
+  UINT32 Index;
+
+  if ((DstBuffer == NULL) ||
+      (SrcBuffer == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Count == 0) {
+    return EFI_SUCCESS;
+  }
+
+  if (Count > MaxDstBufferSize) {
+    Count = MaxDstBufferSize;
+  }
+
+  for (Index = 0; Index < Count; Index++) {
+    if ((SrcBuffer[Index] >= 'a') && (SrcBuffer[Index] <= 'z')) {
+      DstBuffer[Index] = (CHAR8)((UINT8)SrcBuffer[Index] - ('a' - 'A'));
+    } else {
+      DstBuffer[Index] = SrcBuffer[Index];
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Check whether Buffer is a root path ('\').
+
+  This function works for both ASL and AML pathnames.
+  Buffer must be at least 2 bytes long.
+
+  @param  [in]  Buffer   An ASL/AML path.
+
+  @retval TRUE    Buffer is a root path
+  @retval FALSE   Buffer is not a root path.
+**/
+BOOLEAN
+EFIAPI
+AmlIsRootPath (
+  IN  CONST  CHAR8  * Buffer
+  )
+{
+  if (Buffer == NULL) {
+    return FALSE;
+  }
+
+  if ((Buffer[0] == AML_ROOT_CHAR) && (Buffer[1] == '\0')) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/** Check whether Ch is an ASL/AML LeadName.
+
+  This function works for both ASL and AML pathnames.
+
+  ACPI 6.3 specification, s19.2.2. "ASL Name and Pathname Terms":
+  LeadNameChar := 'A'-'Z' | 'a'-'z' | '_'
+
+  ACPI 6.3 specification, s20.2.2. "Name Objects Encoding":
+  LeadNameChar := 'A'-'Z' | 'a'-'z' | '_'
+
+  @param  [in]  Ch    The char to test.
+
+  @retval TRUE    Ch is an ASL/AML LeadName.
+  @retval FALSE   Ch is not an ASL/AML LeadName.
+**/
+BOOLEAN
+EFIAPI
+AmlIsLeadNameChar (
+  IN  CHAR8   Ch
+  )
+{
+  if ((Ch == '_') || (Ch >= 'A' && Ch <= 'Z') || (Ch >= 'a' && Ch <= 'z')) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/** Check whether Ch is an ASL/AML NameChar.
+
+  This function works for both ASL and AML pathnames.
+
+  ACPI 6.3 specification, s19.2.2. "ASL Name and Pathname Terms":
+  NameChar := DigitChar | LeadNameChar
+  LeadNameChar := 'A'-'Z' | 'a'-'z' | '_'
+  DigitChar := '0'-'9'
+
+  ACPI 6.3 specification, s20.2.2. "Name Objects Encoding":
+  NameChar := DigitChar | LeadNameChar
+  LeadNameChar := 'A'-'Z' | 'a'-'z' | '_'
+  DigitChar := '0'-'9'
+
+  @param  [in]  Ch    The char to test.
+
+  @retval TRUE    Ch is an ASL/AML NameChar.
+  @retval FALSE   Ch is not an ASL/AML NameChar.
+**/
+BOOLEAN
+EFIAPI
+AmlIsNameChar (
+  IN  CHAR8  Ch
+  )
+{
+  if (AmlIsLeadNameChar (Ch) || (Ch >= '0' && Ch <= '9')) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/** Check whether AslBuffer is an ASL NameSeg.
+
+  This function only works for ASL NameStrings/pathnames.
+  ASL NameStrings/pathnames are at most 4 chars long.
+
+  @param [in]   AslBuffer   Pointer in an ASL NameString/pathname.
+  @param [out]  Size        Size of the NameSeg.
+
+  @retval TRUE    AslBuffer is an ASL NameSeg.
+  @retval FALSE   AslBuffer is not an ASL NameSeg.
+**/
+BOOLEAN
+EFIAPI
+AslIsNameSeg (
+  IN  CONST  CHAR8    * AslBuffer,
+  OUT        UINT32   * Size
+  )
+{
+  UINT32    Index;
+
+  if ((AslBuffer == NULL) ||
+      (Size == NULL)) {
+    return FALSE;
+  }
+
+  if (!AmlIsLeadNameChar (AslBuffer[0])) {
+    return FALSE;
+  }
+
+  for (Index = 1; Index < AML_NAME_SEG_SIZE; Index++) {
+    if ((AslBuffer[Index] == '.')   ||
+        (AslBuffer[Index] == '\0')) {
+      *Size = Index;
+      return TRUE;
+    } else if (!AmlIsNameChar (AslBuffer[Index])) {
+      return FALSE;
+    }
+  }
+
+  *Size = Index;
+  return TRUE;
+}
+
+/** Check whether AmlBuffer is an AML NameSeg.
+
+  This function only works for AML NameStrings/pathnames.
+  AML NameStrings/pathnames must be 4 chars long.
+
+  @param [in] AmlBuffer   Pointer in an AML NameString/pathname.
+
+  @retval TRUE    AmlBuffer is an AML NameSeg.
+  @retval FALSE   AmlBuffer is not an AML NameSeg.
+**/
+BOOLEAN
+EFIAPI
+AmlIsNameSeg (
+  IN  CONST  CHAR8    * AmlBuffer
+  )
+{
+  UINT32    Index;
+
+  if (AmlBuffer == NULL) {
+    return FALSE;
+  }
+
+  if (!AmlIsLeadNameChar (AmlBuffer[0])) {
+    return FALSE;
+  }
+
+  for (Index = 1; Index < AML_NAME_SEG_SIZE; Index++) {
+    if (!AmlIsNameChar (AmlBuffer[Index])) {
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+/** Parse an ASL NameString/path.
+
+  An ASL NameString/path must be NULL terminated.
+  Information found in the ASL NameString/path is returned via pointers:
+  Root, ParentPrefix, SegCount.
+
+  @param [in]    Buffer       ASL NameString/path.
+  @param [out]   Root         Pointer holding the number of root char.
+                              Can be 0 or 1.
+  @param [out]   ParentPrefix Pointer holding the number of carets char ('^').
+                              Can be [0-255].
+  @param [out]   SegCount     Pointer holding the number of NameSeg (s).
+                              Can be [0-255].
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AslParseNameStringInfo (
+  IN  CONST CHAR8     * Buffer,
+  OUT       UINT32    * Root,
+  OUT       UINT32    * ParentPrefix,
+  OUT       UINT32    * SegCount
+  )
+{
+  UINT32      NameSegSize;
+
+  if ((Buffer == NULL)        ||
+      (Root == NULL)          ||
+      (ParentPrefix == NULL)  ||
+      (SegCount == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *Root = 0;
+  *ParentPrefix = 0;
+  *SegCount = 0;
+
+  // Handle Root and ParentPrefix(s).
+  if (*Buffer == AML_ROOT_CHAR) {
+    *Root = 1;
+    Buffer++;
+  } else if (*Buffer == AML_PARENT_PREFIX_CHAR) {
+    do {
+      Buffer++;
+      (*ParentPrefix)++;
+    } while (*Buffer == AML_PARENT_PREFIX_CHAR);
+  }
+
+  // Handle SegCount(s).
+  while (AslIsNameSeg (Buffer, &NameSegSize)) {
+    // Safety checks on NameSegSize.
+    if ((NameSegSize == 0) || (NameSegSize > AML_NAME_SEG_SIZE)) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+
+    // Increment the NameSeg count.
+    (*SegCount)++;
+    Buffer += NameSegSize;
+
+    // Skip the '.' separator if present.
+    if (*Buffer == '.') {
+      Buffer++;
+    }
+  } // while
+
+  // An ASL NameString/path must be NULL terminated.
+  if (*Buffer != '\0') {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!AmlIsNameString (*Root, *ParentPrefix, *SegCount)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Parse an AML NameString/path.
+
+  It is possible to determine the size of an AML NameString/path just
+  by sight reading it. So no overflow can occur.
+  Information found in the AML NameString/path is returned via pointers:
+  Root, ParentPrefix, SegCount.
+
+  @param [in]    Buffer         AML NameString/path.
+  @param [out]   Root           Pointer holding the number of root char.
+                                Can be 0 or 1.
+  @param [out]   ParentPrefix   Pointer holding the number of carets char ('^').
+                                Can be [0-255].
+  @param [out]   SegCount       Pointer holding the number of NameSeg(s).
+                                Can be [0-255].
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlParseNameStringInfo (
+  IN  CONST CHAR8   * Buffer,
+  OUT       UINT32  * Root,
+  OUT       UINT32  * ParentPrefix,
+  OUT       UINT32  * SegCount
+  )
+{
+  if ((Buffer == NULL) ||
+      (Root == NULL)   ||
+      (ParentPrefix == NULL) ||
+      (SegCount == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *Root = 0;
+  *ParentPrefix = 0;
+  *SegCount = 0;
+
+  // Handle Root and ParentPrefix(s).
+  if (*Buffer == AML_ROOT_CHAR) {
+    *Root = 1;
+    Buffer++;
+  } else if (*Buffer == AML_PARENT_PREFIX_CHAR) {
+    do {
+      Buffer++;
+      (*ParentPrefix)++;
+    } while (*Buffer == AML_PARENT_PREFIX_CHAR);
+  }
+
+  // Handle SegCount(s).
+  if (*Buffer == AML_DUAL_NAME_PREFIX) {
+    *SegCount = 2;
+  } else if (*Buffer == AML_MULTI_NAME_PREFIX) {
+    *SegCount = *((UINT8*)(Buffer + 1));
+  } else if (AmlIsNameSeg (Buffer)) {
+    *SegCount = 1;
+  } else if (*Buffer == AML_ZERO_OP) {
+    *SegCount = 0;
+  } else {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Safety checks on exit.
+  if (!AmlIsNameString (*Root, *ParentPrefix, *SegCount)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Compute the ASL NameString/path size from NameString
+    information (Root, ParentPrefix, SegCount).
+
+  @param [in] Root          Number of root char.
+                            Can be 0 or 1.
+  @param [in] ParentPrefix  Number of carets char ('^').
+                            Can be [0-255].
+  @param [in] SegCount      Pointer holding the number of NameSeg(s).
+                            Can be [0-255].
+
+  @return Size of the ASL NameString/path.
+**/
+UINT32
+EFIAPI
+AslComputeNameStringSize (
+  IN  UINT32    Root,
+  IN  UINT32    ParentPrefix,
+  IN  UINT32    SegCount
+  )
+{
+  UINT32    TotalSize;
+
+  if (!AmlIsNameString (Root, ParentPrefix, SegCount)) {
+    ASSERT (0);
+    return 0;
+  }
+
+  // Root and ParentPrefix(s).
+  TotalSize = Root + ParentPrefix;
+
+  // Add size required for NameSeg(s).
+  TotalSize += (SegCount * AML_NAME_SEG_SIZE);
+
+  // Add size required for '.' separator(s).
+  TotalSize += (SegCount > 1) ? (SegCount - 1) : 0;
+
+  // Add 1 byte for NULL termination '\0'.
+  TotalSize += 1;
+
+  return TotalSize;
+}
+
+/** Compute the AML NameString/path size from NameString
+    information (Root, ParentPrefix, SegCount).
+
+  @param [in] Root          Number of root char.
+                            Can be 0 or 1.
+  @param [in] ParentPrefix  Number of carets char ('^').
+                            Can be [0-255].
+  @param [in] SegCount      Pointer holding the number of NameSeg(s).
+                            Can be [0-255].
+
+  @return Size of the AML NameString/path.
+**/
+UINT32
+EFIAPI
+AmlComputeNameStringSize (
+  IN  UINT32    Root,
+  IN  UINT32    ParentPrefix,
+  IN  UINT32    SegCount
+  )
+{
+  UINT32    TotalSize;
+
+  if (!AmlIsNameString (Root, ParentPrefix, SegCount)) {
+    ASSERT (0);
+    return 0;
+  }
+
+  // Root and ParentPrefix(s).
+  TotalSize = Root + ParentPrefix;
+
+  // If SegCount == 0, '\0' must end the AML NameString/path.
+  TotalSize += (SegCount == 0) ? 1 : (SegCount * AML_NAME_SEG_SIZE);
+
+  // AML prefix. SegCount > 2 = MultiNamePrefix, SegCount = 2 DualNamePrefix.
+  TotalSize += (SegCount > 2) ? 2 : ((SegCount == 2) ? 1 : 0);
+
+  return TotalSize;
+}
+
+/** Get the ASL NameString/path size.
+
+  @param [in]   AslPath         An ASL NameString/path.
+  @param [out]  AslPathSizePtr  Pointer holding the ASL NameString/path size.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AslGetNameStringSize (
+  IN  CONST CHAR8   * AslPath,
+  OUT       UINT32  * AslPathSizePtr
+  )
+{
+  if ((AslPath == NULL) ||
+      (AslPathSizePtr == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *AslPathSizePtr = 0;
+  do {
+    (*AslPathSizePtr)++;
+    AslPath++;
+  } while (*AslPath != '\0');
+
+  return EFI_SUCCESS;
+}
+
+/** Get the AML NameString/path size.
+
+  @param [in]   AmlPath         An AML NameString/path.
+  @param [out]  AmlPathSizePtr  Pointer holding the AML NameString/path size.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetNameStringSize (
+  IN   CONST  CHAR8   * AmlPath,
+  OUT         UINT32  * AmlPathSizePtr
+  )
+{
+  EFI_STATUS    Status;
+
+  UINT32        Root;
+  UINT32        ParentPrefix;
+  UINT32        SegCount;
+
+  if ((AmlPath == NULL) ||
+      (AmlPathSizePtr == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = AmlParseNameStringInfo (
+             AmlPath,
+             &Root,
+             &ParentPrefix,
+             &SegCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  *AmlPathSizePtr = AmlComputeNameStringSize (Root, ParentPrefix, SegCount);
+  if (*AmlPathSizePtr == 0) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return Status;
+}
+
+/** Convert an ASL NameString/path to an AML NameString/path.
+    The caller must free the memory allocated in this function
+    for AmlPath using FreePool ().
+
+  @param  [in]  AslPath     An ASL NameString/path.
+  @param  [out] OutAmlPath  Buffer containing the AML path.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+ConvertAslNameToAmlName (
+  IN  CONST  CHAR8   * AslPath,
+  OUT        CHAR8  ** OutAmlPath
+  )
+{
+  EFI_STATUS      Status;
+
+  UINT32          Root;
+  UINT32          ParentPrefix;
+  UINT32          SegCount;
+  UINT32          TotalSize;
+  UINT32          NameSegSize;
+
+  CONST CHAR8   * AslBuffer;
+  CHAR8         * AmlBuffer;
+  CHAR8         * AmlPath;
+
+  if ((AslPath == NULL) ||
+      (OutAmlPath == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Analyze AslPath. AslPath is checked in the call.
+  Status = AslParseNameStringInfo (AslPath, &Root, &ParentPrefix, &SegCount);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Compute TotalSize.
+  TotalSize = AmlComputeNameStringSize (Root, ParentPrefix, SegCount);
+  if (TotalSize == 0) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Allocate memory.
+  AmlPath = AllocateZeroPool (TotalSize);
+  if (AmlPath == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  AmlBuffer = AmlPath;
+  AslBuffer = AslPath;
+
+  // Handle Root and ParentPrefix(s).
+  if (Root == 1) {
+    *AmlBuffer = AML_ROOT_CHAR;
+    AmlBuffer++;
+    AslBuffer++;
+  } else if (ParentPrefix > 0) {
+    SetMem (AmlBuffer, ParentPrefix, AML_PARENT_PREFIX_CHAR);
+    AmlBuffer += ParentPrefix;
+    AslBuffer += ParentPrefix;
+  }
+
+  // Handle prefix and SegCount(s).
+  if (SegCount > 2) {
+    *AmlBuffer = AML_MULTI_NAME_PREFIX;
+    AmlBuffer++;
+    *AmlBuffer = (UINT8)SegCount;
+    AmlBuffer++;
+  } else if (SegCount == 2) {
+    *AmlBuffer = AML_DUAL_NAME_PREFIX;
+    AmlBuffer++;
+  }
+
+  if (SegCount != 0) {
+    // Write NameSeg(s).
+    while (1) {
+      SegCount--;
+
+      // Get the NameSeg size.
+      if (!AslIsNameSeg (AslBuffer, &NameSegSize)) {
+        ASSERT (0);
+        Status = EFI_INVALID_PARAMETER;
+        goto error_handler;
+      }
+
+      // Convert to Upper case and copy.
+      Status = AmlUpperCaseMemCpyS (
+                 AmlBuffer,
+                 TotalSize,
+                 AslBuffer,
+                 NameSegSize
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        goto error_handler;
+      }
+
+      // Complete the NameSeg with an underscore ('_') if shorter than 4 bytes.
+      SetMem (
+        AmlBuffer + NameSegSize,
+        AML_NAME_SEG_SIZE - NameSegSize,
+        AML_NAME_CHAR__
+        );
+
+      // Go to the next NameSeg.
+      AmlBuffer += AML_NAME_SEG_SIZE;
+      AslBuffer += NameSegSize;
+
+      // Skip the '.' separator.
+      if (SegCount != 0) {
+        if (*AslBuffer == '.') {
+          AslBuffer++;
+        } else {
+          ASSERT (0);
+          Status = EFI_INVALID_PARAMETER;
+          goto error_handler;
+        }
+      } else {
+        // (SegCount == 0)
+        if (*AslBuffer == '\0') {
+          break;
+        } else {
+          ASSERT (0);
+          Status = EFI_INVALID_PARAMETER;
+          goto error_handler;
+        }
+      }
+    } // while
+
+  } else {
+    // (SegCount == 0)
+    // '\0' needs to end the AML NameString/path.
+    *AmlBuffer = AML_ZERO_OP;
+    AmlBuffer++;
+  }
+
+  // Safety checks on exit.
+  // Check that AmlPath has been filled with TotalSize bytes.
+  if ((SegCount != 0)               ||
+      (*AslBuffer != AML_ZERO_OP)   ||
+      (((UINT32)(AmlBuffer - AmlPath)) != TotalSize)) {
+      ASSERT (0);
+      Status = EFI_INVALID_PARAMETER;
+      goto error_handler;
+  }
+
+  *OutAmlPath = AmlPath;
+  return EFI_SUCCESS;
+
+error_handler:
+  FreePool (AmlPath);
+  return Status;
+}
+
+/** Convert an AML NameString/path to an ASL NameString/path.
+    The caller must free the memory allocated in this function.
+    using FreePool ().
+
+  @param  [in]  AmlPath     An AML NameString/path.
+  @param  [out] OutAslPath  Buffer containing the ASL path.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+ConvertAmlNameToAslName (
+  IN  CONST CHAR8     * AmlPath,
+  OUT       CHAR8    ** OutAslPath
+  )
+{
+  EFI_STATUS      Status;
+
+  UINT32          Root;
+  UINT32          ParentPrefix;
+  UINT32          SegCount;
+  UINT32          TotalSize;
+
+  CONST CHAR8   * AmlBuffer;
+  CHAR8         * AslBuffer;
+  CHAR8         * AslPath;
+
+  if ((AmlPath == NULL)   ||
+      (OutAslPath == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Analyze AslPath. AmlPath is checked in the call.
+  Status = AmlParseNameStringInfo (AmlPath, &Root, &ParentPrefix, &SegCount);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Compute TotalSize.
+  TotalSize = AslComputeNameStringSize (Root, ParentPrefix, SegCount);
+  if (TotalSize == 0) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Allocate memory.
+  AslPath = AllocateZeroPool (TotalSize);
+  if (AslPath == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  AmlBuffer = AmlPath;
+  AslBuffer = AslPath;
+
+  // Handle prefix and SegCount(s).
+  if (Root == 1) {
+    *AslBuffer = AML_ROOT_CHAR;
+    AslBuffer++;
+    AmlBuffer++;
+  } else if (ParentPrefix > 0) {
+    SetMem (AslBuffer, ParentPrefix, AML_PARENT_PREFIX_CHAR);
+    AslBuffer += ParentPrefix;
+    AmlBuffer += ParentPrefix;
+  }
+
+  // Handle Root and Parent(s).
+  // Skip the MultiName or DualName prefix chars.
+  if (SegCount > 2) {
+    AmlBuffer += 2;
+  } else if (SegCount == 2) {
+    AmlBuffer += 1;
+  }
+
+  // Write NameSeg(s).
+  while (SegCount) {
+    // NameSeg is already in upper case and always 4 bytes long.
+    CopyMem (AslBuffer, AmlBuffer, AML_NAME_SEG_SIZE);
+    AslBuffer += AML_NAME_SEG_SIZE;
+    AmlBuffer += AML_NAME_SEG_SIZE;
+
+    SegCount--;
+
+    // Write the '.' separator if there is another NameSeg following.
+    if (SegCount != 0) {
+      *AslBuffer = '.';
+      AslBuffer++;
+    }
+  } // while
+
+  // NULL terminate the ASL NameString.
+  *AslBuffer = '\0';
+  AslBuffer++;
+
+  // Safety checks on exit.
+  // Check that AslPath has been filled with TotalSize bytes.
+  if (((UINT32)(AslBuffer - AslPath)) != TotalSize) {
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+    goto error_handler;
+  }
+
+  *OutAslPath = AslPath;
+  return EFI_SUCCESS;
+
+error_handler:
+  FreePool (AslPath);
+  return Status;
+}
+
+/** Compare two ASL NameStrings.
+
+  @param [in] AslName1    First NameString to compare.
+  @param [in] AslName2    Second NameString to compare.
+
+  @retval TRUE if the two strings are identical.
+  @retval FALSE otherwise, or if error.
+**/
+BOOLEAN
+EFIAPI
+AslCompareNameString (
+  IN  CONST CHAR8 *   AslName1,
+  IN  CONST CHAR8 *   AslName2
+  )
+{
+  EFI_STATUS    Status;
+  UINT32        AslName1Len;
+  UINT32        AslName2Len;
+
+  if ((AslName1 == NULL) ||
+      (AslName2 == NULL)) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  Status = AslGetNameStringSize (AslName1, &AslName1Len);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  Status = AslGetNameStringSize (AslName2, &AslName2Len);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  // AslName1 and AslName2 don't have the same length
+  if (AslName1Len != AslName2Len) {
+    return FALSE;
+  }
+
+  return (CompareMem (AslName1, AslName2, AslName1Len) == 0);
+}
+
+/** Compare two AML NameStrings.
+
+  @param [in] AmlName1    First NameString to compare.
+  @param [in] AmlName2    Second NameString to compare.
+
+  @retval TRUE if the two strings are identical.
+  @retval FALSE otherwise, or if error.
+**/
+BOOLEAN
+EFIAPI
+AmlCompareNameString (
+  IN  CONST CHAR8 *   AmlName1,
+  IN  CONST CHAR8 *   AmlName2
+  )
+{
+  EFI_STATUS    Status;
+  UINT32        AmlName1Len;
+  UINT32        AmlName2Len;
+
+  if ((AmlName1 == NULL) ||
+      (AmlName2 == NULL)) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  Status = AmlGetNameStringSize (AmlName1, &AmlName1Len);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  Status = AmlGetNameStringSize (AmlName2, &AmlName2Len);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  // AmlName1 and AmlName2 don't have the same length
+  if (AmlName1Len != AmlName2Len) {
+    return FALSE;
+  }
+
+  return (CompareMem (AmlName1, AmlName2, AmlName1Len) == 0);
+}
+
+/** Compare an AML NameString and an ASL NameString.
+
+  The ASL NameString is converted to an AML NameString before
+  being compared with the ASL NameString. This allows to expand
+  NameSegs shorter than 4 chars.
+  E.g.: AslName: "DEV" will be expanded to "DEV_" before being
+        compared.
+
+  @param [in] AmlName1   AML NameString to compare.
+  @param [in] AslName2   ASL NameString to compare.
+
+  @retval TRUE if the two strings are identical.
+  @retval FALSE otherwise, or if error.
+**/
+BOOLEAN
+EFIAPI
+CompareAmlWithAslNameString (
+  IN  CONST CHAR8 *   AmlName1,
+  IN  CONST CHAR8 *   AslName2
+  )
+{
+  EFI_STATUS    Status;
+
+  CHAR8       * AmlName2;
+  BOOLEAN       RetVal;
+
+  if ((AmlName1 == NULL) ||
+      (AslName2 == NULL)) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  // Convert the AslName2 to an AmlName2.
+  // AmlName2 must be freed.
+  Status = ConvertAmlNameToAslName (AslName2, &AmlName2);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  RetVal = AmlCompareNameString (AmlName1, AmlName2);
+
+  // Free AmlName2.
+  FreePool (AmlName2);
+
+  return RetVal;
+}
+/** Given an AmlPath, return the address of the first NameSeg.
+
+  It is possible to determine the size of an AML NameString/path just
+  by sight reading it. So no overflow can occur.
+
+  @param  [in]  AmlPath       The AML pathname.
+  @param  [in]  Root          The AML pathname starts with a root char.
+                              It is an absolute path.
+  @param  [in]  ParentPrefix  The AML pathname has ParentPrefix
+                              carets in its name.
+
+  @return Pointer to the first NameSeg of the NameString.
+          Return NULL if AmlPath is NULL.
+**/
+CONST
+CHAR8 *
+EFIAPI
+AmlGetFirstNameSeg (
+  IN  CONST  CHAR8    * AmlPath,
+  IN         UINT32     Root,
+  IN         UINT32     ParentPrefix
+  )
+{
+  if (AmlPath == NULL) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  AmlPath += Root;
+  AmlPath += ParentPrefix;
+  AmlPath += ((*AmlPath == AML_MULTI_NAME_PREFIX) ? 2
+               : (*AmlPath == AML_DUAL_NAME_PREFIX) ? 1 : 0);
+  return AmlPath;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h b/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h
new file mode 100644
index 0000000000000000000000000000000000000000..86d9df5f1918d57261c59f8df585d5c80def3d41
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h
@@ -0,0 +1,401 @@
+/** @file
+  AML String.
+
+  Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved. <BR>
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_STRING_H_
+#define AML_STRING_H_
+
+/* This header file does not include internal Node definition,
+   i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions
+   must be included by the caller file. The function prototypes must
+   only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node
+   definitions.
+   This allows to keep the functions defined here both internal and
+   potentially external. If necessary, any function of this file can
+   be exposed externally.
+   The Api folder is internal to the AmlLib, but should only use these
+   functions. They provide a "safe" way to interact with the AmlLib.
+*/
+
+#include <AmlInclude.h>
+
+/** Check NameString/path information is valid.
+
+  Root, ParentPrefix and SegCount cannot be 0 at the same time.
+  This function works for ASL and AML name strings.
+
+  @param [in]   Root          Number of root char.
+                              Must be 0 or 1.
+  @param [in]   ParentPrefix  Number of carets char ('^').
+                              Must be [0-255].
+  @param [in]   SegCount      Number of NameSeg (s).
+                              Must be [0-255].
+
+  @retval TRUE id the input information is in the right boundaries.
+          FALSE otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlIsNameString (
+  IN  UINT32    Root,
+  IN  UINT32    ParentPrefix,
+  IN  UINT32    SegCount
+  );
+
+/** Copy bytes from SrcBuffer to DstBuffer and convert to upper case.
+    Don't copy more than MaxDstBufferSize bytes.
+
+  @param  [out] DstBuffer         Destination buffer.
+  @param  [in]  MaxDstBufferSize  Maximum size of DstBuffer.
+                                  Must be non-zero.
+  @param  [in]  SrcBuffer         Source buffer.
+  @param  [in]  Count             Count of bytes to copy from SrcBuffer.
+                                  Return success if 0.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpperCaseMemCpyS (
+  OUT       CHAR8   * DstBuffer,
+  IN        UINT32    MaxDstBufferSize,
+  IN  CONST CHAR8   * SrcBuffer,
+  IN        UINT32    Count
+  );
+
+/** Check whether Buffer is a root path ('\').
+
+  This function works for both ASL and AML pathnames.
+  Buffer must be at least 2 bytes long.
+
+  @param  [in]  Buffer   An ASL/AML path.
+
+  @retval TRUE    Buffer is a root path
+  @retval FALSE   Buffer is not a root path.
+**/
+BOOLEAN
+EFIAPI
+AmlIsRootPath (
+  IN  CONST  CHAR8  * Buffer
+  );
+
+/** Check whether Ch is an ASL/AML LeadName.
+
+  This function works for both ASL and AML pathnames.
+
+  ACPI 6.3 specification, s19.2.2. "ASL Name and Pathname Terms":
+  LeadNameChar := 'A'-'Z' | 'a'-'z' | '_'
+
+  ACPI 6.3 specification, s20.2.2. "Name Objects Encoding":
+  LeadNameChar := 'A'-'Z' | 'a'-'z' | '_'
+
+  @param  [in]  Ch    The char to test.
+
+  @retval TRUE    Ch is an ASL/AML LeadName.
+  @retval FALSE   Ch is not an ASL/AML LeadName.
+**/
+BOOLEAN
+EFIAPI
+AmlIsLeadNameChar (
+  IN  CHAR8   Ch
+  );
+
+/** Check whether Ch is an ASL/AML NameChar.
+
+  This function works for both ASL and AML pathnames.
+
+  ACPI 6.3 specification, s19.2.2. "ASL Name and Pathname Terms":
+  NameChar := DigitChar | LeadNameChar
+  LeadNameChar := 'A'-'Z' | 'a'-'z' | '_'
+  DigitChar := '0'-'9'
+
+  ACPI 6.3 specification, s20.2.2. "Name Objects Encoding":
+  NameChar := DigitChar | LeadNameChar
+  LeadNameChar := 'A'-'Z' | 'a'-'z' | '_'
+  DigitChar := '0'-'9'
+
+  @param  [in]  Ch    The char to test.
+
+  @retval TRUE    Ch is an ASL/AML NameChar.
+  @retval FALSE   Ch is not an ASL/AML NameChar.
+**/
+BOOLEAN
+EFIAPI
+AmlIsNameChar (
+  IN  CHAR8  Ch
+  );
+
+/** Check whether AslBuffer is an ASL NameSeg.
+
+  This function only works for ASL NameStrings/pathnames.
+  ASL NameStrings/pathnames are at most 4 chars long.
+
+  @param [in]   AslBuffer   Pointer in an ASL NameString/pathname.
+  @param [out]  Size        Size of the NameSeg.
+
+  @retval TRUE    AslBuffer is an ASL NameSeg.
+  @retval FALSE   AslBuffer is not an ASL NameSeg.
+**/
+BOOLEAN
+EFIAPI
+AslIsNameSeg (
+  IN  CONST  CHAR8    * AslBuffer,
+  OUT        UINT32   * Size
+  );
+
+/** Check whether AmlBuffer is an AML NameSeg.
+
+  This function only works for AML NameStrings/pathnames.
+  AML NameStrings/pathnames must be 4 chars long.
+
+  @param [in] AmlBuffer   Pointer in an AML NameString/pathname.
+
+  @retval TRUE    AmlBuffer is an AML NameSeg.
+  @retval FALSE   AmlBuffer is not an AML NameSeg.
+**/
+BOOLEAN
+EFIAPI
+AmlIsNameSeg (
+  IN  CONST  CHAR8    * AmlBuffer
+  );
+
+/** Parse an ASL NameString/path.
+
+  An ASL NameString/path must be NULL terminated.
+  Information found in the ASL NameString/path is returned via pointers:
+  Root, ParentPrefix, SegCount.
+
+  @param [in]    Buffer       ASL NameString/path.
+  @param [out]   Root         Pointer holding the number of root char.
+                              Can be 0 or 1.
+  @param [out]   ParentPrefix Pointer holding the number of carets char ('^').
+                              Can be [0-255].
+  @param [out]   SegCount     Pointer holding the number of NameSeg (s).
+                              Can be [0-255].
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AslParseNameStringInfo (
+  IN  CONST CHAR8     * Buffer,
+  OUT       UINT32    * Root,
+  OUT       UINT32    * ParentPrefix,
+  OUT       UINT32    * SegCount
+  );
+
+/** Parse an AML NameString/path.
+
+  It is possible to determine the size of an AML NameString/path just
+  by sight reading it. So no overflow can occur.
+  Information found in the AML NameString/path is returned via pointers:
+  Root, ParentPrefix, SegCount.
+
+  @param [in]    Buffer         AML NameString/path.
+  @param [out]   Root           Pointer holding the number of root char.
+                                Can be 0 or 1.
+  @param [out]   ParentPrefix   Pointer holding the number of carets char ('^').
+                                Can be [0-255].
+  @param [out]   SegCount       Pointer holding the number of NameSeg(s).
+                                Can be [0-255].
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlParseNameStringInfo (
+  IN  CONST CHAR8   * Buffer,
+  OUT       UINT32  * Root,
+  OUT       UINT32  * ParentPrefix,
+  OUT       UINT32  * SegCount
+  );
+
+/** Compute the ASL NameString/path size from NameString
+    information (Root, ParentPrefix, SegCount).
+
+  @param [in] Root          Number of root char.
+                            Can be 0 or 1.
+  @param [in] ParentPrefix  Number of carets char ('^').
+                            Can be [0-255].
+  @param [in] SegCount      Pointer holding the number of NameSeg(s).
+                            Can be [0-255].
+
+  @return Size of the ASL NameString/path.
+**/
+UINT32
+EFIAPI
+AslComputeNameStringSize (
+  IN  UINT32    Root,
+  IN  UINT32    ParentPrefix,
+  IN  UINT32    SegCount
+  );
+
+/** Compute the AML NameString/path size from NameString
+    information (Root, ParentPrefix, SegCount).
+
+  @param [in] Root          Number of root char.
+                            Can be 0 or 1.
+  @param [in] ParentPrefix  Number of carets char ('^').
+                            Can be [0-255].
+  @param [in] SegCount      Pointer holding the number of NameSeg(s).
+                            Can be [0-255].
+
+  @return Size of the AML NameString/path.
+**/
+UINT32
+EFIAPI
+AmlComputeNameStringSize (
+  IN  UINT32    Root,
+  IN  UINT32    ParentPrefix,
+  IN  UINT32    SegCount
+  );
+
+/** Get the ASL NameString/path size.
+
+  @param [in]   AslPath         An ASL NameString/path.
+  @param [out]  AslPathSizePtr  Pointer holding the ASL NameString/path size.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AslGetNameStringSize (
+  IN  CONST CHAR8   * AslPath,
+  OUT       UINT32  * AslPathSizePtr
+  );
+
+/** Get the AML NameString/path size.
+
+  @param [in]   AmlPath         An AML NameString/path.
+  @param [out]  AmlPathSizePtr  Pointer holding the AML NameString/path size.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetNameStringSize (
+  IN   CONST  CHAR8   * AmlPath,
+  OUT         UINT32  * AmlPathSizePtr
+  );
+
+/** Convert an ASL NameString/path to an AML NameString/path.
+    The caller must free the memory allocated in this function
+    for AmlPath using FreePool ().
+
+  @param  [in]  AslPath     An ASL NameString/path.
+  @param  [out] OutAmlPath  Buffer containing the AML path.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+ConvertAslNameToAmlName (
+  IN  CONST  CHAR8   * AslPath,
+  OUT        CHAR8  ** OutAmlPath
+  );
+
+/** Convert an AML NameString/path to an ASL NameString/path.
+    The caller must free the memory allocated in this function.
+    using FreePool ().
+
+  @param  [in]  AmlPath     An AML NameString/path.
+  @param  [out] OutAslPath  Buffer containing the ASL path.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+ConvertAmlNameToAslName (
+  IN  CONST CHAR8     * AmlPath,
+  OUT       CHAR8    ** OutAslPath
+  );
+
+/** Compare two ASL NameStrings.
+
+  @param [in] AslName1    First NameString to compare.
+  @param [in] AslName2    Second NameString to compare.
+
+  @retval TRUE if the two strings are identical.
+  @retval FALSE otherwise, or if error.
+**/
+BOOLEAN
+EFIAPI
+AslCompareNameString (
+  IN  CONST CHAR8 *   AslName1,
+  IN  CONST CHAR8 *   AslName2
+  );
+
+/** Compare two AML NameStrings.
+
+  @param [in] AmlName1    First NameString to compare.
+  @param [in] AmlName2    Second NameString to compare.
+
+  @retval TRUE if the two strings are identical.
+  @retval FALSE otherwise, or if error.
+**/
+BOOLEAN
+EFIAPI
+AmlCompareNameString (
+  IN  CONST CHAR8 *   AmlName1,
+  IN  CONST CHAR8 *   AmlName2
+  );
+
+/** Compare an AML NameString and an ASL NameString.
+
+  The ASL NameString is converted to an AML NameString before
+  being compared with the ASL NameString. This allows to expand
+  NameSegs shorter than 4 chars.
+  E.g.: AslName: "DEV" will be expanded to "DEV_" before being
+        compared.
+
+  @param [in] AmlName1   AML NameString to compare.
+  @param [in] AslName2   ASL NameString to compare.
+
+  @retval TRUE if the two strings are identical.
+  @retval FALSE otherwise, or if error.
+**/
+BOOLEAN
+EFIAPI
+CompareAmlWithAslNameString (
+  IN  CONST CHAR8 *   AmlName1,
+  IN  CONST CHAR8 *   AslName2
+  );
+
+/** Given an AmlPath, return the address of the first NameSeg.
+
+  It is possible to determine the size of an AML NameString/path just
+  by sight reading it. So no overflow can occur.
+
+  @param  [in]  AmlPath       The AML pathname.
+  @param  [in]  Root          The AML pathname starts with a root char.
+                              It is an absolute path.
+  @param  [in]  ParentPrefix  The AML pathname has ParentPrefix
+                              carets in its name.
+
+  @return Pointer to the first NameSeg of the NameString.
+          Return NULL if AmlPath is NULL.
+**/
+CONST
+CHAR8 *
+EFIAPI
+AmlGetFirstNameSeg (
+  IN  CONST  CHAR8    * AmlPath,
+  IN         UINT32     Root,
+  IN         UINT32     ParentPrefix
+  );
+
+#endif // AML_STRING_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 12/30] DynamicTablesPkg: AML stream interface
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (10 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 11/30] DynamicTablesPkg: AML and ASL string helper Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 13/30] DynamicTablesPkg: AML serialise interface Sami Mujawar
                   ` (18 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

Dynamic AML involves parsing/packing of AML opcode and
data into AML byte streams. The AML stream interface
provides safe buffer management as well as supports
forward and reverse streams. It provides functions to
create, read, write, clone and compare AML streams.

Co-authored-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c | 665 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h | 451 +++++++++++++
 2 files changed, 1116 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c b/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c
new file mode 100644
index 0000000000000000000000000000000000000000..419e796e50cbe29e62234916c6e39c2c625a2951
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c
@@ -0,0 +1,665 @@
+/** @file
+  AML Stream.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Stream/AmlStream.h>
+
+/** Initialize a stream.
+
+  @param  [in, out] Stream          Pointer to the stream to initialize.
+  @param  [in]      Buffer          Buffer to initialize Stream with.
+                                    Point to the beginning of the Buffer.
+  @param  [in]      MaxBufferSize   Maximum size of Buffer.
+  @param  [in]      Direction       Direction Stream is progressing
+                                    (forward, backward).
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamInit (
+  IN  OUT AML_STREAM              * Stream,
+  IN      UINT8                   * Buffer,
+  IN      UINT32                    MaxBufferSize,
+  IN      EAML_STREAM_DIRECTION     Direction
+  )
+{
+  if ((Stream == NULL)                            ||
+      (Buffer == NULL)                            ||
+      (MaxBufferSize == 0)                        ||
+      ((Direction != EAmlStreamDirectionForward)  &&
+       (Direction != EAmlStreamDirectionBackward))) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Stream->Buffer = Buffer;
+  Stream->MaxBufferSize = MaxBufferSize;
+  Stream->Index = 0;
+  Stream->Direction = Direction;
+
+  return EFI_SUCCESS;
+}
+
+/** Clone a stream.
+
+  Cloning a stream means copying all the values of the input Stream
+  in the ClonedStream.
+
+  @param  [in]  Stream          Pointer to the stream to clone.
+  @param  [in]  ClonedStream    Pointer to the stream to initialize.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamClone (
+  IN  CONST AML_STREAM    * Stream,
+  OUT        AML_STREAM   * ClonedStream
+  )
+{
+  if (!IS_STREAM (Stream)   ||
+      (ClonedStream == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ClonedStream->Buffer = Stream->Buffer;
+  ClonedStream->MaxBufferSize = Stream->MaxBufferSize;
+  ClonedStream->Index = Stream->Index;
+  ClonedStream->Direction = Stream->Direction;
+
+  return EFI_SUCCESS;
+}
+
+/** Initialize a sub-stream from a stream.
+
+  A sub-stream is a stream initialized at the current position of the input
+  stream:
+    - the Buffer field points to the current position of the input stream;
+    - the Index field is set to 0;
+    - the MaxBufferSize field is set to the remaining size of the input stream;
+    - the direction is conserved;
+
+  E.g.: For a forward stream:
+                   +----------------+----------------+
+                   |ABCD.........XYZ|   Free Space   |
+                   +----------------+----------------+
+                   ^                ^                ^
+  Stream:        Buffer          CurrPos         EndOfBuff
+  Sub-stream:                Buffer/CurrPos      EndOfBuff
+
+  @param  [in]  Stream      Pointer to the stream from which a sub-stream is
+                            created.
+  @param  [in]  SubStream   Pointer to the stream to initialize.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamInitSubStream (
+  IN  CONST AML_STREAM  * Stream,
+  OUT       AML_STREAM  * SubStream
+  )
+{
+  if (!IS_STREAM (Stream) ||
+      (SubStream == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (IS_STREAM_FORWARD (Stream)) {
+    SubStream->Buffer = AmlStreamGetCurrPos (Stream);
+  } else if (IS_STREAM_BACKWARD (Stream)) {
+    SubStream->Buffer = Stream->Buffer;
+  } else {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SubStream->MaxBufferSize = AmlStreamGetFreeSpace (Stream);
+  SubStream->Index = 0;
+  SubStream->Direction = Stream->Direction;
+
+  return EFI_SUCCESS;
+}
+
+/** Get the buffer of a stream.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return The stream's Buffer.
+          NULL otherwise.
+**/
+UINT8 *
+EFIAPI
+AmlStreamGetBuffer (
+  IN  CONST AML_STREAM  * Stream
+  )
+{
+  if (!IS_STREAM (Stream)) {
+    ASSERT (0);
+    return NULL;
+  }
+  return Stream->Buffer;
+}
+
+/** Get the size of Stream's Buffer.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return The Size of Stream's Buffer.
+          Return 0 if Stream is invalid.
+**/
+UINT32
+EFIAPI
+AmlStreamGetMaxBufferSize (
+  IN  CONST AML_STREAM  * Stream
+  )
+{
+  if (!IS_STREAM (Stream)) {
+    ASSERT (0);
+    return 0;
+  }
+  return Stream->MaxBufferSize;
+}
+
+/** Reduce the maximal size of Stream's Buffer (MaxBufferSize field).
+
+  @param  [in]  Stream    Pointer to a stream.
+  @param  [in]  Diff      Value to subtract to the Stream's MaxBufferSize.
+                          0 < x < MaxBufferSize - Index.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamReduceMaxBufferSize (
+  IN  AML_STREAM  * Stream,
+  IN  UINT32        Diff
+  )
+{
+  if (!IS_STREAM (Stream)       ||
+      (Diff == 0)               ||
+      ((Stream->MaxBufferSize - Diff) <= Stream->Index)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Stream->MaxBufferSize -= Diff;
+  return EFI_SUCCESS;
+}
+
+/** Get Stream's Index.
+
+  Stream's Index is incremented when writing data, reading data,
+  or moving the position in the Stream.
+  It can be seen as an index:
+   - starting at the beginning of Stream's Buffer if the stream goes forward;
+   - starting at the end of Stream's Buffer if the stream goes backward.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return Stream's Index.
+          Return 0 if Stream is invalid.
+**/
+UINT32
+EFIAPI
+AmlStreamGetIndex (
+  IN  CONST AML_STREAM  * Stream
+  )
+{
+  if (!IS_STREAM (Stream)) {
+    ASSERT (0);
+    return 0;
+  }
+  return Stream->Index;
+}
+
+/** Get Stream's Direction.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return Stream's Direction.
+          Return EAmlStreamDirectionUnknown if Stream is invalid.
+**/
+EAML_STREAM_DIRECTION
+EFIAPI
+AmlStreamGetDirection (
+  IN  CONST AML_STREAM  * Stream
+  )
+{
+  if (!IS_STREAM (Stream)) {
+    ASSERT (0);
+    return EAmlStreamDirectionInvalid;
+  }
+  return Stream->Direction;
+}
+
+/** Return a pointer to the current position in the stream.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return The current position in the stream.
+          Return NULL if error.
+**/
+UINT8 *
+EFIAPI
+AmlStreamGetCurrPos (
+  IN  CONST AML_STREAM  * Stream
+  )
+{
+  if (!IS_STREAM (Stream)) {
+    ASSERT (0);
+    return NULL;
+  }
+
+  if (IS_STREAM_FORWARD (Stream)) {
+    return Stream->Buffer + Stream->Index;
+  } else if (IS_STREAM_BACKWARD (Stream)) {
+    return Stream->Buffer + (Stream->MaxBufferSize - 1) - Stream->Index;
+  } else {
+    ASSERT (0);
+    return NULL;
+  }
+}
+
+/** Get the space available in the stream.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return Remaining space available in the stream.
+          Zero in case of error or if the stream is at its end.
+**/
+UINT32
+EFIAPI
+AmlStreamGetFreeSpace (
+  IN  CONST AML_STREAM  * Stream
+  )
+{
+  if (!IS_STREAM (Stream)) {
+    ASSERT (0);
+    return 0;
+  }
+
+  if (Stream->Index > Stream->MaxBufferSize) {
+    ASSERT (0);
+    return 0;
+  }
+
+  return Stream->MaxBufferSize - Stream->Index;
+}
+
+/** Move Stream by Offset bytes.
+
+  The stream current position is moved according to the stream direction
+  (forward, backward).
+
+  @param  [in]  Stream  Pointer to a stream.
+                        The stream must not be at its end.
+  @param  [in]  Offset  Offset to move the stream of.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamProgress (
+  IN  AML_STREAM  * Stream,
+  IN  UINT32        Offset
+  )
+{
+  if (!IS_STREAM (Stream)         ||
+      IS_END_OF_STREAM  (Stream)  ||
+      (Offset == 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (AmlStreamGetFreeSpace (Stream) < Offset) {
+    ASSERT (0);
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  Stream->Index += Offset;
+
+  return EFI_SUCCESS;
+}
+
+/** Rewind Stream of Offset bytes.
+
+  The stream current position is rewound according to the stream direction
+  (forward, backward). A stream going forward will be rewound backward.
+
+  @param  [in]  Stream  Pointer to a stream.
+  @param  [in]  Offset  Offset to rewind the stream of.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamRewind (
+  IN  AML_STREAM  * Stream,
+  IN  UINT32        Offset
+  )
+{
+  if (!IS_STREAM (Stream) ||
+      (Offset == 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (AmlStreamGetIndex (Stream) < Offset) {
+    ASSERT (0);
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  Stream->Index -= Offset;
+
+  return EFI_SUCCESS;
+}
+
+/** Reset the Stream (move the current position to the initial position).
+
+  @param  [in]  Stream  Pointer to a stream.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamReset (
+  IN  AML_STREAM  * Stream
+  )
+{
+  if (!IS_STREAM (Stream)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Stream->Index = 0;
+
+  return EFI_SUCCESS;
+}
+
+/** Peek one byte at Stream's current position.
+
+  Stream's position is not moved when peeking.
+
+  @param  [in]  Stream    Pointer to a stream.
+                          The stream must not be at its end.
+  @param  [out] OutByte   Pointer holding the byte value of
+                          the stream current position.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+  @retval EFI_INVALID_PARAMETER Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL  No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamPeekByte (
+  IN  AML_STREAM  * Stream,
+  OUT UINT8       * OutByte
+  )
+{
+  UINT8   * CurPos;
+
+  if (!IS_STREAM (Stream)       ||
+      IS_END_OF_STREAM (Stream) ||
+      (OutByte == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CurPos = AmlStreamGetCurrPos (Stream);
+  if (CurPos == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *OutByte = *CurPos;
+  return EFI_SUCCESS;
+}
+
+/** Read one byte at Stream's current position.
+
+  The stream current position is moved when reading.
+
+  @param  [in]  Stream    Pointer to a stream.
+                          The stream must not be at its end.
+  @param  [out] OutByte   Pointer holding the byte value of
+                          the stream current position.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+  @retval EFI_INVALID_PARAMETER Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL  No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamReadByte (
+  IN  AML_STREAM  * Stream,
+  OUT UINT8       * OutByte
+  )
+{
+  EFI_STATUS    Status;
+
+  if (!IS_STREAM (Stream)       ||
+      IS_END_OF_STREAM (Stream) ||
+      (OutByte == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Stream is checked in the function call.
+  Status = AmlStreamPeekByte (Stream, OutByte);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = AmlStreamProgress (Stream, 1);
+  ASSERT_EFI_ERROR (Status);
+  return Status;
+}
+
+/** Write Size bytes in the stream.
+
+  If the stream goes backward (toward lower addresses), the bytes written
+  to the stream are not reverted.
+  In the example below, writing "Hello" to the stream will not revert
+  the string. The end of the stream buffer will contain "Hello world!".
+  Stream buffer:
+     +---------------+-----+-----+-----+-----+-----+-----+---- +------+
+     |         ..... | ' ' | 'w' | 'o' | 'r' | 'l' | 'd' | '!' | '\0' |
+     +---------------+-----+-----+-----+-----+-----+-----+---- +------+
+                        ^
+                 Current position.
+
+  @param  [in]  Stream  Pointer to a stream.
+                        The stream must not be at its end.
+  @param  [in]  Buffer  Pointer to the data to write.
+  @param  [in]  Size    Number of bytes to write.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamWrite (
+  IN        AML_STREAM  * Stream,
+  IN  CONST UINT8       * Buffer,
+  IN        UINT32        Size
+  )
+{
+  UINT8   * CurrPos;
+
+  if (!IS_STREAM (Stream)       ||
+      IS_END_OF_STREAM (Stream) ||
+      (Buffer == NULL)          ||
+      (Size == 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (AmlStreamGetFreeSpace (Stream) < Size) {
+    ASSERT (0);
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  CurrPos = AmlStreamGetCurrPos (Stream);
+
+  // If the Stream goes backward, prepare some space to copy the data.
+  if (IS_STREAM_BACKWARD (Stream)) {
+    CurrPos -= Size;
+  }
+
+  CopyMem (CurrPos, Buffer, Size);
+  Stream->Index += Size;
+
+  return EFI_SUCCESS;
+}
+
+/** Compare Size bytes between Stream1 and Stream2 from their
+    respective current position.
+
+  Stream1 and Stream2 must go in the same direction.
+  Stream1 and Stream2 are left unchanged.
+
+  @param  [in]  Stream1   First stream to compare.
+                          The stream must not be at its end.
+  @param  [in]  Stream2   Second stream to compare.
+                          The stream must not be at its end.
+  @param  [in]  Size      Number of bytes to compare.
+                          Must be lower than the minimum remaining space of
+                          Stream1 and Stream2.
+                          Must be non-zero.
+
+  @retval TRUE  If Stream1 and Stream2 have Size bytes equal,
+                from their respective current position.
+                The function completed successfully.
+  @retval FALSE Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlStreamCmp (
+  IN  CONST AML_STREAM    * Stream1,
+  IN  CONST AML_STREAM    * Stream2,
+  IN        UINT32          Size
+  )
+{
+  UINT32          MinSize;
+  UINT8         * CurrPosStream1;
+  UINT8         * CurrPosStream2;
+
+  if (!IS_STREAM (Stream1)                        ||
+      IS_END_OF_STREAM (Stream1)                  ||
+      !IS_STREAM (Stream2)                        ||
+      IS_END_OF_STREAM (Stream2)                  ||
+      (Stream1->Direction != Stream2->Direction)  ||
+      (Size == 0)) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  // Check the Size is not longer than the remaining size of
+  // Stream1 and Stream2.
+  MinSize = MIN (
+              AmlStreamGetFreeSpace (Stream1),
+              AmlStreamGetFreeSpace (Stream2)
+              );
+  if (MinSize < Size) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  CurrPosStream1 = AmlStreamGetCurrPos (Stream1);
+  if (CurrPosStream1 == NULL) {
+    ASSERT (0);
+    return FALSE;
+  }
+  CurrPosStream2 = AmlStreamGetCurrPos (Stream2);
+  if (CurrPosStream2 == NULL) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  if (Stream1->Direction == EAmlStreamDirectionForward) {
+    return (0 == CompareMem (CurrPosStream1, CurrPosStream2, MinSize));
+  }
+
+  // The stream is already pointing on the last byte, thus the (-1).
+  //          +---------------------+
+  // BStream  | | | | | | | |M|E|T|0|
+  //          +---------------------+
+  //                               ^
+  //                             CurrPos
+  return (0 == CompareMem (
+                  CurrPosStream1 - (MinSize - 1),
+                  CurrPosStream2 - (MinSize - 1),
+                  MinSize
+                  ));
+}
+
+/** Copy Size bytes of the stream's data to DstBuffer.
+
+  For a backward stream, the bytes are copied starting from the
+  current stream position.
+
+  @param  [out] DstBuffer         Destination Buffer to copy the data to.
+  @param  [in]  MaxDstBufferSize  Maximum size of DstBuffer.
+                                  Must be non-zero.
+  @param  [in]  Stream            Pointer to the stream to copy the data from.
+  @param  [in]  Size              Number of bytes to copy from the stream
+                                  buffer.
+                                  Must be lower than MaxDstBufferSize.
+                                  Must be lower than Stream's MaxBufferSize.
+                                  Return success if zero.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+  @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamCpyS (
+  OUT CHAR8         * DstBuffer,
+  IN  UINT32          MaxDstBufferSize,
+  IN  AML_STREAM    * Stream,
+  IN  UINT32          Size
+  )
+{
+  CHAR8   * StreamBufferStart;
+
+  // Stream is checked in the function call.
+  if ((DstBuffer == NULL)       ||
+      (MaxDstBufferSize == 0)   ||
+      (Size > MaxDstBufferSize) ||
+      (Size > AmlStreamGetMaxBufferSize (Stream))) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Size == 0) {
+    return EFI_SUCCESS;
+  }
+
+  // Find the address at which the data is starting.
+  StreamBufferStart = (CHAR8*)(IS_STREAM_FORWARD (Stream) ?
+                                 Stream->Buffer :
+                                 AmlStreamGetCurrPos (Stream));
+
+  CopyMem (DstBuffer, StreamBufferStart, Size);
+
+  return EFI_SUCCESS;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h b/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd2da89b072bd38f5c191f9ef35753b445b3f83d
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h
@@ -0,0 +1,451 @@
+/** @file
+  AML Stream.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_STREAM_H_
+#define AML_STREAM_H_
+
+#include <AmlInclude.h>
+
+/** Stream direction.
+
+  Enum to choose the direction the stream is progressing.
+*/
+typedef enum EAmlStreamDirection {
+  EAmlStreamDirectionInvalid,     ///< Invalid AML Stream direction.
+  EAmlStreamDirectionForward,     ///< Forward direction.
+                                  ///  The Stream goes toward higher addresses.
+  EAmlStreamDirectionBackward,    ///< Forward direction.
+                                  ///  The Stream goes toward lower addresses.
+  EAmlStreamDirectionMax,         ///< Max enum.
+} EAML_STREAM_DIRECTION;
+
+/** Stream.
+
+  This structure is used as a wrapper around a buffer. It allows to do common
+  buffer manipulations (read, write, etc.) while preventing buffer overflows.
+*/
+typedef struct AmlStream {
+  /// Pointer to a buffer.
+  UINT8                 * Buffer;
+
+  /// Size of Buffer.
+  UINT32                  MaxBufferSize;
+
+  /// Index in the Buffer.
+  /// The Index field allows to keep track of how many bytes have been
+  /// read/written in the Buffer, and to retrieve the current stream position.
+  /// 0 <= Index <= MaxBufferSize.
+  /// If Index == MaxBufferSize, no more action is allowed on the stream.
+  UINT32                  Index;
+
+  /// The direction the stream is progressing.
+  /// If the stream goes backward (toward lower addresses), the bytes written
+  /// to the stream are not reverted.
+  /// In the example below, writing "Hello" to the stream will not revert
+  /// the string. The end of the stream buffer will contain "Hello world!".
+  /// Similarly, moving the stream position will be done according to the
+  /// direction of the stream.
+  /// Stream buffer:
+  ///    +---------------+-----+-----+-----+-----+-----+-----+---- +------+
+  ///    |-------------- | ' ' | 'w' | 'o' | 'r' | 'l' | 'd' | '!' | '\0' |
+  ///    +---------------+-----+-----+-----+-----+-----+-----+---- +------+
+  ///                       ^
+  ///                Current position.
+  EAML_STREAM_DIRECTION   Direction;
+} AML_STREAM;
+
+/** Check whether a StreamPtr is a valid Stream.
+
+  @param  [in]  Stream   Pointer to a stream.
+
+  @retval TRUE  Stream is a pointer to a stream.
+  @retval FALSE Otherwise.
+*/
+#define IS_STREAM(Stream)  (                                                  \
+          (((AML_STREAM*)Stream) != NULL)                                 &&  \
+          (((AML_STREAM*)Stream)->Buffer != NULL))
+
+/** Check whether a Stream is at the end of its buffer.
+
+  @param  [in]  Stream   Pointer to a stream.
+
+  @retval TRUE  Stream is a pointer to a non-full stream.
+  @retval FALSE Otherwise.
+*/
+#define IS_END_OF_STREAM(Stream)  (                                           \
+          (((AML_STREAM*)Stream)->Index ==                                    \
+             ((AML_STREAM*)Stream)->MaxBufferSize))
+
+/** Check Stream goes forward.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @retval TRUE  Stream goes forward.
+  @retval FALSE Otherwise.
+*/
+#define IS_STREAM_FORWARD(Stream)   (                                         \
+    ((AML_STREAM*)Stream)->Direction == EAmlStreamDirectionForward)
+
+/** Check Stream goes backward.
+
+  @param  [in]  Stream   Pointer to a stream.
+
+  @retval TRUE  Stream goes backward.
+  @retval FALSE Otherwise.
+*/
+#define IS_STREAM_BACKWARD(Stream)   (                                        \
+    ((AML_STREAM*)Stream)->Direction == EAmlStreamDirectionBackward)
+
+/** Initialize a stream.
+
+  @param  [in, out] Stream          Pointer to the stream to initialize.
+  @param  [in]      Buffer          Buffer to initialize Stream with.
+                                    Point to the beginning of the Buffer.
+  @param  [in]      MaxBufferSize   Maximum size of Buffer.
+  @param  [in]      Direction       Direction Stream is progressing
+                                    (forward, backward).
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamInit (
+  IN  OUT AML_STREAM              * Stream,
+  IN      UINT8                   * Buffer,
+  IN      UINT32                    MaxBufferSize,
+  IN      EAML_STREAM_DIRECTION     Direction
+  );
+
+/** Clone a stream.
+
+  Cloning a stream means copying all the values of the input Stream
+  in the ClonedStream.
+
+  @param  [in]  Stream          Pointer to the stream to clone.
+  @param  [in]  ClonedStream    Pointer to the stream to initialize.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamClone (
+  IN  CONST AML_STREAM    * Stream,
+  OUT        AML_STREAM   * ClonedStream
+  );
+
+/** Initialize a sub-stream from a stream.
+
+  A sub-stream is a stream initialized at the current position of the input
+  stream:
+    - the Buffer field points to the current position of the input stream;
+    - the Index field is set to 0;
+    - the MaxBufferSize field is set to the remaining size of the input stream;
+    - the direction is conserved;
+
+  E.g.: For a forward stream:
+                   +----------------+----------------+
+                   |ABCD.........XYZ|   Free Space   |
+                   +----------------+----------------+
+                   ^                ^                ^
+  Stream:        Buffer          CurrPos         EndOfBuff
+  Sub-stream:                Buffer/CurrPos      EndOfBuff
+
+  @param  [in]  Stream      Pointer to the stream from which a sub-stream is
+                            created.
+  @param  [in]  SubStream   Pointer to the stream to initialize.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamInitSubStream (
+  IN  CONST AML_STREAM  * Stream,
+  OUT       AML_STREAM  * SubStream
+  );
+
+/** Get the buffer of a stream.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return The stream's Buffer.
+          NULL otherwise.
+**/
+UINT8 *
+EFIAPI
+AmlStreamGetBuffer (
+  IN  CONST AML_STREAM  * Stream
+  );
+
+/** Get the size of Stream's Buffer.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return The Size of Stream's Buffer.
+          Return 0 if Stream is invalid.
+**/
+UINT32
+EFIAPI
+AmlStreamGetMaxBufferSize (
+  IN  CONST AML_STREAM  * Stream
+  );
+
+/** Reduce the maximal size of Stream's Buffer (MaxBufferSize field).
+
+  @param  [in]  Stream    Pointer to a stream.
+  @param  [in]  Diff      Value to subtract to the Stream's MaxBufferSize.
+                          0 < x < MaxBufferSize - Index.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamReduceMaxBufferSize (
+  IN  AML_STREAM  * Stream,
+  IN  UINT32        Diff
+  );
+
+/** Get Stream's Index.
+
+  Stream's Index is incremented when writing data, reading data,
+  or moving the position in the Stream.
+  It can be seen as an index:
+   - starting at the beginning of Stream's Buffer if the stream goes forward;
+   - starting at the end of Stream's Buffer if the stream goes backward.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return Stream's Index.
+          Return 0 if Stream is invalid.
+**/
+UINT32
+EFIAPI
+AmlStreamGetIndex (
+  IN  CONST AML_STREAM  * Stream
+  );
+
+/** Get Stream's Direction.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return Stream's Direction.
+          Return EAmlStreamDirectionUnknown if Stream is invalid.
+**/
+EAML_STREAM_DIRECTION
+EFIAPI
+AmlStreamGetDirection (
+  IN  CONST AML_STREAM  * Stream
+  );
+
+/** Return a pointer to the current position in the stream.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return The current position in the stream.
+          Return NULL if error.
+**/
+UINT8 *
+EFIAPI
+AmlStreamGetCurrPos (
+  IN  CONST AML_STREAM  * Stream
+  );
+
+/** Get the space available in the stream.
+
+  @param  [in]  Stream    Pointer to a stream.
+
+  @return Remaining space available in the stream.
+          Zero in case of error or if the stream is at its end.
+**/
+UINT32
+EFIAPI
+AmlStreamGetFreeSpace (
+  IN  CONST AML_STREAM  * Stream
+  );
+
+/** Move Stream by Offset bytes.
+
+  The stream current position is moved according to the stream direction
+  (forward, backward).
+
+  @param  [in]  Stream  Pointer to a stream.
+                        The stream must not be at its end.
+  @param  [in]  Offset  Offset to move the stream of.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamProgress (
+  IN  AML_STREAM  * Stream,
+  IN  UINT32        Offset
+  );
+
+/** Rewind Stream of Offset bytes.
+
+  The stream current position is rewound according to the stream direction
+  (forward, backward). A stream going forward will be rewound backward.
+
+  @param  [in]  Stream  Pointer to a stream.
+  @param  [in]  Offset  Offset to rewind the stream of.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamRewind (
+  IN  AML_STREAM  * Stream,
+  IN  UINT32        Offset
+  );
+
+/** Reset the Stream (move the current position to the initial position).
+
+  @param  [in]  Stream  Pointer to a stream.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamReset (
+  IN  AML_STREAM  * Stream
+  );
+
+/** Peek one byte at Stream's current position.
+
+  Stream's position is not moved when peeking.
+
+  @param  [in]  Stream    Pointer to a stream.
+                          The stream must not be at its end.
+  @param  [out] OutByte   Pointer holding the byte value of
+                          the stream current position.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+  @retval EFI_INVALID_PARAMETER Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL  No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamPeekByte (
+  IN  AML_STREAM  * Stream,
+  OUT UINT8       * OutByte
+  );
+
+/** Read one byte at Stream's current position.
+
+  The stream current position is moved when reading.
+
+  @param  [in]  Stream    Pointer to a stream.
+                          The stream must not be at its end.
+  @param  [out] OutByte   Pointer holding the byte value of
+                          the stream current position.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+  @retval EFI_INVALID_PARAMETER Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL  No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamReadByte (
+  IN  AML_STREAM  * Stream,
+  OUT UINT8       * OutByte
+  );
+
+/** Write Size bytes in the stream.
+
+  If the stream goes backward (toward lower addresses), the bytes written
+  to the stream are not reverted.
+  In the example below, writing "Hello" to the stream will not revert
+  the string. The end of the stream buffer will contain "Hello world!".
+  Stream buffer:
+     +---------------+-----+-----+-----+-----+-----+-----+---- +------+
+     |         ..... | ' ' | 'w' | 'o' | 'r' | 'l' | 'd' | '!' | '\0' |
+     +---------------+-----+-----+-----+-----+-----+-----+---- +------+
+                        ^
+                 Current position.
+
+  @param  [in]  Stream  Pointer to a stream.
+                        The stream must not be at its end.
+  @param  [in]  Buffer  Pointer to the data to write.
+  @param  [in]  Size    Number of bytes to write.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamWrite (
+  IN        AML_STREAM  * Stream,
+  IN  CONST UINT8       * Buffer,
+  IN        UINT32        Size
+  );
+
+/** Compare Size bytes between Stream1 and Stream2 from their
+    respective current position.
+
+  Stream1 and Stream2 must go in the same direction.
+  Stream1 and Stream2 are left unchanged.
+
+  @param  [in]  Stream1   First stream to compare.
+                          The stream must not be at its end.
+  @param  [in]  Stream2   Second stream to compare.
+                          The stream must not be at its end.
+  @param  [in]  Size      Number of bytes to compare.
+                          Must be lower than the minimum remaining space of
+                          Stream1 and Stream2.
+                          Must be non-zero.
+
+  @retval TRUE  If Stream1 and Stream2 have Size bytes equal,
+                from their respective current position.
+                The function completed successfully.
+  @retval FALSE Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlStreamCmp (
+  IN  CONST AML_STREAM    * Stream1,
+  IN  CONST AML_STREAM    * Stream2,
+  IN        UINT32          Size
+  );
+
+/** Copy Size bytes of the stream's data to DstBuffer.
+
+  For a backward stream, the bytes are copied starting from the
+  current stream position.
+
+  @param  [out] DstBuffer         Destination Buffer to copy the data to.
+  @param  [in]  MaxDstBufferSize  Maximum size of DstBuffer.
+                                  Must be non-zero.
+  @param  [in]  Stream            Pointer to the stream to copy the data from.
+  @param  [in]  Size              Number of bytes to copy from the stream
+                                  buffer.
+                                  Must be lower than MaxDstBufferSize.
+                                  Must be lower than Stream's MaxBufferSize.
+                                  Return success if zero.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+  @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlStreamCpyS (
+  OUT CHAR8         * DstBuffer,
+  IN  UINT32          MaxDstBufferSize,
+  IN  AML_STREAM    * Stream,
+  IN  UINT32          Size
+  );
+
+#endif // AML_STREAM_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 13/30] DynamicTablesPkg: AML serialise interface
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (11 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 12/30] DynamicTablesPkg: AML stream interface Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 14/30] DynamicTablesPkg: AML debug logging Sami Mujawar
                   ` (17 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

AML Fixup and AML Codegen facilitate dynamic generation
of Definition Block tables. The AML byte stream that is
generated is represented in an AML tree. Once the AML
table generation is completed, the AML tree needs to be
serialised for installing as an ACPI table.

The AML serialise interface implements the functionality
to iterate the nodes in the AML tree, collating the AML
bytecode, computing the checksum and writing the AML byte
stream to a buffer that represents the Definition Block
table.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Co-authored-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Serialize/AmlSerialize.c | 324 ++++++++++++++++++++
 1 file changed, 324 insertions(+)

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


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 14/30] DynamicTablesPkg: AML debug logging
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (12 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 13/30] DynamicTablesPkg: AML serialise interface Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 15/30] DynamicTablesPkg: AML ACPI Namespace interface Sami Mujawar
                   ` (16 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

The AML debug print functions enable logging
of the operations on the AML tree and the data
output. The debug logging functionality is
enabled for debug builds when the DEBUG_INFO
or DEBUG_VERBOSE mask is enabled in the PCD
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c | 546 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h | 154 ++++++
 2 files changed, 700 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c b/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c
new file mode 100644
index 0000000000000000000000000000000000000000..7b11cc8e994903b3a9d2dcfd1241b8a447759717
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c
@@ -0,0 +1,546 @@
+/** @file
+  AML Print Function.
+
+  Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved. <BR>
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+#include <AmlDbgPrint/AmlDbgPrint.h>
+
+#include <AmlCoreInterface.h>
+#include <String/AmlString.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTreeTraversal.h>
+
+#if !defined (MDEPKG_NDEBUG)
+
+/** String table representing AML Data types as defined by EAML_NODE_DATA_TYPE.
+*/
+CONST CHAR8 * NodeDataTypeStrTbl[] = {
+  "EAmlNodeDataTypeNone",
+  "EAmlNodeDataTypeReserved1",
+  "EAmlNodeDataTypeReserved2",
+  "EAmlNodeDataTypeReserved3",
+  "EAmlNodeDataTypeReserved4",
+  "EAmlNodeDataTypeReserved5",
+  "EAmlNodeDataTypeNameString",
+  "EAmlNodeDataTypeString",
+  "EAmlNodeDataTypeUInt",
+  "EAmlNodeDataTypeRaw",
+  "EAmlNodeDataTypeResourceData",
+  "EAmlNodeDataTypeFieldPkgLen",
+  "EAmlNodeDataTypeMax"
+};
+
+/** String table representing AML Node types as defined by EAML_NODE_TYPE.
+*/
+CONST CHAR8 * NodeTypeStrTbl[] = {
+  "EAmlNodeUnknown",
+  "EAmlNodeRoot",
+  "EAmlNodeObject",
+  "EAmlNodeData",
+  "EAmlNodeMax"
+};
+
+/** Print Size chars at Buffer address.
+
+  @param  [in]  ErrorLevel    Error level for the DEBUG macro.
+  @param  [in]  Buffer        Buffer containing the chars.
+  @param  [in]  Size          Number of chars to print.
+**/
+VOID
+EFIAPI
+AmlDbgPrintChars (
+  IN        UINT32      ErrorLevel,
+  IN  CONST CHAR8     * Buffer,
+  IN        UINT32      Size
+  )
+{
+  UINT32  i;
+
+  if (Buffer == NULL) {
+    ASSERT (0);
+    return;
+  }
+
+  for (i = 0; i < Size; i++) {
+    DEBUG ((ErrorLevel, "%c", Buffer[i]));
+  }
+}
+
+/** Print an AML NameSeg.
+    Don't print trailing underscores ('_').
+
+  @param  [in] Buffer   Buffer containing an AML NameSeg.
+**/
+VOID
+EFIAPI
+AmlDbgPrintNameSeg (
+  IN  CONST CHAR8   * Buffer
+  )
+{
+  if (Buffer == NULL) {
+    ASSERT (0);
+    return;
+  }
+
+  DEBUG ((DEBUG_INFO, "%c", Buffer[0]));
+  if ((Buffer[1] == AML_NAME_CHAR__)  &&
+      (Buffer[2] == AML_NAME_CHAR__)  &&
+      (Buffer[3] == AML_NAME_CHAR__)) {
+    return;
+  }
+  DEBUG ((DEBUG_INFO, "%c", Buffer[1]));
+  if ((Buffer[2] == AML_NAME_CHAR__)  &&
+      (Buffer[3] == AML_NAME_CHAR__)) {
+    return;
+  }
+  DEBUG ((DEBUG_INFO, "%c", Buffer[2]));
+  if (Buffer[3] == AML_NAME_CHAR__) {
+    return;
+  }
+  DEBUG ((DEBUG_INFO, "%c", Buffer[3]));
+  return;
+}
+
+/** Print an AML NameString.
+
+  @param  [in] Buffer   Buffer containing an AML NameString.
+  @param  [in] NewLine  Print a newline char at the end of the NameString.
+**/
+VOID
+EFIAPI
+AmlDbgPrintNameString (
+  IN  CONST CHAR8   * Buffer,
+  IN        BOOLEAN   NewLine
+  )
+{
+  UINT8     SegCount;
+  UINT8     Index;
+
+  if (Buffer == NULL) {
+    ASSERT (0);
+    return;
+  }
+
+  // Handle Root and Parent(s).
+  if (*Buffer == AML_ROOT_CHAR) {
+    Buffer++;
+    DEBUG ((DEBUG_INFO, "\\"));
+  } else if (*Buffer == AML_PARENT_PREFIX_CHAR) {
+    do {
+      Buffer++;
+      DEBUG ((DEBUG_INFO, "^"));
+    } while (*Buffer == AML_PARENT_PREFIX_CHAR);
+  }
+
+  // Handle SegCount(s).
+  if (*Buffer == AML_DUAL_NAME_PREFIX) {
+    Buffer++;
+    SegCount = 2;
+  } else if (*Buffer == AML_MULTI_NAME_PREFIX) {
+    Buffer++;
+    // For multi name prefix the seg count is in the second byte.
+    SegCount = *Buffer;
+    Buffer++;
+  } else if (AmlIsLeadNameChar (*Buffer)) {
+    // Only check the first char first to avoid overflow.
+    // Then the whole NameSeg can be checked.
+    if (!AmlIsNameSeg (Buffer)) {
+      ASSERT (0);
+      return;
+    }
+    SegCount = 1;
+  } else if (*Buffer == AML_ZERO_OP) {
+    SegCount = 0;
+  } else {
+    // Should not be possible.
+    ASSERT (0);
+    return;
+  }
+
+  if (SegCount != 0) {
+    AmlDbgPrintNameSeg (Buffer);
+    Buffer += AML_NAME_SEG_SIZE;
+    for (Index = 0; Index < SegCount - 1; Index++) {
+      DEBUG ((DEBUG_INFO, "."));
+      AmlDbgPrintNameSeg (Buffer);
+      Buffer += AML_NAME_SEG_SIZE;
+    }
+  }
+
+  if (NewLine) {
+    DEBUG ((DEBUG_INFO, "\n"));
+  }
+
+  return;
+}
+
+/** Print the information contained in the header of the Node.
+
+  @param  [in]  Node    Pointer to a node.
+  @param  [in]  Level   Level of the indentation.
+**/
+STATIC
+VOID
+EFIAPI
+AmlDbgPrintNodeHeader (
+  IN  AML_NODE_HEADER  * Node,
+  IN  UINT8              Level
+  )
+{
+  if (!IS_AML_NODE_VALID (Node)) {
+    ASSERT (0);
+    return;
+  }
+
+  DEBUG ((
+    DEBUG_INFO,
+    "%3d | %-15s | ",
+    Level,
+    NodeTypeStrTbl[Node->NodeType]
+    ));
+}
+
+/** Print fields of a data node.
+
+  @param  [in]  DataNode  Pointer to a data node.
+  @param  [in]  Level     Level of the indentation.
+**/
+STATIC
+VOID
+EFIAPI
+AmlDbgPrintDataNode (
+  IN  AML_DATA_NODE   * DataNode,
+  IN  UINT8             Level
+  )
+{
+  UINT32  Idx;
+
+  if (!IS_AML_DATA_NODE (DataNode)) {
+    ASSERT (0);
+    return;
+  }
+
+  AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)DataNode, Level);
+
+  DEBUG ((DEBUG_INFO, "%-36s | ", NodeDataTypeStrTbl[DataNode->DataType]));
+  DEBUG ((DEBUG_INFO, "0x%04x | ", DataNode->Size));
+
+  if ((DataNode->DataType == EAmlNodeDataTypeNameString) ||
+      (DataNode->DataType == EAmlNodeDataTypeString)) {
+    AmlDbgPrintChars (
+      DEBUG_INFO,
+      (CONST CHAR8*)DataNode->Buffer,
+      DataNode->Size
+      );
+  } else if (DataNode->DataType == EAmlNodeDataTypeUInt) {
+    switch (DataNode->Size) {
+      case 1:
+      {
+        DEBUG ((DEBUG_INFO, "0x%0x", *((UINT8*)DataNode->Buffer)));
+        break;
+      }
+      case 2:
+      {
+        DEBUG ((DEBUG_INFO, "0x%0x", *((UINT16*)DataNode->Buffer)));
+        break;
+      }
+      case 4:
+      {
+        DEBUG ((DEBUG_INFO, "0x%0lx", *((UINT32*)DataNode->Buffer)));
+        break;
+      }
+      case 8:
+      {
+        DEBUG ((DEBUG_INFO, "0x%0llx", *((UINT64*)DataNode->Buffer)));
+        break;
+      }
+      default:
+      {
+        ASSERT (0);
+        return;
+      }
+    }
+  } else {
+    // No specific format.
+    for (Idx = 0; Idx < DataNode->Size; Idx++) {
+      DEBUG ((DEBUG_INFO, "%02x ", DataNode->Buffer[Idx]));
+    }
+  }
+
+  DEBUG ((DEBUG_INFO, "\n"));
+}
+
+/** Print fields of an object node.
+
+  @param  [in]  ObjectNode  Pointer to an object node.
+  @param  [in]  Level       Level of the indentation.
+**/
+STATIC
+VOID
+EFIAPI
+AmlDbgPrintObjectNode (
+  IN  AML_OBJECT_NODE  * ObjectNode,
+  IN  UINT8              Level
+  )
+{
+  if (!IS_AML_OBJECT_NODE (ObjectNode)) {
+    ASSERT (0);
+    return;
+  }
+
+  AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)ObjectNode, Level);
+
+  DEBUG ((DEBUG_INFO, "0x%02x | ", ObjectNode->AmlByteEncoding->OpCode));
+  DEBUG ((DEBUG_INFO, "0x%02x | ", ObjectNode->AmlByteEncoding->SubOpCode));
+
+  // Print a string corresponding to the field object OpCode/SubOpCode.
+  if (AmlNodeHasAttribute (ObjectNode, AML_IS_FIELD_ELEMENT)) {
+    DEBUG ((DEBUG_INFO, "%-15s ", AmlGetFieldOpCodeStr (
+                                    ObjectNode->AmlByteEncoding->OpCode,
+                                    0
+                                    )));
+  } else {
+    // Print a string corresponding to the object OpCode/SubOpCode.
+    DEBUG ((DEBUG_INFO, "%-15s | ", AmlGetOpCodeStr (
+                                      ObjectNode->AmlByteEncoding->OpCode,
+                                      ObjectNode->AmlByteEncoding->SubOpCode)
+                                      ));
+  }
+
+  DEBUG ((DEBUG_INFO, "%3d | ", ObjectNode->AmlByteEncoding->MaxIndex));
+  DEBUG ((DEBUG_INFO, "0x%08x | ", ObjectNode->AmlByteEncoding->Attribute));
+  DEBUG ((DEBUG_INFO, "0x%04x | ", ObjectNode->PkgLen));
+  if (AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE)) {
+    AmlDbgPrintNameString (
+      AmlNodeGetName ((CONST AML_OBJECT_NODE*)ObjectNode),
+      FALSE
+      );
+  }
+
+  DEBUG ((DEBUG_INFO, "\n"));
+}
+
+/** Print fields of a root node.
+
+  @param  [in]  RootNode  Pointer to a root node.
+  @param  [in]  Level     Level of the indentation.
+**/
+STATIC
+VOID
+EFIAPI
+AmlDbgPrintRootNode (
+  IN  AML_ROOT_NODE  * RootNode,
+  IN  UINT8            Level
+  )
+{
+  if (!IS_AML_ROOT_NODE (RootNode)) {
+    ASSERT (0);
+    return;
+  }
+
+  AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)RootNode, Level);
+
+  DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->Signature));
+  DEBUG ((DEBUG_INFO, "0x%08x | ", RootNode->SdtHeader->Length));
+  DEBUG ((DEBUG_INFO, "%3d | ", RootNode->SdtHeader->Revision));
+  DEBUG ((DEBUG_INFO, "0x%02x | ", RootNode->SdtHeader->Checksum));
+  DEBUG ((
+    DEBUG_INFO,
+    "%c%c%c%c%c%c | ",
+    RootNode->SdtHeader->OemId[0],
+    RootNode->SdtHeader->OemId[1],
+    RootNode->SdtHeader->OemId[2],
+    RootNode->SdtHeader->OemId[3],
+    RootNode->SdtHeader->OemId[4],
+    RootNode->SdtHeader->OemId[5]
+    ));
+  DEBUG ((DEBUG_INFO, "%-16llx | ", RootNode->SdtHeader->OemTableId));
+  DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->OemRevision));
+  DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->CreatorId));
+  DEBUG ((DEBUG_INFO, "%8x", RootNode->SdtHeader->CreatorRevision));
+  DEBUG ((DEBUG_INFO, "\n"));
+}
+
+/** Print a header to help interpreting node information.
+**/
+STATIC
+VOID
+EFIAPI
+AmlDbgPrintTableHeader (
+  VOID
+  )
+{
+  DEBUG ((DEBUG_INFO, "Lvl | Node Type       |\n"));
+  DEBUG ((
+    DEBUG_INFO,
+    "    | %-15s | Signature| Length     | Rev | CSum | OemId  | "
+      "OemTableId       | OemRev   | CreatorId| CreatorRev\n",
+    NodeTypeStrTbl[EAmlNodeRoot]
+    ));
+  DEBUG ((
+    DEBUG_INFO,
+    "    | %-15s | Op   | SubOp| OpName          | MaxI| Attribute  | "
+      "PkgLen | NodeName (opt)\n",
+    NodeTypeStrTbl[EAmlNodeObject]
+    ));
+  DEBUG ((
+    DEBUG_INFO,
+    "    | %-15s | Data Type                            | Size   | "
+      "Buffer\n",
+    NodeTypeStrTbl[EAmlNodeData]
+    ));
+  DEBUG ((
+    DEBUG_INFO,
+    "---------------------------------------"
+      "---------------------------------------\n"
+    ));
+}
+
+/** Recursively print the subtree under the Node.
+    This is an internal function.
+
+  @param  [in]  Node            Pointer to the root of the subtree to print.
+                                Can be a root/object/data node.
+  @param  [in]  Recurse         If TRUE, recurse.
+  @param  [in]  Level           Level in the tree.
+**/
+STATIC
+VOID
+EFIAPI
+AmlDbgPrintTreeInternal (
+  IN  AML_NODE_HEADER   * Node,
+  IN  BOOLEAN             Recurse,
+  IN  UINT8               Level
+  )
+{
+  AML_NODE_HEADER   * ChildNode;
+
+  if (!IS_AML_NODE_VALID (Node)) {
+    ASSERT (0);
+    return;
+  }
+
+  if (IS_AML_DATA_NODE (Node)) {
+    AmlDbgPrintDataNode ((AML_DATA_NODE*)Node, Level);
+    return;
+  } else if (IS_AML_OBJECT_NODE (Node)) {
+    AmlDbgPrintObjectNode ((AML_OBJECT_NODE*)Node, Level);
+  } else if (IS_AML_ROOT_NODE (Node)) {
+    AmlDbgPrintRootNode ((AML_ROOT_NODE*)Node, Level);
+  } else {
+    // Should not be possible.
+    ASSERT (0);
+    return;
+  }
+
+  if (!Recurse) {
+    return;
+  }
+
+  // Get the first child node.
+  ChildNode = AmlGetNextSibling (Node, NULL);
+  while (ChildNode != NULL) {
+    ASSERT (Level < MAX_UINT8);
+    AmlDbgPrintTreeInternal (ChildNode, Recurse, (UINT8)(Level + 1));
+    ChildNode = AmlGetNextSibling (Node, ChildNode);
+  }
+}
+
+/** Print Node information.
+
+  @param  [in]  Node    Pointer to the Node to print.
+                        Can be a root/object/data node.
+**/
+VOID
+EFIAPI
+AmlDbgPrintNode (
+  IN  AML_NODE_HEADER   * Node
+  )
+{
+  AmlDbgPrintTableHeader ();
+  AmlDbgPrintTreeInternal (Node, FALSE, 0);
+}
+
+/** Recursively print the subtree under the Node.
+
+  @param  [in]  Node    Pointer to the root of the subtree to print.
+                        Can be a root/object/data node.
+**/
+VOID
+EFIAPI
+AmlDbgPrintTree (
+  IN  AML_NODE_HEADER   * Node
+  )
+{
+  AmlDbgPrintTableHeader ();
+  AmlDbgPrintTreeInternal (Node, TRUE, 0);
+}
+
+/** This function performs a raw data dump of the ACPI table.
+
+  @param  [in]  Ptr     Pointer to the start of the table buffer.
+  @param  [in]  Length  The length of the buffer.
+**/
+VOID
+EFIAPI
+DumpRaw (
+  IN  CONST UINT8   * Ptr,
+  IN        UINT32    Length
+  )
+{
+  UINT32  ByteCount;
+  UINT32  PartLineChars;
+  UINT32  AsciiBufferIndex;
+  CHAR8   AsciiBuffer[17];
+
+  ByteCount = 0;
+  AsciiBufferIndex = 0;
+
+  DEBUG ((DEBUG_VERBOSE, "Address  : 0x%p\n", Ptr));
+  DEBUG ((DEBUG_VERBOSE, "Length   : %lld", Length));
+
+  while (ByteCount < Length) {
+    if ((ByteCount & 0x0F) == 0) {
+      AsciiBuffer[AsciiBufferIndex] = '\0';
+      DEBUG ((DEBUG_VERBOSE, "  %a\n%08X : ", AsciiBuffer, ByteCount));
+      AsciiBufferIndex = 0;
+    } else if ((ByteCount & 0x07) == 0) {
+      DEBUG ((DEBUG_VERBOSE, "- "));
+    }
+
+    if ((*Ptr >= ' ') && (*Ptr < 0x7F)) {
+      AsciiBuffer[AsciiBufferIndex++] = *Ptr;
+    } else {
+      AsciiBuffer[AsciiBufferIndex++] = '.';
+    }
+
+    DEBUG ((DEBUG_VERBOSE, "%02X ", *Ptr++));
+
+    ByteCount++;
+  }
+
+  // Justify the final line using spaces before printing
+  // the ASCII data.
+  PartLineChars = (Length & 0x0F);
+  if (PartLineChars != 0) {
+    PartLineChars = 48 - (PartLineChars * 3);
+    if ((Length & 0x0F) <= 8) {
+      PartLineChars += 2;
+    }
+    while (PartLineChars > 0) {
+      DEBUG ((DEBUG_VERBOSE, " "));
+      PartLineChars--;
+    }
+  }
+
+  // Print ASCII data for the final line.
+  AsciiBuffer[AsciiBufferIndex] = '\0';
+  DEBUG ((DEBUG_VERBOSE, "  %a\n\n", AsciiBuffer));
+}
+
+#endif // MDEPKG_NDEBUG
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h b/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h
new file mode 100644
index 0000000000000000000000000000000000000000..68f4c7416948a20484b6d5448c231add3f1186dd
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h
@@ -0,0 +1,154 @@
+/** @file
+  AML Debug Print.
+
+  Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved. <BR>
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_PRINT_H_
+#define AML_PRINT_H_
+
+/* This header file does not include internal Node definition,
+   i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions
+   must be included by the caller file. The function prototypes must
+   only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node
+   definitions.
+   This allows to keep the functions defined here both internal and
+   potentially external. If necessary, any function of this file can
+   be exposed externally.
+   The Api folder is internal to the AmlLib, but should only use these
+   functions. They provide a "safe" way to interact with the AmlLib.
+*/
+
+#if !defined (MDEPKG_NDEBUG)
+
+#include <AmlInclude.h>
+
+/**
+  @defgroup DbgPrintApis Print APIs for debugging.
+  @ingroup AMLLib
+  @{
+    Print APIs provide a way to print:
+     - A buffer;
+     - A (root/object/data) node;
+     - An AML tree/branch;
+     - The AML NameSpace from the root node.
+  @}
+*/
+
+/** This function performs a raw data dump of the ACPI table.
+
+  @param  [in]  Ptr     Pointer to the start of the table buffer.
+  @param  [in]  Length  The length of the buffer.
+**/
+VOID
+EFIAPI
+DumpRaw (
+  IN  CONST UINT8   * Ptr,
+  IN        UINT32    Length
+  );
+
+/** Print Size chars at Buffer address.
+
+  @ingroup DbgPrintApis
+
+  @param  [in]  ErrorLevel    Error level for the DEBUG macro.
+  @param  [in]  Buffer        Buffer containing the chars.
+  @param  [in]  Size          Number of chars to print.
+**/
+VOID
+EFIAPI
+AmlDbgPrintChars (
+  IN        UINT32      ErrorLevel,
+  IN  CONST CHAR8     * Buffer,
+  IN        UINT32      Size
+  );
+
+/** Print an AML NameSeg.
+    Don't print trailing underscores ('_').
+
+  @param  [in] Buffer   Buffer containing an AML NameSeg.
+**/
+VOID
+EFIAPI
+AmlDbgPrintNameSeg (
+  IN  CONST CHAR8   * Buffer
+  );
+
+/** Print an AML NameString.
+
+  @param  [in] Buffer   Buffer containing an AML NameString.
+  @param  [in] NewLine  Print a newline char at the end of the NameString.
+**/
+VOID
+EFIAPI
+AmlDbgPrintNameString (
+  IN  CONST CHAR8   * Buffer,
+  IN        BOOLEAN   NewLine
+  );
+
+/** Print Node information.
+
+  @ingroup DbgPrintApis
+
+  @param  [in]  Node    Pointer to the Node to print.
+                        Can be a root/object/data node.
+**/
+VOID
+EFIAPI
+AmlDbgPrintNode (
+  IN  AML_NODE_HANDLE   Node
+  );
+
+/** Recursively print the subtree under the Node.
+
+  @ingroup DbgPrintApis
+
+  @param  [in]  Node    Pointer to the root of the subtree to print.
+                        Can be a root/object/data node.
+**/
+VOID
+EFIAPI
+AmlDbgPrintTree (
+  IN  AML_NODE_HANDLE   Node
+  );
+
+/** Print the absolute pathnames in the AML namespace of
+    all the nodes in the tree starting from the Root node.
+
+  @ingroup DbgPrintApis
+
+  @param  [in]  RootNode    Pointer to a root node.
+
+  @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
+AmlDbgPrintNameSpace (
+  IN  AML_ROOT_NODE_HANDLE  RootNode
+  );
+
+#else
+
+#define DumpRaw(Ptr, Length)
+
+#define AmlDbgPrintChars(ErrorLevel, Buffer, Size)
+
+#define AmlDbgPrintNameSeg(Buffer)
+
+#define AmlDbgPrintNameString(Buffer,NewLine)
+
+#define AmlDbgPrintNode(Node)
+
+#define AmlDbgPrintTree(Node)
+
+#define AmlDbgPrintNameSpace(RootNode)
+
+#endif // MDEPKG_NDEBUG
+
+#endif // AML_PRINT_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 15/30] DynamicTablesPkg: AML ACPI Namespace interface
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (13 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 14/30] DynamicTablesPkg: AML debug logging Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 16/30] DynamicTablesPkg: AML Parser Sami Mujawar
                   ` (15 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

AML is a declarative language that is processed by the
ACPI AML interpreter. The ACPI AML interpreter will
compile the set of declarations into the ACPI Namespace
at definition block load time.

The hardware information described in AML is effectively
mapped in the ACPI Namespace. The AML ACPI namespace
interface implement the functionality to search the ACPI
Namespace. Example: The AmlFindNode() can be used to locate
a device node in the ACPI namespace using an ASL path as
the search input.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c | 1501 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h |   74 +
 2 files changed, 1575 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c b/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c
new file mode 100644
index 0000000000000000000000000000000000000000..dc373748903dd55fa4492874329f2b433c698c02
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c
@@ -0,0 +1,1501 @@
+/** @file
+  AML NameSpace.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+/* Lexicon:
+
+  NameSeg:
+  - An ASL NameSeg is a name made of at most 4 chars.
+    Cf. ACPI 6.3 specification, s19.2.2 'Name and Pathname Terms'.
+  - An AML NameSeg is a name made of 4 chars.
+    Cf. ACPI 6.3 specification, s20.2.2 'Name Objects Encoding'.
+
+  NameString:
+  A NameString is analogous to a pathname. It is made of 0 to 255 NameSegs.
+  A NameString can be prefixed by a root char ('\') or 0 to 255 carets ('^').
+
+  A NameString can be ASL or AML encoded.
+  AML NameStrings can have a NameString prefix (dual or multi-name prefix)
+  between the root/carets and the list of NameSegs. If the prefix is the
+  multi-name prefix, then the number of NameSegs is encoded on one single byte.
+  Cf. ACPI 6.3 specification, s19.2.2 'Name and Pathname Terms'.
+  Cf. ACPI 6.3 specification, s20.2.2 'Name Objects Encoding'.
+
+  Namespace level:
+  One level in the AML Namespace level corresponds to one NameSeg. In ASL,
+  objects names are NameStrings. This means a device can have a name which
+  spans multiple levels.
+  E.g.: The ASL code: Device (CLU0.CPU0) corresponds to 2 levels.
+
+  Namespace node:
+  A namespace node is an object node which has an associated name, and which
+  changes the current scope.
+  E.g.:
+    1. The "Device ()" ASL statement adds a name to the AML namespace and
+       changes the current scope to the device scope, this is a namespace node.
+    2. The "Scope ()" ASL statement changes the current scope, this is a
+       namespace node.
+    3. A method invocation has a name, but does not add nor change the current
+       AML scope. This is not a namespace node.
+
+  - Object nodes with the AML_IN_NAMESPACE attribute are namespace nodes.
+    Buffers (), Packages (), etc. are not part of the namespace. It is however
+    possible to associate them with a name with the Name () ASL statement.
+  - The root node is considered as being part of the namespace.
+  - Some resource data elements can have a name when defining them in
+    an ASL statement. However, this name is stripped by the ASL compiler.
+    Thus, they don't have a name in the AML bytestream, and are therefore
+    not part of the AML namespace.
+  - Field list elements are part of the namespace.
+    Fields created by an CreateXXXField () ASL statement are part of the
+    namespace. The name of these node can be found in the third or fourth
+    fixed argument. The exact index of the name can be found in the NameIndex
+    field of the AML_BYTE_ENCODING array.
+    Field are at the same level as their ASL statement in the namespace.
+    E.g:
+    Scope (\) {
+      OperationRegion (REG0, SystemIO, 0x100, 0x100)
+      Field (REG0, ByteAcc, NoLock, Preserve) {
+        FIE0, 1,
+        FIE1, 5
+      }
+
+      Name (BUF0, Buffer (100) {})
+      CreateField (BUF0, 5, 2, MEM0)
+    }
+
+    produces this namespace:
+    \ (Root)
+    \-REG0
+    \-FIE0
+    \-FIE1
+    \-BUF0
+    \-MEM0
+
+  Raw AML pathname or Raw AML NameString:
+  In order to easily manipulate AML NameStrings, the non-NameSegs chars are
+  removed in raw pathnames/NameStrings. Non-NameSegs chars are the
+  root char ('\'), carets ('^') and NameString prefixes (Dual/Multi name char).
+  E.g. The following terminology is defined in this AML Library.
+  ASL absolute path:  "[RootChar]AAAA.BBBB.CCCC\0"
+  AML absolute path:  "[RootChar][MultiNamePrefix][3(NameSegs)]AAAABBBBCCCC"
+  Raw absolute path:  "AAAABBBBCCCC"
+
+  Multi-name:
+  A NameString with at least 2 NameSegs. A node can have a name which spans
+  multiple namespace levels.
+*/
+
+#include <NameSpace/AmlNameSpace.h>
+
+#include <AmlCoreInterface.h>
+#include <AmlDbgPrint/AmlDbgPrint.h>
+#include <String/AmlString.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+#include <Tree/AmlTreeTraversal.h>
+
+/** Context of the path search callback function.
+
+  The function finding a node from a path and a reference node enumerates
+  the namespace nodes in the tree and compares their absolute path with the
+  searched path. The enumeration function uses a callback function that can
+  receive a context.
+  This structure is used to store the context information required in the
+  callback function.
+*/
+typedef struct AmlPathSearchContext {
+  /// Backward stream holding the raw AML absolute searched path.
+  AML_STREAM        * SearchPathBStream;
+
+  /// An empty backward stream holding a pre-allocated buffer. This prevents
+  /// from having to do multiple allocations during the search.
+  /// This stream is used to query the raw AML absolute path of the node
+  /// currently being probed.
+  AML_STREAM        * CurrNodePathBStream;
+
+  /// If the node being visited is the node being searched,
+  /// i.e. its path and the searched path match,
+  /// save its reference in this pointer.
+  AML_NODE_HEADER   * OutNode;
+} AML_PATH_SEARCH_CONTEXT;
+
+/** Return the first AML namespace node up in the parent hierarchy.
+
+    Return the root node if no namespace node is found is the hierarchy.
+
+  @param  [in]  Node      Node to look at the parents from.
+                          If Node is the root node, OutNode is NULL.
+  @param  [out] OutNode   If a namespace node is found, pointer to the
+                          first namespace node of Node's parents.
+                          Stop at the root node otherwise.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  **/
+EFI_STATUS
+EFIAPI
+AmlGetFirstAncestorNameSpaceNode (
+  IN  CONST AML_NODE_HEADER   * Node,
+  OUT       AML_NODE_HEADER  ** OutNode
+  )
+{
+  if (!IS_AML_NODE_VALID (Node) ||
+      (OutNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // If Node is the root node, return NULL.
+  if (IS_AML_ROOT_NODE (Node)) {
+    *OutNode = NULL;
+    return EFI_SUCCESS;
+  } else {
+    // Else, get the parent node.
+    Node = AmlGetParent ((AML_NODE_HEADER*)Node);
+    if (!IS_AML_NODE_VALID (Node)) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  // Continue getting the parent node while no namespace node is encountered.
+  while (TRUE) {
+    if (IS_AML_ROOT_NODE (Node)) {
+      break;
+    } else if (AmlNodeHasAttribute (
+                 (CONST AML_OBJECT_NODE*)Node,
+                 AML_IN_NAMESPACE
+                 )) {
+      break;
+    } else {
+      Node = AmlGetParent ((AML_NODE_HEADER*)Node);
+      if (!IS_AML_NODE_VALID (Node)) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+    }
+  } // while
+
+  *OutNode = (AML_NODE_HEADER*)Node;
+  return EFI_SUCCESS;
+}
+
+/** Climb up the AML namespace hierarchy.
+
+  This function get the ancestor namespace node in the AML namespace.
+  If Levels is not zero, skip Levels namespace nodes in the AML namespace.
+  If Levels is zero, return the first ancestor namespace node.
+    I.e. if Levels = n, this function returns the (n + 1) ancestor.
+
+  @param  [in] Node         Pointer to an object node.
+  @param  [in, out] Levels  Pointer holding a number of AML namespace levels:
+                             - At entry, the number of levels to go up in
+                               the AML namespace;
+                             - At exit, the number of levels that still need
+                               to be climbed in case of a multi-named node.
+                               Indeed, if a node with a multi-name is found,
+                               and Levels is less than the number of NameSegs
+                               in this name, then the function returns with
+                               the number of levels that still need to be
+                               climbed.
+                               E.g.: If the first ancestor node's name is
+                                     "AAAA.BBBB.CCCC" and
+                                     Levels = 2  -> i.e go up 3 levels
+                                      \
+                                      ...
+                                      \-"AAAA.BBBB.CCCC"    <----- OutNode
+                                         \-"DDDD"           <----- Node (Input)
+
+                                     The function should ideally return a node
+                                     with the name "AAAA". However, it is not
+                                     possible to split the node name
+                                     "AAAA.BBBB.CCCC" to "AAAA".
+                                     Thus, OutNode is set to the input node,
+                                     and Levels = 2.
+                               In most cases the number of levels to climb
+                               correspond to non multi-name node, and therefore
+                               Levels = 0 at exit.
+  @param  [out] HasRoot     The returned node in OutNode has an AML absolute
+                            name, starting with a root char ('\'), or if OutNode
+                            is the root node.
+  @param  [out] OutNode     The Levels+1 namespace ancestor of the input node in
+                            the AML namespace. Must be the root node or a
+                            namespace node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlGetAncestorNameSpaceNode (
+  IN      CONST AML_OBJECT_NODE   * Node,
+  IN OUT        UINT32            * Levels,
+     OUT        UINT32            * HasRoot,
+     OUT  CONST AML_NODE_HEADER  ** OutNode
+  )
+{
+  EFI_STATUS                Status;
+
+  CONST AML_NODE_HEADER   * NameSpaceNode;
+  CHAR8                   * NodeName;
+  UINT32                    ParentCnt;
+
+  UINT32                    Root;
+  UINT32                    ParentPrefix;
+  UINT32                    SegCount;
+
+  if (!IS_AML_OBJECT_NODE (Node)    ||
+      (Levels == NULL)              ||
+      (HasRoot == NULL)             ||
+      (OutNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ParentCnt = *Levels;
+  *HasRoot = 0;
+
+  // ParentCnt namespace levels need to be climbed.
+  do {
+    // Get the next namespace node in the hierarchy.
+    Status = AmlGetFirstAncestorNameSpaceNode (
+               (CONST AML_NODE_HEADER*)Node,
+               (AML_NODE_HEADER**)&NameSpaceNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    Node = (CONST AML_OBJECT_NODE*)NameSpaceNode;
+
+    if (IS_AML_ROOT_NODE (Node)) {
+      // Node is the root node. It is not possible to go beyond.
+      if (ParentCnt != 0) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+      *HasRoot = 1;
+      break;
+    }
+
+    NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node);
+    if (NodeName == NULL) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+
+    // Analyze the node name.
+    Status = AmlParseNameStringInfo (
+                NodeName,
+                &Root,
+                &ParentPrefix,
+                &SegCount
+                );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    if (Root != 0) {
+      // NodeName is an absolute pathname.
+      *HasRoot = Root;
+
+      // If the node has Root then it cannot have ParentPrefixes (Carets).
+      if (ParentPrefix != 0) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      if (SegCount == ParentCnt) {
+        // There are exactly enough AML namespace levels to consume.
+        // This means the root node was the searched node.
+        Node = (CONST AML_OBJECT_NODE*)AmlGetRootNode (
+                                         (CONST AML_NODE_HEADER*)Node
+                                         );
+        if (!IS_AML_ROOT_NODE (Node)) {
+          ASSERT (0);
+          return EFI_INVALID_PARAMETER;
+        }
+
+        ParentCnt = 0;
+        break;
+      } else if (ParentCnt < SegCount) {
+        // There are too many AML namespace levels in this name.
+        // ParentCnt has the right value, just return.
+        break;
+      } else {
+        // ParentCnt > SegCount
+        // Return error as there must be at least ParentCnt AML namespace
+        // levels left in the absolute path.
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+    } else {
+      // Root is 0.
+      if (ParentCnt < SegCount) {
+        // NodeName is a relative path.
+        // NodeName has enough levels to consume all the ParentCnt.
+        // Exit.
+        break;
+      } else if (SegCount == ParentCnt) {
+        // There are exactly enough AML namespace levels to consume.
+        if (ParentPrefix == 0) {
+          // The node name doesn't have any carets. Get the next namespace
+          // node and return.
+          Status = AmlGetFirstAncestorNameSpaceNode (
+                     (CONST AML_NODE_HEADER*)Node,
+                     (AML_NODE_HEADER**)&NameSpaceNode
+                     );
+          if (EFI_ERROR (Status)) {
+            ASSERT (0);
+            return Status;
+          }
+          Node = (CONST AML_OBJECT_NODE*)NameSpaceNode;
+          ParentCnt = 0;
+          break;
+        } else {
+          // The node name has carets. Need to continue climbing the
+          // AML namespace.
+          ParentCnt = ParentPrefix;
+        }
+      } else {
+        // ParentCnt > SegCount
+        // NodeName doesn't have enough levels to consume all the ParentCnt.
+        // Update ParentCnt: Consume SegCount levels and add ParentPrefix
+        // levels. Continue climbing the tree.
+        ParentCnt = ParentCnt + ParentPrefix - SegCount;
+      }
+    }
+  } while (ParentCnt != 0);
+
+  *OutNode = (CONST AML_NODE_HEADER*)Node;
+  *Levels = ParentCnt;
+
+  return EFI_SUCCESS;
+}
+
+/** Build the raw absolute AML pathname to Node and write it to a stream.
+
+  A raw AML pathname is an AML pathname where the root char ('\'),
+  prefix chars ('^') and NameString prefix byte (e.g.: DualNamePrefix)
+  have been removed. A raw AML pathname is a list of concatenated
+  NameSegs.
+
+  E.g.:
+  ASL absolute path:  "[RootChar]AAAA.BBBB.CCCC\0"
+  AML absolute path:  "[RootChar][MultiNamePrefix][3(NameSegs)]AAAABBBBCCCC"
+  Raw absolute path:  "AAAABBBBCCCC"
+
+  @param  [in]   Node         Node to build the raw absolute path to
+                              Must be a root node, or a namespace node.
+  @param  [in]  InputParent   Skip InputParent AML namespace levels before
+                              starting building the raw absolute pathname.
+                              E.g.: - Node's name being "^AAAA.BBBB.CCCC";
+                                    - InputParent = 2;
+                                    "BBBB.CCCC" will be skipped (2
+                                    levels), and "^AAAA" will remain. The
+                                    first caret is not related to InputParent.
+  @param  [out]  RawAbsPathBStream  Backward stream to write the raw
+                                    pathname to.
+                                    If Node is the root node, the Stream data
+                                    Buffer will stay empty.
+                                    The stream must not be at its end.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetRawNameSpacePath (
+  IN  CONST AML_NODE_HEADER   * Node,
+  IN        UINT32              InputParent,
+  OUT       AML_STREAM        * RawAbsPathBStream
+  )
+{
+  EFI_STATUS          Status;
+
+  AML_NODE_HEADER   * ParentNode;
+  CHAR8             * NodeName;
+
+  UINT32              Root;
+  UINT32              ParentPrefix;
+  UINT32              SegCount;
+  CONST   CHAR8     * NameSeg;
+
+  if ((!IS_AML_ROOT_NODE (Node)                 &&
+       !AmlNodeHasAttribute (
+         (CONST AML_OBJECT_NODE*)Node,
+         AML_IN_NAMESPACE))                     ||
+      !IS_STREAM (RawAbsPathBStream)            ||
+      IS_END_OF_STREAM (RawAbsPathBStream)      ||
+      !IS_STREAM_BACKWARD (RawAbsPathBStream)   ||
+      (InputParent > MAX_UINT8)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  while (1) {
+    if (IS_AML_ROOT_NODE (Node)) {
+      break;
+    }
+
+    NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node);
+    if (NodeName == NULL) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+
+    Status = AmlParseNameStringInfo (
+               NodeName,
+               &Root,
+               &ParentPrefix,
+               &SegCount
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    if (SegCount > InputParent) {
+      // 1.1. If the Node's name has enough levels to consume all the
+      //      InputParent carets, write the levels that are left.
+      NameSeg = AmlGetFirstNameSeg (NodeName, Root, ParentPrefix);
+      Status = AmlStreamWrite (
+                  RawAbsPathBStream,
+                  (CONST UINT8*)NameSeg,
+                  (SegCount - InputParent) * AML_NAME_SEG_SIZE
+                  );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+      InputParent = 0;
+    } else {
+      // (SegCount <= InputParent)
+      // 1.2. Else save the InputParent in TotalParent to climb
+      //      them later.
+      InputParent -= SegCount;
+    }
+
+    InputParent += ParentPrefix;
+
+    if (Root != 0) {
+    // 2. The Node's name is an absolute path.
+    //    Exit, the root has been reached.
+      if (InputParent != 0) {
+        ASSERT (0);
+        return EFI_NOT_FOUND;
+      }
+      break;
+    }
+
+    Status = AmlGetAncestorNameSpaceNode (
+               (CONST AML_OBJECT_NODE*)Node,
+               &InputParent,
+               &Root,
+               (CONST AML_NODE_HEADER**)&ParentNode
+               );
+    if (EFI_ERROR (Status)  ||
+        (!IS_AML_NODE_VALID (ParentNode))) {
+      ASSERT (0);
+      return Status;
+    }
+
+    Node = ParentNode;
+
+    if (IS_AML_ROOT_NODE (Node)) {
+      // 3.1. If the root node has been found while climbing,
+      //      no need to write NameSegs.
+      //      Exit.
+      break;
+    } else if (Root != 0) {
+      // 3.2. An absolute path has been found while climbing the tree.
+      //      If (InputParent != 0), the raw pathname is not the root.
+      //      Write the first [SegCount - InputParent] NameSegs of this
+      //      absolute path.
+      //      Then exit.
+      if (InputParent != 0) {
+        // Get the absolute pathname.
+        NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node);
+        if (NodeName == NULL) {
+          ASSERT (0);
+          return EFI_INVALID_PARAMETER;
+        }
+
+        // Analyze the absolute pathname.
+        Status = AmlParseNameStringInfo (
+                    NodeName,
+                    &Root,
+                    &ParentPrefix,
+                    &SegCount
+                    );
+        if (EFI_ERROR (Status)) {
+          ASSERT (0);
+          return Status;
+        }
+
+        // Writing the n first NameSegs.
+        // n = SegCount - InputParent
+        NameSeg = AmlGetFirstNameSeg (NodeName, Root, ParentPrefix);
+        Status = AmlStreamWrite (
+                    RawAbsPathBStream,
+                    (CONST UINT8*)NameSeg,
+                    (SegCount - InputParent) * AML_NAME_SEG_SIZE
+                    );
+        if (EFI_ERROR (Status)) {
+          ASSERT (0);
+          return Status;
+        }
+
+        break;
+      } // (InputParent != 0)
+
+    }
+  } // while
+
+  return EFI_SUCCESS;
+}
+
+/** Add the RootChar and prefix byte to the raw AML NameString in the
+    input Stream to create a valid absolute path.
+
+  The prefix byte can be AML_DUAL_NAME_PREFIX, AML_MULTI_NAME_PREFIX
+  or nothing.
+
+  @param  [in, out] AmlPathBStream  The Stream initially contains a raw
+                                    NameString (i.e. a list of NameSegs).
+                                    The Stream can be empty (e.g.: for the
+                                    root path).
+                                    The stream must not be at its end.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlAddPrefix (
+  IN  OUT AML_STREAM    * AmlPathBStream
+  )
+{
+  EFI_STATUS    Status;
+  UINT32        NameSegCount;
+  UINT32        NameSegSize;
+
+  // At most 3 bytes are needed for: RootChar + MultiNamePrefix + SegCount.
+  CHAR8         Prefix[3];
+  UINT32        PrefixSize;
+
+  // The Stream contains concatenated NameSegs.
+  if (!IS_STREAM (AmlPathBStream)       ||
+      IS_END_OF_STREAM (AmlPathBStream) ||
+      !IS_STREAM_BACKWARD (AmlPathBStream)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Its size should be a multiple of AML_NAME_SEG_SIZE.
+  // AML_NAME_SEG_SIZE = 4. Check the 2 lowest bits.
+  NameSegSize = AmlStreamGetIndex (AmlPathBStream);
+  if ((NameSegSize & (AML_NAME_SEG_SIZE - 1)) != 0) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Each NameSeg is 4 bytes so divide the NameSegSize by 4.
+  NameSegCount = NameSegSize >> 2;
+  if (NameSegCount > MAX_UINT8) {
+    // There can be at most 255 NameSegs.
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Prefix[0] = AML_ROOT_CHAR;
+
+  switch (NameSegCount) {
+    case 0:
+    {
+      // Root and parents only NameString (no NameSeg(s)) end with '\0'.
+      Prefix[1] = AML_ZERO_OP;
+      PrefixSize = 2;
+      break;
+    }
+    case 1:
+    {
+      PrefixSize = 1;
+      break;
+    }
+    case 2:
+    {
+      Prefix[1] = AML_DUAL_NAME_PREFIX;
+      PrefixSize = 2;
+      break;
+    }
+    default:
+    {
+      Prefix[1] = AML_MULTI_NAME_PREFIX;
+      Prefix[2] = (UINT8)NameSegCount;
+      PrefixSize = 3;
+      break;
+    }
+  }
+
+  // Add the RootChar + prefix (if needed) at the beginning of the pathname.
+  Status = AmlStreamWrite (AmlPathBStream, (CONST UINT8*)Prefix, PrefixSize);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  return Status;
+}
+
+/** Remove the prefix bytes of an AML NameString stored in a backward stream
+    to get a raw NameString.
+
+  The AML encoding for '\', '^', Dual name or multi-name prefix are
+  stripped off.
+  E.g: If the ASL path was "\AAAA.BBBB", the AML equivalent would be
+       "{RootChar}{DualNamePrefix}AAAABBBB". So resultant raw NameString
+       is "AAAABBBB".
+
+  @param  [in, out] AmlPathBStream    Backward stream containing an AML
+                                      NameString.
+                                      The stream must not be at its end.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlRemovePrefix (
+  IN  OUT AML_STREAM  * AmlPathBStream
+  )
+{
+  EFI_STATUS    Status;
+
+  UINT32        TotalSize;
+  UINT32        RewindSize;
+
+  UINT32        Root;
+  UINT32        ParentPrefix;
+  UINT32        SegCount;
+
+  if (!IS_STREAM (AmlPathBStream)         ||
+      IS_END_OF_STREAM (AmlPathBStream)   ||
+      !IS_STREAM_BACKWARD (AmlPathBStream)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = AmlParseNameStringInfo (
+             (CHAR8*)AmlStreamGetCurrPos (AmlPathBStream),
+             &Root,
+             &ParentPrefix,
+             &SegCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  TotalSize = AmlComputeNameStringSize (Root, ParentPrefix, SegCount);
+  if (TotalSize == 0) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Rewind the stream of all the bytes that are not SegCounts
+  // to drop the prefix.
+  RewindSize = TotalSize - (SegCount * AML_NAME_SEG_SIZE);
+  if (RewindSize != 0) {
+    Status = AmlStreamRewind (AmlPathBStream, RewindSize);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Build the absolute ASL pathname to Node.
+
+  BufferSize is always updated to the size of the pathname.
+
+  If:
+   - the content of BufferSize is >= to the size of the pathname AND;
+   - Buffer is not NULL.
+  then copy the pathname in the Buffer. A buffer of the size
+  MAX_ASL_NAMESTRING_SIZE is big enough to receive any ASL pathname.
+
+  @param  [in]      Node            Node to build the absolute path to.
+                                    Must be a root node, or a namespace node.
+  @param  [out]     Buffer          Buffer to write the path to.
+                                    If NULL, only update *BufferSize.
+  @param  [in, out] BufferSize      Pointer holding:
+                                    - At entry, the size of the Buffer;
+                                    - At exit, the size of the pathname.
+
+  @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
+AmlGetAslPathName (
+  IN      AML_NODE_HEADER   * Node,
+      OUT CHAR8             * Buffer,
+  IN  OUT UINT32            * BufferSize
+  )
+{
+  EFI_STATUS      Status;
+
+  // Backward stream used to build the raw AML absolute path to the node.
+  AML_STREAM      RawAmlAbsPathBStream;
+  CHAR8         * RawAmlAbsPathBuffer;
+  UINT32          RawAmlAbsPathBufferSize;
+
+  CHAR8         * AmlPathName;
+  CHAR8         * AslPathName;
+  UINT32          AslPathNameSize;
+
+  UINT32          Root;
+  UINT32          ParentPrefix;
+  UINT32          SegCount;
+
+  if ((!IS_AML_ROOT_NODE (Node)         &&
+       !AmlNodeHasAttribute (
+         (CONST AML_OBJECT_NODE*)Node,
+         AML_IN_NAMESPACE))             ||
+      (BufferSize == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  AslPathName = NULL;
+
+  // Allocate a Stream to get the raw AML absolute pathname.
+  RawAmlAbsPathBufferSize = MAX_AML_NAMESTRING_SIZE;
+  RawAmlAbsPathBuffer = AllocateZeroPool (RawAmlAbsPathBufferSize);
+  if (RawAmlAbsPathBuffer == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Status = AmlStreamInit (
+             &RawAmlAbsPathBStream,
+             (UINT8*)RawAmlAbsPathBuffer,
+             RawAmlAbsPathBufferSize,
+             EAmlStreamDirectionBackward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Get the raw pathname of the Node. The raw pathname being an
+  // AML NameString without the RootChar and prefix byte.
+  // It is a list of concatenated NameSegs.
+  Status = AmlGetRawNameSpacePath (Node, 0, &RawAmlAbsPathBStream);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Add the RootChar and prefix byte.
+  Status = AmlAddPrefix (&RawAmlAbsPathBStream);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  AmlPathName = (CHAR8*)AmlStreamGetCurrPos (&RawAmlAbsPathBStream);
+
+  // Analyze the NameString.
+  Status = AmlParseNameStringInfo (
+             (CONST CHAR8*)AmlPathName,
+             &Root,
+             &ParentPrefix,
+             &SegCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Compute the size the ASL pathname will take.
+  AslPathNameSize = AslComputeNameStringSize (Root, ParentPrefix, SegCount);
+  if (AslPathNameSize == 0) {
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+    goto exit_handler;
+  }
+
+  // Input Buffer is large enough. Copy the pathname if the Buffer is valid.
+  if ((Buffer != NULL) && (AslPathNameSize <= *BufferSize)) {
+    Status = ConvertAmlNameToAslName (AmlPathName, &AslPathName);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      Status = EFI_OUT_OF_RESOURCES;
+      goto exit_handler;
+    }
+
+    CopyMem (Buffer, AslPathName, AslPathNameSize);
+  }
+
+  *BufferSize = AslPathNameSize;
+
+exit_handler:
+  // Free allocated memory.
+  FreePool (RawAmlAbsPathBuffer);
+  if (AslPathName != NULL) {
+    FreePool (AslPathName);
+  }
+
+  return Status;
+}
+
+#if !defined (MDEPKG_NDEBUG)
+
+/** Recursively print the pathnames in the AML namespace in Node's branch.
+
+  @param  [in]  Node          Pointer to a node.
+  @param  [in]  Context       An empty forward stream holding a pre-allocated
+                              buffer. This prevents from having to do multiple
+                              allocations during the enumeration.
+  @param  [in, out] Status    At entry, contains the status returned by the
+                              last call to this exact function during the
+                              enumeration.
+                              As exit, contains the returned status of the
+                              call to this function.
+                              Optional, can be NULL.
+
+  @retval TRUE if the enumeration can continue or has finished without
+          interruption.
+  @retval FALSE if the enumeration needs to stopped or has stopped.
+**/
+STATIC
+BOOLEAN
+EFIAPI
+AmlDbgPrintNameSpaceCallback (
+  IN      AML_NODE_HEADER  * Node,
+  IN      VOID             * Context,
+  IN  OUT EFI_STATUS       * Status   OPTIONAL
+  )
+{
+  BOOLEAN           ContinueEnum;
+  EFI_STATUS        Status1;
+
+  AML_STREAM      * CurrNodePathFStream;
+  CHAR8           * CurrNodePathBuffer;
+  UINT32            CurrNodePathBufferSize;
+
+  ContinueEnum = TRUE;
+  Status1 = EFI_SUCCESS;
+
+  if (!IS_AML_NODE_VALID (Node) ||
+      (Context == NULL)) {
+    ASSERT (0);
+    Status1 = EFI_INVALID_PARAMETER;
+    ContinueEnum = FALSE;
+    goto exit_handler;
+  }
+
+  if (!IS_AML_ROOT_NODE (Node)  &&
+      !AmlNodeHasAttribute (
+         (CONST AML_OBJECT_NODE*)Node,
+         AML_IN_NAMESPACE)) {
+    // Skip this node and continue enumeration.
+    goto exit_handler;
+  }
+
+  if (IS_AML_ROOT_NODE (Node)) {
+    DEBUG ((DEBUG_INFO, "\\\n"));
+  } else if (AmlNodeHasAttribute (
+               (CONST AML_OBJECT_NODE*)Node,
+               AML_IN_NAMESPACE)) {
+
+  CurrNodePathFStream = (AML_STREAM*)Context;
+
+  // Check the Context's content.
+  if (!IS_STREAM (CurrNodePathFStream)           ||
+      IS_END_OF_STREAM (CurrNodePathFStream)     ||
+      !IS_STREAM_FORWARD (CurrNodePathFStream)) {
+    ASSERT (0);
+    Status1 = EFI_INVALID_PARAMETER;
+    ContinueEnum = FALSE;
+    goto exit_handler;
+  }
+
+  CurrNodePathBuffer = (CHAR8*)AmlStreamGetBuffer (CurrNodePathFStream);
+  CurrNodePathBufferSize = AmlStreamGetMaxBufferSize (CurrNodePathFStream);
+
+  Status1 = AmlGetAslPathName (
+              (AML_NODE_HEADER*)Node,
+              CurrNodePathBuffer,
+              &CurrNodePathBufferSize
+              );
+  if (EFI_ERROR (Status1)) {
+    ASSERT (0);
+    ContinueEnum = FALSE;
+    goto exit_handler;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a\n", CurrNodePathBuffer));
+
+  } else {
+    ASSERT (0);
+    Status1 = EFI_INVALID_PARAMETER;
+    ContinueEnum = FALSE;
+  }
+
+exit_handler:
+  if (Status != NULL) {
+    *Status = Status1;
+  }
+
+  return ContinueEnum;
+}
+
+/** Print the absolute pathnames in the AML namespace of
+    all the nodes in the tree starting from the Root node.
+
+  @param  [in]  RootNode    Pointer to a root node.
+
+  @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
+AmlDbgPrintNameSpace (
+  IN  AML_ROOT_NODE   * RootNode
+  )
+{
+  EFI_STATUS      Status;
+
+  AML_STREAM      CurrNodePathFStream;
+  CHAR8         * CurrNodePathBuffer;
+  UINT32          CurrNodePathBufferSize;
+
+  if (!IS_AML_ROOT_NODE (RootNode)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((DEBUG_INFO, "AmlNameSpace: AML namespace:\n"));
+
+  // Allocate memory to build the absolute ASL path to each node.
+  CurrNodePathBufferSize = MAX_AML_NAMESTRING_SIZE;
+  CurrNodePathBuffer = AllocateZeroPool (CurrNodePathBufferSize);
+  if (CurrNodePathBuffer == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  // An empty forward stream holding a pre-allocated buffer is used
+  // to avoid multiple allocations during the enumeration.
+  Status = AmlStreamInit (
+             &CurrNodePathFStream,
+             (UINT8*)CurrNodePathBuffer,
+             CurrNodePathBufferSize,
+             EAmlStreamDirectionForward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  AmlEnumTree (
+    (AML_NODE_HEADER*)RootNode,
+    AmlDbgPrintNameSpaceCallback,
+    (VOID*)&CurrNodePathFStream,
+    &Status
+    );
+  ASSERT_EFI_ERROR (Status);
+
+exit_handler:
+  FreePool (CurrNodePathBuffer);
+
+  return Status;
+}
+
+#endif // MDEPKG_NDEBUG
+
+/** Callback function to find the node corresponding to an absolute pathname.
+
+  For each namespace node, build its raw AML absolute path. Then compare this
+  path with the raw AML absolute path of the search node available in the
+  Context.
+
+  @param  [in]      Node      Pointer to the node to whose pathname is being
+                              tested.
+  @param  [in, out] Context   A pointer to AML_PATH_SEARCH_CONTEXT that has:
+                               - The searched path stored in a stream;
+                               - An empty stream to query the pathname of the
+                                 probed node;
+                               - A node pointer to store the searched node
+                                 if found.
+  @param  [in, out] Status    At entry, contains the status returned by the
+                              last call to this exact function during the
+                              enumeration.
+                              As exit, contains the returned status of the
+                              call to this function.
+                              Optional, can be NULL.
+
+  @retval TRUE if the enumeration can continue or has finished without
+          interruption.
+  @retval FALSE if the enumeration needs to stopped or has stopped.
+**/
+STATIC
+BOOLEAN
+EFIAPI
+AmlEnumeratePathCallback (
+  IN      AML_NODE_HEADER  * Node,
+  IN  OUT VOID             * Context,
+  IN  OUT EFI_STATUS       * Status   OPTIONAL
+)
+{
+  BOOLEAN                     ContinueEnum;
+  EFI_STATUS                  Status1;
+
+  AML_PATH_SEARCH_CONTEXT   * PathSearchContext;
+
+  AML_STREAM                * SearchPathBStream;
+  CHAR8                     * SearchedPath;
+
+  AML_STREAM                * CurrNodePathBStream;
+  CHAR8                     * CurrNodePath;
+  UINT32                      CurrNodePathSize;
+
+  ContinueEnum = TRUE;
+  Status1 = EFI_SUCCESS;
+
+  if (!IS_AML_NODE_VALID (Node) ||
+      (Context == NULL)) {
+    ASSERT (0);
+    Status1 = EFI_INVALID_PARAMETER;
+    ContinueEnum = FALSE;
+    goto exit_handler;
+  }
+
+  if (!AmlNodeHasAttribute (
+         (CONST AML_OBJECT_NODE*)Node,
+         AML_IN_NAMESPACE)) {
+    goto exit_handler;
+  }
+
+  PathSearchContext = (AML_PATH_SEARCH_CONTEXT*)Context;
+  SearchPathBStream = PathSearchContext->SearchPathBStream;
+  CurrNodePathBStream = PathSearchContext->CurrNodePathBStream;
+
+  // Check the Context's content.
+  if (!IS_STREAM (SearchPathBStream)            ||
+      IS_END_OF_STREAM (SearchPathBStream)      ||
+      !IS_STREAM_BACKWARD (SearchPathBStream)   ||
+      !IS_STREAM (CurrNodePathBStream)          ||
+      IS_END_OF_STREAM (CurrNodePathBStream)    ||
+      !IS_STREAM_BACKWARD (CurrNodePathBStream)) {
+    ASSERT (0);
+    Status1 = EFI_INVALID_PARAMETER;
+    ContinueEnum = FALSE;
+    goto exit_handler;
+  }
+
+  CurrNodePathSize = AmlStreamGetMaxBufferSize (CurrNodePathBStream);
+  if (CurrNodePathSize == 0) {
+    ASSERT (0);
+    Status1 = EFI_INVALID_PARAMETER;
+    ContinueEnum = FALSE;
+    goto exit_handler;
+  }
+
+  SearchedPath = (CHAR8*)AmlStreamGetCurrPos (SearchPathBStream);
+  CurrNodePath = (CHAR8*)AmlStreamGetCurrPos (CurrNodePathBStream);
+
+  // Get the raw AML absolute pathname of the current node.
+  Status1 = AmlGetRawNameSpacePath (Node, 0, CurrNodePathBStream);
+  if (EFI_ERROR (Status1)) {
+    ASSERT (0);
+    ContinueEnum = FALSE;
+    goto exit_handler;
+  }
+
+  DEBUG ((
+    DEBUG_VERBOSE,
+    "AmlNameSpace: "
+    "Comparing search path with current node path.\n"
+    ));
+  DEBUG ((DEBUG_VERBOSE, "Search path:"));
+  AmlDbgPrintChars (
+    DEBUG_VERBOSE,
+    (CHAR8*)AmlStreamGetCurrPos (SearchPathBStream),
+    AmlStreamGetIndex (SearchPathBStream)
+    );
+  DEBUG ((DEBUG_VERBOSE, "\nPath of the current node: "));
+  AmlDbgPrintChars (
+    DEBUG_VERBOSE,
+    (CHAR8*)AmlStreamGetCurrPos (CurrNodePathBStream),
+    AmlStreamGetIndex (CurrNodePathBStream)
+    );
+  DEBUG ((DEBUG_VERBOSE, "\n"));
+
+  // Compare the searched path and Node's path.
+  if ((AmlStreamGetIndex (CurrNodePathBStream)  ==
+         AmlStreamGetIndex (SearchPathBStream))     &&
+      (CompareMem (
+         AmlStreamGetCurrPos (CurrNodePathBStream),
+         AmlStreamGetCurrPos (SearchPathBStream),
+         AmlStreamGetIndex (SearchPathBStream)) == 0)) {
+    Status1 = EFI_SUCCESS;
+    ContinueEnum = FALSE;
+    PathSearchContext->OutNode = Node;
+  } else {
+    // If the paths don't match, reset the CurrNodePathStream's content.
+    Status1 = AmlStreamReset (CurrNodePathBStream);
+    if (EFI_ERROR (Status1)) {
+      ASSERT (0);
+      ContinueEnum = FALSE;
+    }
+  }
+
+exit_handler:
+  if (Status != NULL) {
+    *Status = Status1;
+  }
+
+  return ContinueEnum;
+}
+
+/** Build a raw AML absolute path from a reference node and a relative
+    ASL path.
+
+  The AslPath can be a relative path or an absolute path.
+  Node must be a root node or a namespace node.
+  A root node is expected to be at the top of the tree.
+
+  @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".
+  @param  [in, out] RawAmlAbsSearchPathBStream  Backward stream to write the
+                                                raw absolute AML path of the
+                                                searched node.
+                                                The stream must not be at
+                                                its end.
+
+  @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlBuildAbsoluteAmlPath (
+  IN      AML_NODE_HEADER   * ReferenceNode,
+  IN      CHAR8             * AslPath,
+  IN  OUT AML_STREAM        * RawAmlAbsSearchPathBStream
+  )
+{
+  EFI_STATUS    Status;
+  CHAR8       * AmlPath;
+
+  UINT32        AmlNameStringSize;
+  UINT32        Root;
+  UINT32        ParentPrefix;
+  UINT32        SegCount;
+
+  if ((!IS_AML_ROOT_NODE (ReferenceNode)              &&
+       !AmlNodeHasAttribute (
+         (CONST AML_OBJECT_NODE*)ReferenceNode,
+         AML_IN_NAMESPACE))                           ||
+      (AslPath == NULL)                               ||
+      !IS_STREAM (RawAmlAbsSearchPathBStream)         ||
+      IS_END_OF_STREAM (RawAmlAbsSearchPathBStream)   ||
+      !IS_STREAM_BACKWARD (RawAmlAbsSearchPathBStream)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // 1. Validate, analyze and convert the AslPath to an AmlPath.
+  Status = ConvertAslNameToAmlName (AslPath, &AmlPath);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = AmlParseNameStringInfo (AmlPath, &Root, &ParentPrefix, &SegCount);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Not possible to go beyond the root.
+  if (IS_AML_ROOT_NODE (ReferenceNode) && (ParentPrefix != 0)) {
+    Status = EFI_INVALID_PARAMETER;
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  AmlNameStringSize = AmlComputeNameStringSize (Root, ParentPrefix, SegCount);
+  if (AmlNameStringSize == 0) {
+    Status = EFI_INVALID_PARAMETER;
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // 2.1. Write the AML path to the stream.
+  Status = AmlStreamWrite (
+              RawAmlAbsSearchPathBStream,
+              (CONST UINT8*)AmlPath,
+              AmlNameStringSize
+              );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // 2.2. Then remove the AML prefix (root char, parent prefix, etc.)
+  //      to obtain a raw AML NameString. Raw AML NameString are easier
+  //      to manipulate.
+  Status = AmlRemovePrefix (RawAmlAbsSearchPathBStream);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // 3. If AslPath is a relative path and the reference Node is not
+  //    the root node, fill the Stream with the absolute path to the
+  //    reference node.
+  if ((Root == 0) && !IS_AML_ROOT_NODE (ReferenceNode)) {
+    Status = AmlGetRawNameSpacePath (
+               ReferenceNode,
+               ParentPrefix,
+               RawAmlAbsSearchPathBStream
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+  }
+
+exit_handler:
+  // Free allocated memory.
+  FreePool (AmlPath);
+
+  return Status;
+}
+
+/** 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
+
+  @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_HEADER     * ReferenceNode,
+  IN  CHAR8               * AslPath,
+  OUT AML_NODE_HEADER    ** OutNode
+  )
+{
+  EFI_STATUS                  Status;
+
+  AML_PATH_SEARCH_CONTEXT     PathSearchContext;
+  AML_ROOT_NODE             * RootNode;
+
+  // Backward stream used to build the raw AML absolute path to the searched
+  // node.
+  AML_STREAM                  RawAmlAbsSearchPathBStream;
+  CHAR8                     * RawAmlAbsSearchPathBuffer;
+  UINT32                      RawAmlAbsSearchPathBufferSize;
+
+  // Backward stream used to store the raw AML absolute path of the node
+  // currently enumerated in the tree. This path can then be compared to the
+  // RawAmlAbsSearchPath.
+  AML_STREAM                  RawAmlAbsCurrNodePathBStream;
+  CHAR8                     * RawAmlAbsCurrNodePathBuffer;
+  UINT32                      RawAmlAbsCurrNodePathBufferSize;
+
+  if ((!IS_AML_ROOT_NODE (ReferenceNode)        &&
+       !AmlNodeHasAttribute (
+         (CONST AML_OBJECT_NODE*)ReferenceNode,
+         AML_IN_NAMESPACE))                     ||
+      (AslPath == NULL)                         ||
+      (OutNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *OutNode = NULL;
+  RawAmlAbsCurrNodePathBuffer = NULL;
+
+  // 1. Build a raw absolute AML path from the reference node and the ASL
+  //    path. For this:
+  // 1.1. First initialize a backward stream.
+  RawAmlAbsSearchPathBufferSize = MAX_AML_NAMESTRING_SIZE;
+  RawAmlAbsSearchPathBuffer = AllocateZeroPool (RawAmlAbsSearchPathBufferSize);
+  if (RawAmlAbsSearchPathBuffer == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Status = AmlStreamInit (
+             &RawAmlAbsSearchPathBStream,
+             (UINT8*)RawAmlAbsSearchPathBuffer,
+             RawAmlAbsSearchPathBufferSize,
+             EAmlStreamDirectionBackward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // 1.2. Then build the raw AML absolute path.
+  Status = AmlBuildAbsoluteAmlPath (
+             ReferenceNode,
+             AslPath,
+             &RawAmlAbsSearchPathBStream
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // 2. Find the root node by climbing up the tree from the reference node.
+  RootNode = AmlGetRootNode (ReferenceNode);
+  if (RootNode == NULL) {
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+    goto exit_handler;
+  }
+
+  // 3. If the searched node is the root node, return.
+  //    For the Root Node there is no NameSegs so the length of
+  //     the stream will be zero.
+  if (AmlStreamGetIndex (&RawAmlAbsSearchPathBStream) == 0) {
+    *OutNode = (AML_NODE_HEADER*)RootNode;
+    Status = EFI_SUCCESS;
+    goto exit_handler;
+  }
+
+  // 4. Create a backward stream large enough to hold the current node path
+  //    during enumeration. This prevents from doing multiple allocation/free
+  //    operations.
+  RawAmlAbsCurrNodePathBufferSize = MAX_ASL_NAMESTRING_SIZE;
+  RawAmlAbsCurrNodePathBuffer = AllocateZeroPool (
+                                  RawAmlAbsCurrNodePathBufferSize
+                                  );
+  if (RawAmlAbsCurrNodePathBuffer == NULL) {
+    ASSERT (0);
+    Status = EFI_OUT_OF_RESOURCES;
+    goto exit_handler;
+  }
+
+  Status = AmlStreamInit (
+             &RawAmlAbsCurrNodePathBStream,
+             (UINT8*)RawAmlAbsCurrNodePathBuffer,
+             RawAmlAbsCurrNodePathBufferSize,
+             EAmlStreamDirectionBackward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // 5. Fill a path search context structure with:
+  //     - SearchPathStream: backward stream containing the raw absolute AML
+  //       path to the searched node;
+  //     - CurrNodePathStream: backward stream containing the raw absolute AML
+  //       of the node currently being enumerated;
+  //     - OutNode: node pointer to the store the potentially found node.
+  PathSearchContext.SearchPathBStream = &RawAmlAbsSearchPathBStream;
+  PathSearchContext.CurrNodePathBStream = &RawAmlAbsCurrNodePathBStream;
+  PathSearchContext.OutNode = NULL;
+
+  // 6. Iterate through the namespace nodes of the tree.
+  //    For each namespace node, build its raw AML absolute path. Then compare
+  //    it with the search path.
+  AmlEnumTree (
+    (AML_NODE_HEADER*)RootNode,
+    AmlEnumeratePathCallback,
+    (VOID*)&PathSearchContext,
+    &Status
+    );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  *OutNode = PathSearchContext.OutNode;
+  if (*OutNode == NULL) {
+    Status = EFI_NOT_FOUND;
+  }
+
+exit_handler:
+  // Free allocated memory.
+  FreePool (RawAmlAbsSearchPathBuffer);
+  if (RawAmlAbsCurrNodePathBuffer != NULL) {
+    FreePool (RawAmlAbsCurrNodePathBuffer);
+  }
+
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h b/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h
new file mode 100644
index 0000000000000000000000000000000000000000..3d8ebc8b182983606af7e7ddfe3078fd729fb5b6
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h
@@ -0,0 +1,74 @@
+/** @file
+  AML NameSpace.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_NAMESPACE_H_
+#define AML_NAMESPACE_H_
+
+#include <AmlNodeDefines.h>
+#include <Stream/AmlStream.h>
+
+/** Return the first AML namespace node up in the parent hierarchy.
+
+    Return the root node if no namespace node is found is the hierarchy.
+
+  @param  [in]  Node      Node to look at the parents from.
+                          If Node is the root node, OutNode is NULL.
+  @param  [out] OutNode   If a namespace node is found, pointer to the
+                          first namespace node of Node's parents.
+                          Stop at the root node otherwise.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  **/
+EFI_STATUS
+EFIAPI
+AmlGetFirstAncestorNameSpaceNode (
+  IN  CONST AML_NODE_HEADER   * Node,
+  OUT       AML_NODE_HEADER  ** OutNode
+  );
+
+/** Build the raw absolute AML pathname to Node and write it to a stream.
+
+  A raw AML pathname is an AML pathname where the root char ('\'),
+  prefix chars ('^') and NameString prefix byte (e.g.: DualNamePrefix)
+  have been removed. A raw AML pathname is a list of concatenated
+  NameSegs.
+
+  E.g.:
+  ASL absolute path:  "[RootChar]AAAA.BBBB.CCCC\0"
+  AML absolute path:  "[RootChar][MultiNamePrefix][3(NameSegs)]AAAABBBBCCCC"
+  Raw absolute path:  "AAAABBBBCCCC"
+
+  @param  [in]   Node         Node to build the raw absolute path to
+                              Must be a root node, or a namespace node.
+  @param  [in]  InputParent   Skip InputParent AML namespace levels before
+                              starting building the raw absolute pathname.
+                              E.g.: - Node's name being "^AAAA.BBBB.CCCC";
+                                    - InputParent = 2;
+                                    "BBBB.CCCC" will be skipped (2
+                                    levels), and "^AAAA" will remain. The
+                                    first caret is not related to InputParent.
+  @param  [out]  RawAbsPathBStream  Backward stream to write the raw
+                                    pathname to.
+                                    If Node is the root node, the Stream data
+                                    Buffer will stay empty.
+                                    The stream must not be at its end.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetRawNameSpacePath (
+  IN  CONST AML_NODE_HEADER   * Node,
+  IN        UINT32              InputParent,
+  OUT       AML_STREAM        * RawAbsPathBStream
+  );
+
+#endif // AML_NAMESPACE_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 16/30] DynamicTablesPkg: AML Parser
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (14 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 15/30] DynamicTablesPkg: AML ACPI Namespace interface Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 17/30] DynamicTablesPkg: AML resource data helper Sami Mujawar
                   ` (14 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

Both ASL and AML are declarative language. The ASL code
is compiled to AML bytecode. The AML bytecode is processed
by the ACPI AML interpreter that runs as part of an OS.
AML has a complex encoding making dynamic generation of
Definition Block tables difficult.

Dynamic AML generation involves techniques like AML Fixup
and AML Codegen, both requiring parsing of AML bytecode.

The AML parser is a module that parses an AML byte stream
and represents it as an AML tree. Representing the AML
bytecode as an AML tree is key to reducing the complexity
and enabling Dynamic AML generation.

In an AML Tree each AML statement (that also corresponds
to an ASL statement) is represented as an 'Object Node'.
Each Object Node has an OpCode and up to 6 Fixed Arguments
followed by a list of Variable Arguments.

(ObjectNode)
    \
    |- [0][1][2][3][4][5]             # Fixed Arguments
    |- {(VarArg1)->(VarArg2)->...N}   # Variable Arguments

A Fixed Argument or Variable Argument can be either an
Object Node or a Data Node.

A 'Data Node' consists of a data buffer.

A 'Root Node' is a special type of Object Node that does
not have an Opcode or Fixed Arguments. It only has a list
of Variable Arguments. The Root Node is at the top of the
AML tree and contains the Definition Block Header.

The AML parser uses the 'AML Encoding' to parse an AML byte
stream and represents it as an AML Tree. Representing in the
form of an AML tree simplifies modification, addition and
removal of the tree nodes. The modified tree can then be
serialised to a buffer representing a Definition Block table.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c | 1448 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h |   72 +
 2 files changed, 1520 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c
new file mode 100644
index 0000000000000000000000000000000000000000..380ac9bbcebef01d1d1f5dbd01971992aaf66d89
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c
@@ -0,0 +1,1448 @@
+/** @file
+  AML Parser.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Parser/AmlParser.h>
+
+#include <AmlCoreInterface.h>
+#include <AmlDbgPrint/AmlDbgPrint.h>
+#include <Parser/AmlFieldListParser.h>
+#include <Parser/AmlMethodParser.h>
+#include <Parser/AmlResourceDataParser.h>
+#include <String/AmlString.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+
+/*
+  AML Tree
+  --------
+
+  Each ASL Statement is represented in AML as and ObjectNode.
+  Each ObjectNode has an Opcode and has up to six FixedArguments
+  followed by a list of VariableArguments.
+  (ObjectNode)
+    \
+    |- [0][1][2][3][4][5]                        # Fixed Arguments
+    |- {(VarArg1)->(VarArg2)->(VarArg3)->...N}   # Variable Arguments
+
+  A RootNode is a special type of Object Node that does not have an
+  Opcode or Fixed Arguments. It only has a list of VariableArguments
+  (RootNode)
+    \
+    |- {(VarArg1)->(VarArg2)->(VarArg3)->...N}   # Variable Arguments
+
+  A DataNode consists of a data buffer.
+
+  A FixedArgument or VariableArgument can be either an ObjectNode or
+  a DataNode.
+
+  Example:
+  ASL code sample:
+  Device (DEV0) {
+    Name (VAR0, 0x6)
+  }
+
+  Tree generated from the ASL code:
+  (RootNode)
+    \
+    |- {(Device statement (ObjectNode))}                # Variable Arg of the
+          \                                             #   RootNode
+           |
+           |- [0] - Device Name (DataNode)(="DEV0")     # Fixed Arg0 of the
+           |                                            #   Device() statement
+           |
+           |- {(Name statement (ObjectNode))}           # Variable Arg of the
+                \                                       #   Device() statement
+                |
+                |- [0] - Name statement(DataNode)(="VAR0")  # Fixed Arg0 of the
+                |                                           #   Name() statement
+                |- [1] - Value(DataNode)(=0x6)              # Fixed Arg1 of the
+                                                            #   Name() statement
+*/
+
+// Forward declaration.
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseStream (
+  IN      AML_NODE_HEADER   * Node,
+  IN  OUT AML_STREAM        * FStream,
+  IN  OUT LIST_ENTRY        * NameSpaceRefList
+  );
+
+/** Function pointer to parse an AML construct.
+
+  The expected format of the AML construct is passed in the
+  ExpectedFormat argument. The available formats are available in
+  the AML_PARSE_FORMAT enum definition.
+
+  An object node or a data node is created in the function,
+  and returned through the OutNode parameter. This node should
+  be attached after this function returns.
+
+  @param  [in]      ParentNode      Parent node to which the parsed
+                                    AML construct will be attached.
+  @param  [in]      ExpectedFormat  Format of the AML construct to parse.
+  @param  [in, out] FStream         Forward stream containing the AML bytecode
+                                    to parse.
+                                    The stream must not be at its end.
+  @param  [out]     OutNode         Pointer holding the node created from the
+                                    parsed AML bytecode.
+
+  @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.
+**/
+typedef
+EFI_STATUS
+EFIAPI
+(*AML_PARSE_FUNCTION) (
+  IN      CONST AML_NODE_HEADER     * Node,
+  IN            AML_PARSE_FORMAT      ExpectedFormat,
+  IN  OUT       AML_STREAM          * FStream,
+      OUT       AML_NODE_HEADER    ** OutNode
+  );
+
+/** Parse a UInt<X> (where X=8, 16, 32 or 64).
+
+  A data node is created and returned through the OutNode parameter.
+
+  @param  [in]      ParentNode      Parent node to which the parsed
+                                    AML construct will be attached.
+  @param  [in]      ExpectedFormat  Format of the AML construct to parse.
+  @param  [in, out] FStream         Forward stream containing the AML bytecode
+                                    to parse.
+                                    The stream must not be at its end.
+  @param  [out]     OutNode         Pointer holding the node created from the
+                                    parsed AML bytecode.
+
+  @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseUIntX (
+  IN      CONST AML_NODE_HEADER     * ParentNode,
+  IN            AML_PARSE_FORMAT      ExpectedFormat,
+  IN  OUT       AML_STREAM          * FStream,
+      OUT       AML_NODE_HEADER    ** OutNode
+  )
+{
+  EFI_STATUS    Status;
+  UINT32        UIntXSize;
+
+  if ((!IS_AML_ROOT_NODE (ParentNode)       &&
+       !IS_AML_OBJECT_NODE (ParentNode))    ||
+      ((ExpectedFormat != EAmlUInt8)        &&
+       (ExpectedFormat != EAmlUInt16)       &&
+       (ExpectedFormat != EAmlUInt32)       &&
+       (ExpectedFormat != EAmlUInt64))      ||
+      !IS_STREAM (FStream)                  ||
+      IS_END_OF_STREAM (FStream)            ||
+      !IS_STREAM_FORWARD (FStream)          ||
+      (OutNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  switch (ExpectedFormat) {
+  case EAmlUInt8:
+    UIntXSize = 1;
+    break;
+  case EAmlUInt16:
+    UIntXSize = 2;
+    break;
+  case EAmlUInt32:
+    UIntXSize = 4;
+    break;
+  case EAmlUInt64:
+    UIntXSize = 8;
+    break;
+  default:
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = AmlCreateDataNode (
+             AmlTypeToNodeDataType (ExpectedFormat),
+             AmlStreamGetCurrPos (FStream),
+             UIntXSize,
+             (AML_DATA_NODE**)OutNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  DumpRaw (AmlStreamGetCurrPos (FStream), UIntXSize);
+
+  // Move stream forward by the size of UIntX.
+  Status = AmlStreamProgress (FStream, UIntXSize);
+  if (EFI_ERROR (Status)) {
+    AmlDeleteTree (*OutNode);
+    ASSERT (0);
+  }
+
+  return Status;
+}
+
+/** Parse an AML NameString.
+
+  A data node is created and returned through the OutNode parameter.
+
+  @param  [in]      ParentNode      Parent node to which the parsed
+                                    AML construct will be attached.
+  @param  [in]      ExpectedFormat  Format of the AML construct to parse.
+  @param  [in, out] FStream         Forward stream containing the AML bytecode
+                                    to parse.
+                                    The stream must not be at its end.
+  @param  [out]     OutNode         Pointer holding the node created from the
+                                    parsed AML bytecode.
+
+  @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseNameString (
+  IN      CONST AML_NODE_HEADER     * ParentNode,
+  IN            AML_PARSE_FORMAT      ExpectedFormat,
+  IN  OUT       AML_STREAM          * FStream,
+      OUT       AML_NODE_HEADER    ** OutNode
+  )
+{
+  EFI_STATUS                  Status;
+
+  CONST UINT8               * Buffer;
+  CONST AML_BYTE_ENCODING   * ByteEncoding;
+  UINT32                      StrSize;
+
+  if ((!IS_AML_ROOT_NODE (ParentNode)     &&
+       !IS_AML_OBJECT_NODE (ParentNode))  ||
+      (ExpectedFormat != EAmlName)        ||
+      !IS_STREAM (FStream)                ||
+      IS_END_OF_STREAM (FStream)          ||
+      !IS_STREAM_FORWARD (FStream)        ||
+      (OutNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream);
+  ByteEncoding = AmlGetByteEncoding (Buffer);
+  if ((ByteEncoding == NULL)    ||
+      ((ByteEncoding->Attribute & AML_IS_NAME_CHAR) == 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Parse the NameString.
+  Status = AmlGetNameStringSize ((CONST CHAR8*)Buffer, &StrSize);
+  if ((EFI_ERROR (Status))  ||
+      (StrSize > AmlStreamGetFreeSpace (FStream))) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = AmlCreateDataNode (
+             EAmlNodeDataTypeNameString,
+             Buffer,
+             StrSize,
+             (AML_DATA_NODE**)OutNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  DumpRaw (AmlStreamGetCurrPos (FStream), StrSize);
+
+  // Move the stream forward by StrSize.
+  Status = AmlStreamProgress (FStream, StrSize);
+  if (EFI_ERROR (Status)) {
+    AmlDeleteTree (*OutNode);
+    ASSERT (0);
+  }
+
+  return Status;
+}
+
+/** Parse an AML String.
+
+  A data node is created and returned through the OutNode parameter.
+
+  @param  [in]      ParentNode      Parent node to which the parsed
+                                    AML construct will be attached.
+  @param  [in]      ExpectedFormat  Format of the AML construct to parse.
+  @param  [in, out] FStream         Forward stream containing the AML bytecode
+                                    to parse.
+                                    The stream must not be at its end.
+  @param  [out]     OutNode         Pointer holding the node created from the
+                                    parsed AML bytecode.
+
+  @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseString (
+  IN      CONST AML_NODE_HEADER     * ParentNode,
+  IN            AML_PARSE_FORMAT      ExpectedFormat,
+  IN  OUT       AML_STREAM          * FStream,
+      OUT       AML_NODE_HEADER    ** OutNode
+  )
+{
+  EFI_STATUS      Status;
+  UINT32          StrSize;
+  UINT8           Byte;
+  CONST UINT8   * Buffer;
+
+  if ((!IS_AML_ROOT_NODE (ParentNode)     &&
+       !IS_AML_OBJECT_NODE (ParentNode))  ||
+      (ExpectedFormat != EAmlString)      ||
+      !IS_STREAM (FStream)                ||
+      IS_END_OF_STREAM (FStream)          ||
+      !IS_STREAM_FORWARD (FStream)        ||
+      (OutNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream);
+  StrSize = 0;
+  // AML String is NULL terminated.
+  do {
+    // Reading the stream moves the stream forward aswell.
+    Status = AmlStreamReadByte (FStream, &Byte);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+    StrSize++;
+  } while (Byte != '\0');
+
+  DumpRaw (Buffer, StrSize);
+
+  Status = AmlCreateDataNode (
+             AmlTypeToNodeDataType (ExpectedFormat),
+             Buffer,
+             StrSize,
+             (AML_DATA_NODE**)OutNode
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Parse an AML object.
+
+  An object can be resolved as an AML object with an OpCode,
+  or a NameString. An object node or a data node is created
+  and returned through the OutNode parameter.
+
+  @param  [in]      ParentNode      Parent node to which the parsed
+                                    AML construct will be attached.
+  @param  [in]      ExpectedFormat  Format of the AML construct to parse.
+  @param  [in, out] FStream         Forward stream containing the AML bytecode
+                                    to parse.
+                                    The stream must not be at its end.
+  @param  [out]     OutNode         Pointer holding the node created from the
+                                    parsed AML bytecode.
+
+  @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseObject (
+  IN      CONST AML_NODE_HEADER     * ParentNode,
+  IN            AML_PARSE_FORMAT      ExpectedFormat,
+  IN  OUT       AML_STREAM          * FStream,
+      OUT       AML_NODE_HEADER    ** OutNode
+  )
+{
+  EFI_STATUS                  Status;
+
+  UINT8                       OpCodeSize;
+  UINT32                      PkgLength;
+  UINT32                      PkgOffset;
+  UINT32                      FreeSpace;
+
+  CONST AML_BYTE_ENCODING   * AmlByteEncoding;
+  CONST UINT8               * Buffer;
+
+  if ((!IS_AML_ROOT_NODE (ParentNode)     &&
+       !IS_AML_OBJECT_NODE (ParentNode))  ||
+      (ExpectedFormat != EAmlObject)      ||
+      !IS_STREAM (FStream)                ||
+      IS_END_OF_STREAM (FStream)          ||
+      !IS_STREAM_FORWARD (FStream)        ||
+      (OutNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  PkgLength = 0;
+
+  // 0. Get the AML Byte encoding.
+  AmlByteEncoding = AmlGetByteEncoding (AmlStreamGetCurrPos (FStream));
+  if (AmlByteEncoding == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // 1. Check for NameString.
+  //    Indeed a NameString can be found when an AML object is expected.
+  //    e.g. VAR0 = 3         // VAR0 is assigned an object which is a UINT.
+  //         VAR1 = VAR2      // VAR2 is a NameString.
+  //    If this is a NameString, return. A NameString can be a variable, a
+  //    method invocation, etc.
+  if ((AmlByteEncoding->Attribute & AML_IS_NAME_CHAR) != 0) {
+    Status = AmlParseNameString (
+               ParentNode,
+               EAmlName,
+               FStream,
+               OutNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+    return Status;
+  }
+
+  // 2. Determine the OpCode size to move the stream forward.
+  Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream);
+  if (*Buffer == AML_EXT_OP) {
+    OpCodeSize = 2;
+  } else {
+    OpCodeSize = 1;
+  }
+  Status = AmlStreamProgress (FStream, OpCodeSize);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Print the opcode.
+  DumpRaw (Buffer, OpCodeSize);
+
+  if (!IS_END_OF_STREAM (FStream)) {
+    // 3. Parse the PkgLength field, if present.
+    if ((AmlByteEncoding->Attribute & AML_HAS_PKG_LENGTH) != 0) {
+      Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream);
+      PkgOffset = AmlGetPkgLength (Buffer, &PkgLength);
+      if (PkgOffset == 0) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      // Print the package length.
+      DumpRaw (Buffer, PkgOffset);
+
+      // Adjust the size of the stream if it is valid  package length.
+      FreeSpace = AmlStreamGetFreeSpace (FStream);
+      if (FreeSpace > PkgLength) {
+        // Reduce the stream size by (FreeSpace - PkgLength) bytes.
+        AmlStreamReduceMaxBufferSize (FStream, FreeSpace - PkgLength);
+      } else if (FreeSpace != PkgLength) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      Status = AmlStreamProgress (FStream, PkgOffset);
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+    }
+  } else if ((AmlByteEncoding->Attribute & AML_HAS_PKG_LENGTH) != 0) {
+    // The stream terminated unexpectedly. A PkgLen had to be parsed.
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // 4. Create an Object Node.
+  Status = AmlCreateObjectNode (
+             AmlByteEncoding,
+             PkgLength,
+             (AML_OBJECT_NODE**)OutNode
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Parse a FieldPkgLen.
+
+  A FieldPkgLen can only be found in a field list, i.e. in a NamedField field
+  element. The PkgLen is otherwise part of the object node structure.
+  A data node is created and returned through the OutNode parameter.
+
+  @param  [in]      ParentNode      Parent node to which the parsed
+                                    AML construct will be attached.
+  @param  [in]      ExpectedFormat  Format of the AML construct to parse.
+  @param  [in, out] FStream         Forward stream containing the AML bytecode
+                                    to parse.
+                                    The stream must not be at its end.
+  @param  [out]     OutNode         Pointer holding the node created from the
+                                    parsed AML bytecode.
+
+  @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseFieldPkgLen (
+  IN      CONST AML_NODE_HEADER     * ParentNode,
+  IN            AML_PARSE_FORMAT      ExpectedFormat,
+  IN  OUT       AML_STREAM          * FStream,
+      OUT       AML_NODE_HEADER    ** OutNode
+  )
+{
+  EFI_STATUS      Status;
+  EFI_STATUS      Status1;
+  CONST UINT8   * Buffer;
+  UINT32          PkgOffset;
+  UINT32          PkgLength;
+
+  if (!AmlNodeHasAttribute (
+         (CONST AML_OBJECT_NODE*)ParentNode,
+         AML_IS_FIELD_ELEMENT
+         )                                ||
+      (ExpectedFormat != EAmlFieldPkgLen) ||
+      !IS_STREAM (FStream)                ||
+      IS_END_OF_STREAM (FStream)          ||
+      !IS_STREAM_FORWARD (FStream)        ||
+      (OutNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream);
+
+  PkgOffset = AmlGetPkgLength (Buffer, &PkgLength);
+  if (PkgOffset == 0) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Warning: Since, updating of field elements is not supported, store the
+  // FieldPkgLength in a Data Node as a raw buffer.
+  Status = AmlCreateDataNode (
+             AmlTypeToNodeDataType (ExpectedFormat),
+             Buffer,
+             PkgOffset,
+             (AML_DATA_NODE**)OutNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  DumpRaw (Buffer, PkgOffset);
+
+  Status = AmlStreamProgress (FStream, PkgOffset);
+  if (EFI_ERROR (Status)) {
+    Status1 = AmlDeleteNode (*OutNode);
+    ASSERT_EFI_ERROR (Status1);
+    ASSERT (0);
+  }
+
+  return Status;
+}
+
+/** Array of functions pointers to parse the AML constructs.
+
+  The AML Byte encoding tables in Aml.c describe the format of the AML
+  statements. The AML_PARSE_FORMAT enum definition lists these constructs
+  and the corresponding parsing functions.
+*/
+AML_PARSE_FUNCTION mParseType[EAmlParseFormatMax] = {
+  NULL,                    // EAmlNone
+  AmlParseUIntX,           // EAmlUInt8
+  AmlParseUIntX,           // EAmlUInt16
+  AmlParseUIntX,           // EAmlUInt32
+  AmlParseUIntX,           // EAmlUInt64
+  AmlParseObject,          // EAmlObject
+  AmlParseNameString,      // EAmlName
+  AmlParseString,          // EAmlString
+  AmlParseFieldPkgLen      // EAmlFieldPkgLen
+};
+
+/** Check whether the NameString stored in the data node is a method invocation.
+    If so, create a method invocation node and return it.
+
+  @param  [in]      ParentNode        Node to which the parsed AML construct
+                                      will be attached.
+  @param  [in]      DataNode          Data node containing a NameString,
+                                      potentially being a method invocation.
+  @param  [in, out] NameSpaceRefList  List of namespace reference nodes.
+  @param  [out]     OutNode           Pointer holding the method invocation
+                                      node if the NameString contained in the
+                                      data node is a method invocation.
+                                      NULL otherwise.
+
+  @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlCheckAndParseMethodInvoc (
+  IN  CONST AML_NODE_HEADER     * ParentNode,
+  IN        AML_DATA_NODE       * DataNode,
+  IN  OUT   LIST_ENTRY          * NameSpaceRefList,
+      OUT   AML_OBJECT_NODE    ** OutNode
+  )
+{
+  EFI_STATUS                Status;
+  AML_NAMESPACE_REF_NODE  * NameSpaceRefNode;
+  AML_OBJECT_NODE         * MethodInvocationNode;
+  AML_STREAM                FStream;
+
+  if ((!IS_AML_ROOT_NODE (ParentNode)                     &&
+       !IS_AML_OBJECT_NODE (ParentNode))                  ||
+      !IS_AML_DATA_NODE (DataNode)                        ||
+      (DataNode->DataType != EAmlNodeDataTypeNameString)  ||
+      (NameSpaceRefList == NULL)                          ||
+      (OutNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Initialize a stream containing the NameString which is checked.
+  Status = AmlStreamInit (
+             &FStream,
+             DataNode->Buffer,
+             DataNode->Size,
+             EAmlStreamDirectionForward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Check whether the NameString is a method invocation.
+  NameSpaceRefNode = NULL;
+  Status = AmlIsMethodInvocation (
+              ParentNode,
+              &FStream,
+              NameSpaceRefList,
+              &NameSpaceRefNode
+              );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  MethodInvocationNode = NULL;
+  if (NameSpaceRefNode != NULL) {
+    // A matching method definition has been found.
+    // Create a method invocation node.
+    Status = AmlCreateMethodInvocationNode (
+               NameSpaceRefNode,
+               (AML_DATA_NODE*)DataNode,
+               &MethodInvocationNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  }
+
+  *OutNode = MethodInvocationNode;
+
+  return EFI_SUCCESS;
+}
+
+/** Call the appropriate function to parse the AML construct in the stream.
+
+  The ExpectedFormat parameter allows to choose the right parsing function.
+  An object node or a data node is created according to format.
+
+  @param  [in]      ParentNode        Node to which the parsed AML construct
+                                      will be attached.
+  @param  [in]      ExpectedFormat    Format of the AML construct to parse.
+  @param  [in, out] FStream           Forward stream containing the AML
+                                      bytecode to parse.
+                                      The stream must not be at its end.
+  @param  [in, out] NameSpaceRefList  List of namespace reference nodes.
+  @param  [out]     OutNode           Pointer holding the node created from the
+                                      parsed AML bytecode.
+
+  @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseArgument (
+  IN      CONST AML_NODE_HEADER     * ParentNode,
+  IN            AML_PARSE_FORMAT      ExpectedFormat,
+  IN  OUT       AML_STREAM          * FStream,
+  IN  OUT       LIST_ENTRY          * NameSpaceRefList,
+      OUT       AML_NODE_HEADER    ** OutNode
+  )
+{
+  EFI_STATUS                Status;
+  AML_PARSE_FUNCTION        ParsingFunction;
+  AML_DATA_NODE           * DataNode;
+  AML_OBJECT_NODE         * MethodInvocationNode;
+
+  if ((!IS_AML_ROOT_NODE (ParentNode)         &&
+       !IS_AML_OBJECT_NODE (ParentNode))      ||
+      (ExpectedFormat >= EAmlParseFormatMax)  ||
+      !IS_STREAM (FStream)                    ||
+      IS_END_OF_STREAM (FStream)              ||
+      !IS_STREAM_FORWARD (FStream)            ||
+      (NameSpaceRefList == NULL)              ||
+      (OutNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ParsingFunction = mParseType[ExpectedFormat];
+  if (ParsingFunction == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Note: The ParsingFunction moves the stream forward as it
+  // consumes the AML bytecode
+  Status = ParsingFunction (
+             ParentNode,
+             ExpectedFormat,
+             FStream,
+             OutNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Check whether the parsed argument is a NameString when an object
+  // is expected. In such case, it could be a method invocation.
+  DataNode = (AML_DATA_NODE*)*OutNode;
+  if (IS_AML_DATA_NODE (DataNode)                         &&
+      (DataNode->DataType == EAmlNodeDataTypeNameString)  &&
+      (ExpectedFormat == EAmlObject)) {
+    Status = AmlCheckAndParseMethodInvoc (
+               ParentNode,
+               (AML_DATA_NODE*)*OutNode,
+               NameSpaceRefList,
+               &MethodInvocationNode);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // A method invocation node has been created and the DataNode containing
+    // the NameString has been attached to the MethodInvocationNode.
+    // Replace the OutNode with the MethodInvocationNode.
+    if (MethodInvocationNode != NULL) {
+      *OutNode = (AML_NODE_HEADER*)MethodInvocationNode;
+    }
+  }
+
+  return Status;
+}
+
+/** Parse the Bytelist in the stream.
+    According to the content of the stream, create data node(s)
+    and add them to the variable list of arguments.
+    The byte list may be a list of resource data element or a simple byte list.
+
+  @param  [in]  BufferNode    Object node having a byte list.
+  @param  [in, out] FStream   Forward stream containing the AML bytecode
+                              to parse.
+                              The stream must not be at its end.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Could not allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseByteList (
+  IN      AML_OBJECT_NODE   * BufferNode,
+  IN  OUT AML_STREAM        * FStream
+  )
+{
+  EFI_STATUS          Status;
+  AML_NODE_HEADER   * NewNode;
+  CONST UINT8       * Buffer;
+  UINT32              BufferSize;
+
+  // Check whether the node is an Object Node and has byte list.
+  if (!AmlNodeHasAttribute (BufferNode, AML_HAS_BYTE_LIST)  ||
+      !IS_STREAM (FStream)                                  ||
+      IS_END_OF_STREAM (FStream)                            ||
+      !IS_STREAM_FORWARD (FStream)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // The buffer contains a list of resource data elements.
+  if (AmlRdIsResourceDataBuffer (FStream)) {
+    // Parse the resource data elements and add them as data nodes.
+    // AmlParseResourceData() moves the stream forward.
+    Status = AmlParseResourceData (BufferNode, FStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+  } else {
+    // The buffer doesn't contain a list of resource data elements.
+    // Create a single node holding the whole buffer data.
+
+    // CreateDataNode checks the Buffer and BufferSize values.
+    Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream);
+    BufferSize = AmlStreamGetFreeSpace (FStream);
+
+    Status = AmlCreateDataNode (
+               EAmlNodeDataTypeRaw,
+               Buffer,
+               BufferSize,
+               (AML_DATA_NODE**)&NewNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    Status = AmlVarListAddTailInternal (
+                (AML_NODE_HEADER*)BufferNode,
+                NewNode
+                );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      AmlDeleteTree (NewNode);
+      return Status;
+    }
+
+    DumpRaw (Buffer, BufferSize);
+
+    // Move the stream forward as we have consumed the Buffer.
+    Status = AmlStreamProgress (FStream, BufferSize);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+  }
+
+  return Status;
+}
+
+/** Parse the list of fixed arguments of the input ObjectNode.
+
+  For each argument, create a node and add it to the fixed argument list
+  of the Node.
+  If a fixed argument has children, parse them.
+
+  @param  [in]  ObjectNode        Object node to parse the fixed arguments
+                                  from.
+  @param  [in]  FStream           Forward stream containing the AML
+                                  bytecode to parse.
+                                  The stream must not be at its end.
+  @param  [in]  NameSpaceRefList  List of namespace reference nodes.
+
+  @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
+AmlParseFixedArguments (
+  IN  AML_OBJECT_NODE   * ObjectNode,
+  IN  AML_STREAM        * FStream,
+  IN  LIST_ENTRY        * NameSpaceRefList
+  )
+{
+  EFI_STATUS                Status;
+
+  AML_NODE_HEADER         * FixedArgNode;
+  AML_STREAM                FixedArgFStream;
+
+  EAML_PARSE_INDEX          TermIndex;
+  EAML_PARSE_INDEX          MaxIndex;
+  CONST AML_PARSE_FORMAT  * Format;
+
+  // Fixed arguments of method invocations node are handled differently.
+  if (!IS_AML_OBJECT_NODE (ObjectNode)                              ||
+      AmlNodeCompareOpCode (ObjectNode, AML_METHOD_INVOC_OP, 0)     ||
+      !IS_STREAM (FStream)                                          ||
+      IS_END_OF_STREAM (FStream)                                    ||
+      !IS_STREAM_FORWARD (FStream)                                  ||
+      (NameSpaceRefList == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  TermIndex = EAmlParseIndexTerm0;
+  MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
+                                 (AML_OBJECT_NODE*)ObjectNode
+                                 );
+  if ((ObjectNode->AmlByteEncoding != NULL)   &&
+      (ObjectNode->AmlByteEncoding->Format != NULL)) {
+    Format = ObjectNode->AmlByteEncoding->Format;
+  } else {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Parse all the FixedArgs.
+  while ((TermIndex < MaxIndex)       &&
+         !IS_END_OF_STREAM (FStream)  &&
+         (Format[TermIndex] != EAmlNone)) {
+    // Initialize a FixedArgStream to parse the current fixed argument.
+    Status = AmlStreamInitSubStream (FStream, &FixedArgFStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Parse the current fixed argument.
+    Status = AmlParseArgument (
+               (CONST AML_NODE_HEADER*)ObjectNode,
+               Format[TermIndex],
+               &FixedArgFStream,
+               NameSpaceRefList,
+               &FixedArgNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Add the fixed argument to the parent node's fixed argument list.
+    // FixedArgNode can be an object or data node.
+    Status = AmlSetFixedArgument (
+               (AML_OBJECT_NODE*)ObjectNode,
+               TermIndex,
+               FixedArgNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      // Delete the sub-tree if the insertion failed.
+      // Otherwise its reference will be lost.
+      // Use DeleteTree because if the argument was a method invocation,
+      // multiple nodes have been created.
+      AmlDeleteTree (FixedArgNode);
+      return Status;
+    }
+
+    // Parse the AML bytecode of the FixedArgNode if this is an object node.
+    if (IS_AML_OBJECT_NODE (FixedArgNode) &&
+        !IS_END_OF_STREAM (&FixedArgFStream)) {
+      Status = AmlParseStream (
+                 FixedArgNode,
+                 &FixedArgFStream,
+                 NameSpaceRefList
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+    }
+
+    // Move the stream forward as we have consumed the sub-stream.
+    Status = AmlStreamProgress (
+               FStream,
+               AmlStreamGetIndex (&FixedArgFStream)
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    TermIndex++;
+  } // while
+
+  return EFI_SUCCESS;
+}
+
+/** Parse the variable list of arguments of the input ObjectNode.
+
+  For each variable argument, create a node and add it to the variable list of
+  arguments of the Node.
+  If a variable argument has children, parse them recursively.
+
+  The arguments of method invocation nodes are added to the variable list of
+  arguments of the method invocation node. It is necessary to first get
+  the number of arguments to parse for this kind of node. A method invocation
+  can have at most 7 fixed arguments.
+
+  @param  [in]  Node              Node to parse the variable arguments
+                                  from.
+  @param  [in]  FStream           Forward stream containing the AML
+                                  bytecode to parse.
+                                  The stream must not be at its end.
+  @param  [in]  NameSpaceRefList  List of namespace reference nodes.
+
+  @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
+AmlParseVariableArguments (
+  IN  AML_NODE_HEADER   * Node,
+  IN  AML_STREAM        * FStream,
+  IN  LIST_ENTRY        * NameSpaceRefList
+  )
+{
+  EFI_STATUS                Status;
+
+  BOOLEAN                   IsMethodInvocation;
+  UINT8                     MethodInvocationArgCount;
+
+  AML_NODE_HEADER         * VarArgNode;
+  AML_STREAM                VarArgFStream;
+
+  if ((!AmlNodeHasAttribute (
+          (CONST AML_OBJECT_NODE*)Node,
+          AML_HAS_CHILD_OBJ
+          ) &&
+       !IS_AML_ROOT_NODE (Node))        ||
+      !IS_STREAM (FStream)              ||
+      IS_END_OF_STREAM (FStream)        ||
+      !IS_STREAM_FORWARD (FStream)      ||
+      (NameSpaceRefList == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = AmlGetMethodInvocationArgCount (
+             (CONST AML_OBJECT_NODE*)Node,
+             &IsMethodInvocation,
+             &MethodInvocationArgCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Parse variable arguments while the Stream is not empty.
+  while (!IS_END_OF_STREAM (FStream)) {
+    // If the number of variable arguments are counted, decrement the counter.
+    if ((IsMethodInvocation) && (MethodInvocationArgCount-- == 0)) {
+      return EFI_SUCCESS;
+    }
+
+    // Initialize a VarArgStream to parse the current variable argument.
+    Status = AmlStreamInitSubStream (FStream, &VarArgFStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Parse the current variable argument.
+    Status = AmlParseArgument (
+               Node,
+               EAmlObject,
+               &VarArgFStream,
+               NameSpaceRefList,
+               &VarArgNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Add the variable argument to its parent variable list of arguments.
+    // VarArgNode can be an object or data node.
+    Status = AmlVarListAddTailInternal (
+               (AML_NODE_HEADER*)Node,
+               VarArgNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      // Delete the sub-tree if the insertion failed.
+      // Otherwise its reference will be lost.
+      // Use DeleteTree because if the argument was a method invocation,
+      // multiple nodes have been created.
+      AmlDeleteTree (VarArgNode);
+      return Status;
+    }
+
+    // Parse the AML bytecode of the VarArgNode if this is an object node.
+    if (IS_AML_OBJECT_NODE (VarArgNode)       &&
+        (!IS_END_OF_STREAM (&VarArgFStream))) {
+      Status = AmlParseStream (VarArgNode, &VarArgFStream, NameSpaceRefList);
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+    }
+
+    // Move the stream forward as we have consumed the sub-stream.
+    Status = AmlStreamProgress (
+               FStream,
+               AmlStreamGetIndex (&VarArgFStream)
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  } // while
+
+  // If the number of variable arguments are counted, check all the
+  // MethodInvocationArgCount have been parsed.
+  if (IsMethodInvocation && (MethodInvocationArgCount != 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return Status;
+}
+
+/** Parse the AML stream and populate the root node.
+
+  @param  [in]      RootNode          RootNode to which the children are
+                                      added.
+  @param  [in, out] FStream           Forward stream containing the AML
+                                      bytecode to parse.
+                                      The stream must not be at its end.
+  @param  [in, out] NameSpaceRefList  List of namespace reference nodes.
+
+  @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.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlPopulateRootNode (
+  IN      AML_ROOT_NODE     * RootNode,
+  IN  OUT AML_STREAM        * FStream,
+  IN  OUT LIST_ENTRY        * NameSpaceRefList
+  )
+{
+  EFI_STATUS      Status;
+
+  if (!IS_AML_ROOT_NODE (RootNode)  ||
+      !IS_STREAM (FStream)          ||
+      IS_END_OF_STREAM (FStream)    ||
+      !IS_STREAM_FORWARD (FStream)  ||
+      (NameSpaceRefList == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // A Root Node only has variable arguments.
+  Status = AmlParseVariableArguments (
+             (AML_NODE_HEADER*)RootNode,
+             FStream,
+             NameSpaceRefList
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Parse the AML stream an populate the object node.
+
+  @param  [in]      ObjectNode        ObjectNode to which the children are
+                                      added.
+  @param  [in, out] FStream           Forward stream containing the AML
+                                      bytecode to parse.
+                                      The stream must not be at its end.
+  @param  [in, out] NameSpaceRefList  List of namespace reference nodes.
+
+  @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.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlPopulateObjectNode (
+  IN      AML_OBJECT_NODE   * ObjectNode,
+  IN  OUT AML_STREAM        * FStream,
+  IN  OUT LIST_ENTRY        * NameSpaceRefList
+  )
+{
+  EFI_STATUS      Status;
+
+  if (!IS_AML_OBJECT_NODE (ObjectNode)  ||
+      !IS_STREAM (FStream)              ||
+      IS_END_OF_STREAM (FStream)        ||
+      !IS_STREAM_FORWARD (FStream)      ||
+      (NameSpaceRefList == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = EFI_SUCCESS;
+
+  // Don't parse the fixed arguments of method invocation nodes.
+  // The AML encoding for method invocations in the ACPI specification 6.3 is:
+  // MethodInvocation := NameString TermArgList
+  // Since the AML specification does not define an OpCode for method
+  // invocation, this AML parser defines a pseudo opcode and redefines the
+  // grammar for simplicity as:
+  // MethodInvocation := MethodInvocationOp NameString ArgumentCount TermArgList
+  // ArgumentCount    := ByteData
+  // Due to this difference, the MethodInvocationOp and the fixed argument
+  // i.e. ArgumentCount is not available in the AML stream and need to be
+  // handled differently.
+  if (!AmlNodeCompareOpCode (ObjectNode, AML_METHOD_INVOC_OP, 0)) {
+    // Parse the fixed list of arguments.
+    Status = AmlParseFixedArguments (
+               ObjectNode,
+               FStream,
+               NameSpaceRefList
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  }
+
+  // Save the association [node reference/pathname] in the NameSpaceRefList.
+  // This allows to identify method invocations from other namespace
+  // paths. Method invocation need to be parsed differently.
+  if (AmlNodeHasAttribute (
+         (CONST AML_OBJECT_NODE*)ObjectNode,
+         AML_IN_NAMESPACE)) {
+    Status = AmlAddNameSpaceReference (
+               (CONST AML_OBJECT_NODE*)ObjectNode,
+               NameSpaceRefList
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  }
+
+  if (!IS_END_OF_STREAM (FStream)) {
+    // Parse the variable list of arguments if present.
+    if (AmlNodeHasAttribute (ObjectNode, AML_HAS_CHILD_OBJ)) {
+      Status = AmlParseVariableArguments (
+                (AML_NODE_HEADER*)ObjectNode,
+                FStream,
+                NameSpaceRefList
+                );
+    } else if (AmlNodeHasAttribute (ObjectNode, AML_HAS_BYTE_LIST)) {
+      // Parse the byte list if present.
+      Status = AmlParseByteList (
+                ObjectNode,
+                FStream
+                );
+    } else if (AmlNodeHasAttribute (ObjectNode, AML_HAS_FIELD_LIST)) {
+      // Parse the field list if present.
+      Status = AmlParseFieldList (
+                ObjectNode,
+                FStream,
+                NameSpaceRefList
+                );
+    }
+
+    // Check status and assert
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+  }
+
+  return Status;
+}
+
+/** Invoke the appropriate parsing functions based on the Node type.
+
+  @param  [in]      Node              Node from which the children are parsed.
+                                      Must be a root node or an object node.
+  @param  [in]      FStream           Forward stream containing the AML
+                                      bytecode to parse.
+                                      The stream must not be at its end.
+  @param  [in]      NameSpaceRefList  List of namespace reference nodes.
+
+  @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.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseStream (
+  IN  AML_NODE_HEADER   * Node,
+  IN  AML_STREAM        * FStream,
+  IN  LIST_ENTRY        * NameSpaceRefList
+  )
+{
+  EFI_STATUS    Status;
+
+  if (IS_AML_ROOT_NODE (Node)) {
+    Status = AmlPopulateRootNode (
+               (AML_ROOT_NODE*)Node,
+               FStream,
+               NameSpaceRefList
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+
+  } else if (IS_AML_OBJECT_NODE (Node)) {
+    Status = AmlPopulateObjectNode (
+               (AML_OBJECT_NODE*)Node,
+               FStream,
+               NameSpaceRefList
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+
+  } else {
+    // Data node or other.
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+  }
+
+  return Status;
+}
+
+/** Parse the definition block.
+
+  This 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.
+
+  @param  [in]  DefinitionBlock   Pointer to the definition block.
+  @param  [out] RootPtr           Pointer to the root node of the 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                ** RootPtr
+  )
+{
+  EFI_STATUS              Status;
+  EFI_STATUS              Status1;
+  AML_STREAM              Stream;
+  AML_ROOT_NODE         * Root;
+
+  LIST_ENTRY              NameSpaceRefList;
+
+  UINT8                 * Buffer;
+  UINT32                  MaxBufferSize;
+
+  if ((DefinitionBlock == NULL)   ||
+      (RootPtr == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Buffer = (UINT8*)DefinitionBlock + sizeof (EFI_ACPI_DESCRIPTION_HEADER);
+  if (DefinitionBlock->Length < sizeof (EFI_ACPI_DESCRIPTION_HEADER)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+  MaxBufferSize = DefinitionBlock->Length -
+                    (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER);
+
+  // Create a root node.
+  Status = AmlCreateRootNode (
+             (EFI_ACPI_DESCRIPTION_HEADER*)DefinitionBlock,
+             &Root
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  *RootPtr = Root;
+
+  if (MaxBufferSize == 0) {
+    return EFI_SUCCESS;
+  }
+
+  // Initialize a stream to parse the AML bytecode.
+  Status = AmlStreamInit (
+             &Stream,
+             Buffer,
+             MaxBufferSize,
+             EAmlStreamDirectionForward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto error_handler;
+  }
+
+  // Initialize the NameSpaceRefList, holding references to nodes declaring
+  // a name in the AML namespace.
+  InitializeListHead (&NameSpaceRefList);
+
+  // Parse the whole AML blob.
+  Status = AmlParseStream (
+             (AML_NODE_HEADER*)Root,
+             &Stream,
+             &NameSpaceRefList
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto error_handler;
+  }
+
+  // Check the whole AML blob has been parsed.
+  if (!IS_END_OF_STREAM (&Stream)) {
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+    goto error_handler;
+  }
+
+  // Print the list of NameSpace reference nodes.
+  // AmlDbgPrintNameSpaceRefList (&NameSpaceRefList);
+
+  // Delete the NameSpaceRefList
+  goto exit_handler;
+
+error_handler:
+  if (Root != NULL) {
+    AmlDeleteTree ((AML_NODE_HEADER*)Root);
+  }
+
+exit_handler:
+  Status1 = AmlDeleteNameSpaceRefList (&NameSpaceRefList);
+  if (EFI_ERROR (Status1)) {
+    ASSERT (0);
+    if (!EFI_ERROR (Status)) {
+      return Status1;
+    }
+  }
+
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..096a9596e161848bf5786f87a736946aee69d4e3
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h
@@ -0,0 +1,72 @@
+/** @file
+  AML Parser.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_PARSER_H_
+#define AML_PARSER_H_
+
+#include <AmlNodeDefines.h>
+#include <Stream/AmlStream.h>
+
+/** Parse the list of fixed arguments of the input ObjectNode.
+
+  For each argument, create a node and add it to the fixed argument list
+  of the Node.
+  If a fixed argument has children, parse them.
+
+  @param  [in]  ObjectNode        Object node to parse the fixed arguments
+                                  from.
+  @param  [in]  FStream           Forward stream containing the AML
+                                  bytecode to parse.
+                                  The stream must not be at its end.
+  @param  [in]  NameSpaceRefList  List of namespace reference nodes.
+
+  @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
+AmlParseFixedArguments (
+  IN  AML_OBJECT_NODE   * ObjectNode,
+  IN  AML_STREAM        * FStream,
+  IN  LIST_ENTRY        * NameSpaceRefList
+  );
+
+/** Parse the variable list of arguments of the input ObjectNode.
+
+  For each variable argument, create a node and add it to the variable list of
+  arguments of the Node.
+  If a variable argument has children, parse them recursively.
+
+  The arguments of method invocation nodes are added to the variable list of
+  arguments of the method invocation node. It is necessary to first get
+  the number of arguments to parse for this kind of node. A method invocation
+  can have at most 7 fixed arguments.
+
+  @param  [in]  Node              Node to parse the variable arguments
+                                  from.
+  @param  [in]  FStream           Forward stream containing the AML
+                                  bytecode to parse.
+                                  The stream must not be at its end.
+  @param  [in]  NameSpaceRefList  List of namespace reference nodes.
+
+  @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
+AmlParseVariableArguments (
+  IN  AML_NODE_HEADER   * Node,
+  IN  AML_STREAM        * FStream,
+  IN  LIST_ENTRY        * NameSpaceRefList
+  );
+
+#endif // AML_PARSER_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 17/30] DynamicTablesPkg: AML resource data helper
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (15 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 16/30] DynamicTablesPkg: AML Parser Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 18/30] DynamicTablesPkg: AML resource data parser Sami Mujawar
                   ` (13 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

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. To simplify
operations on resource data, the resource data parser
examines the Bytelist to detect the presence of resource
data. If the data matches the encoding of resource
data type(s), the parser fragments the resource data
buffer into resource data elements (data nodes) and
stores them in the variable arguments list of the
BufferOp node.

The resource data helper provides functions and macros
to assist operations on resource data elements.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c | 103 ++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h | 174 ++++++++++++++++++++
 2 files changed, 277 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c b/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c
new file mode 100644
index 0000000000000000000000000000000000000000..8b46c7232df3bb7d49e5faa1362a485a6a413198
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c
@@ -0,0 +1,103 @@
+/** @file
+  AML Resource Data.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+  - Rd or RD   - Resource Data
+  - Rds or RDS - Resource Data Small
+  - Rdl or RDL - Resource Data Large
+**/
+
+#include <ResourceData/AmlResourceData.h>
+
+/** Check whether the resource data has the input descriptor Id.
+
+  The small/large bit is included in the descriptor Id,
+  but the size bits are not included for small resource data elements.
+
+  @param  [in]  Header        Pointer to the first byte of a resource data
+                              element.
+  @param  [in]  DescriptorId  The descriptor to check against.
+
+  @retval TRUE    The resource data has the descriptor Id.
+  @retval FALSE   Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlRdCompareDescId (
+  IN  CONST AML_RD_HEADER   * Header,
+  IN        AML_RD_HEADER     DescriptorId
+  )
+{
+  if (Header == NULL) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  if (AML_RD_IS_LARGE (Header)) {
+    return ((*Header ^ DescriptorId) == 0);
+  } else {
+    return (((*Header & AML_RD_SMALL_ID_MASK) ^ DescriptorId) == 0);
+  }
+}
+
+/** Get the descriptor Id of the resource data.
+
+  The small/large bit is included in the descriptor Id,
+  but the size bits are not included for small resource data elements.
+
+  @param  [in]  Header  Pointer to the first byte of a resource data.
+
+  @return A descriptor Id.
+**/
+AML_RD_HEADER
+EFIAPI
+AmlRdGetDescId (
+  IN  CONST AML_RD_HEADER   * Header
+  )
+{
+  if (Header == NULL) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  if (AML_RD_IS_LARGE (Header)) {
+    return *Header;
+  }
+
+  // Header is a small resource data element.
+  return *Header & AML_RD_SMALL_ID_MASK;
+}
+
+/** Get the size of a resource data element.
+
+  If the resource data element is of the large type, the Header
+  is expected to be at least 3 bytes long.
+
+  @param  [in]  Header  Pointer to the first byte of a resource data.
+
+  @return The size of the resource data element.
+**/
+UINT32
+EFIAPI
+AmlRdGetSize (
+  IN  CONST AML_RD_HEADER   * Header
+  )
+{
+  if (Header == NULL) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  if (AML_RD_IS_LARGE (Header)) {
+    return ((ACPI_LARGE_RESOURCE_HEADER*)Header)->Length +
+             sizeof (ACPI_LARGE_RESOURCE_HEADER);
+  }
+
+  // Header is a small resource data element.
+  return ((ACPI_SMALL_RESOURCE_HEADER*)Header)->Bits.Length +
+           sizeof (ACPI_SMALL_RESOURCE_HEADER);
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h b/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h
new file mode 100644
index 0000000000000000000000000000000000000000..48e4e2aaddb47e3847cb896e9bc64d4c68bda9f4
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h
@@ -0,0 +1,174 @@
+/** @file
+  AML Resource Data.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+  - Rd or RD   - Resource Data
+  - Rds or RDS - Resource Data Small
+  - Rdl or RDL - Resource Data Large
+**/
+
+#ifndef AML_RESOURCE_DATA_H_
+#define AML_RESOURCE_DATA_H_
+
+/* This header file does not include internal Node definition,
+   i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions
+   must be included by the caller file. The function prototypes must
+   only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node
+   definitions.
+   This allows to keep the functions defined here both internal and
+   potentially external. If necessary, any function of this file can
+   be exposed externally.
+   The Api folder is internal to the AmlLib, but should only use these
+   functions. They provide a "safe" way to interact with the AmlLib.
+*/
+
+#include <AmlInclude.h>
+#include <IndustryStandard/Acpi.h>
+
+/**
+  @defgroup ResourceDataLibrary Resource data library
+  @ingroup AMLLib
+  @{
+    Resource data are defined in the ACPI 6.3 specification,
+    s6.4 "Resource Data Types for ACPI". They can be created in ASL via the
+    ResourceTemplate () statement, cf s19.3.3 "ASL Resource Templates".
+
+    Resource data can be of the small or large type. The difference between
+    small and large resource data elements is their encoding.
+
+    Resource data are stored in the variable list of arguments of object
+    nodes.
+  @}
+*/
+
+/** Resource Descriptor header for Small/Large Resource Data Object.
+    This is the first byte of a Small/Large Resource Data element.
+
+  Can be a ACPI_SMALL_RESOURCE_HEADER or ACPI_LARGE_RESOURCE_HEADER.
+
+  @ingroup ResourceDataStructures
+*/
+typedef UINT8 AML_RD_HEADER;
+
+/** Mask for the small resource data size.
+
+  @ingroup ResourceDataStructures
+*/
+#define AML_RD_SMALL_SIZE_MASK    (0x7U)
+
+/** Mask for the small resource data ID.
+
+  @ingroup ResourceDataStructures
+*/
+#define AML_RD_SMALL_ID_MASK      (0xFU << 3)
+
+/** Mask for the large resource data ID.
+
+  @ingroup ResourceDataStructures
+*/
+#define AML_RD_LARGE_ID_MASK      (0x7FU)
+
+/**
+  @defgroup ResourceDataApis Resource data APIs
+  @ingroup ResourceDataLibrary
+  @{
+    Resource data APIs allow to manipulate/decode resource data elements.
+  @}
+*/
+
+/** Check whether a resource data is of the large type.
+
+  @ingroup ResourceDataApis
+
+  @param  [in]  Header  Pointer to the first byte of a resource data.
+
+  @retval TRUE  If the resource data is of the large type.
+  @retval FALSE Otherwise.
+**/
+#define AML_RD_IS_LARGE(Header)                                               \
+          (((ACPI_SMALL_RESOURCE_HEADER*)Header)->Bits.Type ==                \
+          ACPI_LARGE_ITEM_FLAG)
+
+/** Build a small resource data descriptor Id.
+    The small/large bit is included in the descriptor Id,
+    but the size bits are not included.
+
+  @ingroup ResourceDataApis
+
+  @param  [in]  Id  Descriptor Id.
+
+  @return A descriptor Id.
+**/
+#define AML_RD_BUILD_SMALL_DESC_ID(Id)  ((AML_RD_HEADER)((Id & 0xF) << 3))
+
+/** Build a large resource data descriptor Id.
+    The small/large bit is included in the descriptor Id.
+
+  @ingroup ResourceDataApis
+
+  @param  [in]  Id  Id of the descriptor.
+
+  @return A descriptor Id.
+**/
+#define AML_RD_BUILD_LARGE_DESC_ID(Id)  ((AML_RD_HEADER)((BIT7) | Id))
+
+/** Check whether the resource data has the input descriptor Id.
+
+  The small/large bit is included in the descriptor Id,
+  but the size bits are not included for small resource data elements.
+
+  @ingroup ResourceDataApis
+
+  @param  [in]  Header        Pointer to the first byte of a resource data
+                              element.
+  @param  [in]  DescriptorId  The descriptor to check against.
+
+  @retval TRUE    The resource data has the descriptor Id.
+  @retval FALSE   Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlRdCompareDescId (
+  IN  CONST AML_RD_HEADER   * Header,
+  IN        AML_RD_HEADER     DescriptorId
+  );
+
+/** Get the descriptor Id of the resource data.
+
+  The small/large bit is included in the descriptor Id,
+  but the size bits are not included for small resource data elements.
+
+  @ingroup ResourceDataApis
+
+  @param  [in]  Header  Pointer to the first byte of a resource data.
+
+  @return A descriptor Id.
+**/
+AML_RD_HEADER
+EFIAPI
+AmlRdGetDescId (
+  IN  CONST AML_RD_HEADER   * Header
+  );
+
+/** Get the size of a resource data element.
+
+  If the resource data element is of the large type, the Header
+  is expected to be at least 3 bytes long.
+
+  @ingroup ResourceDataApis
+
+  @param  [in]  Header  Pointer to the first byte of a resource data.
+
+  @return The size of the resource data element.
+**/
+UINT32
+EFIAPI
+AmlRdGetSize (
+  IN  CONST AML_RD_HEADER   * Header
+  );
+
+#endif // AML_RESOURCE_DATA_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 18/30] DynamicTablesPkg: AML resource data parser
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (16 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 17/30] DynamicTablesPkg: AML resource data helper Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 19/30] DynamicTablesPkg: AML Method parser Sami Mujawar
                   ` (12 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

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>
---
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c | 328 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h |  71 +++++
 2 files changed, 399 insertions(+)

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


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 19/30] DynamicTablesPkg: AML Method parser
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (17 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 18/30] DynamicTablesPkg: AML resource data parser Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 20/30] DynamicTablesPkg: AML Field list parser Sami Mujawar
                   ` (11 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

The AML language allows a Definition Block to implement
methods that an Operating System can invoke at runtime.

Although Dynamic AML does not provide interfaces to
modify AML methods; an AML template code may contain
methods and/or method invocations.

Method definitions have an opcode defined in the AML
encoding and can be easily parsed. However, the language
does not define an opcode for method invocation. Method
invocations are represented as a NameString followed by
the arguments to the method. This poses a significant
challenge for the AML parser as it has to determine if
a NameString appearing in the AML byte stream is a method
invocation and if it is a method invocation, then how
many arguments follow.

This also means the Method definition must occur prior to
the method invocation in the AML byte stream. This is a
hard requirement for the AML parser.

The AML method parser maintains a NameSpaceRefList that
keeps a track of every namespace node and its raw AML
absolute path. The AmlIsMethodInvocation() searches the
NameSpaceRefList to determine if a NameString matches
a Method definition.

A pseudo opcode has been defined in the AML encoding to
represent the Method invocation in the AML tree.

The AML encoding for method invocations in the ACPI
specification 6.3 is:
    MethodInvocation := NameString TermArgList

The AmlLib library redefines this as:
    MethodInvocation := MethodInvocationOp NameString
                          ArgumentCount TermArgList
    ArgumentCount    := ByteData

    Where MethodInvocationOp is the pseudo opcode and
    ArgumentCount is the number of arguments passed to
    the method.

NOTE:
  The AmlLib library's definition for a method
  invocation only applies to the representation
  of method invocation node in the AML tree.
  When computing the size of a tree or serialising
  it, the additional data is not taken into account
  i.e. the MethodInvocationOp and the ArgumentCount
  are stripped before serialising.

  Method invocation nodes have the AML_METHOD_INVOVATION
  attribute set in the AmlLib library's representation of
  the AML encoding.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c | 1458 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h |  188 +++
 2 files changed, 1646 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c
new file mode 100644
index 0000000000000000000000000000000000000000..bc98d950b579dec3243dab355a711a56dca978ea
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c
@@ -0,0 +1,1458 @@
+/** @file
+  AML Method Parser.
+
+  Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Parser/AmlMethodParser.h>
+
+#include <AmlCoreInterface.h>
+#include <AmlDbgPrint/AmlDbgPrint.h>
+#include <NameSpace/AmlNameSpace.h>
+#include <Parser/AmlParser.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+#include <String/AmlString.h>
+
+/** Delete a namespace reference node and its pathname.
+
+  It is the caller's responsibility to check the NameSpaceRefNode has been
+  removed from any list the node is part of.
+
+  @param  [in]  NameSpaceRefNode   Pointer to an AML_NAMESPACE_REF_NODE.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlDeleteNameSpaceRefNode (
+  IN  AML_NAMESPACE_REF_NODE    * NameSpaceRefNode
+  )
+{
+  if (NameSpaceRefNode == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (NameSpaceRefNode->RawAbsolutePath != NULL) {
+    FreePool ((CHAR8*)NameSpaceRefNode->RawAbsolutePath);
+  } else {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  FreePool (NameSpaceRefNode);
+  return EFI_SUCCESS;
+}
+
+/** Delete a list of namespace reference nodes.
+
+  @param  [in]  NameSpaceRefList    List of namespace reference nodes.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteNameSpaceRefList (
+  IN  LIST_ENTRY      * NameSpaceRefList
+  )
+{
+  EFI_STATUS        Status;
+  LIST_ENTRY      * CurrentLink;
+
+  if (NameSpaceRefList == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  while (!IsListEmpty (NameSpaceRefList)) {
+    CurrentLink = NameSpaceRefList->ForwardLink;
+    RemoveEntryList (CurrentLink);
+    Status = AmlDeleteNameSpaceRefNode (
+               (AML_NAMESPACE_REF_NODE*)CurrentLink
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  } // while
+
+  return EFI_SUCCESS;
+}
+
+/** Create an AML_NAMESPACE_REF_NODE.
+
+  A Buffer is allocated to store the raw AML absolute path.
+
+  @param  [in]  ObjectNode          Node being part of the namespace.
+                                    Must be have the AML_IN_NAMESPACE
+                                    attribute.
+  @param  [in]  RawAbsolutePath     AML raw absolute path of the ObjectNode.
+                                    A raw NameString is a concatenated list
+                                    of 4 chars long names.
+  @param  [in]  RawAbsolutePathSize Size of the RawAbsolutePath buffer.
+  @param  [out] NameSpaceRefNodePtr The created AML_METHOD_REF_NODE.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Could not allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlCreateMethodRefNode (
+  IN  CONST AML_OBJECT_NODE         * ObjectNode,
+  IN  CONST CHAR8                   * RawAbsolutePath,
+  IN        UINT32                    RawAbsolutePathSize,
+  OUT       AML_NAMESPACE_REF_NODE ** NameSpaceRefNodePtr
+  )
+{
+  AML_NAMESPACE_REF_NODE     * NameSpaceRefNode;
+
+  if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE) ||
+      (RawAbsolutePath == NULL)                           ||
+      (RawAbsolutePathSize == 0)                          ||
+      (NameSpaceRefNodePtr == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  NameSpaceRefNode = AllocateZeroPool (sizeof (AML_NAMESPACE_REF_NODE));
+  if (NameSpaceRefNode == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  NameSpaceRefNode->RawAbsolutePathSize = RawAbsolutePathSize;
+  NameSpaceRefNode->RawAbsolutePath = AllocateCopyPool (
+                                        RawAbsolutePathSize,
+                                        RawAbsolutePath
+                                        );
+  if (NameSpaceRefNode->RawAbsolutePath == NULL) {
+    FreePool (NameSpaceRefNode);
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  InitializeListHead (&NameSpaceRefNode->Link);
+
+  NameSpaceRefNode->NodeRef = ObjectNode;
+  *NameSpaceRefNodePtr = NameSpaceRefNode;
+
+  return EFI_SUCCESS;
+}
+
+#if !defined (MDEPKG_NDEBUG)
+
+/** Print the list of raw absolute paths of the NameSpace reference list.
+
+  @param  [in]  NameSpaceRefList    List of NameSpace reference nodes.
+**/
+VOID
+EFIAPI
+AmlDbgPrintNameSpaceRefList (
+  IN  CONST LIST_ENTRY    * NameSpaceRefList
+  )
+{
+  LIST_ENTRY              * CurrLink;
+  AML_NAMESPACE_REF_NODE  * CurrNameSpaceNode;
+
+  if (NameSpaceRefList == NULL) {
+    ASSERT (0);
+    return;
+  }
+
+  DEBUG ((DEBUG_INFO, "AmlMethodParser: List of available raw AML paths:\n"));
+
+  CurrLink = NameSpaceRefList->ForwardLink;
+  while (CurrLink != NameSpaceRefList) {
+    CurrNameSpaceNode = (AML_NAMESPACE_REF_NODE*)CurrLink;
+
+    AmlDbgPrintChars (
+      DEBUG_INFO,
+      CurrNameSpaceNode->RawAbsolutePath,
+      CurrNameSpaceNode->RawAbsolutePathSize
+      );
+    DEBUG ((DEBUG_INFO, "\n"));
+
+    CurrLink = CurrLink->ForwardLink;
+  }
+
+  DEBUG ((DEBUG_INFO, "\n"));
+}
+
+#endif // MDEPKG_NDEBUG
+
+/** From a forward stream pointing to a NameString,
+    initialize a raw backward stream.
+
+        StartOfStream
+  Fstream: CurrPos                                 EndOfStream
+             v                                        v
+             +-----------------------------------------+
+             |^^^[Multi-name prefix]AAAA.BBBB.CCCC     |
+             +-----------------------------------------+
+                                    ^            ^
+  RawPathNameBStream:           EndOfStream    CurrPos
+                                            StartOfStream
+
+  No memory is allocated when initializing the stream.
+
+  @param  [in]  FStream             Forward stream pointing to a NameString.
+                                    The stream must not be at its end.
+  @param  [out] RawPathNameBStream  Backward stream containing the
+                                    raw AML path.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlInitRawPathBStream (
+  IN  CONST AML_STREAM    * FStream,
+  OUT       AML_STREAM    * RawPathNameBStream
+  )
+{
+  EFI_STATUS    Status;
+
+  UINT8       * RawPathBuffer;
+  CONST CHAR8 * Buffer;
+
+  UINT32        Root;
+  UINT32        ParentPrefix;
+  UINT32        SegCount;
+
+  if (!IS_STREAM (FStream)          ||
+      IS_END_OF_STREAM (FStream)    ||
+      !IS_STREAM_FORWARD  (FStream) ||
+      (RawPathNameBStream == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Buffer = (CONST CHAR8*)AmlStreamGetCurrPos (FStream);
+  if (Buffer == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Parse the NameString information.
+  Status = AmlParseNameStringInfo (
+             Buffer,
+             &Root,
+             &ParentPrefix,
+             &SegCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Get the beginning of the raw NameString.
+  RawPathBuffer = (UINT8*)AmlGetFirstNameSeg (
+                            Buffer,
+                            Root,
+                            ParentPrefix
+                            );
+  if (RawPathBuffer == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Initialize a backward stream containing the raw path.
+  Status = AmlStreamInit (
+             RawPathNameBStream,
+             RawPathBuffer,
+             (SegCount * AML_NAME_SEG_SIZE),
+             EAmlStreamDirectionBackward
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Get the first node in the ParentNode branch that is part of the
+    AML namespace and has its name defined.
+
+  This is different from getting the first namespace node. This function is
+  necessary because an absolute path is built while the tree is not complete
+  yet. The parsing is ongoing.
+
+  For instance, the ASL statement "CreateXXXField ()" adds a field in the
+  AML namespace, but the name it defines is the last fixed argument of the
+  corresponding object.
+  If an AML path is referenced in its first fixed argument, it is not
+  possible to resolve the name of the CreateXXXField object. However, the AML
+  path is not part of the scope created by the CreateXXXField object, so this
+  scope can be skipped.
+
+  In the following ASL code, the method invocation to MET0 is done in the
+  "CreateField" statement. The "CreateField" statement defines the "FIEL"
+  path in the AML namespace. However, MET0 must be not be resolved in the
+  "CreateField" object scope. It needs to be resolved in its parent.
+  ASL code:
+  Method (MET0, 0,,, BuffObj) {
+    Return (Buffer (0x1000) {})
+  }
+  CreateField (MET0(), 0x100, 0x4, FIEL)
+
+  @param  [in]  Node          Node to get the first named node from, in
+                              its hierarchy.
+  @param  [out] OutNamedNode  First named node in Node's hierarchy.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlGetFirstNamedAncestorNode (
+  IN  CONST AML_NODE_HEADER   * Node,
+  OUT       AML_NODE_HEADER  ** OutNamedNode
+  )
+{
+  EFI_STATUS                Status;
+  CONST AML_NODE_HEADER   * NameSpaceNode;
+
+  if ((!IS_AML_OBJECT_NODE (Node)   &&
+       !IS_AML_ROOT_NODE (Node))    ||
+      (OutNamedNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // If Node is not the root node and doesn't have a name defined yet,
+  // get the ancestor NameSpace node.
+  while (!IS_AML_ROOT_NODE (Node)             &&
+         !(AmlNodeHasAttribute (
+             (CONST AML_OBJECT_NODE*)Node,
+              AML_IN_NAMESPACE)               &&
+           AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node) != NULL)) {
+    Status = AmlGetFirstAncestorNameSpaceNode (
+                Node,
+                (AML_NODE_HEADER**)&NameSpaceNode
+                );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+    // The NameSpaceNode may not have its name defined as yet. In this
+    // case get the next ancestor node.
+    Node = NameSpaceNode;
+  }
+
+  *OutNamedNode = (AML_NODE_HEADER*)Node;
+
+  return EFI_SUCCESS;
+}
+
+/** From a ParentNode and a forward stream pointing to a relative path,
+    build a raw AML absolute path and return it in a backward stream.
+
+  No memory is allocated in this function, the out stream must be initialized
+  with a buffer long enough to hold any raw absolute AML path.
+
+  @param  [in]  ParentNode                  Parent node of the namespace
+                                            node from which the absolute
+                                            path is built. ParentNode isn't
+                                            necessarily a namespace node.
+                                            Must be a root or an object node.
+  @param  [in]  PathnameFStream             Forward stream pointing to the
+                                            beginning of a pathname (any
+                                            NameString).
+                                            The stream must not be at its end.
+  @param  [in, out] AbsolutePathBStream     Backward stream where the raw
+                                            absolute path is written. The
+                                            stream must be already initialized.
+                                            The stream must not be at its end.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlBuildRawMethodAbsolutePath (
+  IN      CONST AML_NODE_HEADER   * ParentNode,
+  IN      CONST AML_STREAM        * PathnameFStream,
+  IN  OUT       AML_STREAM        * AbsolutePathBStream
+  )
+{
+  EFI_STATUS          Status;
+
+  AML_NODE_HEADER   * NamedParentNode;
+  UINT8             * RawPathBuffer;
+  CONST CHAR8       * CurrPos;
+
+  UINT32              Root;
+  UINT32              ParentPrefix;
+  UINT32              SegCount;
+
+  if ((!IS_AML_OBJECT_NODE (ParentNode)         &&
+       !IS_AML_ROOT_NODE (ParentNode))          ||
+      !IS_STREAM (PathnameFStream)              ||
+      IS_END_OF_STREAM (PathnameFStream)        ||
+      !IS_STREAM_FORWARD (PathnameFStream)      ||
+      !IS_STREAM (AbsolutePathBStream)          ||
+      IS_END_OF_STREAM (AbsolutePathBStream)    ||
+      !IS_STREAM_BACKWARD (AbsolutePathBStream)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CurrPos = (CONST CHAR8*)AmlStreamGetCurrPos (PathnameFStream);
+  if (CurrPos == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Parse the NameString information.
+  Status = AmlParseNameStringInfo (
+             CurrPos,
+             &Root,
+             &ParentPrefix,
+             &SegCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Copy the method invocation raw relative path at the end of the Stream.
+  if (SegCount != 0) {
+    // Get the beginning of the raw NameString.
+    RawPathBuffer = (UINT8*)AmlGetFirstNameSeg (
+                              CurrPos,
+                              Root,
+                              ParentPrefix
+                              );
+
+    Status = AmlStreamWrite (
+               AbsolutePathBStream,
+               RawPathBuffer,
+               SegCount * AML_NAME_SEG_SIZE
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  }
+
+  // If the pathname contained an absolute path, this is finished, return.
+  if (Root) {
+    return Status;
+  }
+
+  // Get the first named node of the parent node in its hierarchy.
+  Status = AmlGetFirstNamedAncestorNode (ParentNode, &NamedParentNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Build the raw absolute path of the namespace node.
+  Status = AmlGetRawNameSpacePath (
+             NamedParentNode,
+             ParentPrefix,
+             AbsolutePathBStream
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Compare two raw NameStrings stored in forward streams.
+    Compare them NameSeg by NameSeg (a NameSeg is 4 bytes long).
+
+  The two raw NameStrings can be of different size.
+
+  @param  [in]  RawFStream1     First forward stream to compare.
+                                Points to the beginning of the raw NameString.
+  @param  [in]  RawFStream2     Second forward stream to compare.
+                                Points to the beginning of the raw NameString.
+  @param  [out] CompareCount    Count of identic bytes.
+                                Must be a multiple of 4 (size of a NameSeg).
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlCompareRawNameString (
+  IN  CONST AML_STREAM    * RawFStream1,
+  IN  CONST AML_STREAM    * RawFStream2,
+  OUT       UINT32        * CompareCount
+  )
+{
+  EFI_STATUS    Status;
+  UINT32        Index;
+
+  AML_STREAM    RawFStream1Clone;
+  AML_STREAM    RawFStream2Clone;
+  UINT32        Stream1Size;
+  UINT32        Stream2Size;
+  UINT32        CompareLen;
+
+  // Raw NameStrings have a size that is a multiple of the size of NameSegs.
+  if (!IS_STREAM (RawFStream1)          ||
+      IS_END_OF_STREAM (RawFStream1)    ||
+      !IS_STREAM_FORWARD (RawFStream1)  ||
+      !IS_STREAM (RawFStream2)          ||
+      IS_END_OF_STREAM (RawFStream2)    ||
+      (CompareCount == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Stream1Size = AmlStreamGetFreeSpace (RawFStream1);
+  if ((Stream1Size & (AML_NAME_SEG_SIZE - 1)) != 0) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Stream2Size = AmlStreamGetFreeSpace (RawFStream2);
+  if ((Stream2Size & (AML_NAME_SEG_SIZE - 1)) != 0) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = AmlStreamClone (RawFStream1, &RawFStream1Clone);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = AmlStreamClone (RawFStream2, &RawFStream2Clone);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  CompareLen = MIN (Stream1Size, Stream2Size);
+  Index = 0;
+  // Check there is enough space for a NameSeg in both Stream1 and Stream2.
+  while (Index < CompareLen) {
+    if (!AmlStreamCmp (
+           &RawFStream1Clone,
+           &RawFStream2Clone,
+           AML_NAME_SEG_SIZE)
+           ) {
+      // NameSegs are different. Break.
+      break;
+    }
+
+    Status = AmlStreamProgress (&RawFStream1Clone, AML_NAME_SEG_SIZE);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+    Status = AmlStreamProgress (&RawFStream2Clone, AML_NAME_SEG_SIZE);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    Index += AML_NAME_SEG_SIZE;
+  }
+
+  *CompareCount = Index;
+
+  return EFI_SUCCESS;
+}
+
+/** Check whether an alias can be resolved to a method definition.
+
+  Indeed, the following ASL code must be handled:
+    Method (MET0, 1) {
+      Return (0x9)
+    }
+    Alias (\MET0, \ALI0)
+    Alias (\ALI0, \ALI1)
+    \ALI1(0x5)
+  When searching for \ALI1 in the AML NameSpace, it resolves to \ALI0.
+  When searching for \ALI0 in the AML NameSpace, it resolves to \MET0.
+  When searching for \MET0 in the AML NameSpace, it resolves to a method
+  definition.
+
+  This method is a wrapper to recursively call AmlFindMethodDefinition.
+
+  @param  [in]  AliasNode             Pointer to an Alias object node.
+  @param  [in]  NameSpaceRefList      List of NameSpaceRef nodes.
+  @param  [out] OutNameSpaceRefNode   If success, and if the alias is resolved
+                                      to a method definition (this can go
+                                      through other alias indirections),
+                                      containing the corresponding
+                                      NameSpaceRef node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlResolveAliasMethod (
+  IN  CONST AML_OBJECT_NODE           * AliasNode,
+  IN  CONST LIST_ENTRY                * NameSpaceRefList,
+  OUT       AML_NAMESPACE_REF_NODE   ** OutNameSpaceRefNode
+  )
+{
+  EFI_STATUS              Status;
+  AML_STREAM              SourceAliasFStream;
+  CONST AML_DATA_NODE   * DataNode;
+
+  if (!AmlNodeCompareOpCode (AliasNode, AML_ALIAS_OP, 0)  ||
+      (NameSpaceRefList == NULL)                          ||
+      (OutNameSpaceRefNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // The aliased NameString (the source name) is the first fixed argument,
+  // cf. ACPI6.3 spec, s19.6.4: Alias (SourceObject, AliasObject)
+  DataNode = (CONST AML_DATA_NODE*)AmlGetFixedArgument (
+                                     (AML_OBJECT_NODE*)AliasNode,
+                                     EAmlParseIndexTerm0
+                                     );
+  if (DataNode == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Initialize a stream on the source alias NameString.
+  Status = AmlStreamInit (
+             &SourceAliasFStream,
+             DataNode->Buffer,
+             DataNode->Size,
+             EAmlStreamDirectionForward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Recursively check whether the source alias NameString
+  // is a method invocation.
+  Status = AmlIsMethodInvocation (
+             AmlGetParent ((AML_NODE_HEADER*)AliasNode),
+             &SourceAliasFStream,
+             NameSpaceRefList,
+             OutNameSpaceRefNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+
+  return Status;
+}
+
+/** Iterate through the MethodList to find the best namespace resolution.
+    If the pathname resolves to a method definition, returns it.
+
+  For instance, if the AML namespace is:
+  \
+  \-MET0         <- Device definition, absolute path: \MET0
+  \-AAAA
+    \-MET0       <- Method definition, absolute path: \AAAA.MET0
+    \-MET1       <- Method definition, absolute path: \AAAA.MET1
+    \-BBBB
+      \-CCCC
+        \-DDDD
+          \-MET0 <- Method definition, absolute path: \AAAA.BBBB.CCCC.DDDD.MET0
+
+  The list of the available pathnames is:
+  [NameSpaceRefList]
+   - \MET0                          <-  Device definition
+   - \AAAA
+   - \AAAA.MET0                     <-  Method definition
+   - \AAAA.MET1                     <-  Method definition
+   - \AAAA.BBBB
+   - \AAAA.BBBB.CCCC
+   - \AAAA.BBBB.CCCC.DDDD
+   - \AAAA.BBBB.CCCC.DDDD.MET0      <-  Method definition
+
+  Depending on where the method invocation is done, the method definition
+  referenced changes. If the method call "MET0" is done from
+  \AAAA.BBBB.CCCC:
+    1. Identify which pathnames end with "MET0":
+     - \MET0                          <-  Device definition
+     - \AAAA.MET0                     <-  Method definition
+     - \AAAA.BBBB.CCCC.DDDD.MET0      <-  Method definition
+    2. Resolve the method invocation:
+     - \AAAA.MET0                     <-  Method definition
+    3. \AAAA.MET0 is a method definition, so return the corresponding
+       reference node.
+
+  @param  [in]  RawAbsolutePathFStream    Forward stream pointing to a raw
+                                          absolute path.
+                                          The stream must not be at its end.
+  @param  [in]  RawPathNameBStream        Backward stream pointing to a raw
+                                          pathname. This raw pathname is the
+                                          raw NameString of namespace node.
+                                          The stream must not be at its end.
+  @param  [in]  NameSpaceRefList          List of NameSpaceRef nodes.
+  @param  [out] OutNameSpaceRefNode       If the two input paths are
+                                          referencing a method definition,
+                                          returns the corresponding
+                                          NameSpaceRef node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlFindMethodDefinition (
+  IN  CONST AML_STREAM                * RawAbsolutePathFStream,
+  IN  CONST AML_STREAM                * RawPathNameBStream,
+  IN  CONST LIST_ENTRY                * NameSpaceRefList,
+  OUT       AML_NAMESPACE_REF_NODE   ** OutNameSpaceRefNode
+  )
+{
+  EFI_STATUS                Status;
+
+  LIST_ENTRY              * NextLink;
+
+  // To resolve a pathname, scope levels need to be compared.
+  UINT32                    NameSegScopeCount;
+  UINT32                    PathNameSegScopeCount;
+  UINT32                    ProbedScopeCount;
+  UINT32                    BestScopeCount;
+
+  AML_STREAM                ProbedRawAbsoluteFStream;
+  AML_STREAM                ProbedRawAbsoluteBStream;
+
+  AML_NAMESPACE_REF_NODE  * ProbedNameSpaceRefNode;
+  AML_NAMESPACE_REF_NODE  * BestNameSpaceRefNode;
+
+  if (!IS_STREAM (RawAbsolutePathFStream)                               ||
+      IS_END_OF_STREAM (RawAbsolutePathFStream)                         ||
+      !IS_STREAM_FORWARD (RawAbsolutePathFStream)                       ||
+      ((AmlStreamGetIndex (RawAbsolutePathFStream) &
+        (AML_NAME_SEG_SIZE - 1)) != 0)                                  ||
+      !IS_STREAM (RawPathNameBStream)                                   ||
+      IS_END_OF_STREAM (RawPathNameBStream)                             ||
+      !IS_STREAM_BACKWARD (RawPathNameBStream)                          ||
+      ((AmlStreamGetIndex (RawPathNameBStream) &
+        (AML_NAME_SEG_SIZE - 1)) != 0)                                  ||
+      (NameSpaceRefList == NULL)                                        ||
+      (OutNameSpaceRefNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((DEBUG_VERBOSE, "AmlMethodParser: Checking absolute name: "));
+  AmlDbgPrintChars (
+    DEBUG_VERBOSE,
+    (CONST CHAR8*)AmlStreamGetCurrPos (RawAbsolutePathFStream),
+    AmlStreamGetMaxBufferSize (RawAbsolutePathFStream)
+    );
+  DEBUG ((DEBUG_VERBOSE, ".\n"));
+
+  BestNameSpaceRefNode = NULL;
+  BestScopeCount = 0;
+  NameSegScopeCount = AmlStreamGetMaxBufferSize (RawAbsolutePathFStream);
+  PathNameSegScopeCount = AmlStreamGetMaxBufferSize (RawPathNameBStream);
+
+  // Iterate through the raw AML absolute path to find the best match.
+  DEBUG ((DEBUG_VERBOSE, "AmlMethodParser: Comparing with: "));
+  NextLink = NameSpaceRefList->ForwardLink;
+  while (NextLink != NameSpaceRefList) {
+    ProbedNameSpaceRefNode = (AML_NAMESPACE_REF_NODE*)NextLink;
+
+    // Print the raw absolute path of the probed node.
+    AmlDbgPrintChars (
+      DEBUG_VERBOSE,
+      ProbedNameSpaceRefNode->RawAbsolutePath,
+      ProbedNameSpaceRefNode->RawAbsolutePathSize
+      );
+    DEBUG ((DEBUG_VERBOSE, "; "));
+
+    // If the raw AML absolute path of the probed node is longer than the
+    // searched pathname, continue.
+    // E.g.: The method call \MET0 cannot resolve to a method defined at
+    //       \AAAA.MET0. The method definition is out of scope.
+    if (PathNameSegScopeCount > ProbedNameSpaceRefNode->RawAbsolutePathSize) {
+      NextLink = NextLink->ForwardLink;
+      continue;
+    }
+
+    // Initialize a backward stream for the probed node.
+    // This stream is used to compare the ending of the pathnames.
+    // E.g. if the method searched ends with "MET0", pathnames not ending with
+    //      "MET0" should be skipped.
+    Status = AmlStreamInit (
+               &ProbedRawAbsoluteBStream,
+               (UINT8*)ProbedNameSpaceRefNode->RawAbsolutePath,
+               ProbedNameSpaceRefNode->RawAbsolutePathSize,
+               EAmlStreamDirectionBackward
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Compare the pathname endings. If they don't match, continue.
+    if (!AmlStreamCmp (
+           RawPathNameBStream,
+           &ProbedRawAbsoluteBStream,
+           AmlStreamGetMaxBufferSize (RawPathNameBStream))) {
+      NextLink = NextLink->ForwardLink;
+      continue;
+    }
+
+    // Initialize a forward stream for the probed node.
+    // This stream is used to count how many scope levels from the root
+    // are common with the probed node. The more there are, the better it is.
+    // E.g.: For the method invocation \AAAA.BBBB.MET0, if there are 2
+    //       pathnames ending with MET0:
+    //        - \AAAA.MET0 has 1 NameSeg in common with \AAAA.BBBB.MET0
+    //          from the root (this is "AAAA");
+    //        - \MET0 has 0 NameSeg in common with \AAAA.BBBB.MET0
+    //          from the root;
+    //       Thus, the best match is \AAAA.MET0.
+    Status = AmlStreamInit (
+               &ProbedRawAbsoluteFStream,
+               (UINT8*)ProbedNameSpaceRefNode->RawAbsolutePath,
+               ProbedNameSpaceRefNode->RawAbsolutePathSize,
+               EAmlStreamDirectionForward
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Count how many namespace levels are in common from the root.
+    Status = AmlCompareRawNameString (
+               RawAbsolutePathFStream,
+               &ProbedRawAbsoluteFStream,
+               &ProbedScopeCount
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (ProbedScopeCount == NameSegScopeCount) {
+      // This is a perfect match. Exit the loop.
+      BestNameSpaceRefNode = ProbedNameSpaceRefNode;
+      break;
+    } else if (ProbedScopeCount > BestScopeCount) {
+      // The probed node has more scope levels in common than the
+      // last best match. Update the best match.
+      BestScopeCount = ProbedScopeCount;
+      BestNameSpaceRefNode = ProbedNameSpaceRefNode;
+    } else if (ProbedScopeCount == BestScopeCount) {
+      // The probed node has the same number of scope levels in
+      // common as the last best match.
+      if (ProbedScopeCount == 0) {
+        // There was not best match previously. Set it.
+        BestNameSpaceRefNode = ProbedNameSpaceRefNode;
+      } else {
+        // (ProbedScopeCount != 0)
+        // If there is an equivalent candidate, the best has the shortest
+        // absolute path. Indeed, a similar ProbedScopeCount and a longer
+        // path means the definition is out of the scope.
+        // E.g.: For the method invocation \AAAA.BBBB.MET0, if there are 2
+        //       pathnames ending with MET0:
+        //        - \AAAA.MET0 has 1 NameSegs in common with \AAAA.BBBB.MET0
+        //          from the root (this is "AAAA");
+        //        - \AAAA.CCCC.MET0 has 1 NameSegs in common with
+        //          \AAAA.BBBB.MET0 from the root (this is "AAAA");
+        //       As \AAAA.CCCC.MET0 is longer than \AAAA.MET0, it means that
+        //       the pathname could have matched on more NameSegs, but it
+        //       didn't because it is out of scope.
+        //       Thus, the best match is \AAAA.MET0.
+        if (AmlStreamGetIndex (&ProbedRawAbsoluteFStream) <
+              BestNameSpaceRefNode->RawAbsolutePathSize) {
+          BestScopeCount = ProbedScopeCount;
+          BestNameSpaceRefNode = ProbedNameSpaceRefNode;
+        } else if (AmlStreamGetIndex (&ProbedRawAbsoluteFStream) ==
+                     BestNameSpaceRefNode->RawAbsolutePathSize) {
+          ASSERT (0);
+          return EFI_INVALID_PARAMETER;
+        }
+      }
+    }
+
+    NextLink = NextLink->ForwardLink;
+  }
+
+  DEBUG ((DEBUG_VERBOSE, "\n"));
+
+  // Check whether the BestNameSpaceRefNode is a method definition.
+  if (BestNameSpaceRefNode != NULL) {
+    if (AmlIsMethodDefinitionNode (BestNameSpaceRefNode->NodeRef)) {
+      *OutNameSpaceRefNode = BestNameSpaceRefNode;
+    } else if (AmlNodeCompareOpCode (
+                 BestNameSpaceRefNode->NodeRef,
+                 AML_ALIAS_OP, 0)) {
+      // The path matches an alias. Resolve the alias and check whether
+      // this is a method defintion.
+      Status = AmlResolveAliasMethod (
+                 BestNameSpaceRefNode->NodeRef,
+                 NameSpaceRefList,
+                 OutNameSpaceRefNode
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+    }
+  } else {
+    // If no, return NULL, even if a matching pathname has been found.
+    *OutNameSpaceRefNode = NULL;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Check whether a pathname is a method invocation.
+
+  If there is a matching method definition, returns the corresponding
+  NameSpaceRef node.
+
+  To do so, the NameSpaceRefList is keeping track of every namespace node
+  and its raw AML absolute path.
+  To check whether a pathname is a method invocation, a corresponding raw
+  absolute pathname is built. This raw absolute pathname is then compared
+  to the list of available pathnames. If a pathname defining a method
+  matches the scope of the input pathname, return.
+
+  @param  [in]  ParentNode          Parent node. Node to which the node to be
+                                    created will be attached.
+  @param  [in]  FStream             Forward stream pointing to the NameString
+                                    to find.
+  @param  [in]  NameSpaceRefList    List of NameSpaceRef nodes.
+  @param  [out] OutNameSpaceRefNode If the NameString pointed by FStream is
+                                    a method invocation, OutNameSpaceRefNode
+                                    contains the NameSpaceRef corresponding
+                                    to the method definition.
+                                    NULL otherwise.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlIsMethodInvocation (
+  IN  CONST AML_NODE_HEADER     * ParentNode,
+  IN  CONST AML_STREAM          * FStream,
+  IN  CONST LIST_ENTRY          * NameSpaceRefList,
+  OUT AML_NAMESPACE_REF_NODE   ** OutNameSpaceRefNode
+  )
+{
+  EFI_STATUS                Status;
+
+  AML_STREAM                RawPathNameBStream;
+  AML_STREAM                RawAbsolutePathFStream;
+
+  AML_STREAM                RawAbsolutePathBStream;
+  UINT8                   * RawAbsolutePathBuffer;
+  UINT32                    RawAbsolutePathBufferSize;
+
+  AML_NAMESPACE_REF_NODE  * NameSpaceRefNode;
+
+  if ((!IS_AML_OBJECT_NODE (ParentNode) &&
+       !IS_AML_ROOT_NODE (ParentNode))  ||
+      !IS_STREAM (FStream)              ||
+      IS_END_OF_STREAM (FStream)        ||
+      !IS_STREAM_FORWARD (FStream)      ||
+      (NameSpaceRefList == NULL)        ||
+      (OutNameSpaceRefNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // There cannot be a method invocation in a field list. Return.
+  if (AmlNodeHasAttribute (
+        (CONST AML_OBJECT_NODE*)ParentNode,
+        AML_HAS_FIELD_LIST)) {
+    *OutNameSpaceRefNode = NULL;
+    return EFI_SUCCESS;
+  }
+
+  // Allocate memory for the raw absolute path.
+  RawAbsolutePathBufferSize = MAX_AML_NAMESTRING_SIZE;
+  RawAbsolutePathBuffer = AllocateZeroPool (RawAbsolutePathBufferSize);
+  if (RawAbsolutePathBuffer == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  // Initialize a backward stream to get the raw absolute path.
+  Status = AmlStreamInit (
+             &RawAbsolutePathBStream,
+             RawAbsolutePathBuffer,
+             RawAbsolutePathBufferSize,
+             EAmlStreamDirectionBackward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Build the raw AML absolute path of the namespace node.
+  Status = AmlBuildRawMethodAbsolutePath (
+             ParentNode,
+             FStream,
+             &RawAbsolutePathBStream
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // If this is the root path: it cannot be a method invocation. Just return.
+  if (AmlStreamGetIndex (&RawAbsolutePathBStream) == 0) {
+    DEBUG ((
+      DEBUG_VERBOSE,
+      "AmlMethodParser: "
+      "Root node cannot be a method invocation\n"
+      ));
+    *OutNameSpaceRefNode = NULL;
+    Status = EFI_SUCCESS;
+    goto exit_handler;
+  }
+
+  // Create a forward stream for the raw absolute path.
+  // This forward stream only contains the raw absolute path with
+  // no extra free space.
+  Status = AmlStreamInit (
+             &RawAbsolutePathFStream,
+             AmlStreamGetCurrPos (&RawAbsolutePathBStream),
+             AmlStreamGetIndex (&RawAbsolutePathBStream),
+             EAmlStreamDirectionForward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Create a backward stream for the node name.
+  Status = AmlInitRawPathBStream (
+             FStream,
+             &RawPathNameBStream
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Go through the NameSpaceRefList elements to check for
+  // a corresponding method definition.
+  NameSpaceRefNode = NULL;
+  Status = AmlFindMethodDefinition (
+             &RawAbsolutePathFStream,
+             &RawPathNameBStream,
+             NameSpaceRefList,
+             &NameSpaceRefNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+#if !defined(MDEPKG_NDEBUG)
+  // Print whether a method definition has been found.
+  if (NameSpaceRefNode != NULL) {
+    DEBUG ((
+      DEBUG_VERBOSE,
+      "AmlMethodParser: Corresponding method definition: "
+      ));
+    AmlDbgPrintChars (
+      DEBUG_VERBOSE,
+      NameSpaceRefNode->RawAbsolutePath,
+      NameSpaceRefNode->RawAbsolutePathSize
+      );
+    DEBUG ((DEBUG_VERBOSE, ".\n"));
+
+  } else {
+    DEBUG ((DEBUG_VERBOSE, "AmlMethodParser: No method definition found.\n"));
+  }
+#endif // MDEPKG_NDEBUG
+
+  *OutNameSpaceRefNode = NameSpaceRefNode;
+
+exit_handler:
+  // Free allocated memory.
+  FreePool (RawAbsolutePathBuffer);
+  return Status;
+}
+
+/** Create a namespace reference node and add it to the NameSpaceRefList.
+
+  When a namespace node is encountered, the namespace it defines must be
+  associated to the node. This allow to keep track of the nature of each
+  name present in the AML namespace.
+
+  In the end, this allows to recognize method invocations and parse the right
+  number of arguments after the method name.
+
+  @param [in]       Node              Namespace node.
+  @param [in, out]  NameSpaceRefList  List of namespace reference nodes.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlAddNameSpaceReference (
+  IN      CONST AML_OBJECT_NODE   * Node,
+  IN  OUT       LIST_ENTRY        * NameSpaceRefList
+  )
+{
+  EFI_STATUS                Status;
+  AML_NAMESPACE_REF_NODE  * NameSpaceRefNode;
+
+  AML_STREAM                NodeNameFStream;
+  EAML_PARSE_INDEX          NameIndex;
+  CONST AML_DATA_NODE     * NameNode;
+
+  AML_STREAM                RawAbsolutePathBStream;
+  UINT32                    RawAbsolutePathBStreamSize;
+
+  CHAR8                   * AbsolutePathBuffer;
+  UINT32                    AbsolutePathBufferSize;
+
+  CONST AML_NODE_HEADER   * ParentNode;
+
+  if (!AmlNodeHasAttribute (Node, AML_IN_NAMESPACE)   ||
+      (NameSpaceRefList == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Allocate a buffer to get the raw AML absolute pathname of the
+  // namespace node.
+  AbsolutePathBufferSize = MAX_AML_NAMESTRING_SIZE;
+  AbsolutePathBuffer = AllocateZeroPool (AbsolutePathBufferSize);
+  if (AbsolutePathBuffer == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Status = AmlStreamInit (
+             &RawAbsolutePathBStream,
+             (UINT8*)AbsolutePathBuffer,
+             AbsolutePathBufferSize,
+             EAmlStreamDirectionBackward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+    goto exit_handler;
+  }
+
+  // Get the index where the name of the Node is stored in its
+  // fixed list of arguments.
+  Status = AmlNodeGetNameIndex (Node, &NameIndex);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+    goto exit_handler;
+  }
+
+  // Get the Node name.
+  NameNode = (CONST AML_DATA_NODE*)AmlGetFixedArgument (
+                                     (AML_OBJECT_NODE*)Node,
+                                     NameIndex
+                                     );
+  if (!IS_AML_DATA_NODE (NameNode)    ||
+      (NameNode->DataType != EAmlNodeDataTypeNameString)) {
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+    goto exit_handler;
+  }
+
+  // Initialize a stream on the node name of the namespace node.
+  // This is an AML NameString.
+  Status = AmlStreamInit (
+             &NodeNameFStream,
+             NameNode->Buffer,
+             NameNode->Size,
+             EAmlStreamDirectionForward
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+    goto exit_handler;
+  }
+
+  ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node);
+  if (ParentNode == NULL) {
+    ASSERT (0);
+    Status = EFI_INVALID_PARAMETER;
+    goto exit_handler;
+  }
+
+  // Build the raw AML absolute path of the namespace node.
+  Status = AmlBuildRawMethodAbsolutePath (
+             ParentNode,
+             &NodeNameFStream,
+             &RawAbsolutePathBStream
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  RawAbsolutePathBStreamSize = AmlStreamGetIndex (&RawAbsolutePathBStream);
+  // This is the root path: this cannot be a method invocation.
+  if (RawAbsolutePathBStreamSize == 0) {
+    Status = EFI_SUCCESS;
+    goto exit_handler;
+  }
+
+  // Create a NameSpace reference node.
+  Status = AmlCreateMethodRefNode (
+             Node,
+             (CONST CHAR8*)AmlStreamGetCurrPos (&RawAbsolutePathBStream),
+             RawAbsolutePathBStreamSize,
+             &NameSpaceRefNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Add the created NameSpaceRefNode to the list.
+  InsertTailList (NameSpaceRefList, &NameSpaceRefNode->Link);
+
+  DEBUG ((
+    DEBUG_VERBOSE,
+    "AmlMethodParser: Adding namespace reference with name:\n"
+    ));
+  AmlDbgPrintChars (
+    DEBUG_VERBOSE,
+    (CONST CHAR8*)AmlStreamGetCurrPos (&RawAbsolutePathBStream),
+    AmlStreamGetIndex (&RawAbsolutePathBStream)
+    );
+  DEBUG ((DEBUG_VERBOSE, "\n"));
+
+exit_handler:
+  // Free allocated memory.
+  FreePool (AbsolutePathBuffer);
+
+  return Status;
+}
+
+/** Create a method invocation node.
+
+  The AML grammar does not attribute an OpCode/SubOpCode couple for
+  method invocations. This library is representing method invocations
+  as if they had one.
+
+  The AML encoding for method invocations in the ACPI specification 6.3 is:
+    MethodInvocation := NameString TermArgList
+  In this library, it is:
+    MethodInvocation := MethodInvocationOp NameString ArgumentCount TermArgList
+    ArgumentCount    := ByteData
+
+  When computing the size of a tree or serializing it, the additional data is
+  not taken into account (i.e. the MethodInvocationOp and the ArgumentCount).
+
+  Method invocation nodes have the AML_METHOD_INVOVATION attribute.
+
+  @param  [in]  NameSpaceRefNode          NameSpaceRef node pointing to the
+                                          the definition of the invoked
+                                          method.
+  @param  [in]  MethodInvocationName      Data node containing the method
+                                          invocation name.
+  @param  [out] MethodInvocationNodePtr   Created method invocation node.
+
+  @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
+AmlCreateMethodInvocationNode (
+  IN  CONST AML_NAMESPACE_REF_NODE   * NameSpaceRefNode,
+  IN        AML_DATA_NODE            * MethodInvocationName,
+  OUT       AML_OBJECT_NODE         ** MethodInvocationNodePtr
+  )
+{
+  EFI_STATUS            Status;
+
+  UINT8                 ArgCount;
+  AML_DATA_NODE       * ArgCountNode;
+  AML_NODE_HEADER    ** FixedArgs;
+  AML_OBJECT_NODE     * MethodDefinitionNode;
+  AML_OBJECT_NODE     * MethodInvocationNode;
+
+  if ((NameSpaceRefNode == NULL)                                      ||
+      !AmlIsMethodDefinitionNode (NameSpaceRefNode->NodeRef)          ||
+      !IS_AML_DATA_NODE (MethodInvocationName)                        ||
+      (MethodInvocationName->DataType != EAmlNodeDataTypeNameString)  ||
+      (MethodInvocationNodePtr == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the number of arguments of the method.
+  MethodDefinitionNode = (AML_OBJECT_NODE*)NameSpaceRefNode->NodeRef;
+  FixedArgs = MethodDefinitionNode->FixedArgs;
+  // The method definition is an actual method definition.
+  if (AmlNodeCompareOpCode (MethodDefinitionNode, AML_METHOD_OP, 0)) {
+    // Cf ACPI 6.3 specification:
+    //  DefMethod := MethodOp PkgLength NameString MethodFlags TermList
+    //  MethodOp := 0x14
+    //  MethodFlags := ByteData  bit 0-2: ArgCount (0-7)
+    //                           bit 3: SerializeFlag
+    //                                    0 NotSerialized
+    //                                    1 Serialized
+    //                           bit 4-7: SyncLevel (0x00-0x0f)
+
+    // Read the MethodFlags to decode the ArgCount.
+    ArgCountNode = (AML_DATA_NODE*)FixedArgs[EAmlParseIndexTerm1];
+    ArgCount = *((UINT8*)ArgCountNode->Buffer) & 0x7;
+  } else if (AmlNodeCompareOpCode (MethodDefinitionNode, AML_EXTERNAL_OP, 0)) {
+    // The method definition is an external statement.
+    // Cf ACPI 6.3 specification:
+    //  DefExternal := ExternalOp NameString ObjectType ArgumentCount
+    //  ExternalOp := 0x15
+    //  ObjectType := ByteData
+    //  ArgumentCount := ByteData (0 – 7)
+
+    // Read the ArgumentCount.
+    ArgCountNode = (AML_DATA_NODE*)FixedArgs[EAmlParseIndexTerm2];
+    ArgCount = *((UINT8*)ArgCountNode->Buffer);
+  } else {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Create the object node for the method invocation.
+  // MethodInvocation := MethodInvocationOp NameString ArgumentCount
+  // MethodInvocationOp := Pseudo Opcode for Method Invocation
+  // NameString := Method Name
+  // ArgumentCount := ByteData (0 – 7)
+  Status = AmlCreateObjectNode (
+             AmlGetByteEncodingByOpCode (AML_METHOD_INVOC_OP, 0),
+             0,
+             &MethodInvocationNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // The first fixed argument is the method name.
+  Status = AmlSetFixedArgument (
+             MethodInvocationNode,
+             EAmlParseIndexTerm0,
+             (AML_NODE_HEADER*)MethodInvocationName
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto error_handler;
+  }
+
+  // Create a data node holding the number of arguments
+  // of the method invocation.
+  ArgCountNode = NULL;
+  Status = AmlCreateDataNode (
+             EAmlNodeDataTypeUInt,
+             &ArgCount,
+             sizeof (UINT8),
+             &ArgCountNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto error_handler;
+  }
+
+  // The second fixed argument is the number of arguments.
+  Status = AmlSetFixedArgument (
+              MethodInvocationNode,
+              EAmlParseIndexTerm1,
+              (AML_NODE_HEADER*)ArgCountNode
+              );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto error_handler;
+  }
+
+  *MethodInvocationNodePtr = MethodInvocationNode;
+  return Status;
+
+error_handler:
+  // Delete the sub-tree: the method invocation name is already attached.
+  AmlDeleteTree ((AML_NODE_HEADER*)MethodInvocationNode);
+  if (ArgCountNode != NULL) {
+    AmlDeleteNode ((AML_NODE_HEADER*)ArgCountNode);
+  }
+
+  return Status;
+}
+
+/** Get the number of arguments of a method invocation node.
+
+  This function also allow to identify whether a node is a method invocation
+  node. If the input node is not a method invocation node, just return.
+
+  @param  [in]  MethodInvocationNode  Method invocation node.
+  @param  [out] IsMethodInvocation    Boolean stating whether the input
+                                      node is a method invocation.
+  @param  [out] ArgCount              Number of arguments of the method
+                                      invocation.
+                                      Set to 0 if MethodInvocationNode
+                                      is not a method invocation.
+
+  @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
+AmlGetMethodInvocationArgCount (
+  IN  CONST AML_OBJECT_NODE   * MethodInvocationNode,
+  OUT       BOOLEAN           * IsMethodInvocation,
+  OUT       UINT8             * ArgCount
+  )
+{
+  AML_DATA_NODE   * NumArgsNode;
+
+  if (!IS_AML_NODE_VALID (MethodInvocationNode) ||
+      (IsMethodInvocation == NULL)              ||
+      (ArgCount == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check whether MethodInvocationNode is a method invocation.
+  if (!AmlNodeCompareOpCode (MethodInvocationNode, AML_METHOD_INVOC_OP, 0)) {
+    *IsMethodInvocation = FALSE;
+    *ArgCount = 0;
+    return EFI_SUCCESS;
+  }
+
+  // MethodInvocation := MethodInvocationOp NameString ArgumentCount
+  // MethodInvocationOp := Pseudo Opcode for Method Invocation
+  // NameString := Method Name
+  // ArgumentCount := ByteData (0 - 7)
+  NumArgsNode = (AML_DATA_NODE*)AmlGetFixedArgument (
+                                  (AML_OBJECT_NODE*)MethodInvocationNode,
+                                  EAmlParseIndexTerm1
+                                  );
+  if (!IS_AML_NODE_VALID (NumArgsNode)                ||
+      (NumArgsNode->Buffer == NULL)                   ||
+      (NumArgsNode->DataType != EAmlNodeDataTypeUInt) ||
+      (NumArgsNode->Size != 1)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+  *ArgCount = *NumArgsNode->Buffer;
+
+  *IsMethodInvocation = TRUE;
+  return EFI_SUCCESS;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..b082f2debde88da8a7dcabee7b4a8b5003d4c592
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h
@@ -0,0 +1,188 @@
+/** @file
+  AML Method Parser.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_METHOD_PARSER_H_
+#define AML_METHOD_PARSER_H_
+
+#include <AmlNodeDefines.h>
+#include <Stream/AmlStream.h>
+
+/** AML namespace reference node.
+
+  Namespace reference nodes allow to associate an AML absolute pathname
+  to the tree node defining this object in the namespace.
+
+  Namespace reference nodes are stored in a separate list. They are not part of
+  the tree.
+*/
+typedef struct AmlNameSpaceRefNode {
+  /// Double linked list.
+  /// This must be the first field in this structure.
+  LIST_ENTRY                Link;
+
+  /// Node part of the AML namespace. It must have the AML_IN_NAMESPACE
+  /// attribute.
+  CONST AML_OBJECT_NODE   * NodeRef;
+
+  /// Raw AML absolute pathname of the NodeRef.
+  /// This is a raw AML NameString (cf AmlNameSpace.c: A concatenated list
+  /// of 4 chars long names. The dual/multi NameString prefix have been
+  /// stripped.).
+  CONST CHAR8             * RawAbsolutePath;
+
+  /// Size of the raw AML absolute pathname buffer.
+  UINT32                    RawAbsolutePathSize;
+} AML_NAMESPACE_REF_NODE;
+
+/** Delete a list of namespace reference nodes.
+
+  @param  [in]  NameSpaceRefList    List of namespace reference nodes.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteNameSpaceRefList (
+  IN  LIST_ENTRY      * NameSpaceRefList
+  );
+
+
+#if !defined (MDEPKG_NDEBUG)
+/** Print the list of raw absolute paths of the NameSpace reference list.
+
+  @param  [in]    NameSpaceRefList    List of NameSpace reference nodes.
+**/
+VOID
+EFIAPI
+AmlDbgPrintNameSpaceRefList (
+  IN  CONST LIST_ENTRY    * NameSpaceRefList
+  );
+
+#endif // MDEPKG_NDEBUG
+
+/** Check whether a pathname is a method invocation.
+
+  If there is a matching method definition, returns the corresponding
+  NameSpaceRef node.
+
+  To do so, the NameSpaceRefList is keeping track of every namespace node
+  and its raw AML absolute path.
+  To check whether a pathname is a method invocation, a corresponding raw
+  absolute pathname is built. This raw absolute pathname is then compared
+  to the list of available pathnames. If a pathname defining a method
+  matches the scope of the input pathname, return.
+
+  @param  [in]  ParentNode          Parent node. Node to which the node to be
+                                    created will be attached.
+  @param  [in]  FStream             Forward stream pointing to the NameString
+                                    to find.
+  @param  [in]  NameSpaceRefList    List of NameSpaceRef nodes.
+  @param  [out] OutNameSpaceRefNode If the NameString pointed by FStream is
+                                    a method invocation, OutNameSpaceRefNode
+                                    contains the NameSpaceRef corresponding
+                                    to the method definition.
+                                    NULL otherwise.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlIsMethodInvocation (
+  IN  CONST AML_NODE_HEADER     * ParentNode,
+  IN  CONST AML_STREAM          * FStream,
+  IN  CONST LIST_ENTRY          * NameSpaceRefList,
+  OUT AML_NAMESPACE_REF_NODE   ** OutNameSpaceRefNode
+  );
+
+/** Create a namespace reference node and add it to the NameSpaceRefList.
+
+  When a namespace node is encountered, the namespace it defines must be
+  associated to the node. This allow to keep track of the nature of each
+  name present in the AML namespace.
+
+  In the end, this allows to recognize method invocations and parse the right
+  number of arguments after the method name.
+
+  @param [in]       Node              Namespace node.
+  @param [in, out]  NameSpaceRefList  List of namespace reference nodes.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlAddNameSpaceReference (
+  IN      CONST AML_OBJECT_NODE   * Node,
+  IN  OUT       LIST_ENTRY        * NameSpaceRefList
+  );
+
+/** Create a method invocation node.
+
+  The AML grammar does not attribute an OpCode/SubOpCode couple for
+  method invocations. This library is representing method invocations
+  as if they had one.
+
+  The AML encoding for method invocations in the ACPI specification 6.3 is:
+    MethodInvocation := NameString TermArgList
+  In this library, it is:
+    MethodInvocation := MethodInvocationOp NameString ArgumentCount TermArgList
+    ArgumentCount    := ByteData
+
+  When computing the size of a tree or serializing it, the additional data is
+  not taken into account (i.e. the MethodInvocationOp and the ArgumentCount).
+
+  Method invocation nodes have the AML_METHOD_INVOVATION attribute.
+
+  @param  [in]  NameSpaceRefNode          NameSpaceRef node pointing to the
+                                          the definition of the invoked
+                                          method.
+  @param  [in]  MethodInvocationName      Data node containing the method
+                                          invocation name.
+  @param  [out] MethodInvocationNodePtr   Created method invocation node.
+
+  @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
+AmlCreateMethodInvocationNode (
+  IN  CONST AML_NAMESPACE_REF_NODE   * NameSpaceRefNode,
+  IN        AML_DATA_NODE            * MethodInvocationName,
+  OUT       AML_OBJECT_NODE         ** MethodInvocationNodePtr
+  );
+
+/** Get the number of arguments of a method invocation node.
+
+  This function also allow to identify whether a node is a method invocation
+  node. If the input node is not a method invocation node, just return.
+
+  @param  [in]  MethodInvocationNode  Method invocation node.
+  @param  [out] IsMethodInvocation    Boolean stating whether the input
+                                      node is a method invocation.
+  @param  [out] ArgCount              Number of arguments of the method
+                                      invocation.
+                                      Set to 0 if MethodInvocationNode
+                                      is not a method invocation.
+
+  @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
+AmlGetMethodInvocationArgCount (
+  IN  CONST AML_OBJECT_NODE   * MethodInvocationNode,
+  OUT       BOOLEAN           * IsMethodInvocation,
+  OUT       UINT8             * ArgCount
+  );
+
+#endif // AML_METHOD_PARSER_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 20/30] DynamicTablesPkg: AML Field list parser
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (18 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 19/30] DynamicTablesPkg: AML Method parser Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 21/30] DynamicTablesPkg: AML Codegen Sami Mujawar
                   ` (10 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

The AML language allows defining field lists in a Definition
Block. Although Dynamic AML does not provide interfaces to
modify Field Lists; an AML template code may contain Field
lists and the AML parser must be capable of parsing and
representing the Field lists in the AML tree.

The AML parser creates an Object node that represents the
'Field Node'. The AML Field list parser creates an object
node for each field element parsed in the AML byte stream,
and adds them to the variable list of arguments of the
'Field Node'.

Nodes that can have a field list are referred as 'Field
nodes'. They have the AML_HAS_FIELD_LIST attribute set in
the AML encoding.

According to the ACPI 6.3 specification, s20.2.5.2 "Named
Objects Encoding", field elements can be:
 - NamedField           := NameSeg PkgLength;
 - ReservedField        := 0x00 PkgLength;
 - AccessField          := 0x01 AccessType AccessAttrib;
 - ConnectField         := <0x02 NameString> | <0x02 BufferData>;
 - ExtendedAccessField  := 0x03 AccessType ExtendedAccessAttrib
                             AccessLength.

A small set of opcodes describes the field elements. They are
referred as field opcodes. An AML_BYTE_ENCODING table has been
created for field OpCodes.
Field elements:
 - don't have a SubOpCode;
 - have at most 3 fixed arguments (as opposed to 6 for standard
     AML objects);
 - don't have a variable list of arguments;
 - only the NamedField field element is part of the AML namespace.

ConnectField's BufferData is a buffer node containing a single
resource data element.
NamedField field elements do not have an AML OpCode. NameSeg
starts with a Char type and can thus be differentiated from the
Opcodes for other fields.

A pseudo OpCode has been created to simplify the parser.

Following is a representation of a field node in an AML tree:
(FieldNode)
    \
     |- [0][1][3]                      # Fixed Arguments
     |- {(FldEl0)->(FldEl1)->...)}     # Variable Arguments

Where FldEl[n] is one of NamedField, ReservedField, AccessField,
ConnectField, ExtendedAccessField.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c | 375 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h |  77 ++++
 2 files changed, 452 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c
new file mode 100644
index 0000000000000000000000000000000000000000..c25ee22dc7c4b22b1448e11cf9bbd4e5e77c4316
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c
@@ -0,0 +1,375 @@
+/** @file
+  AML Field List Parser.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Parser/AmlFieldListParser.h>
+
+#include <AmlCoreInterface.h>
+#include <AmlDbgPrint/AmlDbgPrint.h>
+#include <Parser/AmlMethodParser.h>
+#include <Parser/AmlParser.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+
+/** Parse a field element.
+
+  The field elements this function can parse are one of:
+   - ReservedField;
+   - AccessField;
+   - ConnectField;
+   - ExtendedAccessField.
+  Indeed, the NamedField field element doesn't have an OpCode. Thus it needs
+  to be parsed differently.
+
+  @param  [in]      FieldByteEncoding       Field byte encoding to parse.
+  @param  [in, out] FieldNode               Field node to attach the field
+                                            element to.
+                                            Must have the AML_HAS_FIELD_LIST
+                                            attribute.
+  @param  [in, out] FStream                 Forward stream pointing to a field
+                                            element not being a named field.
+                                            The stream must not be at its end.
+  @param  [in, out] NameSpaceRefList        List of namespace reference nodes,
+                                            allowing to associate an absolute
+                                            path to a node in the 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.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseFieldElement (
+  IN      CONST AML_BYTE_ENCODING   * FieldByteEncoding,
+  IN  OUT       AML_OBJECT_NODE     * FieldNode,
+  IN  OUT       AML_STREAM          * FStream,
+  IN  OUT       LIST_ENTRY          * NameSpaceRefList
+  )
+{
+  EFI_STATUS          Status;
+
+  UINT8             * CurrPos;
+  AML_OBJECT_NODE   * NewNode;
+
+  UINT32              PkgLenOffset;
+  UINT32              PkgLenSize;
+
+  // Check whether the node is an Object Node and has a field list.
+  // The byte encoding must be a field element.
+  if ((FieldByteEncoding == NULL)                                   ||
+      ((FieldByteEncoding->Attribute & AML_IS_FIELD_ELEMENT) == 0)  ||
+      ((FieldByteEncoding->Attribute & AML_IS_PSEUDO_OPCODE) ==
+          AML_IS_PSEUDO_OPCODE)                                     ||
+      !AmlNodeHasAttribute (FieldNode, AML_HAS_FIELD_LIST)          ||
+      !IS_STREAM (FStream)                                          ||
+      IS_END_OF_STREAM (FStream)                                    ||
+      !IS_STREAM_FORWARD (FStream)                                  ||
+      (NameSpaceRefList == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CurrPos = AmlStreamGetCurrPos (FStream);
+  if (CurrPos == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Skip the field opcode (1 byte) as it is already in the FieldByteEncoding.
+  DumpRaw (CurrPos, 1);
+  Status = AmlStreamProgress (FStream, 1);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  CurrPos = AmlStreamGetCurrPos (FStream);
+  if (CurrPos == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Parse the PkgLen if available.
+  PkgLenSize = 0;
+  if ((FieldByteEncoding->Attribute & AML_HAS_PKG_LENGTH) ==
+        AML_HAS_PKG_LENGTH) {
+    PkgLenOffset = AmlGetPkgLength (CurrPos, &PkgLenSize);
+    if (PkgLenOffset == 0) {
+      ASSERT (0);
+      return EFI_INVALID_PARAMETER;
+    }
+
+    // Move stream forward as the PkgLen has been read.
+    DumpRaw (CurrPos, PkgLenOffset);
+    Status = AmlStreamProgress (FStream, PkgLenOffset);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Update the current position as PkgLen has been parsed.
+    CurrPos = AmlStreamGetCurrPos (FStream);
+  }
+
+  Status = AmlCreateObjectNode (
+             FieldByteEncoding,
+             PkgLenSize,
+             &NewNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Add the FieldElement to the Variable Argument List.
+  Status = AmlVarListAddTailInternal (
+             (AML_NODE_HEADER*)FieldNode,
+             (AML_NODE_HEADER*)NewNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    // Delete the sub-tree if the insertion failed.
+    // Otherwise its reference will be lost.
+    AmlDeleteTree ((AML_NODE_HEADER*)NewNode);
+    return Status;
+  }
+
+  // Some field elements do not have fixed arguments.
+  if (!IS_END_OF_STREAM (FStream)) {
+    // Parse the fixed arguments of the field element.
+    Status = AmlParseFixedArguments (
+              NewNode,
+              FStream,
+              NameSpaceRefList
+              );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+    }
+  }
+
+  return Status;
+}
+
+/** Parse a named field element.
+
+  Indeed, the NamedField field element doesn't have an OpCode. Thus it needs
+  to be parsed differently. NamedField field element start with a char.
+
+  @param  [in]      NamedFieldByteEncoding  Field byte encoding to parse.
+  @param  [in, out] FieldNode               Field node to attach the field
+                                            element to.
+                                            Must have the AML_HAS_FIELD_LIST
+                                            attribute.
+  @param  [in, out] FStream                 Forward stream pointing to a named
+                                            field element.
+                                            The stream must not be at its end.
+  @param  [in, out] NameSpaceRefList        List of namespace reference nodes,
+                                            allowing to associate an absolute
+                                            path to a node in the 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.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlParseNamedFieldElement (
+  IN      CONST AML_BYTE_ENCODING   * NamedFieldByteEncoding,
+  IN  OUT       AML_OBJECT_NODE     * FieldNode,
+  IN  OUT       AML_STREAM          * FStream,
+  IN  OUT       LIST_ENTRY          * NameSpaceRefList
+)
+{
+  EFI_STATUS          Status;
+  AML_OBJECT_NODE   * NewNode;
+
+  // Check whether the node is an Object Node and has a field list.
+  // The byte encoding must be a char.
+  if ((NamedFieldByteEncoding == NULL)                              ||
+      ((NamedFieldByteEncoding->Attribute & AML_IS_NAME_CHAR) == 0) ||
+      !AmlNodeHasAttribute (FieldNode, AML_HAS_FIELD_LIST)          ||
+      !IS_STREAM (FStream)                                          ||
+      IS_END_OF_STREAM (FStream)                                    ||
+      !IS_STREAM_FORWARD (FStream)                                  ||
+      (NameSpaceRefList == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Create a NamedField node.
+  Status = AmlCreateObjectNode (
+             AmlGetFieldEncodingByOpCode (AML_FIELD_NAMED_OP, 0),
+             0,
+             &NewNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Add the NamedField node to the variable argument list.
+  Status = AmlVarListAddTailInternal (
+             (AML_NODE_HEADER*)FieldNode,
+             (AML_NODE_HEADER*)NewNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    // Delete the sub-tree if the insertion failed.
+    // Otherwise its reference will be lost.
+    AmlDeleteTree ((AML_NODE_HEADER*)NewNode);
+    return Status;
+  }
+
+  // Parse the fixed arguments: [0]NameSeg, [1]PkgLen.
+  Status = AmlParseFixedArguments (
+             NewNode,
+             FStream,
+             NameSpaceRefList
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Add the NamedField to the namespace reference list.
+  Status = AmlAddNameSpaceReference (
+             NewNode,
+             NameSpaceRefList
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Parse the FieldList contained in the stream.
+
+  Create an object node for each field element parsed in the field list
+  available in the Stream, and add them to the variable list of arguments
+  of the FieldNode.
+
+  Nodes that can have a field list are referred as field nodes. They have the
+  AML_HAS_FIELD_LIST attribute.
+
+  According to the ACPI 6.3 specification, s20.2.5.2 "Named Objects Encoding",
+  field elements can be:
+   - NamedField           := NameSeg PkgLength;
+   - ReservedField        := 0x00 PkgLength;
+   - AccessField          := 0x01 AccessType AccessAttrib;
+   - ConnectField         := <0x02 NameString> | <0x02 BufferData>;
+   - ExtendedAccessField  := 0x03 AccessType ExtendedAccessAttrib AccessLength.
+
+  A small set of opcodes describes the field elements. They are referred as
+  field opcodes. An AML_BYTE_ENCODING table has been created for field OpCodes.
+  Field elements:
+   - don't have a SubOpCode;
+   - have at most 3 fixed arguments (as opposed to 6 for standard AML objects);
+   - don't have a variable list of arguments;
+   - only the NamedField field element is part of the AML namespace.
+
+  ConnectField's BufferData is a buffer node containing a single
+  resource data element.
+  NamedField field elements don't have an AML OpCode. NameSeg starts with a
+  Char type and can thus be differentiated from the Opcodes for other fields.
+  A pseudo OpCode has been created to simplify the parser.
+
+  The branch created from parsing a field node is as:
+  (FieldNode)
+      \
+       |- [FixedArg[0]][FixedArg[1]]                      # Fixed Arguments
+       |- {(FieldElement[0])->(FieldElement[1])->...)}    # Variable Arguments
+
+  With FieldElement[n] being one of NamedField, ReservedField, AccessField,
+  ConnectField, ExtendedAccessField.
+
+  @param  [in]  FieldNode         Field node.
+                                  Must have the AML_HAS_FIELD_LIST
+                                  attribute.
+  @param  [in]  FStream           Forward stream pointing to a field list.
+                                  The stream must not be at its end.
+  @param  [in]  NameSpaceRefList  List of namespace reference nodes,
+                                  allowing to associate an absolute
+                                  path to a node in the 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
+AmlParseFieldList (
+  IN  AML_OBJECT_NODE   * FieldNode,
+  IN  AML_STREAM        * FStream,
+  IN  LIST_ENTRY        * NameSpaceRefList
+  )
+{
+  EFI_STATUS                  Status;
+
+  UINT8                     * CurrPos;
+  CONST AML_BYTE_ENCODING   * FieldByteEncoding;
+  CONST AML_BYTE_ENCODING   * NamedFieldByteEncoding;
+
+  // Check whether the node is an Object Node and has a field list.
+  if (!AmlNodeHasAttribute (FieldNode, AML_HAS_FIELD_LIST)  ||
+      !IS_STREAM (FStream)                                  ||
+      IS_END_OF_STREAM (FStream)                            ||
+      !IS_STREAM_FORWARD (FStream)                          ||
+      (NameSpaceRefList == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Iterate through the field elements, creating nodes
+  // and adding them to the variable list of elements of Node.
+  while (!IS_END_OF_STREAM (FStream)) {
+    CurrPos = AmlStreamGetCurrPos (FStream);
+
+    // Check for a field opcode.
+    FieldByteEncoding = AmlGetFieldEncoding (CurrPos);
+    if (FieldByteEncoding != NULL) {
+      Status = AmlParseFieldElement (
+                 FieldByteEncoding,
+                 FieldNode,
+                 FStream,
+                 NameSpaceRefList
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+    } else {
+      // Handle the case of Pseudo OpCodes.
+      // NamedField has a Pseudo OpCode and starts with a NameChar. Therefore,
+      // call AmlGetByteEncoding() to check that the encoding is NameChar.
+      NamedFieldByteEncoding = AmlGetByteEncoding (CurrPos);
+      if ((NamedFieldByteEncoding != NULL) &&
+          (NamedFieldByteEncoding->Attribute & AML_IS_NAME_CHAR)) {
+        // This is a NamedField field element since it is starting with a char.
+        Status = AmlParseNamedFieldElement (
+                   NamedFieldByteEncoding,
+                   FieldNode,
+                   FStream,
+                   NameSpaceRefList
+                   );
+        if (EFI_ERROR (Status)) {
+          ASSERT (0);
+          return Status;
+        }
+      } else {
+        // A field opcode or an AML byte encoding is expected.
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+    }
+  } // while
+
+  return EFI_SUCCESS;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..576f6c41d5620468b524b62684f67af1dacee24d
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h
@@ -0,0 +1,77 @@
+/** @file
+  AML Field List.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_FIELD_LIST_PARSER_H_
+#define AML_FIELD_LIST_PARSER_H_
+
+#include <AmlNodeDefines.h>
+#include <Stream/AmlStream.h>
+
+/** Parse the FieldList contained in the stream.
+
+  Create an object node for each field element parsed in the field list
+  available in the Stream, and add them to the variable list of arguments
+  of the FieldNode.
+
+  Nodes that can have a field list are referred as field nodes. They have the
+  AML_HAS_FIELD_LIST attribute.
+
+  According to the ACPI 6.3 specification, s20.2.5.2 "Named Objects Encoding",
+  field elements can be:
+   - NamedField           := NameSeg PkgLength;
+   - ReservedField        := 0x00 PkgLength;
+   - AccessField          := 0x01 AccessType AccessAttrib;
+   - ConnectField         := <0x02 NameString> | <0x02 BufferData>;
+   - ExtendedAccessField  := 0x03 AccessType ExtendedAccessAttrib AccessLength.
+
+  A small set of opcodes describes the field elements. They are referred as
+  field opcodes. An AML_BYTE_ENCODING table has been created for field OpCodes.
+  Field elements:
+   - don't have a SubOpCode;
+   - have at most 3 fixed arguments (as opposed to 6 for standard AML objects);
+   - don't have a variable list of arguments;
+   - only the NamedField field element is part of the AML namespace.
+
+  ConnectField's BufferData is a buffer node containing a single
+  resource data element.
+  NamedField field elements don't have an AML OpCode. NameSeg starts with a
+  Char type and can thus be differentiated from the Opcodes for other fields.
+  A pseudo OpCode has been created to simplify the parser.
+
+  The branch created from parsing a field node is as:
+  (FieldNode)
+      \
+       |- [FixedArg[0]][FixedArg[1]]                      # Fixed Arguments
+       |- {(FieldElement[0])->(FieldElement[1])->...)}    # Variable Arguments
+
+  With FieldElement[n] being one of NamedField, ReservedField, AccessField,
+  ConnectField, ExtendedAccessField.
+
+  @param  [in]  FieldNode         Field node.
+                                  Must have the AML_HAS_FIELD_LIST
+                                  attribute.
+  @param  [in]  FStream           Forward stream pointing to a field list.
+                                  The stream must not be at its end.
+  @param  [in]  NameSpaceRefList  List of namespace reference nodes,
+                                  allowing to associate an absolute
+                                  path to a node in the 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
+AmlParseFieldList (
+  IN  AML_OBJECT_NODE   * FieldNode,
+  IN  AML_STREAM        * FStream,
+  IN  LIST_ENTRY        * NameSpaceRefList
+  );
+
+#endif // AML_FIELD_LIST_PARSER_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 21/30] DynamicTablesPkg: AML Codegen
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (19 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 20/30] DynamicTablesPkg: AML Field list parser Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 22/30] DynamicTablesPkg: AML Resource Data Codegen Sami Mujawar
                   ` (9 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

AML Codegen is a Dynamic AML technique that facilitates
generation of small segments of AML code. The AML code
generated using AML Codegen is represented as nodes in
the AML Tree.

Some examples where AML Codegen can be used are:
 - AML Codegen APIs can be used to generate a simple
   AML tree.
 - An AML template can be parsed to create an AML
   tree. This AML Tree can be searched to locate a
   node that needs updating. The AML Codegen APIs
   can be used to attach new AML nodes.
 - A combination of AML Fixup and AML Codegen can
   be used to generate an AML tree.

   The AML tree can then be serialised as a Definition
   Block table.

Following AML Codegen APIs are implemented:
 - AmlCodeGenDefinitionBlock()
 - AmlCodeGenScope()
 - AmlCodeGenNameString()
 - AmlCodeGenNameInteger()
 - AmlCodeGenDevice()

These AML Codegen APIs in combination with AML Resource
Data Codegen APIs can be used to generate a simple AML
tree.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c | 701 ++++++++++++++++++++
 1 file changed, 701 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c b/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c
new file mode 100644
index 0000000000000000000000000000000000000000..d6d9f5dfe839aa8ef79153459ec9d2c2ffe72316
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c
@@ -0,0 +1,701 @@
+/** @file
+  AML Code Generation.
+
+  Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+
+#include <AcpiTableGenerator.h>
+
+#include <AmlCoreInterface.h>
+#include <AmlEncoding/Aml.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+#include <String/AmlString.h>
+#include <Utils/AmlUtility.h>
+
+/** 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,
+  IN  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] 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    ** 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;
+}
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 22/30] DynamicTablesPkg: AML Resource Data Codegen
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (20 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 21/30] DynamicTablesPkg: AML Codegen Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 23/30] DynamicTablesPkg: AML Core interface Sami Mujawar
                   ` (8 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

AML Codegen is a Dynamic AML technique that facilitates
generation of small segments of AML code. The AML code
generated using AML Codegen is represented as nodes in
the AML Tree.

AML Resource Data Codegen implements interfaces required
for generating Resource Data elements that can be attached
to an AML tree.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c | 256 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h |  59 +++++
 2 files changed, 315 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c b/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c
new file mode 100644
index 0000000000000000000000000000000000000000..9e7a508e60721558ab59b375dbd526c7066d7329
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c
@@ -0,0 +1,256 @@
+/** @file
+  AML Resource Data Code Generation.
+
+  Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+  - Rd or RD   - Resource Data
+  - Rds or RDS - Resource Data Small
+  - Rdl or RDL - Resource Data Large
+**/
+
+#include <AmlNodeDefines.h>
+#include <CodeGen/AmlResourceDataCodeGen.h>
+
+#include <AmlCoreInterface.h>
+#include <AmlDefines.h>
+#include <Api/AmlApiHelper.h>
+#include <Tree/AmlNode.h>
+#include <ResourceData/AmlResourceData.h>
+
+/** If ParentNode is not NULL, append RdNode.
+    If NewRdNode is not NULL, update its value to RdNode.
+
+  @param [in]  RdNode       Newly created Resource Data node.
+  @param [in]  ParentNode   If not NULL, add the generated node
+                            to the end of the variable list of
+                            argument of the ParentNode, but
+                            before the "End Tag" Resource Data.
+                            Must be a BufferOpNode.
+  @param [out] NewRdNode    If not NULL, update the its value to RdNode.
+
+  @retval  EFI_SUCCESS            The function completed successfully.
+  @retval  EFI_INVALID_PARAMETER  Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+LinkRdNode (
+  IN  AML_DATA_NODE      * RdNode,
+  IN  AML_OBJECT_NODE    * ParentNode,
+  IN  AML_DATA_NODE     ** NewRdNode
+  )
+{
+  EFI_STATUS    Status;
+  EFI_STATUS    Status1;
+
+  if (NewRdNode != NULL) {
+    *NewRdNode = RdNode;
+  }
+
+  // Add RdNode as the last element, but before the EndTag.
+  if (ParentNode != NULL) {
+    Status = AmlAppendRdNode (ParentNode, RdNode);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      Status1 = AmlDeleteTree ((AML_NODE_HEADER*)RdNode);
+      ASSERT_EFI_ERROR (Status1);
+      // Return original error.
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Code generation for the "Interrupt ()" ASL function.
+
+  This function creates a Resource Data element corresponding to the
+  "Interrupt ()" ASL function and stores it in an AML Data Node.
+
+  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.
+
+  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.
+
+  @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.
+  @param [in]  ParentNode          If not NULL, add the generated node
+                                   to the end of the variable list of
+                                   argument of the ParentNode, but
+                                   before the "End Tag" Resource Data.
+                                   Must be a BufferOpNode.
+  @param  [out] NewRdNode          If success, contains the generated node.
+
+  @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
+AmlCodeGenInterrupt (
+  IN  BOOLEAN             ResourceConsumer,
+  IN  BOOLEAN             EdgeTriggered,
+  IN  BOOLEAN             ActiveLow,
+  IN  BOOLEAN             Shared,
+  IN  UINT32            * IrqList,
+  IN  UINT8               IrqCount,
+  IN  AML_OBJECT_NODE   * ParentNode,   OPTIONAL
+  OUT AML_DATA_NODE    ** NewRdNode     OPTIONAL
+  )
+{
+  EFI_STATUS                               Status;
+
+  AML_DATA_NODE                          * RdNode;
+  EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR   RdInterrupt;
+  UINT32                                 * FirstInterrupt;
+
+  if ((IrqList == NULL) ||
+      (IrqCount == 0)   ||
+      ((ParentNode == NULL) && (NewRdNode == NULL))) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  RdInterrupt.Header.Header.Bits.Name =
+    ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME;
+  RdInterrupt.Header.Header.Bits.Type = ACPI_LARGE_ITEM_FLAG;
+  RdInterrupt.Header.Length = sizeof (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR) -
+                                sizeof (ACPI_LARGE_RESOURCE_HEADER);
+  RdInterrupt.InterruptVectorFlags = (ResourceConsumer ? BIT0 : 0) |
+                                     (EdgeTriggered ? BIT1 : 0)    |
+                                     (ActiveLow ? BIT2 : 0)        |
+                                     (Shared ? BIT3 : 0);
+  RdInterrupt.InterruptTableLength = IrqCount;
+
+  // Get the address of the first interrupt field.
+  FirstInterrupt = RdInterrupt.InterruptNumber;
+
+  // Copy the list of interrupts.
+  CopyMem (FirstInterrupt, IrqList, (sizeof (UINT32) * IrqCount));
+
+  Status = AmlCreateDataNode (
+             EAmlNodeDataTypeResourceData,
+             (UINT8*)&RdInterrupt,
+             sizeof (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR),
+             &RdNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  return LinkRdNode (RdNode, ParentNode, NewRdNode);
+}
+
+/** 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 () {
+               ...
+             }
+
+  @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
+  )
+{
+  EFI_STATUS              Status;
+
+  AML_OBJECT_NODE_HANDLE  BufferOpNode;
+
+  if ((IrqList == NULL)                                                   ||
+      (IrqCount == 0)                                                     ||
+      (!AmlNodeHasOpCode (NameOpCrsNode, AML_NAME_OP, 0))                 ||
+      (!AmlNameOpCompareName (NameOpCrsNode, "_CRS"))) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // 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;
+  }
+
+  // Generate the Extended Interrupt Resource Data node,
+  // and attach it as the last variable argument of the BufferOpNode.
+  Status = AmlCodeGenInterrupt (
+             ResourceConsumer,
+             EdgeTriggered,
+             ActiveLow,
+             Shared,
+             IrqList,
+             IrqCount,
+             BufferOpNode,
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h b/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h
new file mode 100644
index 0000000000000000000000000000000000000000..08364db4431f8d41c70f220ee7417453ceb2496d
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h
@@ -0,0 +1,59 @@
+/** @file
+  AML Resource Data Code Generation.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_RESOURCE_DATA_CODE_GEN_H_
+#define AML_RESOURCE_DATA_CODE_GEN_H_
+
+/** Code generation for the "Interrupt ()" ASL function.
+
+  This function creates a Resource Data element corresponding to the
+  "Interrupt ()" ASL function and stores it in an AML Data Node.
+
+  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.
+
+  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.
+
+  @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.
+  @param [in]  ParentNode          If not NULL, add the generated node
+                                   to the end of the variable list of
+                                   argument of the ParentNode, but
+                                   before the "End Tag" Resource Data.
+                                   Must be a BufferOpNode.
+  @param  [out] NewRdNode          If success, contains the generated node.
+
+  @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
+AmlCodeGenInterrupt (
+  IN  BOOLEAN             ResourceConsumer,
+  IN  BOOLEAN             EdgeTriggered,
+  IN  BOOLEAN             ActiveLow,
+  IN  BOOLEAN             Shared,
+  IN  UINT32            * IrqList,
+  IN  UINT8               IrqCount,
+  IN  AML_OBJECT_NODE   * ParentNode,   OPTIONAL
+  OUT AML_DATA_NODE    ** NewRdNode     OPTIONAL
+  );
+
+#endif // AML_RESOURCE_DATA_CODE_GEN_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 23/30] DynamicTablesPkg: AML Core interface
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (21 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 22/30] DynamicTablesPkg: AML Resource Data Codegen Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 24/30] DynamicTablesPkg: AmlLib APIs Sami Mujawar
                   ` (7 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

AML Core interface APIs are internal APIs of the
AmlLib library. These APIs can be used to:
 - Create/Delete/Clone an AML tree/node
 - Get/update Fixed and Variable arguments
 - Serialize an AML tree.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h | 767 ++++++++++++++++++++
 1 file changed, 767 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h b/DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h
new file mode 100644
index 0000000000000000000000000000000000000000..9905cfe551b50eccb3576ff9bdb6a1b582199601
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h
@@ -0,0 +1,767 @@
+/** @file
+  AML Core Interface.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_CORE_INTERFACE_H_
+#define AML_CORE_INTERFACE_H_
+
+/* This header file does not include internal Node definition,
+   i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions
+   must be included by the caller file. The function prototypes must
+   only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node
+   definitions.
+   This allows to keep the functions defined here both internal and
+   potentially external. If necessary, any function of this file can
+   be exposed externally.
+   The Api folder is internal to the AmlLib, but should only use these
+   functions. They provide a "safe" way to interact with the AmlLib.
+*/
+
+#include <AmlDefines.h>
+#include <Include/Library/AmlLib/AmlLib.h>
+#include <ResourceData/AmlResourceData.h>
+
+/**
+  @defgroup CoreApis Core APIs
+  @ingroup AMLLib
+  @{
+    Core APIs are the main APIs of the library. They allow to:
+     - Create an AML tree;
+     - Delete an AML tree;
+     - Clone an AML tree/node;
+     - Serialize an AML tree (convert the tree to a DSDT/SSDT table).
+  @}
+*/
+
+/** Serialize a tree to create a DSDT/SSDT table.
+
+  If:
+   - the content of BufferSize is >= to the size needed to serialize the
+     definition block;
+   - Buffer is not NULL;
+   first serialize the ACPI DSDT/SSDT header from the root node,
+   then serialize the AML blob from the rest of the tree.
+
+  The content of BufferSize is always updated to the size needed to
+  serialize the definition block.
+
+  @ingroup CoreApis
+
+  @param  [in]      RootNode    Pointer to a root node.
+  @param  [in]      Buffer      Buffer to write the DSDT/SSDT table to.
+                                If Buffer is NULL, the size needed to
+                                serialize the DSDT/SSDT table is returned
+                                in BufferSize.
+  @param  [in, out] BufferSize  Pointer holding the size of the Buffer.
+                                Its content is always updated to the size
+                                needed to serialize the DSDT/SSDT table.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
+**/
+EFI_STATUS
+EFIAPI
+AmlSerializeTree (
+  IN      AML_ROOT_NODE_HANDLE    RootNode,
+  IN      UINT8                 * Buffer,     OPTIONAL
+  IN  OUT UINT32                * BufferSize
+  );
+
+/** Clone a node.
+
+  This function does not clone the children nodes.
+  The cloned node returned is not attached to any tree.
+
+  @ingroup CoreApis
+
+  @param  [in]  Node        Pointer to a node.
+  @param  [out] ClonedNode  Pointer holding the cloned node.
+
+  @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
+AmlCloneNode (
+  IN  AML_NODE_HANDLE   Node,
+  OUT AML_NODE_HANDLE * ClonedNode
+  );
+
+/**
+  @defgroup TreeModificationApis Tree modification APIs
+  @ingroup AMLLib
+  @{
+    Tree modification APIs allow to add/remove/replace nodes that are in a
+    variable list of arguments.
+
+    No interface is provided to add/remove/replace nodes that are in a fixed
+    list of arguments. Indeed, these nodes are the spine of the tree and a
+    mismanipulation would make the tree inconsistent.
+
+    It is however possible to modify the content of fixed argument nodes via
+    @ref NodeInterfaceApis APIs.
+  @}
+*/
+
+/** Remove the Node from its parent's variable list of arguments.
+
+  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 TreeModificationApis
+
+  @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
+AmlRemoveNodeFromVarArgList (
+  IN  AML_NODE_HANDLE   Node
+  );
+
+/** Add the NewNode to the head of the variable list of arguments
+    of the ParentNode.
+
+  @ingroup TreeModificationApis
+
+  @param  [in]  ParentNode  Pointer to the parent node.
+                            Must be a root or an object node.
+  @param  [in]  NewNode     Pointer to the node to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlVarListAddHead (
+  IN  AML_NODE_HANDLE   ParentNode,
+  IN  AML_NODE_HANDLE   NewNode
+  );
+
+/** Add the NewNode to the tail of the variable list of arguments
+    of the ParentNode.
+
+  @ingroup TreeModificationApis
+
+  @param  [in]  ParentNode  Pointer to the parent node.
+                            Must be a root or an object node.
+  @param  [in]  NewNode     Pointer to the node to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlVarListAddTail (
+  IN  AML_NODE_HANDLE   ParentNode,
+  IN  AML_NODE_HANDLE   NewNode
+  );
+
+/** Add the NewNode before the Node in the list of variable
+    arguments of the Node's parent.
+
+  @ingroup TreeModificationApis
+
+  @param  [in]  Node      Pointer to a node.
+                          Must be a root or an object node.
+  @param  [in]  NewNode   Pointer to the node to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlVarListAddBefore (
+  IN  AML_NODE_HANDLE   Node,
+  IN  AML_NODE_HANDLE   NewNode
+  );
+
+/** Add the NewNode after the Node in the variable list of arguments
+    of the Node's parent.
+
+  @ingroup TreeModificationApis
+
+  @param  [in]  Node      Pointer to a node.
+                          Must be a root or an object node.
+  @param  [in]  NewNode   Pointer to the node to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlVarListAddAfter (
+  IN  AML_NODE_HANDLE   Node,
+  IN  AML_NODE_HANDLE   NewNode
+  );
+
+/** Append a Resource Data node to the BufferOpNode.
+
+  The Resource Data node is added at the end of the variable
+  list of arguments of the BufferOpNode, but before the End Tag.
+  If no End Tag is found, the function returns an error.
+
+  @param  [in]  BufferOpNode  Buffer node containing resource data elements.
+  @param  [in]  NewRdNode     The new Resource Data node to add.
+
+  @retval  EFI_SUCCESS            The function completed successfully.
+  @retval  EFI_INVALID_PARAMETER  Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlAppendRdNode (
+  IN  AML_OBJECT_NODE_HANDLE   BufferOpNode,
+  IN  AML_DATA_NODE_HANDLE     NewRdNode
+  );
+
+/** Replace the OldNode, which is in a variable list of arguments,
+    with the NewNode.
+
+  Note: This function unlinks the OldNode from the tree. It is the callers
+        responsibility to delete the OldNode if needed.
+
+  @ingroup TreeModificationApis
+
+  @param  [in]  OldNode   Pointer to the node to replace.
+                          Must be a data node or an object node.
+  @param  [in]  NewNode   The new node to insert.
+                          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
+AmlReplaceVariableArgument (
+  IN  AML_NODE_HANDLE   OldNode,
+  IN  AML_NODE_HANDLE   NewNode
+  );
+
+/**
+  @defgroup NodeInterfaceApis Node Interface APIs
+  @ingroup AMLLib
+  @{
+    Node Interface APIs allow to query information from a node. Some functions
+    expect a specific node type among the root/object/data node types.
+
+    For instance, AmlGetRootNodeInfo expects to receive a root node.
+
+    E.g.: Query the node type, the ACPI header stored in the root node,
+          the OpCode/SubOpCode/PkgLen of an object node, the type of data
+          stored in a data node, etc.
+
+    These APIs also allow to update some information.
+
+    E.g.: The ACPI header stored in the root node, the buffer of a data node.
+
+    The information of object nodes and the data type of data nodes cannot be
+    modified. This prevents the creation of an inconsistent tree.
+
+    It is however possible to remove a node from a variable list of arguments
+    and replace it. Use the @ref TreeModificationApis APIs for this.
+  @}
+*/
+
+/** Returns the tree node type (Root/Object/Data).
+
+  @ingroup NodeInterfaceApis
+
+  @param [in] Node  Pointer to a Node.
+
+  @return The node type.
+           EAmlNodeUnknown if invalid parameter.
+**/
+EAML_NODE_TYPE
+EFIAPI
+AmlGetNodeType (
+  IN  AML_NODE_HANDLE   Node
+  );
+
+/** Get the RootNode information.
+    The Node must be a root node.
+
+  @ingroup NodeInterfaceApis
+
+  @param  [in]  RootNode          Pointer to a root node.
+  @param  [out] SdtHeaderBuffer   Buffer to copy the ACPI DSDT/SSDT header to.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetRootNodeInfo (
+  IN  AML_ROOT_NODE_HANDLE            RootNode,
+  OUT EFI_ACPI_DESCRIPTION_HEADER   * SdtHeaderBuffer
+  );
+
+/** Get the ObjectNode information.
+    The Node must be an object node.
+
+  @ingroup NodeInterfaceApis
+
+  @param  [in]  ObjectNode        Pointer to an object node.
+  @param  [out] OpCode            Pointer holding the OpCode.
+                                  Optional, can be NULL.
+  @param  [out] SubOpCode         Pointer holding the SubOpCode.
+                                  Optional, can be NULL.
+  @param  [out] PkgLen            Pointer holding the PkgLen.
+                                  The PkgLen is 0 for nodes
+                                  not having the Pkglen attribute.
+                                  Optional, can be NULL.
+  @param  [out] IsNameSpaceNode   Pointer holding TRUE if the node is defining
+                                  or changing the NameSpace scope.
+                                  E.g.: The "Name ()" and "Scope ()" ASL
+                                  statements add/modify the NameSpace scope.
+                                  Their corresponding node are NameSpace nodes.
+                                  Optional, can be NULL.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetObjectNodeInfo (
+  IN  AML_OBJECT_NODE_HANDLE    ObjectNode,
+  OUT UINT8                   * OpCode,           OPTIONAL
+  OUT UINT8                   * SubOpCode,        OPTIONAL
+  OUT UINT32                  * PkgLen,           OPTIONAL
+  OUT BOOLEAN                 * IsNameSpaceNode   OPTIONAL
+  );
+
+/** Returns the count of the fixed arguments for the input Node.
+
+  @ingroup NodeInterfaceApis
+
+  @param  [in]  Node  Pointer to an object node.
+
+  @return Number of fixed arguments of the object node.
+          Return 0 if the node is not an object node.
+**/
+UINT8
+AmlGetFixedArgumentCount (
+  IN  AML_OBJECT_NODE_HANDLE  Node
+  );
+
+/** Get the data type of the DataNode.
+    The Node must be a data node.
+
+  @ingroup NodeInterfaceApis
+
+  @param  [in]  DataNode  Pointer to a data node.
+  @param  [out] DataType  Pointer holding the data type of the data buffer.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetNodeDataType (
+  IN  AML_DATA_NODE_HANDLE    DataNode,
+  OUT EAML_NODE_DATA_TYPE   * DataType
+  );
+
+/** Get the descriptor Id of the resource data element
+    contained in the DataNode.
+
+  The Node must be a data node.
+  The Node must have the resource data type, i.e. have the
+  EAmlNodeDataTypeResourceData data type.
+
+  @ingroup NodeInterfaceApis
+
+  @param  [in]  DataNode          Pointer to a data node containing a
+                                  resource data element.
+  @param  [out] ResourceDataType  Pointer holding the descriptor Id of
+                                  the resource data.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetResourceDataType (
+  IN  AML_DATA_NODE_HANDLE    DataNode,
+  OUT AML_RD_HEADER         * ResourceDataType
+  );
+
+/** Get the data buffer and size of the DataNode.
+    The Node must be a data node.
+
+  BufferSize is always updated to the size of buffer of the DataNode.
+
+  If:
+   - the content of BufferSize is >= to the DataNode's buffer size;
+   - Buffer is not NULL;
+  then copy the content of the DataNode's buffer in Buffer.
+
+  @ingroup NodeInterfaceApis
+
+  @param  [in]      DataNode      Pointer to a data node.
+  @param  [out]     Buffer        Buffer to write the data to.
+                                  Optional, if NULL, only update BufferSize.
+  @param  [in, out] BufferSize    Pointer holding:
+                                   - At entry, the size of the Buffer;
+                                   - At exit, the size of the DataNode's
+                                     buffer size.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+  @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetDataNodeBuffer (
+  IN      AML_DATA_NODE_HANDLE    DataNode,
+      OUT UINT8                 * Buffer,     OPTIONAL
+  IN  OUT UINT32                * BufferSize
+  );
+
+/** Update the ACPI DSDT/SSDT table header.
+
+  The input SdtHeader information is copied to the tree RootNode.
+  The table Length field is automatically updated.
+  The checksum field is only updated when serializing the tree.
+
+  @ingroup NodeInterfaceApis
+
+  @param  [in]  RootNode    Pointer to a root node.
+  @param  [in]  SdtHeader   Pointer to an ACPI DSDT/SSDT table header.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpdateRootNode (
+  IN        AML_ROOT_NODE_HANDLE            RootNode,
+  IN  CONST EFI_ACPI_DESCRIPTION_HEADER   * SdtHeader
+  );
+
+/** Update an object node representing an integer with a new value.
+
+  The object node must have one of the following OpCodes:
+   - AML_BYTE_PREFIX
+   - AML_WORD_PREFIX
+   - AML_DWORD_PREFIX
+   - AML_QWORD_PREFIX
+   - AML_ZERO_OP
+   - AML_ONE_OP
+
+  The following OpCode is not supported:
+   - AML_ONES_OP
+
+  @param  [in] IntegerOpNode   Pointer an object node containing an integer.
+                               Must not be an object node with an AML_ONES_OP
+                               OpCode.
+  @param  [in] NewInteger      New integer value to set.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpdateInteger (
+  IN  AML_OBJECT_NODE_HANDLE    IntegerOpNode,
+  IN  UINT64                    NewInteger
+  );
+
+/** Update the buffer of a data node.
+
+  Note: The data type of the buffer's content must match the data type of the
+        DataNode. This is a hard restriction to prevent undesired behaviour.
+
+  @ingroup NodeInterfaceApis
+
+  @param  [in]  DataNode  Pointer to a data node.
+  @param  [in]  DataType  Data type of the Buffer's content.
+  @param  [in]  Buffer    Buffer containing the new data. The content of
+                          the Buffer is copied.
+  @param  [in]  Size      Size of the Buffer.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_UNSUPPORTED         Operation not supporter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpdateDataNode (
+  IN  AML_DATA_NODE_HANDLE    DataNode,
+  IN  EAML_NODE_DATA_TYPE     DataType,
+  IN  UINT8                 * Buffer,
+  IN  UINT32                  Size
+  );
+
+/**
+  @defgroup NavigationApis Navigation APIs
+  @ingroup AMLLib
+  @{
+    Navigation APIs allow to navigate in the AML tree. There are different
+    ways to navigate in the tree by:
+     - Direct relation (@ref CoreNavigationApis);
+     - Enumeration: enumerate all the nodes and call a callback function
+       (@ref EnumerationApis);
+     - Iteration: instantiate an iterator and use it to navigate
+       (@ref IteratorApis);
+     - NameSpace path: use the AML namespace to navigate the tree
+       (@ref NameSpaceApis).
+  @}
+*/
+
+/**
+  @defgroup CoreNavigationApis Core Navigation APIs
+  @ingroup NavigationApis
+  @{
+    Core Navigation APIs allow to get a node by specifying a relation.
+
+    E.g.: Get the parent, the n-th fixed argument, the next variable
+          argument, etc.
+  @}
+*/
+
+/** Get the parent node of the input Node.
+
+  @ingroup CoreNavigationApis
+
+  @param [in] Node  Pointer to a node.
+
+  @return The parent node of the input Node.
+          NULL otherwise.
+**/
+AML_NODE_HANDLE
+EFIAPI
+AmlGetParent (
+  IN  AML_NODE_HANDLE   Node
+  );
+
+/** Get the node at the input Index in the fixed argument list of the input
+    ObjectNode.
+
+  @ingroup CoreNavigationApis
+
+  @param  [in]  ObjectNode  Pointer to an object node.
+  @param  [in]  Index       The Index of the fixed argument to get.
+
+  @return The node at the input Index in the fixed argument list
+          of the input ObjectNode.
+          NULL otherwise, e.g. if the node is not an object node, or no
+          node is available at this Index.
+**/
+AML_NODE_HANDLE
+EFIAPI
+AmlGetFixedArgument (
+  IN  AML_OBJECT_NODE_HANDLE  ObjectNode,
+  IN  EAML_PARSE_INDEX        Index
+  );
+
+/** Get the sibling node among the nodes being in
+    the same variable argument list.
+
+  (ParentNode)  /-i                 # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]              # Fixed Arguments
+       |- {(VarArgNode)->(f)->(g)}  # Variable Arguments
+             \
+              \-h                   # Child of variable argument e
+
+  Node must be in a variable list of arguments.
+  Traversal Order: VarArgNode, f, g, NULL
+
+  @ingroup CoreNavigationApis
+
+  @param  [in]  VarArgNode  Pointer to a node.
+                            Must be in a variable list of arguments.
+
+  @return The next node after VarArgNode in the variable list of arguments.
+          Return NULL if
+          - VarArgNode is the last node of the list, or
+          - VarArgNode is not part of a variable list of arguments.
+**/
+AML_NODE_HANDLE
+EFIAPI
+AmlGetSiblingVariableArgument (
+  IN  AML_NODE_HANDLE   VarArgNode
+  );
+
+/** Get the next variable argument.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: e, f, g, NULL
+
+  @ingroup CoreNavigationApis
+
+  @param  [in]  Node        Pointer to a Root node or Object Node.
+  @param  [in]  CurrVarArg  Pointer to the Current Variable Argument.
+
+  @return The node after the CurrVarArg in the variable list of arguments.
+          If CurrVarArg is NULL, return the first node of the
+          variable argument list.
+          Return NULL if
+          - CurrVarArg is the last node of the list, or
+          - Node does not have a variable list of arguments.
+**/
+AML_NODE_HANDLE
+EFIAPI
+AmlGetNextVariableArgument (
+  IN  AML_NODE_HANDLE   Node,
+  IN  AML_NODE_HANDLE   CurrVarArg
+  );
+
+/** Get the previous variable argument.
+
+  (Node)        /-i           # Child of fixed argument b
+      \        /
+       |- [a][b][c][d]        # Fixed Arguments
+       |- {(e)->(f)->(g)}     # Variable Arguments
+             \
+              \-h             # Child of variable argument e
+
+  Traversal Order: g, f, e, NULL
+
+  @ingroup CoreNavigationApis
+
+  @param  [in]  Node        Pointer to a root node or an object node.
+  @param  [in]  CurrVarArg  Pointer to the Current Variable Argument.
+
+  @return The node before the CurrVarArg in the variable list of
+          arguments.
+          If CurrVarArg is NULL, return the last node of the
+          variable list of arguments.
+          Return NULL if:
+          - CurrVarArg is the first node of the list, or
+          - Node doesn't have a variable list of arguments.
+**/
+AML_NODE_HANDLE
+EFIAPI
+AmlGetPreviousVariableArgument (
+  IN  AML_NODE_HANDLE   Node,
+  IN  AML_NODE_HANDLE   CurrVarArg
+  );
+
+/**
+  @defgroup EnumerationApis Enumeration APIs
+  @ingroup NavigationApis
+  @{
+    Enumeration APIs are navigation APIs, allowing to call a callback function
+    on each node enumerated. Nodes are enumerated in the AML bytestream order,
+    i.e. in a depth first order.
+  @}
+*/
+
+/**
+  Callback function prototype used when iterating through the tree.
+
+  @ingroup EnumerationApis
+
+  @param  [in]      Node      The Node currently being processed.
+  @param  [in, out] Context   A context for the callback function.
+                                Can be optional.
+  @param  [in, out] Status    End the enumeration if pointing to a value
+                                evaluated to TRUE.
+                                Can be optional.
+
+  @retval TRUE if the enumeration can continue or has finished without
+          interruption.
+  @retval FALSE if the enumeration needs to stopped or has stopped.
+**/
+typedef
+BOOLEAN
+(EFIAPI * EDKII_AML_TREE_ENUM_CALLBACK) (
+  IN       AML_NODE_HANDLE     Node,
+  IN  OUT  VOID              * Context,    OPTIONAL
+  IN  OUT  EFI_STATUS        * Status      OPTIONAL
+  );
+
+/** Enumerate all nodes of the subtree under the input Node in the AML
+    bytestream order (i.e. in a depth first order), and call the CallBack
+    function with the input Context.
+    The prototype of the Callback function is EDKII_AML_TREE_ENUM_CALLBACK.
+
+  @ingroup EnumerationApis
+
+  @param  [in]      Node      Enumerate nodes of the subtree under this Node.
+                              Must be a valid node.
+  @param  [in]      CallBack  Callback function to call on each node.
+  @param  [in, out] Context   Void pointer used to pass some information
+                              to the Callback function.
+                              Optional, can be NULL.
+  @param  [out]     Status    Optional parameter that can be used to get
+                              the status of the Callback function.
+                              If used, need to be init to EFI_SUCCESS.
+
+  @retval TRUE if the enumeration can continue or has finished without
+          interruption.
+  @retval FALSE if the enumeration needs to stopped or has stopped.
+**/
+BOOLEAN
+EFIAPI
+AmlEnumTree (
+  IN      AML_NODE_HANDLE                 Node,
+  IN      EDKII_AML_TREE_ENUM_CALLBACK    CallBack,
+  IN  OUT VOID                          * Context,  OPTIONAL
+      OUT EFI_STATUS                    * Status    OPTIONAL
+  );
+
+/**
+  @defgroup NameSpaceApis NameSpace APIs
+  @ingroup NavigationApis
+  @{
+    NameSpace APIs allow to find a node from an AML path, and reciprocally
+    get the AML path of a node.
+
+    These APIs only operate on "NameSpace nodes", i.e. nodes that are
+    part of the AML namespace. These are the root node and object nodes
+    acknowledged by AmlGetObjectNodeInfo in @ref NodeInterfaceApis.
+  @}
+*/
+
+/** Build the absolute ASL pathname to Node.
+
+  BufferSize is always updated to the size of the pathname.
+
+  If:
+   - the content of BufferSize is >= to the size of the pathname AND;
+   - Buffer is not NULL;
+  then copy the pathname in the Buffer. A buffer of the size
+  MAX_ASL_NAMESTRING_SIZE is big enough to receive any ASL pathname.
+
+  @ingroup NameSpaceApis
+
+  @param  [in]      Node            Node to build the absolute path to.
+                                    Must be a root node, or a namespace node.
+  @param  [out]     Buffer          Buffer to write the path to.
+                                    If NULL, only update *BufferSize.
+  @param  [in, out] BufferSize      Pointer holding:
+                                     - At entry, the size of the Buffer;
+                                     - At exit, the size of the pathname.
+
+  @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
+AmlGetAslPathName (
+  IN      AML_NODE_HANDLE     Node,
+      OUT CHAR8             * Buffer,
+  IN  OUT UINT32            * BufferSize
+  );
+
+#endif // AML_CORE_INTERFACE_H_
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 24/30] DynamicTablesPkg: AmlLib APIs
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (22 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 23/30] DynamicTablesPkg: AML Core interface Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 25/30] DynamicTablesPkg: Dynamic AML: Add AmlLib library Sami Mujawar
                   ` (6 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

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 <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h                | 631 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c             | 382 ++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c       | 219 +++++++
 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h       |  93 +++
 DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c | 320 ++++++++++
 5 files changed, 1645 insertions(+)

diff --git a/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h b/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h
new file mode 100644
index 0000000000000000000000000000000000000000..1dcb93861436851e848f9cb5fd69621ccd7bf7a1
--- /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.<BR>
+
+  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 <IndustryStandard/Acpi.h>
+
+#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 0000000000000000000000000000000000000000..fdf04acc6212f9d6b6f691f30a60fea6f0b43e6f
--- /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.<BR>
+
+  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 <AmlNodeDefines.h>
+
+#include <AmlCoreInterface.h>
+#include <AmlInclude.h>
+#include <Api/AmlApiHelper.h>
+#include <String/AmlString.h>
+
+/** 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 0000000000000000000000000000000000000000..9693f28b543f2008d17d09031ed3b53577935a5d
--- /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.<BR>
+
+  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 <Api/AmlApiHelper.h>
+
+#include <AmlCoreInterface.h>
+#include <AmlInclude.h>
+#include <String/AmlString.h>
+
+/** 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 0000000000000000000000000000000000000000..9872adddc36739559c86268564483480a6d65294
--- /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.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_HELPER_H_
+#define AML_HELPER_H_
+
+#include <AmlNodeDefines.h>
+#include <ResourceData/AmlResourceData.h>
+
+/** 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 0000000000000000000000000000000000000000..913c8dcdb0c04d7180a0732cfc6c5f495105f8e9
--- /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.<BR>
+
+  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 <AmlNodeDefines.h>
+
+#include <AmlCoreInterface.h>
+#include <AmlInclude.h>
+#include <Api/AmlApiHelper.h>
+#include <CodeGen/AmlResourceDataCodeGen.h>
+
+/** 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;
+}
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 25/30] DynamicTablesPkg: Dynamic AML: Add AmlLib library
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (23 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 24/30] DynamicTablesPkg: AmlLib APIs Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 26/30] DynamicTablesPkg: Add AsciiFromHex helper function Sami Mujawar
                   ` (5 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

ACPI Definition blocks are implemented using AML which has
a complex grammar making run-time generation of definition
blocks difficult. Dynamic AML is a feature of Dynamic Tables
framework that provides a solution for dynamic generation of
ACPI Definition block tables.

Since, AML bytecode represents complex AML grammar, an AmlLib
library is introduced to assist parsing and traversing of the
AML bytecode at run-time.

The AmlLib library parses a definition block and represents it
as an AML tree. The AML objects, methods and data are represented
as tree nodes. Since the AML data is represented as tree nodes,
it is possible to traverse the tree, locate a node and modify the
node data. The tree can then be serialized to a buffer (that
represents the definition block). This definition block containing
the fixed-up AML code can then be installed as an ACPI Definition
Block table.

Dynamic AML introduces the following techniques:
* AML Fixup
* AML Codegen
* AML Fixup + Codegen

AML Fixup is a technique that involves compiling an ASL template
file to generate AML bytecode. This template AML bytecode can be
parsed at run-time and a fixup code can update the required fields
in the AML template.

AML Codegen employs generating small segments of AML code.

AmlLib provides a rich set of APIs to operate on AML data for AML
Fixup and Codegen.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/DynamicTables.dsc.inc            |  1 +
 DynamicTablesPkg/DynamicTablesPkg.ci.yaml         |  6 +-
 DynamicTablesPkg/DynamicTablesPkg.dec             |  5 +-
 DynamicTablesPkg/DynamicTablesPkg.dsc             |  3 +-
 DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf | 76 ++++++++++++++++++++
 5 files changed, 88 insertions(+), 3 deletions(-)

diff --git a/DynamicTablesPkg/DynamicTables.dsc.inc b/DynamicTablesPkg/DynamicTables.dsc.inc
index bc03c09d6c0ebb2c6b1458d27e0bde00224e55cd..928529f797b49f2f0dd52508a9aad557f568719a 100644
--- a/DynamicTablesPkg/DynamicTables.dsc.inc
+++ b/DynamicTablesPkg/DynamicTables.dsc.inc
@@ -13,6 +13,7 @@ [BuildOptions]
   RELEASE_*_*_CC_FLAGS     = -DMDEPKG_NDEBUG
 
 [LibraryClasses.common]
+  AmlLib|DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf
   TableHelperLib|DynamicTablesPkg/Library/Common/TableHelperLib/TableHelperLib.inf
 
 [Components.common]
diff --git a/DynamicTablesPkg/DynamicTablesPkg.ci.yaml b/DynamicTablesPkg/DynamicTablesPkg.ci.yaml
index f39b8019461ea151f9b1cf4bc751604d6e044af2..5ee20357326f4b79444d63418f56aae0b00508de 100644
--- a/DynamicTablesPkg/DynamicTablesPkg.ci.yaml
+++ b/DynamicTablesPkg/DynamicTablesPkg.ci.yaml
@@ -1,7 +1,7 @@
 ## @file
 # CI configuration for DynamicTablesPkg
 #
-# Copyright (c) 2020, ARM Limited. All rights reserved.<BR>
+# Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 ##
 {
@@ -69,11 +69,15 @@
         "IgnoreFiles": [],           # use gitignore syntax to ignore errors
                                      # in matching files
         "ExtendWords": [
+           "EISAID",
            "CCIDX",
            "CCSIDR",
            "countof",
            "EOBJECT",
+           "invoc",
            "GTBLOCK",
+           "lgreater",
+           "lless",
            "MPIDR",
            "pytool",
            "Roadmap",
diff --git a/DynamicTablesPkg/DynamicTablesPkg.dec b/DynamicTablesPkg/DynamicTablesPkg.dec
index 6cb8a0b4f2c91b59e888951fda034bfdc56d95e1..57e6815fa159c5ce8d5cb0c930375e7322bff211 100644
--- a/DynamicTablesPkg/DynamicTablesPkg.dec
+++ b/DynamicTablesPkg/DynamicTablesPkg.dec
@@ -1,7 +1,7 @@
 ## @file
 # dec file for Dynamic Tables Framework.
 #
-# Copyright (c) 2017 - 2020, ARM Limited. All rights reserved.<BR>
+# Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
 #
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -17,6 +17,9 @@ [Includes]
   Include
 
 [LibraryClasses]
+  ##  @libraryclass  Defines a set of APIs for Dynamic AML generation.
+  AmlLib|Include/Library/AmlLib/AmlLib.h
+
   ##  @libraryclass  Defines a set of helper methods.
   TableHelperLib|Include/Library/TableHelperLib.h
 
diff --git a/DynamicTablesPkg/DynamicTablesPkg.dsc b/DynamicTablesPkg/DynamicTablesPkg.dsc
index 5fb30a9315a75750a7d4c3e802c610a7991111c7..add6b192ad4187e2769b374ef68f93a65a1af8b6 100644
--- a/DynamicTablesPkg/DynamicTablesPkg.dsc
+++ b/DynamicTablesPkg/DynamicTablesPkg.dsc
@@ -2,7 +2,7 @@
 #  Dsc file for Dynamic Tables Framework.
 #
 #  Copyright (c) 2019, Linaro Limited. All rights reserved.<BR>
-#  Copyright (c) 2019 - 2020, ARM Limited. All rights reserved.<BR>
+#  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
 #
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -36,6 +36,7 @@ [LibraryClasses.ARM, LibraryClasses.AARCH64]
   PL011UartLib|ArmPlatformPkg/Library/PL011UartLib/PL011UartLib.inf
 
 [Components.common]
+  DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf
   DynamicTablesPkg/Library/Common/TableHelperLib/TableHelperLib.inf
 
 [BuildOptions]
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf b/DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf
new file mode 100644
index 0000000000000000000000000000000000000000..e2babef445d5ed7dbd2f6aefbe26fa39df2b1ada
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf
@@ -0,0 +1,76 @@
+## @file
+#  AML Generation Library
+#
+#  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x0001001B
+  BASE_NAME      = DynamicAmlLib
+  FILE_GUID      = 23A6AFDA-F2A5-45EC-BEFF-420639D345B9
+  VERSION_STRING = 1.0
+  MODULE_TYPE    = DXE_DRIVER
+  LIBRARY_CLASS  = AmlLib
+
+[Sources]
+  AmlCoreInterface.h
+  AmlDefines.h
+  AmlInclude.h
+  AmlNodeDefines.h
+  AmlDbgPrint/AmlDbgPrint.c
+  AmlDbgPrint/AmlDbgPrint.h
+  AmlEncoding/Aml.c
+  AmlEncoding/Aml.h
+  Api/AmlApi.c
+  Api/AmlApiHelper.c
+  Api/AmlApiHelper.h
+  Api/AmlResourceDataApi.c
+  CodeGen/AmlCodeGen.c
+  CodeGen/AmlResourceDataCodeGen.c
+  CodeGen/AmlResourceDataCodeGen.h
+  NameSpace/AmlNameSpace.c
+  NameSpace/AmlNameSpace.h
+  Parser/AmlFieldListParser.c
+  Parser/AmlFieldListParser.h
+  Parser/AmlMethodParser.c
+  Parser/AmlMethodParser.h
+  Parser/AmlParser.c
+  Parser/AmlParser.h
+  Parser/AmlResourceDataParser.c
+  Parser/AmlResourceDataParser.h
+  ResourceData/AmlResourceData.c
+  ResourceData/AmlResourceData.h
+  Serialize/AmlSerialize.c
+  Stream/AmlStream.c
+  Stream/AmlStream.h
+  String/AmlString.c
+  String/AmlString.h
+  Tree/AmlClone.c
+  Tree/AmlTreeIterator.h
+  Tree/AmlNode.c
+  Tree/AmlNode.h
+  Tree/AmlNodeInterface.c
+  Tree/AmlTree.c
+  Tree/AmlTree.h
+  Tree/AmlTreeEnumerator.c
+  Tree/AmlTreeIterator.c
+  Tree/AmlTreeTraversal.c
+  Tree/AmlTreeTraversal.h
+  Utils/AmlUtility.c
+  Utils/AmlUtility.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+  BaseLib
+
+[BuildOptions]
+  *_*_*_CC_FLAGS = -DAML_HANDLE
+
+[Protocols]
+
+[Guids]
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 26/30] DynamicTablesPkg: Add AsciiFromHex helper function
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (24 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 25/30] DynamicTablesPkg: Dynamic AML: Add AmlLib library Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 27/30] DynamicTablesPkg: SSDT Serial Port Fixup library Sami Mujawar
                   ` (4 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

AsciiFromHex is a function converts a hex number to an
ASCII character. This function is used across multiple
generators, so add it to the TableHelperLib.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Include/Library/TableHelperLib.h            | 15 +++++++++-
 DynamicTablesPkg/Library/Common/TableHelperLib/TableHelper.c | 30 ++++++++++++++++++--
 2 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/DynamicTablesPkg/Include/Library/TableHelperLib.h b/DynamicTablesPkg/Include/Library/TableHelperLib.h
index e4a8dfa046bd97d89f0297ccad521f317bed5c36..099a0a4544e3d1f746d4be8533cb006786f11611 100644
--- a/DynamicTablesPkg/Include/Library/TableHelperLib.h
+++ b/DynamicTablesPkg/Include/Library/TableHelperLib.h
@@ -1,6 +1,6 @@
 /** @file
 
-  Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+  Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 
@@ -107,4 +107,17 @@ FindDuplicateValue (
   IN        PFN_IS_EQUAL    EqualTestFunction
   );
 
+/** Convert a hex number to its ASCII code.
+
+ @param [in]  x   Hex number to convert.
+                  Must be 0 <= x < 16.
+
+ @return The ASCII code corresponding to x.
+**/
+UINT8
+EFIAPI
+AsciiFromHex (
+  IN  UINT8   x
+  );
+
 #endif // TABLE_HELPER_LIB_H_
diff --git a/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelper.c b/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelper.c
index fc6cf3b088da1f7ad89dd4356b414bede9e80575..0d9daad3b05b6e82089f92afb6de4eeee5af9a28 100644
--- a/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelper.c
+++ b/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelper.c
@@ -1,8 +1,9 @@
 /** @file
   Table Helper
 
-Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
-SPDX-License-Identifier: BSD-2-Clause-Patent
+  Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
 #include <Protocol/AcpiTable.h>
@@ -244,3 +245,28 @@ FindDuplicateValue (
   }
   return FALSE;
 }
+
+/** Convert a hex number to its ASCII code.
+
+ @param [in]  x   Hex number to convert.
+                  Must be 0 <= x < 16.
+
+ @return The ASCII code corresponding to x.
+**/
+UINT8
+EFIAPI
+AsciiFromHex (
+  IN  UINT8   x
+  )
+{
+  if (x < 10) {
+    return (UINT8)(x + '0');
+  }
+
+  if (x < 16) {
+    return (UINT8)(x - 10 + 'A');
+  }
+
+  ASSERT (FALSE);
+  return (UINT8)0;
+}
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 27/30] DynamicTablesPkg: SSDT Serial Port Fixup library
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (25 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 26/30] DynamicTablesPkg: Add AsciiFromHex helper function Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 28/30] DynamicTablesPkg: SSDT Serial Port generator Sami Mujawar
                   ` (3 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

According to Arm Server Base Boot Requirements,
Platform Design Document version 1.2 revision D,
September 2, 2019, section '4.2.1.8 SPCR'; The
SPCR console device must be included in the DSDT.

Additionally, it is often desirable to describe the
serial ports available on a platform so that they
are available for use by a rich OS.

To facilitate the description of serial ports on a
platform a common SSDT Serial Port Fixup library is
introduced. It provides interfaces to build a SSDT
serial port definition block table based on the
serial port information.

The SSDT Serial Port Fixup library is used by the
SPCR, DBG2 and SSDT Serial Port generator to describe
the serial port information in a definition block.

  +------------+   +------------+   +------------+
  |  SPCR Gen  |   |  DBG2 Gen  |   | SERIAL Gen |
  +------------+   +------------+   +------------+
        +----------------------------------+
        |  SSDT Serial Port Fixup library  |
        +----------------------------------+

The SSDT Serial Port Fixup library:
  - Parses the SSDT Serial Port template using the
    AmlLib library to generate an AML tree.
  - Updates the _UID, _HID and _CID values.
  - Fixes up the Serial port base address, length
    and the interrupt number in the _CRS descriptor.
  - Fixes up the serial-port name.
  - Serialises the AML Tree to a buffer containing
    the definition block data.
  The definition block data is then installed by the
  corresponding table generator.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/DynamicTables.dsc.inc                                            |   1 +
 DynamicTablesPkg/DynamicTablesPkg.ci.yaml                                         |   3 +
 DynamicTablesPkg/DynamicTablesPkg.dec                                             |   3 +
 DynamicTablesPkg/DynamicTablesPkg.dsc                                             |   1 +
 DynamicTablesPkg/Include/AcpiTableGenerator.h                                     |   7 +-
 DynamicTablesPkg/Include/ArmNameSpaceObjects.h                                    |   9 +-
 DynamicTablesPkg/Include/Library/SsdtSerialPortFixupLib.h                         |  68 +++
 DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c   | 524 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf |  30 ++
 DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortTemplate.asl |  60 +++
 10 files changed, 703 insertions(+), 3 deletions(-)

diff --git a/DynamicTablesPkg/DynamicTables.dsc.inc b/DynamicTablesPkg/DynamicTables.dsc.inc
index 928529f797b49f2f0dd52508a9aad557f568719a..0063fc3c671f4ae6089dd4df76074445364c6011 100644
--- a/DynamicTablesPkg/DynamicTables.dsc.inc
+++ b/DynamicTablesPkg/DynamicTables.dsc.inc
@@ -14,6 +14,7 @@ [BuildOptions]
 
 [LibraryClasses.common]
   AmlLib|DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf
+  SsdtSerialPortFixupLib|DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf
   TableHelperLib|DynamicTablesPkg/Library/Common/TableHelperLib/TableHelperLib.inf
 
 [Components.common]
diff --git a/DynamicTablesPkg/DynamicTablesPkg.ci.yaml b/DynamicTablesPkg/DynamicTablesPkg.ci.yaml
index 5ee20357326f4b79444d63418f56aae0b00508de..c0d09e79fdf7f6003b5bbda45abc82a0caf4e53f 100644
--- a/DynamicTablesPkg/DynamicTablesPkg.ci.yaml
+++ b/DynamicTablesPkg/DynamicTablesPkg.ci.yaml
@@ -69,6 +69,8 @@
         "IgnoreFiles": [],           # use gitignore syntax to ignore errors
                                      # in matching files
         "ExtendWords": [
+           "ARMHB",                  # ARMHB000
+           "ARMLTD",
            "EISAID",
            "CCIDX",
            "CCSIDR",
@@ -81,6 +83,7 @@
            "MPIDR",
            "pytool",
            "Roadmap",
+           "ssdtserialporttemplate",
            "SMMUV",
            "standardised",
            "TABLEEX",
diff --git a/DynamicTablesPkg/DynamicTablesPkg.dec b/DynamicTablesPkg/DynamicTablesPkg.dec
index 57e6815fa159c5ce8d5cb0c930375e7322bff211..f36a6e8bb7c17f82acff3c766b15202b064a64b5 100644
--- a/DynamicTablesPkg/DynamicTablesPkg.dec
+++ b/DynamicTablesPkg/DynamicTablesPkg.dec
@@ -20,6 +20,9 @@ [LibraryClasses]
   ##  @libraryclass  Defines a set of APIs for Dynamic AML generation.
   AmlLib|Include/Library/AmlLib/AmlLib.h
 
+  ##  @libraryclass  Defines a set of methods for fixing up a SSDT Serial Port.
+  SsdtSerialPortFixupLib|Include/Library/SsdtSerialPortFixupLib.h
+
   ##  @libraryclass  Defines a set of helper methods.
   TableHelperLib|Include/Library/TableHelperLib.h
 
diff --git a/DynamicTablesPkg/DynamicTablesPkg.dsc b/DynamicTablesPkg/DynamicTablesPkg.dsc
index add6b192ad4187e2769b374ef68f93a65a1af8b6..0232bda459c87016e1e62f52a5c98347e551b14c 100644
--- a/DynamicTablesPkg/DynamicTablesPkg.dsc
+++ b/DynamicTablesPkg/DynamicTablesPkg.dsc
@@ -37,6 +37,7 @@ [LibraryClasses.ARM, LibraryClasses.AARCH64]
 
 [Components.common]
   DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf
+  DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf
   DynamicTablesPkg/Library/Common/TableHelperLib/TableHelperLib.inf
 
 [BuildOptions]
diff --git a/DynamicTablesPkg/Include/AcpiTableGenerator.h b/DynamicTablesPkg/Include/AcpiTableGenerator.h
index b55feb4e7507f0e4e1e0a64c3c4b8068f17dad47..ef5018c312c1abbc205a06b037ffd6063cf02f0a 100644
--- a/DynamicTablesPkg/Include/AcpiTableGenerator.h
+++ b/DynamicTablesPkg/Include/AcpiTableGenerator.h
@@ -1,6 +1,6 @@
 /** @file
 
-  Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+  Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 
@@ -55,6 +55,10 @@ The Dynamic Tables Framework implements the following ACPI table generators:
             the Configuration Manager and builds the PPTT table.
   - SRAT  : The SRAT generator collates the system resource affinity information
             from the Configuration Manager and builds the SRAT table.
+  - SSDT Serial-Port:
+            The SSDT Serial generator collates the Serial port information
+            from the Configuration Manager and patches the SSDT Serial Port
+            template to build the SSDT Serial port table.
 */
 
 /** The ACPI_TABLE_GENERATOR_ID type describes ACPI table generator ID.
@@ -78,6 +82,7 @@ typedef enum StdAcpiTableId {
   EStdAcpiTableIdIort,                          ///< IORT Generator
   EStdAcpiTableIdPptt,                          ///< PPTT Generator
   EStdAcpiTableIdSrat,                          ///< SRAT Generator
+  EStdAcpiTableIdSsdtSerialPort,                ///< SSDT Serial-Port Generator
   EStdAcpiTableIdMax
 } ESTD_ACPI_TABLE_ID;
 
diff --git a/DynamicTablesPkg/Include/ArmNameSpaceObjects.h b/DynamicTablesPkg/Include/ArmNameSpaceObjects.h
index 57a282d5cb6883d0dabb74ceac7c8905ed3ef43a..b2534a6505d6fb695f0751bbb09d365bd93d092e 100644
--- a/DynamicTablesPkg/Include/ArmNameSpaceObjects.h
+++ b/DynamicTablesPkg/Include/ArmNameSpaceObjects.h
@@ -1,6 +1,6 @@
 /** @file
 
-  Copyright (c) 2017 - 2020, ARM Limited. All rights reserved.
+  Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 
@@ -56,6 +56,7 @@ typedef enum ArmObjectID {
   EArmObjDeviceHandleAcpi,             ///< 32 - Device Handle Acpi
   EArmObjDeviceHandlePci,              ///< 33 - Device Handle Pci
   EArmObjGenericInitiatorAffinityInfo, ///< 34 - Generic Initiator Affinity
+  EArmObjSerialPortInfo,               ///< 35 - Generic Serial Port Info
   EArmObjMax
 } EARM_OBJECT_ID;
 
@@ -270,7 +271,8 @@ typedef struct CmArmGicItsInfo {
     Serial Port information for the Platform.
 
     ID: EArmObjSerialConsolePortInfo or
-        EArmObjSerialDebugPortInfo
+        EArmObjSerialDebugPortInfo or
+        EArmObjSerialPortInfo
 */
 typedef struct CmArmSerialPortInfo {
   /// The physical base address for the serial port
@@ -287,6 +289,9 @@ typedef struct CmArmSerialPortInfo {
 
   /// Serial Port subtype
   UINT16  PortSubtype;
+
+  /// The Base address length
+  UINT64  BaseAddressLength;
 } CM_ARM_SERIAL_PORT_INFO;
 
 /** A structure that describes the
diff --git a/DynamicTablesPkg/Include/Library/SsdtSerialPortFixupLib.h b/DynamicTablesPkg/Include/Library/SsdtSerialPortFixupLib.h
new file mode 100644
index 0000000000000000000000000000000000000000..4605f3f34b1d9a2a3af975f801077d6f523e0530
--- /dev/null
+++ b/DynamicTablesPkg/Include/Library/SsdtSerialPortFixupLib.h
@@ -0,0 +1,68 @@
+/** @file
+  Ssdt Serial Port Fixup Library
+
+  Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef SSDT_SERIAL_PORT_LIB_H_
+#define SSDT_SERIAL_PORT_LIB_H_
+
+/** Build a SSDT table describing the input serial port.
+
+  The table created by this function must be freed by FreeSsdtSerialTable.
+
+  @param [in]  AcpiTableInfo    Pointer to the ACPI table information.
+  @param [in]  SerialPortInfo   Serial port to describe in the SSDT table.
+  @param [in]  Name             The Name to give to the Device.
+                                Must be a NULL-terminated ASL NameString
+                                e.g.: "DEV0", "DV15.DEV0", etc.
+  @param [in]  Uid              UID for the Serial Port.
+  @param [out] Table            If success, pointer to the created SSDT table.
+
+  @retval EFI_SUCCESS            Table generated successfully.
+  @retval EFI_INVALID_PARAMETER  A parameter is invalid.
+  @retval EFI_NOT_FOUND          Could not find information.
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+BuildSsdtSerialPortTable (
+  IN  CONST CM_STD_OBJ_ACPI_TABLE_INFO    *  AcpiTableInfo,
+  IN  CONST CM_ARM_SERIAL_PORT_INFO       *  SerialPortInfo,
+  IN  CONST CHAR8                         *  Name,
+  IN  CONST UINT64                           Uid,
+  OUT       EFI_ACPI_DESCRIPTION_HEADER  **  Table
+  );
+
+/** Free an SSDT table previously created by
+    the BuildSsdtSerialTable function.
+
+  @param [in] Table   Pointer to a SSDT table allocated by
+                      the BuildSsdtSerialTable function.
+
+  @retval EFI_SUCCESS           Success.
+**/
+EFI_STATUS
+EFIAPI
+FreeSsdtSerialPortTable (
+  IN EFI_ACPI_DESCRIPTION_HEADER  * Table
+  );
+
+/** Validate the Serial Port Information.
+
+  @param [in]  SerialPortInfoTable    Table of CM_ARM_SERIAL_PORT_INFO.
+  @param [in]  SerialPortCount        Count of SerialPort in the table.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+ValidateSerialPortInfo (
+  IN  CONST CM_ARM_SERIAL_PORT_INFO  * SerialPortInfoTable,
+  IN        UINT32                     SerialPortCount
+  );
+
+#endif // SSDT_SERIAL_PORT_LIB_H_
diff --git a/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c b/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c
new file mode 100644
index 0000000000000000000000000000000000000000..944bfd6eaaabc9dbc7223c9888fb5f11eeb1bda9
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c
@@ -0,0 +1,524 @@
+/** @file
+  SSDT Serial Port Fixup Library.
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - Arm Server Base Boot Requirements (SBBR), s4.2.1.8 "SPCR".
+  - Microsoft Debug Port Table 2 (DBG2) Specification - December 10, 2015.
+**/
+
+#include <IndustryStandard/DebugPort2Table.h>
+#include <Library/AcpiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/AcpiTable.h>
+
+// Module specific include files.
+#include <AcpiTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <ConfigurationManagerHelper.h>
+#include <Library/AmlLib/AmlLib.h>
+#include <Library/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+/** C array containing the compiled AML template.
+    This symbol is defined in the auto generated C file
+    containing the AML bytecode array.
+*/
+extern CHAR8  ssdtserialporttemplate_aml_code[];
+
+/** UART address range length.
+*/
+#define MIN_UART_ADDRESS_LENGTH         0x1000U
+
+/** Validate the Serial Port Information.
+
+  @param [in]  SerialPortInfoTable    Table of CM_ARM_SERIAL_PORT_INFO.
+  @param [in]  SerialPortCount        Count of SerialPort in the table.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+ValidateSerialPortInfo (
+  IN  CONST CM_ARM_SERIAL_PORT_INFO  * SerialPortInfoTable,
+  IN        UINT32                     SerialPortCount
+  )
+{
+  UINT32                            Index;
+  CONST CM_ARM_SERIAL_PORT_INFO   * SerialPortInfo;
+
+  if  ((SerialPortInfoTable == NULL)  ||
+       (SerialPortCount == 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  for (Index = 0; Index < SerialPortCount; Index++) {
+    SerialPortInfo = &SerialPortInfoTable[Index];
+    ASSERT (SerialPortInfo != NULL);
+
+    if ((SerialPortInfo == NULL ) ||
+        (SerialPortInfo->BaseAddress == 0)) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "ERROR: UART port base address is invalid. BaseAddress = 0x%llx\n",
+        SerialPortInfo->BaseAddress
+        ));
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if ((SerialPortInfo->PortSubtype !=
+         EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART) &&
+        (SerialPortInfo->PortSubtype !=
+         EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X) &&
+        (SerialPortInfo->PortSubtype !=
+         EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART) &&
+        (SerialPortInfo->PortSubtype !=
+         EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_DCC) &&
+        (SerialPortInfo->PortSubtype !=
+         EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550)) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "ERROR: UART port subtype is invalid."
+        " UART Base  = 0x%llx, PortSubtype = 0x%x\n",
+        SerialPortInfo->BaseAddress,
+        SerialPortInfo->PortSubtype
+        ));
+      return EFI_INVALID_PARAMETER;
+    }
+
+    DEBUG ((DEBUG_INFO, "UART Configuration:\n"));
+    DEBUG ((
+      DEBUG_INFO,
+      "  UART Base  = 0x%llx\n", SerialPortInfo->BaseAddress
+      ));
+    DEBUG ((
+      DEBUG_INFO,
+      "  Length     = 0x%llx\n",
+      SerialPortInfo->BaseAddressLength
+      ));
+    DEBUG ((DEBUG_INFO, "  Clock      = %lu\n", SerialPortInfo->Clock));
+    DEBUG ((DEBUG_INFO, "  BaudRate   = %llu\n", SerialPortInfo->BaudRate));
+    DEBUG ((DEBUG_INFO, "  Interrupt  = %lu\n", SerialPortInfo->Interrupt));
+  } // for
+
+  return EFI_SUCCESS;
+}
+
+/** Fixup the Serial Port Ids (_UID, _HID, _CID).
+
+  @param  [in]  RootNodeHandle  Pointer to the root of an AML tree.
+  @param  [in]  Uid             UID for the Serial Port.
+  @param  [in]  SerialPortInfo  Pointer to a Serial Port Information
+                                structure.
+                                Get the Serial Port Information from there.
+
+  @retval  EFI_SUCCESS            The function completed successfully.
+  @retval  EFI_INVALID_PARAMETER  Invalid parameter.
+  @retval  EFI_NOT_FOUND          Could not find information.
+  @retval  EFI_OUT_OF_RESOURCES   Out of resources.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FixupIds (
+  IN  OUT       AML_ROOT_NODE_HANDLE        RootNodeHandle,
+  IN      CONST UINT64                      Uid,
+  IN      CONST CM_ARM_SERIAL_PORT_INFO   * SerialPortInfo
+  )
+{
+  EFI_STATUS                Status;
+  AML_OBJECT_NODE_HANDLE    NameOpIdNode;
+  CONST CHAR8             * HidString;
+  CONST CHAR8             * CidString;
+
+  // Get the _CID and _HID value to write.
+  switch (SerialPortInfo->PortSubtype) {
+    case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550:
+    {
+      HidString = "PNP0501";
+      CidString = "PNP0500";
+      break;
+    }
+    case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART:
+    {
+      HidString = "ARMH0011";
+      CidString = "ARMHB000";
+      break;
+    }
+    case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART:
+    case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X:
+    {
+      HidString = "ARMH0011";
+      CidString = "";
+      break;
+    }
+    default:
+    {
+      return EFI_INVALID_PARAMETER;
+    }
+  } // switch
+
+  // Get the _UID NameOp object defined by the "Name ()" statement,
+  // and update its value.
+  Status = AmlFindNode (
+             RootNodeHandle,
+             "\\_SB_.COM0._UID",
+             &NameOpIdNode
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = AmlNameOpUpdateInteger (NameOpIdNode, (UINT64)Uid);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Get the _HID NameOp object defined by the "Name ()" statement,
+  // and update its value.
+  Status = AmlFindNode (
+             RootNodeHandle,
+             "\\_SB_.COM0._HID",
+             &NameOpIdNode
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = AmlNameOpUpdateString (NameOpIdNode, HidString);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Get the _CID NameOp object defined by the "Name ()" statement,
+  // and update its value.
+  Status = AmlFindNode (
+             RootNodeHandle,
+             "\\_SB_.COM0._CID",
+             &NameOpIdNode
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // If we have a CID then update a _CID node else delete the node.
+  if (AsciiStrLen (CidString) != 0) {
+    Status = AmlNameOpUpdateString (NameOpIdNode, CidString);
+  } else {
+    // First detach the node from the tree.
+    Status = AmlDetachNode (NameOpIdNode);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    // Delete the detached node.
+    Status = AmlDeleteTree (NameOpIdNode);
+  }
+
+  return Status;
+}
+
+/** Fixup the Serial Port _CRS values (BaseAddress, ...).
+
+  @param  [in]  RootNodeHandle  Pointer to the root of an AML tree.
+  @param  [in]  SerialPortInfo  Pointer to a Serial Port Information
+                                structure.
+                                Get the Serial Port Information from there.
+
+  @retval  EFI_SUCCESS            The function completed successfully.
+  @retval  EFI_INVALID_PARAMETER  Invalid parameter.
+  @retval  EFI_NOT_FOUND          Could not find information.
+  @retval  EFI_OUT_OF_RESOURCES   Out of resources.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FixupCrs (
+  IN  OUT       AML_ROOT_NODE_HANDLE        RootNodeHandle,
+  IN      CONST CM_ARM_SERIAL_PORT_INFO   * SerialPortInfo
+  )
+{
+  EFI_STATUS                Status;
+  AML_OBJECT_NODE_HANDLE    NameOpCrsNode;
+  AML_DATA_NODE_HANDLE      QWordRdNode;
+  AML_DATA_NODE_HANDLE      InterruptRdNode;
+
+  // Get the "_CRS" object defined by the "Name ()" statement.
+  Status = AmlFindNode (
+             RootNodeHandle,
+             "\\_SB_.COM0._CRS",
+             &NameOpCrsNode
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Get the first Rd node in the "_CRS" object.
+  Status = AmlNameOpCrsGetFirstRdNode (NameOpCrsNode, &QWordRdNode);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (QWordRdNode == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Update the Serial Port base address and length.
+  Status = AmlUpdateRdQWord (
+             QWordRdNode,
+             SerialPortInfo->BaseAddress,
+             ((SerialPortInfo->BaseAddressLength < MIN_UART_ADDRESS_LENGTH) ?
+                 MIN_UART_ADDRESS_LENGTH: SerialPortInfo->BaseAddressLength)
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Get the Interrupt node.
+  // It is the second Resource Data element in the NameOpCrsNode's
+  // variable list of arguments.
+  Status = AmlNameOpCrsGetNextRdNode (QWordRdNode, &InterruptRdNode);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (InterruptRdNode == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Update the interrupt number.
+  return AmlUpdateRdInterrupt (InterruptRdNode, SerialPortInfo->Interrupt);
+}
+
+/** Fixup the Serial Port device name.
+
+  @param  [in]  RootNodeHandle  Pointer to the root of an AML tree.
+  @param  [in]  SerialPortInfo  Pointer to a Serial Port Information
+                                structure.
+                                Get the Serial Port Information from there.
+  @param  [in]  Name            The Name to give to the Device.
+                                Must be a NULL-terminated ASL NameString
+                                e.g.: "DEV0", "DV15.DEV0", etc.
+
+  @retval  EFI_SUCCESS            The function completed successfully.
+  @retval  EFI_INVALID_PARAMETER  Invalid parameter.
+  @retval  EFI_NOT_FOUND          Could not find information.
+  @retval  EFI_OUT_OF_RESOURCES   Out of resources.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FixupName (
+  IN  OUT       AML_ROOT_NODE_HANDLE        RootNodeHandle,
+  IN      CONST CM_ARM_SERIAL_PORT_INFO   * SerialPortInfo,
+  IN      CONST CHAR8                     * Name
+  )
+{
+  EFI_STATUS                Status;
+  AML_OBJECT_NODE_HANDLE    DeviceNode;
+
+  // Get the COM0 variable defined by the "Device ()" statement.
+  Status = AmlFindNode (RootNodeHandle, "\\_SB_.COM0", &DeviceNode);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Update the Device's name.
+  return AmlDeviceOpUpdateName (DeviceNode, (CHAR8*)Name);
+}
+
+/** Fixup the Serial Port Information in the AML tree.
+
+  For each template value:
+   - find the node to update;
+   - update the value.
+
+  @param  [in]  RootNodeHandle  Pointer to the root of the AML tree.
+  @param  [in]  SerialPortInfo  Pointer to a Serial Port Information
+                                structure.
+                                Get the Serial Port Information from there.
+  @param  [in]  Name            The Name to give to the Device.
+                                Must be a NULL-terminated ASL NameString
+                                e.g.: "DEV0", "DV15.DEV0", etc.
+  @param  [in]  Uid             UID for the Serial Port.
+  @param  [out] Table           If success, contains the serialized
+                                SSDT table.
+
+  @retval  EFI_SUCCESS            The function completed successfully.
+  @retval  EFI_INVALID_PARAMETER  Invalid parameter.
+  @retval  EFI_NOT_FOUND          Could not find information.
+  @retval  EFI_OUT_OF_RESOURCES   Out of resources.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FixupSerialPortInfo (
+  IN  OUT       AML_ROOT_NODE_HANDLE              RootNodeHandle,
+  IN      CONST CM_ARM_SERIAL_PORT_INFO         * SerialPortInfo,
+  IN      CONST CHAR8                           * Name,
+  IN      CONST UINT64                            Uid,
+      OUT       EFI_ACPI_DESCRIPTION_HEADER    ** Table
+  )
+{
+  EFI_STATUS                Status;
+
+  ASSERT (RootNodeHandle != NULL);
+  ASSERT (SerialPortInfo != NULL);
+  ASSERT (Name != NULL);
+  ASSERT (Table != NULL);
+
+  // Fixup the _UID, _HID and _CID values.
+  Status = FixupIds (RootNodeHandle, Uid, SerialPortInfo);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Fixup the _CRS values.
+  Status = FixupCrs (RootNodeHandle, SerialPortInfo);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Fixup the serial-port name.
+  // This MUST be done at the end, otherwise AML paths won't be valid anymore.
+  return FixupName (RootNodeHandle, SerialPortInfo, Name);
+}
+
+/** Free an SSDT table previously created by
+    the BuildSsdtSerialTable function.
+
+  @param [in] Table   Pointer to a SSDT table allocated by
+                      the BuildSsdtSerialTable function.
+
+  @retval EFI_SUCCESS           Success.
+**/
+EFI_STATUS
+EFIAPI
+FreeSsdtSerialPortTable (
+  IN EFI_ACPI_DESCRIPTION_HEADER  * Table
+  )
+{
+  ASSERT (Table != NULL);
+  FreePool (Table);
+  return EFI_SUCCESS;
+}
+
+/** Build a SSDT table describing the input serial port.
+
+  The table created by this function must be freed by FreeSsdtSerialTable.
+
+  @param [in]  AcpiTableInfo    Pointer to the ACPI table information.
+  @param [in]  SerialPortInfo   Serial port to describe in the SSDT table.
+  @param [in]  Name             The Name to give to the Device.
+                                Must be a NULL-terminated ASL NameString
+                                e.g.: "DEV0", "DV15.DEV0", etc.
+  @param [in]  Uid              UID for the Serial Port.
+  @param [out] Table            If success, pointer to the created SSDT table.
+
+  @retval EFI_SUCCESS            Table generated successfully.
+  @retval EFI_INVALID_PARAMETER  A parameter is invalid.
+  @retval EFI_NOT_FOUND          Could not find information.
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+BuildSsdtSerialPortTable (
+  IN  CONST CM_STD_OBJ_ACPI_TABLE_INFO    *  AcpiTableInfo,
+  IN  CONST CM_ARM_SERIAL_PORT_INFO       *  SerialPortInfo,
+  IN  CONST CHAR8                         *  Name,
+  IN  CONST UINT64                           Uid,
+  OUT       EFI_ACPI_DESCRIPTION_HEADER  **  Table
+  )
+{
+  EFI_STATUS              Status;
+  EFI_STATUS              Status1;
+  AML_ROOT_NODE_HANDLE    RootNodeHandle;
+
+  ASSERT (AcpiTableInfo != NULL);
+  ASSERT (SerialPortInfo != NULL);
+  ASSERT (Name != NULL);
+  ASSERT (Table != NULL);
+
+  // Validate the Serial Port Info.
+  Status = ValidateSerialPortInfo (SerialPortInfo, 1);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Parse the SSDT Serial Port Template.
+  Status = AmlParseDefinitionBlock (
+             (EFI_ACPI_DESCRIPTION_HEADER*)ssdtserialporttemplate_aml_code,
+             &RootNodeHandle
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: SSDT-SERIAL-PORT-FIXUP:"
+      " Failed to parse SSDT Serial Port Template. Status = %r\n",
+      Status
+      ));
+    return Status;
+  }
+
+  // Fixup the template values.
+  Status = FixupSerialPortInfo (
+             RootNodeHandle,
+             SerialPortInfo,
+             Name,
+             Uid,
+             Table
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to fixup SSDT Serial Port Table."
+      " Status = %r\n",
+      Status
+      ));
+    goto exit_handler;
+  }
+
+  // Serialize the tree.
+  Status = AmlSerializeDefinitionBlock (
+             RootNodeHandle,
+             Table
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to Serialize SSDT Table Data."
+      " Status = %r\n",
+      Status
+      ));
+  }
+
+exit_handler:
+  // Cleanup
+  if (RootNodeHandle != NULL) {
+    Status1 = AmlDeleteTree (RootNodeHandle);
+    if (EFI_ERROR (Status1)) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to cleanup AML tree."
+        " Status = %r\n",
+        Status1
+        ));
+      // If Status was success but we failed to delete the AML Tree
+      // return Status1 else return the original error code, i.e. Status.
+      if (!EFI_ERROR (Status)) {
+        return Status1;
+      }
+    }
+  }
+
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf b/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf
new file mode 100644
index 0000000000000000000000000000000000000000..af3d404393f5f1385ab2d40f45f7222ab66f9b3a
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf
@@ -0,0 +1,30 @@
+## @file
+#  SSDT Serial Port fixup Library
+#
+#  Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x0001001B
+  BASE_NAME      = DynamicSsdtSerialPortFixupLib
+  FILE_GUID      = AC5978CC-5B62-4466-AD04-23644C2C38C2
+  VERSION_STRING = 1.0
+  MODULE_TYPE    = DXE_DRIVER
+  LIBRARY_CLASS  = SsdtSerialPortFixupLib
+
+[Sources]
+  SsdtSerialPortFixupLib.c
+  SsdtSerialPortTemplate.asl
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+  AmlLib
+  BaseLib
+
diff --git a/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortTemplate.asl b/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortTemplate.asl
new file mode 100644
index 0000000000000000000000000000000000000000..fcae2160ac3df6c3e9e48115b0b7195c3001c782
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortTemplate.asl
@@ -0,0 +1,60 @@
+/** @file
+  SSDT Serial Template
+
+  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - Arm Server Base Boot Requirements (SBBR), s4.2.1.8 "SPCR".
+
+  @par Glossary:
+    - {template} - Data fixed up using AML Fixup APIs.
+**/
+
+DefinitionBlock ("SsdtSerialPortTemplate.aml", "SSDT", 2, "ARMLTD", "SERIAL", 1) {
+  Scope (_SB) {
+    // UART PL011
+    Device (COM0) {                                       // {template}
+      Name (_UID, 0x0)                                    // {template}
+      Name (_HID, "HID0000")                              // {template}
+      Name (_CID, "CID0000")                              // {template}
+
+      Method(_STA) {
+        Return(0xF)
+      }
+
+      Name (_CRS, ResourceTemplate() {
+        QWordMemory (
+          ,                   // ResourceUsage
+          ,                   // Decode
+          ,                   // IsMinFixed
+          ,                   // IsMaxFixed
+          ,                   // Cacheable
+          ReadWrite,          // ReadAndWrite
+          0x0,                // AddressGranularity
+          0xA0000000,         // AddressMinimum           // {template}
+          0xAFFFFFFF,         // AddressMaximum           // {template}
+          0,                  // AddressTranslation
+          0x10000000,         // RangeLength              // {template}
+          ,                   // ResourceSourceIndex
+          ,                   // ResourceSource
+          ,                   // DescriptorName
+          ,                   // MemoryRangeType
+                              // TranslationType
+        ) // QWordMemory
+        Interrupt (
+          ResourceConsumer,   // ResourceUsage
+          Level,              // EdgeLevel
+          ActiveHigh,         // ActiveLevel
+          Exclusive,          // Shared
+          ,                   // ResourceSourceIndex
+          ,                   // ResourceSource
+                              // DescriptorName
+          ) {
+            0xA5                                          // {template}
+        } // Interrupt
+      }) // Name
+    } // Device
+  } // Scope (_SB)
+}
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 28/30] DynamicTablesPkg: SSDT Serial Port generator
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (26 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 27/30] DynamicTablesPkg: SSDT Serial Port Fixup library Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 29/30] DynamicTablesPkg: Add SSDT Serial port for SPCR Sami Mujawar
                   ` (2 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

Most platforms have several serial ports. These serial ports
are described to an operating system using definition block
tables.

The SSDT Serial Port Table Generator uses the Configuration
Manager protocol to obtain information for the Serial Ports
on the platform. The serial ports are described using the
CM_ARM_SERIAL_PORT_INFO structure. The EArmObjSerialPortInfo
ID is used to represent a standard serial port.

The SSDT Serial port fixup library provides interfaces to
generate a SSDT Serial port table based on the serial port
information. The SSDT Serial Port Table Generator uses the
SSDT serial port fixup library to build serial port
definition blocks and installs the SSDT tables.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/DynamicTables.dsc.inc                                               |   6 +
 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c | 367 ++++++++++++++++++++
 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf  |  33 ++
 3 files changed, 406 insertions(+)

diff --git a/DynamicTablesPkg/DynamicTables.dsc.inc b/DynamicTablesPkg/DynamicTables.dsc.inc
index 0063fc3c671f4ae6089dd4df76074445364c6011..7fb14d8d1463f7d4502fd3a7708bc94bc336357d 100644
--- a/DynamicTablesPkg/DynamicTables.dsc.inc
+++ b/DynamicTablesPkg/DynamicTables.dsc.inc
@@ -32,6 +32,9 @@ [Components.common]
   DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf
   DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf
 
+  # AML Fixup
+  DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
+
   #
   # Dynamic Table Factory Dxe
   #
@@ -47,6 +50,9 @@ [Components.common]
       NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf
       NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf
       NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf
+
+      # AML Fixup
+      NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
   }
 
   #
diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c
new file mode 100644
index 0000000000000000000000000000000000000000..2197e50dc714707f7ed7cc37759d3dba5a45f0e0
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c
@@ -0,0 +1,367 @@
+/** @file
+  SSDT Serial Port Table Generator.
+
+  Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <IndustryStandard/DebugPort2Table.h>
+#include <Library/AcpiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/AcpiTable.h>
+
+// Module specific include files.
+#include <AcpiTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <ConfigurationManagerHelper.h>
+#include <Library/SsdtSerialPortFixupLib.h>
+#include <Library/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+/** ARM standard SSDT Serial Port Table Generator
+
+  Constructs SSDT tables describing serial ports (other than the serial ports
+  used by the SPCR or DBG2 tables).
+
+Requirements:
+  The following Configuration Manager Object(s) are required by
+  this Generator:
+  - EArmObjSerialPortInfo
+*/
+
+/** This macro expands to a function that retrieves the Serial-port
+    information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+  EObjNameSpaceArm,
+  EArmObjSerialPortInfo,
+  CM_ARM_SERIAL_PORT_INFO
+  );
+
+/** Starting value for the UID to represent the serial ports.
+    Note: The UID 0 and 1 are reserved for use by DBG2 port and SPCR
+          respectively. So, the UIDs for serial ports for general use
+          start at 2.
+*/
+#define SERIAL_PORT_START_UID                      2
+
+/** Maximum serial ports supported by this generator.
+    This generator supports a maximum of 14 (16 - 2) serial ports.
+    The -2 here reflects the reservation for serial ports for the DBG2
+    and SPCR ports regardless of whether the DBG2 or SPCR port is enabled.
+    Note: This is not a hard limitation and can be extended if needed.
+          Corresponding changes would be needed to support the Name and
+          UID fields describing the serial port.
+
+*/
+#define MAX_SERIAL_PORTS_SUPPORTED                 14
+
+/** Free any resources allocated for constructing the tables.
+
+  @param [in]      This           Pointer to the ACPI table generator.
+  @param [in]      AcpiTableInfo  Pointer to the ACPI Table Info.
+  @param [in]      CfgMgrProtocol Pointer to the Configuration Manager
+                                  Protocol Interface.
+  @param [in, out] Table          Pointer to an array of pointers
+                                  to ACPI Table(s).
+  @param [in]      TableCount     Number of ACPI table(s).
+
+  @retval EFI_SUCCESS           The resources were freed successfully.
+  @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FreeSsdtSerialPortTableEx (
+  IN      CONST ACPI_TABLE_GENERATOR                   * CONST This,
+  IN      CONST CM_STD_OBJ_ACPI_TABLE_INFO             * CONST AcpiTableInfo,
+  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL   * CONST CfgMgrProtocol,
+  IN OUT        EFI_ACPI_DESCRIPTION_HEADER          *** CONST Table,
+  IN      CONST UINTN                                          TableCount
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_ACPI_DESCRIPTION_HEADER    ** TableList;
+  UINTN                             Index;
+
+  ASSERT (This != NULL);
+  ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+  ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (AcpiTableInfo != NULL);
+
+  if ((Table == NULL)   ||
+      (*Table == NULL)  ||
+      (TableCount == 0)) {
+    DEBUG ((DEBUG_ERROR, "ERROR: SSDT-SERIAL-PORT: Invalid Table Pointer\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  TableList = *Table;
+
+  for (Index = 0; Index < TableCount; Index++) {
+    if ((TableList[Index] != NULL) &&
+        (TableList[Index]->Signature ==
+         EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE)) {
+      Status = FreeSsdtSerialPortTable (TableList[Index]);
+    } else {
+      Status = EFI_INVALID_PARAMETER;
+    }
+
+    if (EFI_ERROR (Status)) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "ERROR: SSDT-SERIAL-PORT: Could not free SSDT table at index %d."
+        " Status = %r\n",
+        Index,
+        Status
+        ));
+      return Status;
+    }
+  } //for
+
+  // Free the table list.
+  FreePool (*Table);
+
+  return EFI_SUCCESS;
+}
+
+/** Construct SSDT tables describing serial-ports.
+
+  This function invokes the Configuration Manager protocol interface
+  to get the required hardware information for generating the ACPI
+  table.
+
+  If this function allocates any resources then they must be freed
+  in the FreeXXXXTableResourcesEx function.
+
+  @param [in]  This            Pointer to the ACPI table generator.
+  @param [in]  AcpiTableInfo   Pointer to the ACPI table information.
+  @param [in]  CfgMgrProtocol  Pointer to the Configuration Manager
+                               Protocol interface.
+  @param [out] Table           Pointer to a list of generated ACPI table(s).
+  @param [out] TableCount      Number of generated ACPI table(s).
+
+  @retval EFI_SUCCESS            Table generated successfully.
+  @retval EFI_BAD_BUFFER_SIZE    The size returned by the Configuration
+                                 Manager is less than the Object size for
+                                 the requested object.
+  @retval EFI_INVALID_PARAMETER  A parameter is invalid.
+  @retval EFI_NOT_FOUND          Could not find information.
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate memory.
+  @retval EFI_UNSUPPORTED        Unsupported configuration.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildSsdtSerialPortTableEx (
+  IN  CONST ACPI_TABLE_GENERATOR                   *       This,
+  IN  CONST CM_STD_OBJ_ACPI_TABLE_INFO             * CONST AcpiTableInfo,
+  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL   * CONST CfgMgrProtocol,
+  OUT       EFI_ACPI_DESCRIPTION_HEADER          ***       Table,
+  OUT       UINTN                                  * CONST TableCount
+  )
+{
+  EFI_STATUS                      Status;
+  CM_ARM_SERIAL_PORT_INFO       * SerialPortInfo;
+  UINT32                          SerialPortCount;
+  UINTN                           Index;
+  CHAR8                           NewName[] = "COMx";
+  UINT64                          Uid;
+  EFI_ACPI_DESCRIPTION_HEADER  ** TableList;
+
+  ASSERT (This != NULL);
+  ASSERT (AcpiTableInfo != NULL);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (Table != NULL);
+  ASSERT (TableCount != NULL);
+  ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+  ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+  *Table = NULL;
+
+  Status = GetEArmObjSerialPortInfo (
+             CfgMgrProtocol,
+             CM_NULL_TOKEN,
+             &SerialPortInfo,
+             &SerialPortCount
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: SSDT-SERIAL-PORT: Failed to get serial port information."
+      " Status = %r\n",
+      Status
+      ));
+    return Status;
+  }
+
+  if (SerialPortCount > MAX_SERIAL_PORTS_SUPPORTED) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: SSDT-SERIAL-PORT: Too many serial ports: %d."
+      " Maximum serial ports supported = %d.\n",
+      SerialPortCount,
+      MAX_SERIAL_PORTS_SUPPORTED
+      ));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Validate the SerialPort info.
+  Status = ValidateSerialPortInfo (SerialPortInfo, SerialPortCount);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: SSDT-SERIAL-PORT: Invalid serial port information. Status = %r\n",
+      Status
+      ));
+    return Status;
+  }
+
+  // Allocate a table to store pointers to the SSDT tables.
+  TableList = (EFI_ACPI_DESCRIPTION_HEADER**)
+              AllocateZeroPool (
+                (sizeof (EFI_ACPI_DESCRIPTION_HEADER*) * SerialPortCount)
+                );
+  if (TableList == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: SSDT-SERIAL-PORT: Failed to allocate memory for Table List."
+      " Status = %r\n",
+      Status
+      ));
+    return Status;
+  }
+
+  // Setup the table list early so that that appropriate cleanup
+  // can be done in case of failure.
+  *Table = TableList;
+
+  for (Index = 0; Index < SerialPortCount; Index++) {
+    Uid = SERIAL_PORT_START_UID + Index;
+    NewName[3] = AsciiFromHex ((UINT8)(Uid));
+
+    // Build a SSDT table describing the serial port.
+    Status = BuildSsdtSerialPortTable (
+               AcpiTableInfo,
+               &SerialPortInfo[Index],
+               NewName,
+               Uid,
+               &TableList[Index]
+               );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "ERROR: SSDT-SERIAL-PORT: Failed to build associated SSDT table."
+        " Status = %r\n",
+        Status
+        ));
+      goto error_handler;
+    }
+
+    // Increment the table count here so that appropriate cleanup
+    // can be done in case of failure.
+    *TableCount += 1;
+  } // for
+
+error_handler:
+  // Note: Table list and Serial port count has been setup. The
+  // error handler does nothing here as the framework will invoke
+  // FreeSsdtSerialPortTableEx() even on failure.
+  return Status;
+}
+
+/** This macro defines the SSDT Serial Port Table Generator revision.
+*/
+#define SSDT_SERIAL_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the SSDT Serial Port Table Generator.
+*/
+STATIC
+CONST
+ACPI_TABLE_GENERATOR SsdtSerialPortGenerator = {
+  // Generator ID
+  CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtSerialPort),
+  // Generator Description
+  L"ACPI.STD.SSDT.SERIAL.PORT.GENERATOR",
+  // ACPI Table Signature
+  EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE,
+  // ACPI Table Revision - Unused
+  0,
+  // Minimum ACPI Table Revision - Unused
+  0,
+  // Creator ID
+  TABLE_GENERATOR_CREATOR_ID_ARM,
+  // Creator Revision
+  SSDT_SERIAL_GENERATOR_REVISION,
+  // Build table function. Use the extended version instead.
+  NULL,
+  // Free table function. Use the extended version instead.
+  NULL,
+  // Extended Build table function.
+  BuildSsdtSerialPortTableEx,
+  // Extended free function.
+  FreeSsdtSerialPortTableEx
+};
+
+/** Register the Generator with the ACPI Table Factory.
+
+  @param [in]  ImageHandle  The handle to the image.
+  @param [in]  SystemTable  Pointer to the System Table.
+
+  @retval EFI_SUCCESS           The Generator is registered.
+  @retval EFI_INVALID_PARAMETER A parameter is invalid.
+  @retval EFI_ALREADY_STARTED   The Generator for the Table ID
+                                is already registered.
+**/
+EFI_STATUS
+EFIAPI
+AcpiSsdtSerialPortLibConstructor (
+  IN  EFI_HANDLE           ImageHandle,
+  IN  EFI_SYSTEM_TABLE  *  SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  Status = RegisterAcpiTableGenerator (&SsdtSerialPortGenerator);
+  DEBUG ((
+    DEBUG_INFO,
+    "SSDT-SERIAL-PORT: Register Generator. Status = %r\n",
+    Status
+    ));
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/** Deregister the Generator from the ACPI Table Factory.
+
+  @param [in]  ImageHandle  The handle to the image.
+  @param [in]  SystemTable  Pointer to the System Table.
+
+  @retval EFI_SUCCESS           The Generator is deregistered.
+  @retval EFI_INVALID_PARAMETER A parameter is invalid.
+  @retval EFI_NOT_FOUND         The Generator is not registered.
+**/
+EFI_STATUS
+EFIAPI
+AcpiSsdtSerialPortLibDestructor (
+  IN  EFI_HANDLE           ImageHandle,
+  IN  EFI_SYSTEM_TABLE  *  SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  Status = DeregisterAcpiTableGenerator (&SsdtSerialPortGenerator);
+  DEBUG ((
+    DEBUG_INFO,
+    "SSDT-SERIAL-PORT: Deregister Generator. Status = %r\n",
+    Status
+    ));
+  ASSERT_EFI_ERROR (Status);
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
new file mode 100644
index 0000000000000000000000000000000000000000..fb7663e280ad9dc81ac5d3f8fcede883319a569f
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
@@ -0,0 +1,33 @@
+## @file
+# Ssdt Serial Port Table Generator
+#
+#  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x0001001B
+  BASE_NAME      = SsdtSerialPortLibArm
+  FILE_GUID      = D1F92325-2DFB-435C-9B4C-A6B864F19230
+  VERSION_STRING = 1.0
+  MODULE_TYPE    = DXE_DRIVER
+  LIBRARY_CLASS  = NULL|DXE_DRIVER
+  CONSTRUCTOR    = AcpiSsdtSerialPortLibConstructor
+  DESTRUCTOR     = AcpiSsdtSerialPortLibDestructor
+
+[Sources]
+  SsdtSerialPortGenerator.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+  AmlLib
+  BaseLib
+  TableHelperLib
+  SsdtSerialPortFixupLib
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 29/30] DynamicTablesPkg: Add SSDT Serial port for SPCR
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (27 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 28/30] DynamicTablesPkg: SSDT Serial Port generator Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-12 15:22 ` [PATCH v1 30/30] DynamicTablesPkg: Add SSDT Serial port for DBG2 Sami Mujawar
  2020-08-13 15:16 ` [edk2-devel] [PATCH v1 00/30] Add Dynamic AML generation support Alexei Fedorov
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

According to Arm Server Base Boot Requirements,
Platform Design Document version 1.2 revision D,
September 2, 2019, section '4.2.1.8 SPCR'; the
SPCR console device must be included in the DSDT.

The SSDT Serial port fixup library provides
interfaces to generate a SSDT Serial port table
based on the serial port information.

Update the SPCR Generator to use the SSDT serial
port fixup library to build a serial port definition
block corresponding to the SPCR serial port and
install the SSDT table.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf |   3 +-
 DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c    | 219 ++++++++++++++------
 2 files changed, 162 insertions(+), 60 deletions(-)

diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf
index 0ebe1ffbbdf0b53d9bab4fe454dffdceaeb7f48b..e11f878ec83e050592c8b0428c01c2eafba37cd8 100644
--- a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf
+++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf
@@ -1,7 +1,7 @@
 ## @file
 #  SPCR Table Generator
 #
-#  Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.
+#  Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
 #
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 ##
@@ -27,6 +27,7 @@ [Packages]
 
 [LibraryClasses]
   BaseLib
+  SsdtSerialPortFixupLib
 
 [Pcd]
 
diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c
index b80e98ee7e143e39c540557d53a4129bf4ac5eb7..24bb5c014607b0746c4a8bb8bd260510fbdff08b 100644
--- a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c
+++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c
@@ -1,7 +1,8 @@
 /** @file
   SPCR Table Generator
 
-  Copyright (c) 2017 - 2020, ARM Limited. All rights reserved.
+  Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
+
   SPDX-License-Identifier: BSD-2-Clause-Patent
 
   @par Reference(s):
@@ -14,12 +15,14 @@
 #include <IndustryStandard/SerialPortConsoleRedirectionTable.h>
 #include <Library/AcpiLib.h>
 #include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
 #include <Protocol/AcpiTable.h>
 
 // Module specific include files.
 #include <AcpiTableGenerator.h>
 #include <ConfigurationManagerObject.h>
 #include <ConfigurationManagerHelper.h>
+#include <Library/SsdtSerialPortFixupLib.h>
 #include <Library/TableHelperLib.h>
 #include <Protocol/ConfigurationManagerProtocol.h>
 
@@ -40,6 +43,14 @@ NOTE: This implementation ignores the possibility that the Serial settings may
 
 #pragma pack(1)
 
+/** A string representing the name of the SPCR port.
+*/
+#define NAME_STR_SPCR_PORT               "COM1"
+
+/** An UID representing the SPCR port.
+*/
+#define UID_SPCR_PORT                    1
+
 /** This macro defines the no flow control option.
 */
 #define SPCR_FLOW_CONTROL_NONE           0
@@ -92,47 +103,111 @@ GET_OBJECT_LIST (
   CM_ARM_SERIAL_PORT_INFO
   )
 
-/** Construct the SPCR ACPI table.
+/** Free any resources allocated for constructing the tables.
+
+  @param [in]      This           Pointer to the ACPI table generator.
+  @param [in]      AcpiTableInfo  Pointer to the ACPI Table Info.
+  @param [in]      CfgMgrProtocol Pointer to the Configuration Manager
+                                  Protocol Interface.
+  @param [in, out] Table          Pointer to an array of pointers
+                                  to ACPI Table(s).
+  @param [in]      TableCount     Number of ACPI table(s).
+
+  @retval EFI_SUCCESS           The resources were freed successfully.
+  @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FreeSpcrTableEx (
+  IN      CONST ACPI_TABLE_GENERATOR                   * CONST This,
+  IN      CONST CM_STD_OBJ_ACPI_TABLE_INFO             * CONST AcpiTableInfo,
+  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL   * CONST CfgMgrProtocol,
+  IN OUT        EFI_ACPI_DESCRIPTION_HEADER          *** CONST Table,
+  IN      CONST UINTN                                          TableCount
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_ACPI_DESCRIPTION_HEADER    ** TableList;
+
+  ASSERT (This != NULL);
+  ASSERT (AcpiTableInfo != NULL);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+  ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+  if ((Table == NULL)   ||
+      (*Table == NULL)  ||
+      (TableCount != 2)) {
+    DEBUG ((DEBUG_ERROR, "ERROR: SPCR: Invalid Table Pointer\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  TableList = *Table;
+
+  if ((TableList[1] == NULL) ||
+      (TableList[1]->Signature !=
+       EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE)) {
+    DEBUG ((DEBUG_ERROR, "ERROR: SPCR: Invalid SSDT table pointer.\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Only need to free the SSDT table at index 1. The SPCR table is static.
+  Status = FreeSsdtSerialPortTable (TableList[1]);
+  ASSERT_EFI_ERROR (Status);
+
+  // Free the table list.
+  FreePool (*Table);
+
+  return Status;
+}
+
+/** Construct the SPCR ACPI table and its associated SSDT table.
 
   This function invokes the Configuration Manager protocol interface
   to get the required hardware information for generating the ACPI
   table.
 
   If this function allocates any resources then they must be freed
-  in the FreeXXXXTableResources function.
+  in the FreeXXXXTableResourcesEx function.
 
-  @param [in]  This           Pointer to the table generator.
-  @param [in]  AcpiTableInfo  Pointer to the ACPI Table Info.
-  @param [in]  CfgMgrProtocol Pointer to the Configuration Manager
-                              Protocol Interface.
-  @param [out] Table          Pointer to the constructed ACPI Table.
+  @param [in]  This            Pointer to the ACPI table generator.
+  @param [in]  AcpiTableInfo   Pointer to the ACPI table information.
+  @param [in]  CfgMgrProtocol  Pointer to the Configuration Manager
+                               Protocol interface.
+  @param [out] Table           Pointer to a list of generated ACPI table(s).
+  @param [out] TableCount      Number of generated ACPI table(s).
 
-  @retval EFI_SUCCESS           Table generated successfully.
-  @retval EFI_INVALID_PARAMETER A parameter is invalid.
-  @retval EFI_NOT_FOUND         The required object was not found.
-  @retval EFI_UNSUPPORTED       An unsupported baudrate was specified by the
-                                Configuration Manager.
-  @retval EFI_BAD_BUFFER_SIZE   The size returned by the Configuration
-                                Manager is less than the Object size for the
-                                requested object.
+  @retval EFI_SUCCESS            Table generated successfully.
+  @retval EFI_BAD_BUFFER_SIZE    The size returned by the Configuration
+                                 Manager is less than the Object size for
+                                 the requested object.
+  @retval EFI_INVALID_PARAMETER  A parameter is invalid.
+  @retval EFI_NOT_FOUND          Could not find information.
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate memory.
+  @retval EFI_UNSUPPORTED        Unsupported configuration.
 **/
 STATIC
 EFI_STATUS
 EFIAPI
-BuildSpcrTable (
-  IN  CONST ACPI_TABLE_GENERATOR                  * CONST This,
-  IN  CONST CM_STD_OBJ_ACPI_TABLE_INFO            * CONST AcpiTableInfo,
-  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
-  OUT       EFI_ACPI_DESCRIPTION_HEADER          ** CONST Table
+BuildSpcrTableEx (
+  IN  CONST ACPI_TABLE_GENERATOR                   *       This,
+  IN  CONST CM_STD_OBJ_ACPI_TABLE_INFO             * CONST AcpiTableInfo,
+  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL   * CONST CfgMgrProtocol,
+  OUT       EFI_ACPI_DESCRIPTION_HEADER          ***       Table,
+  OUT       UINTN                                  * CONST TableCount
   )
 {
-  EFI_STATUS                 Status;
-  CM_ARM_SERIAL_PORT_INFO  * SerialPortInfo;
+  EFI_STATUS                      Status;
+  CM_ARM_SERIAL_PORT_INFO       * SerialPortInfo;
+  UINT32                          SerialPortCount;
+  EFI_ACPI_DESCRIPTION_HEADER  ** TableList;
 
   ASSERT (This != NULL);
   ASSERT (AcpiTableInfo != NULL);
   ASSERT (CfgMgrProtocol != NULL);
   ASSERT (Table != NULL);
+  ASSERT (TableCount != NULL);
   ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
   ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
 
@@ -155,7 +230,7 @@ BuildSpcrTable (
              CfgMgrProtocol,
              CM_NULL_TOKEN,
              &SerialPortInfo,
-             NULL
+             &SerialPortCount
              );
   if (EFI_ERROR (Status)) {
     DEBUG ((
@@ -163,44 +238,46 @@ BuildSpcrTable (
       "ERROR: SPCR: Failed to get serial port information. Status = %r\n",
       Status
       ));
-    goto error_handler;
+    return Status;
   }
 
-  if (SerialPortInfo->BaseAddress == 0) {
-    Status = EFI_INVALID_PARAMETER;
+  if (SerialPortCount == 0) {
     DEBUG ((
       DEBUG_ERROR,
-      "ERROR: SPCR: Uart port base address is invalid. BaseAddress = 0x%lx\n",
-      SerialPortInfo->BaseAddress
+      "ERROR: SPCR: Serial port information not found. Status = %r\n",
+      EFI_NOT_FOUND
       ));
-    goto error_handler;
+    return EFI_NOT_FOUND;
   }
 
-  if ((SerialPortInfo->PortSubtype !=
-      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART) &&
-      (SerialPortInfo->PortSubtype !=
-      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X) &&
-      (SerialPortInfo->PortSubtype !=
-      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART) &&
-      (SerialPortInfo->PortSubtype !=
-      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550) &&
-      (SerialPortInfo->PortSubtype !=
-      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_DCC)) {
-    Status = EFI_INVALID_PARAMETER;
+  // Validate the SerialPort info. Only one SPCR port can be described.
+  // If platform provides description for multiple SPCR ports, use the
+  // first SPCR port information.
+  Status = ValidateSerialPortInfo (SerialPortInfo, 1);
+  if (EFI_ERROR (Status)) {
     DEBUG ((
       DEBUG_ERROR,
-      "ERROR: SPCR: Uart port subtype is invalid. PortSubtype = 0x%x\n",
-      SerialPortInfo->PortSubtype
+      "ERROR: SPCR: Invalid serial port information. Status = %r\n",
+      Status
       ));
-    goto error_handler;
+    return Status;
   }
 
-  DEBUG ((DEBUG_INFO, "SPCR UART Configuration:\n"));
-  DEBUG ((DEBUG_INFO, "  UART Base  = 0x%lx\n", SerialPortInfo->BaseAddress));
-  DEBUG ((DEBUG_INFO, "  Clock      = %d\n", SerialPortInfo->Clock));
-  DEBUG ((DEBUG_INFO, "  Baudrate   = %ld\n", SerialPortInfo->BaudRate));
-  DEBUG ((DEBUG_INFO, "  Interrupt  = %d\n", SerialPortInfo->Interrupt));
+  // Allocate a table to store pointers to the SPCR and SSDT tables.
+  TableList = (EFI_ACPI_DESCRIPTION_HEADER**)
+              AllocateZeroPool (sizeof (EFI_ACPI_DESCRIPTION_HEADER*) * 2);
+  if (TableList == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: SPCR: Failed to allocate memory for Table List," \
+      " Status = %r\n",
+      Status
+      ));
+    return Status;
+  }
 
+  // Build SPCR table.
   Status = AddAcpiHeader (
              CfgMgrProtocol,
              This,
@@ -267,9 +344,35 @@ BuildSpcrTable (
       goto error_handler;
   } // switch
 
-  *Table = (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiSpcr;
+  TableList[0] = (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiSpcr;
+
+  // Build a SSDT table describing the serial port.
+  Status = BuildSsdtSerialPortTable (
+             AcpiTableInfo,
+             SerialPortInfo,
+             NAME_STR_SPCR_PORT,
+             UID_SPCR_PORT,
+             &TableList[1]
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: SPCR: Failed to build associated SSDT table. Status = %r\n",
+      Status
+      ));
+    goto error_handler;
+  }
+
+  *TableCount = 2;
+  *Table = TableList;
+
+  return Status;
 
 error_handler:
+  if (TableList != NULL) {
+    FreePool (TableList);
+  }
+
   return Status;
 }
 
@@ -287,7 +390,7 @@ ACPI_TABLE_GENERATOR SpcrGenerator = {
   // Generator Description
   L"ACPI.STD.SPCR.GENERATOR",
   // ACPI Table Signature
-  EFI_ACPI_6_2_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE,
+  EFI_ACPI_6_3_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE,
   // ACPI Table Revision supported by this Generator
   EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_REVISION,
   // Minimum supported ACPI Table Revision
@@ -296,16 +399,14 @@ ACPI_TABLE_GENERATOR SpcrGenerator = {
   TABLE_GENERATOR_CREATOR_ID_ARM,
   // Creator Revision
   SPCR_GENERATOR_REVISION,
-  // Build Table function
-  BuildSpcrTable,
-  // No additional resources are allocated by the generator.
-  // Hence the Free Resource function is not required.
+  // Build table function. Use the extended version instead.
   NULL,
-  // Extended build function not needed
+  // Free table function. Use the extended version instead.
   NULL,
-  // Extended build function not implemented by the generator.
-  // Hence extended free resource function is not required.
-  NULL
+  // Extended Build table function.
+  BuildSpcrTableEx,
+  // Extended free function.
+  FreeSpcrTableEx
 };
 
 /** Register the Generator with the ACPI Table Factory.
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v1 30/30] DynamicTablesPkg: Add SSDT Serial port for DBG2
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (28 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 29/30] DynamicTablesPkg: Add SSDT Serial port for SPCR Sami Mujawar
@ 2020-08-12 15:22 ` Sami Mujawar
  2020-08-13 15:16 ` [edk2-devel] [PATCH v1 00/30] Add Dynamic AML generation support Alexei Fedorov
  30 siblings, 0 replies; 32+ messages in thread
From: Sami Mujawar @ 2020-08-12 15:22 UTC (permalink / raw)
  To: devel
  Cc: Sami Mujawar, Alexei.Fedorov, pierre.gondois, ard.biesheuvel,
	Matteo.Carlini, Ben.Adderson, nd

From: Pierre Gondois <pierre.gondois@arm.com>

The SSDT Serial port fixup library provides
interfaces to generate a SSDT Serial port table
based on the serial port information.

Update the DBG2 Generator to use the SSDT serial
port fixup library to build a serial port definition
block for the DBG2 serial port and install the
SSDT table.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf |   3 +-
 DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c    | 228 ++++++++++++++------
 2 files changed, 163 insertions(+), 68 deletions(-)

diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf b/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf
index 0f8d3cd687700d01fce6ced5d31a7a2b890bc551..f7b7c1c025a68989166faf34915e1754bfb44ad0 100644
--- a/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf
+++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf
@@ -1,7 +1,7 @@
 ## @file
 # DBG2 Table Generator
 #
-#  Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.
+#  Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
 #
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 ##
@@ -29,6 +29,7 @@ [Packages]
 [LibraryClasses]
   BaseLib
   PL011UartLib
+  SsdtSerialPortFixupLib
 
 [FixedPcd]
   gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate
diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c b/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c
index 172d99f61cf36e7e65d62249960fd7527d8dae00..d902bbc8463921624f1a6333e8d6bd84c6cb38f2 100644
--- a/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c
+++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c
@@ -1,7 +1,8 @@
 /** @file
   DBG2 Table Generator
 
-  Copyright (c) 2017 - 2020, ARM Limited. All rights reserved.
+  Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
+
   SPDX-License-Identifier: BSD-2-Clause-Patent
 
   @par Reference(s):
@@ -12,6 +13,7 @@
 #include <IndustryStandard/DebugPort2Table.h>
 #include <Library/AcpiLib.h>
 #include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
 #include <Library/PL011UartLib.h>
 #include <Protocol/AcpiTable.h>
 #include <Protocol/SerialIo.h>
@@ -20,6 +22,7 @@
 #include <AcpiTableGenerator.h>
 #include <ConfigurationManagerObject.h>
 #include <ConfigurationManagerHelper.h>
+#include <Library/SsdtSerialPortFixupLib.h>
 #include <Library/TableHelperLib.h>
 #include <Protocol/ConfigurationManagerProtocol.h>
 
@@ -44,17 +47,21 @@ Requirements:
 */
 #define DBG2_NUMBER_OF_GENERIC_ADDRESS_REGISTERS   1
 
-/** The index for the debug port 1 in the Debug port information list.
+/** The index for the debug port 0 in the Debug port information list.
 */
-#define DBG_PORT_INDEX_PORT1                       0
+#define INDEX_DBG_PORT0                            0
 
-/** A string representing the name of the debug port 1.
+/** A string representing the name of the debug port 0.
 */
-#define NAME_STR_PORT1                            "COM1"
+#define NAME_STR_DBG_PORT0                         "COM0"
+
+/** An UID representing the debug port 0.
+*/
+#define UID_DBG_PORT0                              0
 
 /** The length of the namespace string.
 */
-#define DBG2_NAMESPACESTRING_FIELD_SIZE            sizeof (NAME_STR_PORT1)
+#define DBG2_NAMESPACESTRING_FIELD_SIZE            sizeof (NAME_STR_DBG_PORT0)
 
 /** The PL011 UART address range length.
 */
@@ -159,7 +166,7 @@ DBG2_TABLE AcpiDbg2 = {
       0,                    // {Template}: Serial Port Subtype
       0,                    // {Template}: Serial Port Base Address
       PL011_UART_LENGTH,
-      NAME_STR_PORT1
+      NAME_STR_DBG_PORT0
       )
   }
 };
@@ -217,58 +224,115 @@ SetupDebugUart (
              &StopBits
              );
 
-  DEBUG ((DEBUG_INFO, "Debug UART Configuration:\n"));
-  DEBUG ((DEBUG_INFO, "UART Base  = 0x%lx\n", SerialPortInfo->BaseAddress));
-  DEBUG ((DEBUG_INFO, "Clock      = %d\n", SerialPortInfo->Clock));
-  DEBUG ((DEBUG_INFO, "Baudrate   = %ld\n", BaudRate));
-  DEBUG ((DEBUG_INFO, "Configuring Debug UART. Status = %r\n", Status));
-
   ASSERT_EFI_ERROR (Status);
   return Status;
 }
 
-/** Construct the DBG2 ACPI table
+/** Free any resources allocated for constructing the tables.
+
+  @param [in]      This           Pointer to the ACPI table generator.
+  @param [in]      AcpiTableInfo  Pointer to the ACPI Table Info.
+  @param [in]      CfgMgrProtocol Pointer to the Configuration Manager
+                                  Protocol Interface.
+  @param [in, out] Table          Pointer to an array of pointers
+                                  to ACPI Table(s).
+  @param [in]      TableCount     Number of ACPI table(s).
+
+  @retval EFI_SUCCESS           The resources were freed successfully.
+  @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FreeDbg2TableEx (
+  IN      CONST ACPI_TABLE_GENERATOR                   * CONST This,
+  IN      CONST CM_STD_OBJ_ACPI_TABLE_INFO             * CONST AcpiTableInfo,
+  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL   * CONST CfgMgrProtocol,
+  IN OUT        EFI_ACPI_DESCRIPTION_HEADER          *** CONST Table,
+  IN      CONST UINTN                                          TableCount
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_ACPI_DESCRIPTION_HEADER    ** TableList;
+
+  ASSERT (This != NULL);
+  ASSERT (AcpiTableInfo != NULL);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+  ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+  if ((Table == NULL)   ||
+      (*Table == NULL)  ||
+      (TableCount != 2)) {
+    DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Invalid Table Pointer\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  TableList = *Table;
+
+  if ((TableList[1] == NULL) ||
+      (TableList[1]->Signature !=
+       EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE)) {
+    DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Invalid SSDT table pointer.\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Only need to free the SSDT table at index 1. The DBG2 table is static.
+  Status = FreeSsdtSerialPortTable (TableList[1]);
+  ASSERT_EFI_ERROR (Status);
+
+  // Free the table list.
+  FreePool (*Table);
+
+  return Status;
+}
 
-    The BuildDbg2Table function is called by the Dynamic Table Manager
-    to construct the DBG2 ACPI table.
+/** Construct the DBG2 ACPI table and its associated SSDT table.
 
   This function invokes the Configuration Manager protocol interface
   to get the required hardware information for generating the ACPI
   table.
 
   If this function allocates any resources then they must be freed
-  in the FreeXXXXTableResources function.
+  in the FreeXXXXTableResourcesEx function.
 
-  @param [in]  This           Pointer to the table generator.
-  @param [in]  AcpiTableInfo  Pointer to the ACPI Table Info.
-  @param [in]  CfgMgrProtocol Pointer to the Configuration Manager
-                              Protocol Interface.
-  @param [out] Table          Pointer to the constructed ACPI Table.
+  @param [in]  This            Pointer to the ACPI table generator.
+  @param [in]  AcpiTableInfo   Pointer to the ACPI table information.
+  @param [in]  CfgMgrProtocol  Pointer to the Configuration Manager
+                               Protocol interface.
+  @param [out] Table           Pointer to a list of generated ACPI table(s).
+  @param [out] TableCount      Number of generated ACPI table(s).
 
-  @retval EFI_SUCCESS           Table generated successfully.
-  @retval EFI_INVALID_PARAMETER A parameter is invalid.
-  @retval EFI_NOT_FOUND         The required object was not found.
-  @retval EFI_BAD_BUFFER_SIZE   The size returned by the Configuration
-                                Manager is less than the Object size for the
-                                requested object.
+  @retval EFI_SUCCESS            Table generated successfully.
+  @retval EFI_BAD_BUFFER_SIZE    The size returned by the Configuration
+                                 Manager is less than the Object size for
+                                 the requested object.
+  @retval EFI_INVALID_PARAMETER  A parameter is invalid.
+  @retval EFI_NOT_FOUND          Could not find information.
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate memory.
+  @retval EFI_UNSUPPORTED        Unsupported configuration.
 **/
 STATIC
 EFI_STATUS
 EFIAPI
-BuildDbg2Table (
-  IN  CONST ACPI_TABLE_GENERATOR                  * CONST This,
-  IN  CONST CM_STD_OBJ_ACPI_TABLE_INFO            * CONST AcpiTableInfo,
-  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
-  OUT       EFI_ACPI_DESCRIPTION_HEADER          ** CONST Table
+BuildDbg2TableEx (
+  IN  CONST ACPI_TABLE_GENERATOR                   *       This,
+  IN  CONST CM_STD_OBJ_ACPI_TABLE_INFO             * CONST AcpiTableInfo,
+  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL   * CONST CfgMgrProtocol,
+  OUT       EFI_ACPI_DESCRIPTION_HEADER          ***       Table,
+  OUT       UINTN                                  * CONST TableCount
   )
 {
-  EFI_STATUS                 Status;
-  CM_ARM_SERIAL_PORT_INFO  * SerialPortInfo;
+  EFI_STATUS                      Status;
+  CM_ARM_SERIAL_PORT_INFO       * SerialPortInfo;
+  UINT32                          SerialPortCount;
+  EFI_ACPI_DESCRIPTION_HEADER  ** TableList;
 
   ASSERT (This != NULL);
   ASSERT (AcpiTableInfo != NULL);
   ASSERT (CfgMgrProtocol != NULL);
   ASSERT (Table != NULL);
+  ASSERT (TableCount != NULL);
   ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
   ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
 
@@ -291,7 +355,7 @@ BuildDbg2Table (
              CfgMgrProtocol,
              CM_NULL_TOKEN,
              &SerialPortInfo,
-             NULL
+             &SerialPortCount
              );
   if (EFI_ERROR (Status)) {
     DEBUG ((
@@ -299,34 +363,41 @@ BuildDbg2Table (
       "ERROR: DBG2: Failed to get serial port information. Status = %r\n",
       Status
       ));
-    goto error_handler;
+    return Status;
   }
 
-  if (SerialPortInfo->BaseAddress == 0) {
-    Status = EFI_INVALID_PARAMETER;
+  if (SerialPortCount == 0) {
     DEBUG ((
       DEBUG_ERROR,
-      "ERROR: DBG2: Uart port base address is invalid. BaseAddress = 0x%lx\n",
-      SerialPortInfo->BaseAddress
+      "ERROR: DBG2: Serial port information not found. Status = %r\n",
+      EFI_NOT_FOUND
       ));
-    goto error_handler;
+    return EFI_NOT_FOUND;
   }
 
-  if ((SerialPortInfo->PortSubtype !=
-      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART) &&
-      (SerialPortInfo->PortSubtype !=
-      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X) &&
-      (SerialPortInfo->PortSubtype !=
-      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART) &&
-      (SerialPortInfo->PortSubtype !=
-      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_DCC)) {
-    Status = EFI_INVALID_PARAMETER;
+  // Only use the first DBG2 port information.
+  Status = ValidateSerialPortInfo (SerialPortInfo, 1);
+  if (EFI_ERROR (Status)) {
     DEBUG ((
       DEBUG_ERROR,
-      "ERROR: DBG2: Uart port subtype is invalid. PortSubtype = 0x%x\n",
-      SerialPortInfo->PortSubtype
+      "ERROR: DBG2: Invalid serial port information. Status = %r\n",
+      Status
       ));
-    goto error_handler;
+    return Status;
+  }
+
+  // Allocate a table to store pointers to the DBG2 and SSDT tables.
+  TableList = (EFI_ACPI_DESCRIPTION_HEADER**)
+              AllocateZeroPool (sizeof (EFI_ACPI_DESCRIPTION_HEADER*) * 2);
+  if (TableList == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: DBG2: Failed to allocate memory for Table List," \
+      " Status = %r\n",
+      Status
+      ));
+    return Status;
   }
 
   Status = AddAcpiHeader (
@@ -346,11 +417,11 @@ BuildDbg2Table (
   }
 
   // Update the base address
-  AcpiDbg2.Dbg2DeviceInfo[DBG_PORT_INDEX_PORT1].BaseAddressRegister.Address =
+  AcpiDbg2.Dbg2DeviceInfo[INDEX_DBG_PORT0].BaseAddressRegister.Address =
     SerialPortInfo->BaseAddress;
 
   // Update the serial port subtype
-  AcpiDbg2.Dbg2DeviceInfo[DBG_PORT_INDEX_PORT1].Dbg2Device.PortSubtype =
+  AcpiDbg2.Dbg2DeviceInfo[INDEX_DBG_PORT0].Dbg2Device.PortSubtype =
     SerialPortInfo->PortSubtype;
 
   if ((SerialPortInfo->PortSubtype ==
@@ -371,9 +442,35 @@ BuildDbg2Table (
     }
   }
 
-  *Table = (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiDbg2;
+  TableList[0] = (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiDbg2;
+
+  // Build a SSDT table describing the serial port.
+  Status = BuildSsdtSerialPortTable (
+             AcpiTableInfo,
+             SerialPortInfo,
+             NAME_STR_DBG_PORT0,
+             UID_DBG_PORT0,
+             &TableList[1]
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: DBG2: Failed to build associated SSDT table. Status = %r\n",
+      Status
+      ));
+    goto error_handler;
+  }
+
+  *TableCount = 2;
+  *Table = TableList;
+
+  return Status;
 
 error_handler:
+  if (TableList != NULL) {
+    FreePool (TableList);
+  }
+
   return Status;
 }
 
@@ -391,7 +488,7 @@ ACPI_TABLE_GENERATOR Dbg2Generator = {
   // Generator Description
   L"ACPI.STD.DBG2.GENERATOR",
   // ACPI Table Signature
-  EFI_ACPI_6_2_DEBUG_PORT_2_TABLE_SIGNATURE,
+  EFI_ACPI_6_3_DEBUG_PORT_2_TABLE_SIGNATURE,
   // ACPI Table Revision supported by this Generator
   EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT_REVISION,
   // Minimum supported ACPI Table Revision
@@ -400,16 +497,14 @@ ACPI_TABLE_GENERATOR Dbg2Generator = {
   TABLE_GENERATOR_CREATOR_ID_ARM,
   // Creator Revision
   DBG2_GENERATOR_REVISION,
-  // Build Table function
-  BuildDbg2Table,
-  // No additional resources are allocated by the generator.
-  // Hence the Free Resource function is not required.
+  // Build table function. Use the extended version instead.
   NULL,
-  // Extended build function not needed
+  // Free table function. Use the extended version instead.
   NULL,
-  // Extended build function not implemented by the generator.
-  // Hence extended free resource function is not required.
-  NULL
+  // Extended Build table function.
+  BuildDbg2TableEx,
+  // Extended free function.
+  FreeDbg2TableEx
 };
 
 /** Register the Generator with the ACPI Table Factory.
@@ -433,7 +528,6 @@ AcpiDbg2LibConstructor (
   Status = RegisterAcpiTableGenerator (&Dbg2Generator);
   DEBUG ((DEBUG_INFO, "DBG2: Register Generator. Status = %r\n", Status));
   ASSERT_EFI_ERROR (Status);
-
   return Status;
 }
 
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH v1 00/30] Add Dynamic AML generation support
  2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
                   ` (29 preceding siblings ...)
  2020-08-12 15:22 ` [PATCH v1 30/30] DynamicTablesPkg: Add SSDT Serial port for DBG2 Sami Mujawar
@ 2020-08-13 15:16 ` Alexei Fedorov
  30 siblings, 0 replies; 32+ messages in thread
From: Alexei Fedorov @ 2020-08-13 15:16 UTC (permalink / raw)
  To: Sami Mujawar, devel

[-- Attachment #1: Type: text/plain, Size: 54 bytes --]

Reviewed-by: Alexei Fedorov <Alexei.Fedorov@arm.com>

[-- Attachment #2: Type: text/html, Size: 60 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2020-08-13 15:16 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 01/30] DynamicTablesPkg: Introduction to Dynamic AML Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 02/30] DynamicTablesPkg: AmlLib definitions Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 03/30] DynamicTablesPkg: AML grammar definition Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 04/30] DynamicTablesPkg: AML node definitions Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 05/30] DynamicTablesPkg: AML tree interface Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 06/30] DynamicTablesPkg: AML tree enumerator Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 07/30] DynamicTablesPkg: AML tree traversal Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 08/30] DynamicTablesPkg: AML tree iterator Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 09/30] DynamicTablesPkg: AML tree/node cloning Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 10/30] DynamicTablesPkg: AML utility interfaces Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 11/30] DynamicTablesPkg: AML and ASL string helper Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 12/30] DynamicTablesPkg: AML stream interface Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 13/30] DynamicTablesPkg: AML serialise interface Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 14/30] DynamicTablesPkg: AML debug logging Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 15/30] DynamicTablesPkg: AML ACPI Namespace interface Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 16/30] DynamicTablesPkg: AML Parser Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 17/30] DynamicTablesPkg: AML resource data helper Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 18/30] DynamicTablesPkg: AML resource data parser Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 19/30] DynamicTablesPkg: AML Method parser Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 20/30] DynamicTablesPkg: AML Field list parser Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 21/30] DynamicTablesPkg: AML Codegen Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 22/30] DynamicTablesPkg: AML Resource Data Codegen Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 23/30] DynamicTablesPkg: AML Core interface Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 24/30] DynamicTablesPkg: AmlLib APIs Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 25/30] DynamicTablesPkg: Dynamic AML: Add AmlLib library Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 26/30] DynamicTablesPkg: Add AsciiFromHex helper function Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 27/30] DynamicTablesPkg: SSDT Serial Port Fixup library Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 28/30] DynamicTablesPkg: SSDT Serial Port generator Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 29/30] DynamicTablesPkg: Add SSDT Serial port for SPCR Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 30/30] DynamicTablesPkg: Add SSDT Serial port for DBG2 Sami Mujawar
2020-08-13 15:16 ` [edk2-devel] [PATCH v1 00/30] Add Dynamic AML generation support Alexei Fedorov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox