summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/DynamicTablesPkg')
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/AcpiTableFactory/AcpiTableFactory.c220
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DeviceTreeTableFactory/DeviceTreeTableFactory.c219
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactory.h119
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactoryDxe.c84
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactoryDxe.inf52
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/SmbiosTableFactory/SmbiosTableFactory.c220
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableManagerDxe/DynamicTableManagerDxe.c729
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableManagerDxe/DynamicTableManagerDxe.inf47
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTables.dsc.inc64
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTables.fdf.inc18
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.ci.yaml104
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.dec51
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.dsc53
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/AcpiTableGenerator.h370
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ArmNameSpaceObjects.h883
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ConfigurationManagerHelper.h126
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ConfigurationManagerObject.h190
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/DeviceTreeTableGenerator.h176
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h631
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Library/SsdtSerialPortFixupLib.h68
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Library/TableHelperLib.h159
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Protocol/ConfigurationManagerProtocol.h122
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Protocol/DynamicTableFactoryProtocol.h254
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/SmbiosTableGenerator.h234
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/StandardNameSpaceObjects.h131
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/TableGenerator.h247
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf43
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c577
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/AcpiFadtLibArm.inf36
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/FadtGenerator.c683
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/AcpiGtdtLibArm.inf36
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/GtdtGenerator.c780
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/AcpiIortLibArm.inf37
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c2229
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.h44
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/AcpiMadtLibArm.inf36
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/MadtGenerator.c830
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/AcpiMcfgLibArm.inf36
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/McfgGenerator.c364
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf30
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c1540
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.h189
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf36
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/RawGenerator.c144
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf37
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c477
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf29
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/SratGenerator.c835
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c708
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h51
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf34
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl81
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c371
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf33
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h767
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c546
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h178
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h188
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c805
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h330
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h18
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf76
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h183
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c382
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c219
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h93
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c320
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c701
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c256
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h59
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c1496
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h74
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c375
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h77
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c1458
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h188
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c1448
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h72
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c328
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h71
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c103
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h174
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Serialize/AmlSerialize.c324
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c665
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h451
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c1022
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h401
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c205
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c673
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h212
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c566
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c1047
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h127
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c98
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c353
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h220
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c548
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h138
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c906
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h95
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c536
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf32
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortTemplate.asl60
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelper.c343
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelperLib.inf30
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Readme.md281
106 files changed, 36215 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/AcpiTableFactory/AcpiTableFactory.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/AcpiTableFactory/AcpiTableFactory.c
new file mode 100644
index 00000000..6f8330a4
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/AcpiTableFactory/AcpiTableFactory.c
@@ -0,0 +1,220 @@
+/** @file
+ ACPI Table Factory
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Std - Standard
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Protocol/AcpiTable.h>
+
+// Module specific include files.
+#include <AcpiTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+#include <Protocol/DynamicTableFactoryProtocol.h>
+
+#include "DynamicTableFactory.h"
+
+extern EDKII_DYNAMIC_TABLE_FACTORY_INFO TableFactoryInfo;
+
+/** Return a pointer to the ACPI table generator.
+
+ @param [in] This Pointer to the Dynamic Table Factory Protocol.
+ @param [in] GeneratorId The ACPI table generator ID for the
+ requested generator.
+ @param [out] Generator Pointer to the requested ACPI table
+ generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+GetAcpiTableGenerator (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST This,
+ IN CONST ACPI_TABLE_GENERATOR_ID GeneratorId,
+ OUT CONST ACPI_TABLE_GENERATOR ** CONST Generator
+ )
+{
+ UINT16 TableId;
+ EDKII_DYNAMIC_TABLE_FACTORY_INFO * FactoryInfo;
+
+ ASSERT (This != NULL);
+
+ FactoryInfo = This->TableFactoryInfo;
+
+ if (Generator == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: Invalid Generator pointer\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_GENERATOR_TYPE_ACPI (GeneratorId)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: Generator Type is not ACPI\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Generator = NULL;
+ TableId = GET_TABLE_ID (GeneratorId);
+ if (IS_GENERATOR_NAMESPACE_STD (GeneratorId)) {
+ if (TableId >= EStdAcpiTableIdMax) {
+ ASSERT (TableId < EStdAcpiTableIdMax);
+ return EFI_INVALID_PARAMETER;
+ }
+ if (FactoryInfo->StdAcpiTableGeneratorList[TableId] != NULL) {
+ *Generator = FactoryInfo->StdAcpiTableGeneratorList[TableId];
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ } else {
+ if (TableId > FixedPcdGet16 (PcdMaxCustomACPIGenerators)) {
+ ASSERT (TableId <= FixedPcdGet16 (PcdMaxCustomACPIGenerators));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (FactoryInfo->CustomAcpiTableGeneratorList[TableId] != NULL) {
+ *Generator = FactoryInfo->CustomAcpiTableGeneratorList[TableId];
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/** Register ACPI table factory generator.
+
+ The ACPI table factory maintains a list of the Standard and OEM ACPI
+ table generators.
+
+ @param [in] Generator Pointer to the ACPI table generator.
+
+ @retval EFI_SUCCESS The Generator was registered
+ successfully.
+ @retval EFI_INVALID_PARAMETER The Generator ID is invalid or
+ the Generator pointer is NULL.
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID is
+ already registered.
+**/
+EFI_STATUS
+EFIAPI
+RegisterAcpiTableGenerator (
+ IN CONST ACPI_TABLE_GENERATOR * CONST Generator
+ )
+{
+ UINT16 TableId;
+
+ if (Generator == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: ACPI register - Invalid Generator\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_GENERATOR_TYPE_ACPI (Generator->GeneratorID)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: ACPI register - Generator" \
+ " Type is not ACPI\n"
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_INFO, "Registering %s\n", Generator->Description));
+
+ TableId = GET_TABLE_ID (Generator->GeneratorID);
+ if (IS_GENERATOR_NAMESPACE_STD (Generator->GeneratorID)) {
+ if (TableId >= EStdAcpiTableIdMax) {
+ ASSERT (TableId < EStdAcpiTableIdMax);
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.StdAcpiTableGeneratorList[TableId] == NULL) {
+ TableFactoryInfo.StdAcpiTableGeneratorList[TableId] = Generator;
+ } else {
+ return EFI_ALREADY_STARTED;
+ }
+ } else {
+ if (TableId > FixedPcdGet16 (PcdMaxCustomACPIGenerators)) {
+ ASSERT (TableId <= FixedPcdGet16 (PcdMaxCustomACPIGenerators));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.CustomAcpiTableGeneratorList[TableId] == NULL) {
+ TableFactoryInfo.CustomAcpiTableGeneratorList[TableId] = Generator;
+ } else {
+ return EFI_ALREADY_STARTED;
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/** Deregister ACPI generator.
+
+ This function is called by the ACPI table generator to deregister itself
+ from the ACPI table factory.
+
+ @param [in] Generator Pointer to the ACPI table generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER The generator is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+DeregisterAcpiTableGenerator (
+ IN CONST ACPI_TABLE_GENERATOR * CONST Generator
+ )
+{
+ UINT16 TableId;
+
+ if (Generator == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: ACPI deregister - Invalid Generator\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_GENERATOR_TYPE_ACPI (Generator->GeneratorID)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: ACPI deregister - Generator" \
+ " Type is not ACPI\n"
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TableId = GET_TABLE_ID (Generator->GeneratorID);
+ if (IS_GENERATOR_NAMESPACE_STD (Generator->GeneratorID)) {
+ if (TableId >= EStdAcpiTableIdMax) {
+ ASSERT (TableId < EStdAcpiTableIdMax);
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.StdAcpiTableGeneratorList[TableId] != NULL) {
+ if (Generator != TableFactoryInfo.StdAcpiTableGeneratorList[TableId]) {
+ return EFI_INVALID_PARAMETER;
+ }
+ TableFactoryInfo.StdAcpiTableGeneratorList[TableId] = NULL;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ } else {
+ if (TableId > FixedPcdGet16 (PcdMaxCustomACPIGenerators)) {
+ ASSERT (TableId <= FixedPcdGet16 (PcdMaxCustomACPIGenerators));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.CustomAcpiTableGeneratorList[TableId] != NULL) {
+ if (Generator !=
+ TableFactoryInfo.CustomAcpiTableGeneratorList[TableId]) {
+ return EFI_INVALID_PARAMETER;
+ }
+ TableFactoryInfo.CustomAcpiTableGeneratorList[TableId] = NULL;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ DEBUG ((DEBUG_INFO, "Deregistering %s\n", Generator->Description));
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DeviceTreeTableFactory/DeviceTreeTableFactory.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DeviceTreeTableFactory/DeviceTreeTableFactory.c
new file mode 100644
index 00000000..85ef9e66
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DeviceTreeTableFactory/DeviceTreeTableFactory.c
@@ -0,0 +1,219 @@
+/** @file
+ Device Tree Table Factory
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Std - Standard
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+
+// Module specific include files.
+#include <DeviceTreeTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+#include <Protocol/DynamicTableFactoryProtocol.h>
+
+#include "DynamicTableFactory.h"
+
+extern EDKII_DYNAMIC_TABLE_FACTORY_INFO TableFactoryInfo;
+
+/** Return a pointer to the DT table generator.
+
+ @param [in] This Pointer to the Dynamic Table Factory Protocol.
+ @param [in] GeneratorId The DT table generator ID for the
+ requested generator.
+ @param [out] Generator Pointer to the requested DT table
+ generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+GetDtTableGenerator (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST This,
+ IN CONST DT_TABLE_GENERATOR_ID GeneratorId,
+ OUT CONST DT_TABLE_GENERATOR ** CONST Generator
+ )
+{
+ UINT16 TableId;
+ EDKII_DYNAMIC_TABLE_FACTORY_INFO * FactoryInfo;
+
+ ASSERT (This != NULL);
+
+ FactoryInfo = This->TableFactoryInfo;
+
+ if (Generator == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: Invalid Generator pointer\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_GENERATOR_TYPE_DT (GeneratorId)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: Generator Type is not DT\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Generator = NULL;
+ TableId = GET_TABLE_ID (GeneratorId);
+ if (IS_GENERATOR_NAMESPACE_STD (GeneratorId)) {
+ if (TableId >= EStdDtTableIdMax) {
+ ASSERT (TableId < EStdDtTableIdMax);
+ return EFI_INVALID_PARAMETER;
+ }
+ if (FactoryInfo->StdDtTableGeneratorList[TableId] != NULL) {
+ *Generator = FactoryInfo->StdDtTableGeneratorList[TableId];
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ } else {
+ if (TableId > FixedPcdGet16 (PcdMaxCustomDTGenerators)) {
+ ASSERT (TableId <= FixedPcdGet16 (PcdMaxCustomDTGenerators));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (FactoryInfo->CustomDtTableGeneratorList[TableId] != NULL) {
+ *Generator = FactoryInfo->CustomDtTableGeneratorList[TableId];
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/** Register DT table factory generator.
+
+ The DT table factory maintains a list of the Standard and OEM DT
+ table generators.
+
+ @param [in] Generator Pointer to the DT table generator.
+
+ @retval EFI_SUCCESS The Generator was registered
+ successfully.
+ @retval EFI_INVALID_PARAMETER The Generator ID is invalid or
+ the Generator pointer is NULL.
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID is
+ already registered.
+**/
+EFI_STATUS
+EFIAPI
+RegisterDtTableGenerator (
+ IN CONST DT_TABLE_GENERATOR * CONST Generator
+ )
+{
+ UINT16 TableId;
+
+ if (Generator == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: DT register - Invalid Generator\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_GENERATOR_TYPE_DT (Generator->GeneratorID)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: DT register - Generator" \
+ " Type is not DT\n"
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_INFO, "Registering %s\n", Generator->Description));
+
+ TableId = GET_TABLE_ID (Generator->GeneratorID);
+ if (IS_GENERATOR_NAMESPACE_STD (Generator->GeneratorID)) {
+ if (TableId >= EStdDtTableIdMax) {
+ ASSERT (TableId < EStdDtTableIdMax);
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.StdDtTableGeneratorList[TableId] == NULL) {
+ TableFactoryInfo.StdDtTableGeneratorList[TableId] = Generator;
+ } else {
+ return EFI_ALREADY_STARTED;
+ }
+ } else {
+ if (TableId > FixedPcdGet16 (PcdMaxCustomDTGenerators)) {
+ ASSERT (TableId <= FixedPcdGet16 (PcdMaxCustomDTGenerators));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.CustomDtTableGeneratorList[TableId] == NULL) {
+ TableFactoryInfo.CustomDtTableGeneratorList[TableId] = Generator;
+ } else {
+ return EFI_ALREADY_STARTED;
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/** Deregister DT generator.
+
+ This function is called by the DT table generator to deregister itself
+ from the DT table factory.
+
+ @param [in] Generator Pointer to the DT table generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER The generator is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+DeregisterDtTableGenerator (
+ IN CONST DT_TABLE_GENERATOR * CONST Generator
+ )
+{
+ UINT16 TableId;
+
+ if (Generator == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: DT deregister - Invalid Generator\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_GENERATOR_TYPE_DT (Generator->GeneratorID)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: DT deregister - Generator" \
+ " Type is not DT\n"
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TableId = GET_TABLE_ID (Generator->GeneratorID);
+ if (IS_GENERATOR_NAMESPACE_STD (Generator->GeneratorID)) {
+ if (TableId >= EStdDtTableIdMax) {
+ ASSERT (TableId < EStdDtTableIdMax);
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.StdDtTableGeneratorList[TableId] != NULL) {
+ if (Generator != TableFactoryInfo.StdDtTableGeneratorList[TableId]) {
+ return EFI_INVALID_PARAMETER;
+ }
+ TableFactoryInfo.StdDtTableGeneratorList[TableId] = NULL;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ } else {
+ if (TableId > FixedPcdGet16 (PcdMaxCustomDTGenerators)) {
+ ASSERT (TableId <= FixedPcdGet16 (PcdMaxCustomDTGenerators));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.CustomDtTableGeneratorList[TableId] != NULL) {
+ if (Generator !=
+ TableFactoryInfo.CustomDtTableGeneratorList[TableId]) {
+ return EFI_INVALID_PARAMETER;
+ }
+ TableFactoryInfo.CustomDtTableGeneratorList[TableId] = NULL;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ DEBUG ((DEBUG_INFO, "Deregistering %s\n", Generator->Description));
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactory.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactory.h
new file mode 100644
index 00000000..900fe8ef
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactory.h
@@ -0,0 +1,119 @@
+/** @file
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Std - Standard
+ - ACPI - Advanced Configuration and Power Interface
+ - SMBIOS - System Management BIOS
+ - DT - Device Tree
+**/
+
+#ifndef DYNAMIC_TABLE_FACTORY_H_
+#define DYNAMIC_TABLE_FACTORY_H_
+
+#pragma pack(1)
+
+/** A structure that holds the list of registered ACPI and
+ SMBIOS table generators.
+*/
+typedef struct DynamicTableFactoryInfo {
+ /// An array for holding the list of Standard ACPI Table Generators.
+ CONST ACPI_TABLE_GENERATOR *
+ StdAcpiTableGeneratorList[EStdAcpiTableIdMax];
+
+ /// An array for holding the list of Custom ACPI Table Generators.
+ CONST ACPI_TABLE_GENERATOR *
+ CustomAcpiTableGeneratorList[FixedPcdGet16 (
+ PcdMaxCustomACPIGenerators
+ )];
+
+ /// An array for holding the list of Standard SMBIOS Table Generators.
+ CONST SMBIOS_TABLE_GENERATOR *
+ StdSmbiosTableGeneratorList[EStdSmbiosTableIdMax];
+
+ /// An array for holding the list of Custom SMBIOS Table Generators.
+ CONST SMBIOS_TABLE_GENERATOR *
+ CustomSmbiosTableGeneratorList[FixedPcdGet16 (
+ PcdMaxCustomSMBIOSGenerators
+ )];
+
+ /// An array for holding the list of Standard DT Table Generators.
+ CONST DT_TABLE_GENERATOR *
+ StdDtTableGeneratorList[EStdDtTableIdMax];
+
+ /// An array for holding the list of Custom DT Table Generators.
+ CONST DT_TABLE_GENERATOR *
+ CustomDtTableGeneratorList[FixedPcdGet16 (
+ PcdMaxCustomDTGenerators
+ )];
+} EDKII_DYNAMIC_TABLE_FACTORY_INFO;
+
+/** Return a pointer to the ACPI table generator.
+
+ @param [in] This Pointer to the Dynamic Table Factory Protocol.
+ @param [in] GeneratorId The ACPI table generator ID for the
+ requested generator.
+ @param [out] Generator Pointer to the requested ACPI table
+ generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+GetAcpiTableGenerator (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST This,
+ IN CONST ACPI_TABLE_GENERATOR_ID GeneratorId,
+ OUT CONST ACPI_TABLE_GENERATOR ** CONST Generator
+ );
+
+/** Return a pointer to the SMBIOS table generator.
+
+ @param [in] This Pointer to the Dynamic Table Factory Protocol.
+ @param [in] GeneratorId The SMBIOS table generator ID for the
+ requested generator.
+ @param [out] Generator Pointer to the requested SMBIOS table
+ generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+GetSmbiosTableGenerator (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST This,
+ IN CONST SMBIOS_TABLE_GENERATOR_ID GeneratorId,
+ OUT CONST SMBIOS_TABLE_GENERATOR ** CONST Generator
+ );
+
+/** Return a pointer to the DT table generator.
+
+ @param [in] This Pointer to the Dynamic Table Factory Protocol.
+ @param [in] GeneratorId The DT table generator ID for the
+ requested generator.
+ @param [out] Generator Pointer to the requested DT table
+ generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+GetDtTableGenerator (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST This,
+ IN CONST DT_TABLE_GENERATOR_ID GeneratorId,
+ OUT CONST DT_TABLE_GENERATOR ** CONST Generator
+ );
+
+#pragma pack()
+
+#endif // DYNAMIC_TABLE_FACTORY_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactoryDxe.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactoryDxe.c
new file mode 100644
index 00000000..d84dd344
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactoryDxe.c
@@ -0,0 +1,84 @@
+/** @file
+ Dynamic Table Factory Dxe
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/AcpiTable.h>
+
+// Module specific include files.
+#include <AcpiTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <ConfigurationManagerHelper.h>
+#include <DeviceTreeTableGenerator.h>
+#include <Library/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+#include <Protocol/DynamicTableFactoryProtocol.h>
+#include <SmbiosTableGenerator.h>
+
+#include "DynamicTableFactory.h"
+
+/** The Dynamic Table Factory protocol structure that holds the
+ list of registered ACPI and SMBIOS table generators.
+*/
+EDKII_DYNAMIC_TABLE_FACTORY_INFO TableFactoryInfo;
+
+/** A structure describing the Dynamic Table Factory protocol.
+*/
+STATIC
+CONST
+EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL DynamicTableFactoryProtocol = {
+ CREATE_REVISION (1, 0),
+ GetAcpiTableGenerator,
+ RegisterAcpiTableGenerator,
+ DeregisterAcpiTableGenerator,
+ GetSmbiosTableGenerator,
+ RegisterSmbiosTableGenerator,
+ DeregisterSmbiosTableGenerator,
+ GetDtTableGenerator,
+ RegisterDtTableGenerator,
+ DeregisterDtTableGenerator,
+ &TableFactoryInfo
+};
+
+/** Entrypoint for Dynamic Table Factory Dxe.
+
+ @param ImageHandle
+ @param SystemTable
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+ @retval EFI_NOT_FOUND Required interface/object was not found.
+ @retval EFI_INVALID_PARAMETER Some parameter is incorrect/invalid.
+**/
+EFI_STATUS
+EFIAPI
+DynamicTableFactoryDxeInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->InstallProtocolInterface (
+ &ImageHandle,
+ &gEdkiiDynamicTableFactoryProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ (VOID*)&DynamicTableFactoryProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to install the Dynamic Table Factory Protocol." \
+ " Status = %r\n",
+ Status
+ ));
+ }
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactoryDxe.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactoryDxe.inf
new file mode 100644
index 00000000..f1b5d4da
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactoryDxe.inf
@@ -0,0 +1,52 @@
+## @file
+# Module to manage the list of available table factories.
+#
+# Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = DynamicTableFactoryDxe
+ FILE_GUID = FE846898-7403-4932-B8AD-A0491F0C2CBA
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = DynamicTableFactoryDxeInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = ARM AARCH64
+#
+
+[Sources]
+ AcpiTableFactory/AcpiTableFactory.c
+ DeviceTreeTableFactory/DeviceTreeTableFactory.c
+ DynamicTableFactoryDxe.c
+ SmbiosTableFactory/SmbiosTableFactory.c
+ DynamicTableFactory.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ MemoryAllocationLib
+ PrintLib
+ TableHelperLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+
+[FixedPcd]
+ gEdkiiDynamicTablesPkgTokenSpaceGuid.PcdMaxCustomACPIGenerators
+ gEdkiiDynamicTablesPkgTokenSpaceGuid.PcdMaxCustomSMBIOSGenerators
+ gEdkiiDynamicTablesPkgTokenSpaceGuid.PcdMaxCustomDTGenerators
+
+[Protocols]
+ gEdkiiDynamicTableFactoryProtocolGuid # PRODUCES
+
+[Depex]
+ TRUE
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/SmbiosTableFactory/SmbiosTableFactory.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/SmbiosTableFactory/SmbiosTableFactory.c
new file mode 100644
index 00000000..8a83cb09
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/SmbiosTableFactory/SmbiosTableFactory.c
@@ -0,0 +1,220 @@
+/** @file
+ SMBIOS Table Factory
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Std - Standard
+**/
+
+#include <IndustryStandard/SmBios.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+
+// Module specific include files.
+#include <SmbiosTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+#include <Protocol/DynamicTableFactoryProtocol.h>
+
+#include "DynamicTableFactory.h"
+
+extern EDKII_DYNAMIC_TABLE_FACTORY_INFO TableFactoryInfo;
+
+/** Return a pointer to the SMBIOS table generator.
+
+ @param [in] This Pointer to the Dynamic Table Factory Protocol.
+ @param [in] GeneratorId The SMBIOS table generator ID for the
+ requested generator.
+ @param [out] Generator Pointer to the requested SMBIOS table
+ generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+GetSmbiosTableGenerator (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST This,
+ IN CONST SMBIOS_TABLE_GENERATOR_ID GeneratorId,
+ OUT CONST SMBIOS_TABLE_GENERATOR ** CONST Generator
+ )
+{
+ UINT16 TableId;
+ EDKII_DYNAMIC_TABLE_FACTORY_INFO * FactoryInfo;
+
+ ASSERT (This != NULL);
+
+ FactoryInfo = This->TableFactoryInfo;
+
+ if (Generator == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: Invalid Generator pointer\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_GENERATOR_TYPE_SMBIOS (GeneratorId)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: Generator Type is not SMBIOS\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Generator = NULL;
+ TableId = GET_TABLE_ID (GeneratorId);
+ if (IS_GENERATOR_NAMESPACE_STD (GeneratorId)) {
+ if (TableId >= EStdSmbiosTableIdMax) {
+ ASSERT (TableId < EStdSmbiosTableIdMax);
+ return EFI_INVALID_PARAMETER;
+ }
+ if (FactoryInfo->StdSmbiosTableGeneratorList[TableId] != NULL) {
+ *Generator = FactoryInfo->StdSmbiosTableGeneratorList[TableId];
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ } else {
+ if (TableId > FixedPcdGet16 (PcdMaxCustomSMBIOSGenerators)) {
+ ASSERT (TableId <= FixedPcdGet16 (PcdMaxCustomSMBIOSGenerators));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (FactoryInfo->CustomSmbiosTableGeneratorList[TableId] != NULL) {
+ *Generator = FactoryInfo->CustomSmbiosTableGeneratorList[TableId];
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/** Register SMBIOS table factory generator.
+
+ The SMBIOS table factory maintains a list of the Standard and OEM SMBIOS
+ table generators.
+
+ @param [in] Generator Pointer to the SMBIOS table generator.
+
+ @retval EFI_SUCCESS The Generator was registered
+ successfully.
+ @retval EFI_INVALID_PARAMETER The Generator ID is invalid or
+ the Generator pointer is NULL.
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID is
+ already registered.
+**/
+EFI_STATUS
+EFIAPI
+RegisterSmbiosTableGenerator (
+ IN CONST SMBIOS_TABLE_GENERATOR * CONST Generator
+ )
+{
+ UINT16 TableId;
+
+ if (Generator == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: SMBIOS register - Invalid Generator\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_GENERATOR_TYPE_SMBIOS (Generator->GeneratorID)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SMBIOS register - Generator" \
+ " Type is not SMBIOS\n"
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_INFO, "Registering %s\n", Generator->Description));
+
+ TableId = GET_TABLE_ID (Generator->GeneratorID);
+ if (IS_GENERATOR_NAMESPACE_STD (Generator->GeneratorID)) {
+ if (TableId >= EStdSmbiosTableIdMax) {
+ ASSERT (TableId < EStdSmbiosTableIdMax);
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.StdSmbiosTableGeneratorList[TableId] == NULL) {
+ TableFactoryInfo.StdSmbiosTableGeneratorList[TableId] = Generator;
+ } else {
+ return EFI_ALREADY_STARTED;
+ }
+ } else {
+ if (TableId > FixedPcdGet16 (PcdMaxCustomSMBIOSGenerators)) {
+ ASSERT (TableId <= FixedPcdGet16 (PcdMaxCustomSMBIOSGenerators));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.CustomSmbiosTableGeneratorList[TableId] == NULL) {
+ TableFactoryInfo.CustomSmbiosTableGeneratorList[TableId] = Generator;
+ } else {
+ return EFI_ALREADY_STARTED;
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/** Deregister SMBIOS generator.
+
+ This function is called by the SMBIOS table generator to deregister itself
+ from the SMBIOS table factory.
+
+ @param [in] Generator Pointer to the SMBIOS table generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER The generator is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+DeregisterSmbiosTableGenerator (
+ IN CONST SMBIOS_TABLE_GENERATOR * CONST Generator
+ )
+{
+ UINT16 TableId;
+
+ if (Generator == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: SMBIOS deregister - Invalid Generator\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_GENERATOR_TYPE_SMBIOS (Generator->GeneratorID)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SMBIOS deregister - Generator" \
+ " Type is not SMBIOS\n"
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TableId = GET_TABLE_ID (Generator->GeneratorID);
+ if (IS_GENERATOR_NAMESPACE_STD (Generator->GeneratorID)) {
+ if (TableId >= EStdSmbiosTableIdMax) {
+ ASSERT (TableId < EStdSmbiosTableIdMax);
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.StdSmbiosTableGeneratorList[TableId] != NULL) {
+ if (Generator != TableFactoryInfo.StdSmbiosTableGeneratorList[TableId]) {
+ return EFI_INVALID_PARAMETER;
+ }
+ TableFactoryInfo.StdSmbiosTableGeneratorList[TableId] = NULL;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ } else {
+ if (TableId > FixedPcdGet16 (PcdMaxCustomSMBIOSGenerators)) {
+ ASSERT (TableId <= FixedPcdGet16 (PcdMaxCustomSMBIOSGenerators));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (TableFactoryInfo.CustomSmbiosTableGeneratorList[TableId] != NULL) {
+ if (Generator !=
+ TableFactoryInfo.CustomSmbiosTableGeneratorList[TableId]) {
+ return EFI_INVALID_PARAMETER;
+ }
+ TableFactoryInfo.CustomSmbiosTableGeneratorList[TableId] = NULL;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ DEBUG ((DEBUG_INFO, "Deregistering %s\n", Generator->Description));
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableManagerDxe/DynamicTableManagerDxe.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableManagerDxe/DynamicTableManagerDxe.c
new file mode 100644
index 00000000..7fbfeebd
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableManagerDxe/DynamicTableManagerDxe.c
@@ -0,0 +1,729 @@
+/** @file
+ Dynamic Table Manager Dxe
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/AcpiTable.h>
+
+// Module specific include files.
+#include <AcpiTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <ConfigurationManagerHelper.h>
+#include <DeviceTreeTableGenerator.h>
+#include <Library/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+#include <Protocol/DynamicTableFactoryProtocol.h>
+#include <SmbiosTableGenerator.h>
+
+/** This macro expands to a function that retrieves the ACPI Table
+ List from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceStandard,
+ EStdObjAcpiTableList,
+ CM_STD_OBJ_ACPI_TABLE_INFO
+ )
+
+/** A helper function to build and install a single ACPI table.
+
+ This is a helper function that invokes the Table generator interface
+ for building an ACPI table. It uses the AcpiTableProtocol to install the
+ table, then frees the resources allocated for generating it.
+
+ @param [in] TableFactoryProtocol Pointer to the Table Factory Protocol
+ interface.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Generator Pointer to the AcpiTable generator.
+ @param [in] AcpiTableProtocol Pointer to the AcpiTable protocol.
+ @param [in] AcpiTableInfo Pointer to the ACPI table Info.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND Required object is not found.
+ @retval EFI_BAD_BUFFER_SIZE Size returned by the Configuration Manager
+ is less than the Object size for the
+ requested object.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildAndInstallSingleAcpiTable (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST TableFactoryProtocol,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST ACPI_TABLE_GENERATOR * CONST Generator,
+ IN EFI_ACPI_TABLE_PROTOCOL * AcpiTableProtocol,
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS Status1;
+ EFI_ACPI_DESCRIPTION_HEADER * AcpiTable;
+ UINTN TableHandle;
+
+ AcpiTable = NULL;
+ Status = Generator->BuildAcpiTable (
+ Generator,
+ AcpiTableInfo,
+ CfgMgrProtocol,
+ &AcpiTable
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to Build Table." \
+ " TableGeneratorId = 0x%x. Status = %r\n",
+ AcpiTableInfo->TableGeneratorId,
+ Status
+ ));
+ // Free any allocated resources.
+ goto exit_handler;
+ }
+
+ if (AcpiTable == NULL) {
+ Status = EFI_NOT_FOUND;
+ goto exit_handler;
+ }
+
+ // Dump ACPI Table Header
+ DUMP_ACPI_TABLE_HEADER (AcpiTable);
+
+ // Install ACPI table
+ Status = AcpiTableProtocol->InstallAcpiTable (
+ AcpiTableProtocol,
+ AcpiTable,
+ AcpiTable->Length,
+ &TableHandle
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to Install ACPI Table. Status = %r\n",
+ Status
+ ));
+ // Free any allocated resources.
+ goto exit_handler;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: ACPI Table installed. Status = %r\n",
+ Status
+ ));
+
+exit_handler:
+ // Free any resources allocated for generating the tables.
+ if (Generator->FreeTableResources != NULL) {
+ Status1 = Generator->FreeTableResources (
+ Generator,
+ AcpiTableInfo,
+ CfgMgrProtocol,
+ &AcpiTable
+ );
+ if (EFI_ERROR (Status1)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to Free Table Resources." \
+ "TableGeneratorId = 0x%x. Status = %r\n",
+ AcpiTableInfo->TableGeneratorId,
+ Status1
+ ));
+ }
+
+ // Return the first error status in case of failure
+ if (!EFI_ERROR (Status)) {
+ Status = Status1;
+ }
+ }
+ return Status;
+}
+
+/** A helper function to build and install multiple ACPI tables.
+
+ This is a helper function that invokes the Table generator interface
+ for building an ACPI table. It uses the AcpiTableProtocol to install the
+ table, then frees the resources allocated for generating it.
+
+ @param [in] TableFactoryProtocol Pointer to the Table Factory Protocol
+ interface.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Generator Pointer to the AcpiTable generator.
+ @param [in] AcpiTableProtocol Pointer to the AcpiTable protocol.
+ @param [in] AcpiTableInfo Pointer to the ACPI table Info.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND Required object is not found.
+ @retval EFI_BAD_BUFFER_SIZE Size returned by the Configuration Manager
+ is less than the Object size for the
+ requested object.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildAndInstallMultipleAcpiTable (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST TableFactoryProtocol,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST ACPI_TABLE_GENERATOR * CONST Generator,
+ IN EFI_ACPI_TABLE_PROTOCOL * AcpiTableProtocol,
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS Status1;
+ EFI_ACPI_DESCRIPTION_HEADER ** AcpiTable;
+ UINTN TableCount;
+ UINTN TableHandle;
+ UINTN Index;
+
+ AcpiTable = NULL;
+ TableCount = 0;
+ Status = Generator->BuildAcpiTableEx (
+ Generator,
+ AcpiTableInfo,
+ CfgMgrProtocol,
+ &AcpiTable,
+ &TableCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to Build Table." \
+ " TableGeneratorId = 0x%x. Status = %r\n",
+ AcpiTableInfo->TableGeneratorId,
+ Status
+ ));
+ // Free any allocated resources.
+ goto exit_handler;
+ }
+
+ if ((AcpiTable == NULL) || (TableCount == 0)) {
+ Status = EFI_NOT_FOUND;
+ goto exit_handler;
+ }
+
+ for (Index = 0; Index < TableCount; Index++) {
+ // Dump ACPI Table Header
+ DUMP_ACPI_TABLE_HEADER (AcpiTable[Index]);
+ // Install ACPI table
+ Status = AcpiTableProtocol->InstallAcpiTable (
+ AcpiTableProtocol,
+ AcpiTable[Index],
+ AcpiTable[Index]->Length,
+ &TableHandle
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to Install ACPI Table. Status = %r\n",
+ Status
+ ));
+ // Free any allocated resources.
+ goto exit_handler;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: ACPI Table installed. Status = %r\n",
+ Status
+ ));
+ }
+
+exit_handler:
+ // Free any resources allocated for generating the tables.
+ if (Generator->FreeTableResourcesEx != NULL) {
+ Status1 = Generator->FreeTableResourcesEx (
+ Generator,
+ AcpiTableInfo,
+ CfgMgrProtocol,
+ &AcpiTable,
+ TableCount
+ );
+ if (EFI_ERROR (Status1)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to Free Table Resources." \
+ "TableGeneratorId = 0x%x. Status = %r\n",
+ AcpiTableInfo->TableGeneratorId,
+ Status1
+ ));
+ }
+
+ // Return the first error status in case of failure
+ if (!EFI_ERROR (Status)) {
+ Status = Status1;
+ }
+ }
+ return Status;
+}
+
+/** A helper function to invoke a Table generator
+
+ This is a helper function that invokes the Table generator interface
+ for building an ACPI table. It uses the AcpiTableProtocol to install the
+ table, then frees the resources allocated for generating it.
+
+ @param [in] TableFactoryProtocol Pointer to the Table Factory Protocol
+ interface.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] AcpiTableProtocol Pointer to the AcpiTable protocol.
+ @param [in] AcpiTableInfo Pointer to the ACPI table Info.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND Required object is not found.
+ @retval EFI_BAD_BUFFER_SIZE Size returned by the Configuration Manager
+ is less than the Object size for the
+ requested object.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildAndInstallAcpiTable (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST TableFactoryProtocol,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN EFI_ACPI_TABLE_PROTOCOL * AcpiTableProtocol,
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo
+ )
+{
+ EFI_STATUS Status;
+ CONST ACPI_TABLE_GENERATOR * Generator;
+
+ ASSERT (TableFactoryProtocol != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (AcpiTableProtocol != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: EStdObjAcpiTableList: Address = 0x%p," \
+ " TableGeneratorId = 0x%x\n",
+ AcpiTableInfo,
+ AcpiTableInfo->TableGeneratorId
+ ));
+
+ Generator = NULL;
+ Status = TableFactoryProtocol->GetAcpiTableGenerator (
+ TableFactoryProtocol,
+ AcpiTableInfo->TableGeneratorId,
+ &Generator
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Table Generator not found." \
+ " TableGeneratorId = 0x%x. Status = %r\n",
+ AcpiTableInfo->TableGeneratorId,
+ Status
+ ));
+ return Status;
+ }
+
+ if (Generator == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: Generator found : %s\n",
+ Generator->Description
+ ));
+
+ if (Generator->BuildAcpiTableEx != NULL) {
+ Status = BuildAndInstallMultipleAcpiTable (
+ TableFactoryProtocol,
+ CfgMgrProtocol,
+ Generator,
+ AcpiTableProtocol,
+ AcpiTableInfo
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to find build and install ACPI Table." \
+ " Status = %r\n",
+ Status
+ ));
+ }
+ } else if (Generator->BuildAcpiTable != NULL) {
+ Status = BuildAndInstallSingleAcpiTable (
+ TableFactoryProtocol,
+ CfgMgrProtocol,
+ Generator,
+ AcpiTableProtocol,
+ AcpiTableInfo
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to find build and install ACPI Table." \
+ " Status = %r\n",
+ Status
+ ));
+ }
+ } else {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Table Generator does not implement the" \
+ " ACPI_TABLE_GENERATOR_BUILD_TABLE interface." \
+ " TableGeneratorId = 0x%x. Status = %r\n",
+ AcpiTableInfo->TableGeneratorId,
+ Status
+ ));
+ }
+
+ return Status;
+}
+
+/** The function checks if the Configuration Manager has provided the
+ mandatory ACPI tables for installation.
+
+ @param [in] AcpiTableInfo Pointer to the ACPI Table Info list.
+ @param [in] AcpiTableCount Count of ACPI Table Info.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_NOT_FOUND If mandatory table is not found.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+VerifyMandatoryTablesArePresent (
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo,
+ IN UINT32 AcpiTableCount
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN FadtFound;
+ BOOLEAN MadtFound;
+ BOOLEAN GtdtFound;
+ BOOLEAN DsdtFound;
+ BOOLEAN Dbg2Found;
+ BOOLEAN SpcrFound;
+
+ Status = EFI_SUCCESS;
+ FadtFound = FALSE;
+ MadtFound = FALSE;
+ GtdtFound = FALSE;
+ DsdtFound = FALSE;
+ Dbg2Found = FALSE;
+ SpcrFound = FALSE;
+ ASSERT (AcpiTableInfo != NULL);
+
+ while (AcpiTableCount-- != 0) {
+ switch (AcpiTableInfo[AcpiTableCount].AcpiTableSignature) {
+ case EFI_ACPI_6_2_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE:
+ FadtFound = TRUE;
+ break;
+ case EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE:
+ MadtFound = TRUE;
+ break;
+ case EFI_ACPI_6_2_GENERIC_TIMER_DESCRIPTION_TABLE_SIGNATURE:
+ GtdtFound = TRUE;
+ break;
+ case EFI_ACPI_6_2_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE:
+ DsdtFound = TRUE;
+ break;
+ case EFI_ACPI_6_2_DEBUG_PORT_2_TABLE_SIGNATURE:
+ Dbg2Found = TRUE;
+ break;
+ case EFI_ACPI_6_2_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE:
+ SpcrFound = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // We need at least the FADT, MADT, GTDT and the DSDT tables to boot
+ if (!FadtFound) {
+ DEBUG ((DEBUG_ERROR,"ERROR: FADT Table not found\n"));
+ Status = EFI_NOT_FOUND;
+ }
+ if (!MadtFound) {
+ DEBUG ((DEBUG_ERROR, "ERROR: MADT Table not found.\n"));
+ Status = EFI_NOT_FOUND;
+ }
+ if (!GtdtFound) {
+ DEBUG ((DEBUG_ERROR, "ERROR: GTDT Table not found.\n"));
+ Status = EFI_NOT_FOUND;
+ }
+ if (!DsdtFound) {
+ DEBUG ((DEBUG_ERROR, "ERROR: DSDT Table not found.\n"));
+ Status = EFI_NOT_FOUND;
+ }
+ if (!Dbg2Found) {
+ DEBUG ((DEBUG_WARN, "WARNING: DBG2 Table not found.\n"));
+ }
+ if (!SpcrFound) {
+ DEBUG ((DEBUG_WARN, "WARNING: SPCR Table not found.\n"));
+ }
+ return Status;
+}
+
+/** Generate and install ACPI tables.
+
+ The function gathers the information necessary for installing the
+ ACPI tables from the Configuration Manager, invokes the generators
+ and installs them (via BuildAndInstallAcpiTable).
+
+ @param [in] TableFactoryProtocol Pointer to the Table Factory Protocol
+ interface.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_NOT_FOUND If a mandatory table or a generator is not found.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ProcessAcpiTables (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST TableFactoryProtocol,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_TABLE_PROTOCOL * AcpiTableProtocol;
+ CM_STD_OBJ_ACPI_TABLE_INFO * AcpiTableInfo;
+ UINT32 AcpiTableCount;
+ UINT32 Idx;
+
+ ASSERT (TableFactoryProtocol != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+
+ // Find the AcpiTable protocol
+ Status = gBS->LocateProtocol (
+ &gEfiAcpiTableProtocolGuid,
+ NULL,
+ (VOID**)&AcpiTableProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to find AcpiTable protocol. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Status = GetEStdObjAcpiTableList (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &AcpiTableInfo,
+ &AcpiTableCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to get ACPI Table List. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ if (0 == AcpiTableCount) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: EStdObjAcpiTableList: AcpiTableCount = %d\n",
+ AcpiTableCount
+ ));
+ return EFI_NOT_FOUND;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: EStdObjAcpiTableList: AcpiTableCount = %d\n",
+ AcpiTableCount
+ ));
+
+ // Check if mandatory ACPI tables are present.
+ Status = VerifyMandatoryTablesArePresent (
+ AcpiTableInfo,
+ AcpiTableCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to find mandatory ACPI Table(s)."
+ " Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // Add the FADT Table first.
+ for (Idx = 0; Idx < AcpiTableCount; Idx++) {
+ if (CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdFadt) ==
+ AcpiTableInfo[Idx].TableGeneratorId) {
+ Status = BuildAndInstallAcpiTable (
+ TableFactoryProtocol,
+ CfgMgrProtocol,
+ AcpiTableProtocol,
+ &AcpiTableInfo[Idx]
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to find build and install ACPI FADT Table." \
+ " Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+ break;
+ }
+ } // for
+
+ // Add remaining ACPI Tables
+ for (Idx = 0; Idx < AcpiTableCount; Idx++) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: AcpiTableInfo[%d].TableGeneratorId = 0x%x\n",
+ Idx,
+ AcpiTableInfo[Idx].TableGeneratorId
+ ));
+
+ // Skip FADT Table since we have already added
+ if (CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdFadt) ==
+ AcpiTableInfo[Idx].TableGeneratorId) {
+ continue;
+ }
+
+ // Skip the Reserved table Generator ID for standard generators
+ if ((IS_GENERATOR_NAMESPACE_STD (AcpiTableInfo[Idx].TableGeneratorId)) &&
+ ((CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdReserved) >=
+ AcpiTableInfo[Idx].TableGeneratorId) ||
+ (CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdMax) <=
+ AcpiTableInfo[Idx].TableGeneratorId))) {
+ DEBUG ((
+ DEBUG_WARN,
+ "WARNING: Invalid ACPI Generator table ID = 0x%x, Skipping...\n",
+ AcpiTableInfo[Idx].TableGeneratorId
+ ));
+ continue;
+ }
+
+ Status = BuildAndInstallAcpiTable (
+ TableFactoryProtocol,
+ CfgMgrProtocol,
+ AcpiTableProtocol,
+ &AcpiTableInfo[Idx]
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to find, build, and install ACPI Table." \
+ " Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+ } // for
+
+ return Status;
+}
+
+/** Entrypoint of Dynamic Table Manager Dxe.
+
+ The Dynamic Table Manager uses the Configuration Manager Protocol
+ to get the list of ACPI and SMBIOS tables to install. For each table
+ in the list it requests the corresponding ACPI/SMBIOS table factory for
+ a generator capable of building the ACPI/SMBIOS table.
+ If a suitable table generator is found, it invokes the generator interface
+ to build the table. The Dynamic Table Manager then installs the
+ table and invokes another generator interface to free any resources
+ allocated for building the table.
+
+ @param ImageHandle
+ @param SystemTable
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+ @retval EFI_NOT_FOUND Required interface/object was not found.
+ @retval EFI_INVALID_PARAMETER Some parameter is incorrect/invalid.
+**/
+EFI_STATUS
+EFIAPI
+DynamicTableManagerDxeInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EDKII_CONFIGURATION_MANAGER_PROTOCOL * CfgMgrProtocol;
+ CM_STD_OBJ_CONFIGURATION_MANAGER_INFO * CfgMfrInfo;
+ EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * TableFactoryProtocol;
+
+ // Locate the Dynamic Table Factory
+ Status = gBS->LocateProtocol (
+ &gEdkiiDynamicTableFactoryProtocolGuid,
+ NULL,
+ (VOID**)&TableFactoryProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to find Dynamic Table Factory protocol." \
+ " Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // Locate the Configuration Manager for the Platform
+ Status = gBS->LocateProtocol (
+ &gEdkiiConfigurationManagerProtocolGuid,
+ NULL,
+ (VOID**)&CfgMgrProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to find Configuration Manager protocol. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Status = GetCgfMgrInfo (CfgMgrProtocol, &CfgMfrInfo);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to get Configuration Manager info. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: Configuration Manager Version = 0x%x, OemID = %c%c%c%c%c%c\n",
+ CfgMfrInfo->Revision,
+ CfgMfrInfo->OemId[0],
+ CfgMfrInfo->OemId[1],
+ CfgMfrInfo->OemId[2],
+ CfgMfrInfo->OemId[3],
+ CfgMfrInfo->OemId[4],
+ CfgMfrInfo->OemId[5]
+ ));
+
+ Status = ProcessAcpiTables (TableFactoryProtocol, CfgMgrProtocol);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: ACPI Table processing failure. Status = %r\n",
+ Status
+ ));
+ }
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableManagerDxe/DynamicTableManagerDxe.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableManagerDxe/DynamicTableManagerDxe.inf
new file mode 100644
index 00000000..7ba396a0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Drivers/DynamicTableManagerDxe/DynamicTableManagerDxe.inf
@@ -0,0 +1,47 @@
+## @file
+# Module that drives the table generation and installation process.
+#
+# Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = DynamicTableManagerDxe
+ FILE_GUID = 89122868-BCFD-49E8-88A3-06635CB7B3CF
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = DynamicTableManagerDxeInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = ARM AARCH64
+#
+
+[Sources]
+ DynamicTableManagerDxe.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ PrintLib
+ TableHelperLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+
+[Protocols]
+ gEfiAcpiTableProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
+ gEdkiiConfigurationManagerProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEdkiiDynamicTableFactoryProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
+[Depex]
+ gEfiAcpiTableProtocolGuid AND
+ gEdkiiConfigurationManagerProtocolGuid AND
+ gEdkiiDynamicTableFactoryProtocolGuid
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTables.dsc.inc b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTables.dsc.inc
new file mode 100644
index 00000000..d432e673
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTables.dsc.inc
@@ -0,0 +1,64 @@
+## @file
+# Dsc include file for Dynamic Tables Framework.
+#
+# Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+
+[BuildOptions]
+ RELEASE_*_*_CC_FLAGS = -DMDEPKG_NDEBUG
+
+[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]
+ #
+ # Generators
+ #
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/AcpiFadtLibArm.inf
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/AcpiGtdtLibArm.inf
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/AcpiIortLibArm.inf
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/AcpiMadtLibArm.inf
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/AcpiMcfgLibArm.inf
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf
+
+ # AML Fixup
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
+ DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf
+
+ #
+ # Dynamic Table Factory Dxe
+ #
+ DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactoryDxe.inf {
+ <LibraryClasses>
+ NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf
+ NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/AcpiFadtLibArm.inf
+ NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/AcpiGtdtLibArm.inf
+ NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/AcpiIortLibArm.inf
+ NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/AcpiMadtLibArm.inf
+ NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/AcpiMcfgLibArm.inf
+ NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf
+ 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
+ NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf
+ }
+
+ #
+ # Dynamic Tables Manager Dxe
+ #
+ DynamicTablesPkg/Drivers/DynamicTableManagerDxe/DynamicTableManagerDxe.inf
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTables.fdf.inc b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTables.fdf.inc
new file mode 100644
index 00000000..9e7d492c
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTables.fdf.inc
@@ -0,0 +1,18 @@
+## @file
+# fdf include file for Dynamic Tables Framework.
+#
+# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+ #
+ # Dynamic Table Factory Dxe
+ #
+ INF DynamicTablesPkg/Drivers/DynamicTableFactoryDxe/DynamicTableFactoryDxe.inf
+
+ #
+ # Dynamic Tables Dxe
+ #
+ INF DynamicTablesPkg/Drivers/DynamicTableManagerDxe/DynamicTableManagerDxe.inf
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.ci.yaml b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.ci.yaml
new file mode 100644
index 00000000..1f96ffe9
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.ci.yaml
@@ -0,0 +1,104 @@
+## @file
+# CI configuration for DynamicTablesPkg
+#
+# Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+{
+ ## options defined .pytool/Plugin/CompilerPlugin
+ "CompilerPlugin": {
+ "DscPath": "DynamicTablesPkg.dsc"
+ },
+
+ ## options defined .pytool/Plugin/HostUnitTestCompilerPlugin
+ "HostUnitTestCompilerPlugin": {
+ "DscPath": "" # Don't support this test
+ },
+
+ ## options defined .pytool/Plugin/CharEncodingCheck
+ "CharEncodingCheck": {
+ "IgnoreFiles": []
+ },
+
+ ## options defined .pytool/Plugin/DependencyCheck
+ "DependencyCheck": {
+ "AcceptableDependencies": [
+ "ArmPlatformPkg/ArmPlatformPkg.dec",
+ "EmbeddedPkg/EmbeddedPkg.dec",
+ "DynamicTablesPkg/DynamicTablesPkg.dec",
+ "MdeModulePkg/MdeModulePkg.dec",
+ "MdePkg/MdePkg.dec"
+ ],
+ # For host based unit tests
+ "AcceptableDependencies-HOST_APPLICATION":[
+ "UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec"
+ ],
+ # For UEFI shell based apps
+ "AcceptableDependencies-UEFI_APPLICATION":[],
+ "IgnoreInf": []
+ },
+
+ ## options defined .pytool/Plugin/DscCompleteCheck
+ "DscCompleteCheck": {
+ "IgnoreInf": [],
+ "DscPath": "DynamicTablesPkg.dsc"
+ },
+
+ ## options defined .pytool/Plugin/HostUnitTestDscCompleteCheck
+ "HostUnitTestDscCompleteCheck": {
+ "IgnoreInf": [""],
+ "DscPath": "" # Don't support this test
+ },
+
+ ## options defined .pytool/Plugin/GuidCheck
+ "GuidCheck": {
+ "IgnoreGuidName": [],
+ "IgnoreGuidValue": [],
+ "IgnoreFoldersAndFiles": [],
+ "IgnoreDuplicates": [],
+ },
+
+ ## options defined .pytool/Plugin/LibraryClassCheck
+ "LibraryClassCheck": {
+ "IgnoreHeaderFile": []
+ },
+
+ ## options defined .pytool/Plugin/SpellCheck
+ "SpellCheck": {
+ "AuditOnly": False,
+ "IgnoreFiles": [], # use gitignore syntax to ignore errors
+ # in matching files
+ "ExtendWords": [
+ "ARMHB", # ARMHB000
+ "ARMHC", # ARMHC600
+ "ARMLTD",
+ "AMLDBG",
+ "EISAID",
+ "CCIDX",
+ "CCSIDR",
+ "countof",
+ "EOBJECT",
+ "invoc",
+ "GTBLOCK",
+ "lgreater",
+ "lless",
+ "MPIDR",
+ "PERIPHBASE",
+ "pytool",
+ "Roadmap",
+ "ROOTNODEBASE",
+ "ssdtcmn",
+ "ssdtserialporttemplate",
+ "SMMUV",
+ "standardised",
+ "TABLEEX",
+ "TNSID",
+ "Vatos",
+ "WBINVD"
+ ], # words to extend to the dictionary for this package
+ "IgnoreStandardPaths": [], # Standard Plugin defined paths that
+ # should be ignore
+ "AdditionalIncludePaths": [] # Additional paths to spell check
+ # (wildcards supported)
+ }
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.dec b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.dec
new file mode 100644
index 00000000..a9a6073b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.dec
@@ -0,0 +1,51 @@
+## @file
+# dec file for Dynamic Tables Framework.
+#
+# Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ DEC_SPECIFICATION = 0x00010005
+ PACKAGE_NAME = DynamicTablesPkg
+ PACKAGE_GUID = 188EB346-8ABA-460E-A105-0F9D76F7324A
+ PACKAGE_VERSION = 1.0
+
+[Includes]
+ Include
+
+[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
+
+[Protocols]
+ # Configuration Manager Protocol GUID
+ gEdkiiConfigurationManagerProtocolGuid = { 0xd85a4835, 0x5a82, 0x4894, { 0xac, 0x2, 0x70, 0x6f, 0x43, 0xd5, 0x97, 0x8e } }
+
+ # Dynamic Table Factory Protocol GUID
+ gEdkiiDynamicTableFactoryProtocolGuid = { 0x91d1e327, 0xfe5a, 0x49b8, { 0xab, 0x65, 0xe, 0xce, 0x2d, 0xdb, 0x45, 0xec } }
+
+[PcdsFixedAtBuild]
+
+ # Maximum number of Custom ACPI Generators
+ gEdkiiDynamicTablesPkgTokenSpaceGuid.PcdMaxCustomACPIGenerators|1|UINT16|0xC0000001
+
+ # Maximum number of Custom SMBIOS Generators
+ gEdkiiDynamicTablesPkgTokenSpaceGuid.PcdMaxCustomSMBIOSGenerators|1|UINT16|0xC0000002
+
+ # Maximum number of Custom DT Generators
+ gEdkiiDynamicTablesPkgTokenSpaceGuid.PcdMaxCustomDTGenerators|1|UINT16|0xC0000003
+
+ # Non BSA Compliant 16550 Serial HID
+ gEdkiiDynamicTablesPkgTokenSpaceGuid.PcdNonBsaCompliant16550SerialHid|""|VOID*|0x40000008
+
+[Guids]
+ gEdkiiDynamicTablesPkgTokenSpaceGuid = { 0xab226e66, 0x31d8, 0x4613, { 0x87, 0x9d, 0xd2, 0xfa, 0xb6, 0x10, 0x26, 0x3c } }
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.dsc b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.dsc
new file mode 100644
index 00000000..014508df
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/DynamicTablesPkg.dsc
@@ -0,0 +1,53 @@
+## @file
+# 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>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ PLATFORM_NAME = DynamicTables
+ PLATFORM_GUID = f39096a0-7a0a-442a-9413-cf584ef80cbb
+ PLATFORM_VERSION = 0.1
+ DSC_SPECIFICATION = 0x0001001a
+ OUTPUT_DIRECTORY = Build/DynamicTables
+ SUPPORTED_ARCHITECTURES = ARM|AARCH64
+ BUILD_TARGETS = DEBUG|RELEASE|NOOPT
+ SKUID_IDENTIFIER = DEFAULT
+
+!include DynamicTables.dsc.inc
+
+!include MdePkg/MdeLibs.dsc.inc
+
+[LibraryClasses]
+ BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
+ BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+ DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
+ IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
+ MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+ PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+ PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+ UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+ UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+
+[LibraryClasses.ARM, LibraryClasses.AARCH64]
+ NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
+ PL011UartLib|ArmPlatformPkg/Library/PL011UartLib/PL011UartLib.inf
+
+[Components.common]
+ DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf
+ DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf
+ DynamicTablesPkg/Library/Common/TableHelperLib/TableHelperLib.inf
+
+[BuildOptions]
+ *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
+
+!ifdef STATIC_ANALYSIS
+ # Check all rules
+ # Inhibit C6305: Potential mismatch between sizeof and countof quantities.
+ *_VS2017_*_CC_FLAGS = /wd6305 /analyze
+!endif
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/AcpiTableGenerator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/AcpiTableGenerator.h
new file mode 100644
index 00000000..4283a97e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/AcpiTableGenerator.h
@@ -0,0 +1,370 @@
+/** @file
+
+ Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+ - Std or STD - Standard
+**/
+
+#ifndef ACPI_TABLE_GENERATOR_H_
+#define ACPI_TABLE_GENERATOR_H_
+
+#include <IndustryStandard/Acpi.h>
+
+// Module specific include files.
+#include <TableGenerator.h>
+
+#pragma pack(1)
+
+/**
+The Dynamic Tables Framework provisions two classes of ACPI table
+generators.
+ - Standard generators: The ACPI table generators implemented by the
+ Dynamic Tables Framework.
+ - OEM generators: The ACPI table generators customized by the OEM.
+
+The Dynamic Tables Framework implements the following ACPI table generators:
+ - RAW : This is the simplest ACPI table generator. It simply installs
+ the ACPI table provided in the AcpiTableData member of the
+ CM_STD_OBJ_ACPI_TABLE_INFO. The ACPI table data is provided by
+ the Configuration Manager and is generated using an implementation
+ defined mechanism.
+ - DSDT : The DSDT generator is a clone of the RAW generator. The difference
+ is in the way the ACPI Table Data is generated from an AML file.
+ - SSDT : The SSDT generator is a clone of the RAW generator. The difference
+ is in the way the ACPI Table Data is generated from an AML file.
+ - FADT : The FADT generator collates the required platform information from
+ the Configuration Manager and builds the FADT table.
+ - MADT : The MADT generator collates the GIC information from the
+ Configuration Manager and builds the MADT table.
+ - GTDT : The GTDT generator collates the Timer information from the
+ Configuration Manager and builds the GTDT table.
+ - DBG2 : The DBG2 generator collates the debug serial port information from
+ the Configuration Manager and builds the DBG2 table.
+ - SPCR : The SPCR generator collates the serial port information from the
+ Configuration Manager and builds the SPCR table.
+ - MCFG : The MCFG generator collates the PCI configuration space information
+ from the Configuration Manager and builds the MCFG table.
+ - IORT : The IORT generator collates the IO Topology information from the
+ Configuration Manager and builds the IORT table.
+ - PPTT : The PPTT generator collates the processor topology information from
+ 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.
+ - SSDT CMN-600:
+ The SSDT CMN-600 generator collates the CMN-600 information
+ from the Configuration Manager and patches the SSDT CMN-600
+ template to build the SSDT CMN-600 table.
+*/
+
+/** The ACPI_TABLE_GENERATOR_ID type describes ACPI table generator ID.
+*/
+typedef TABLE_GENERATOR_ID ACPI_TABLE_GENERATOR_ID;
+
+/** The ESTD_ACPI_TABLE_ID enum describes the ACPI table IDs reserved for
+ the standard generators.
+*/
+typedef enum StdAcpiTableId {
+ EStdAcpiTableIdReserved = 0x0000, ///< Reserved
+ EStdAcpiTableIdRaw, ///< RAW Generator
+ EStdAcpiTableIdDsdt = EStdAcpiTableIdRaw, ///< DSDT Generator
+ EStdAcpiTableIdSsdt = EStdAcpiTableIdRaw, ///< SSDT Generator
+ EStdAcpiTableIdFadt, ///< FADT Generator
+ EStdAcpiTableIdMadt, ///< MADT Generator
+ EStdAcpiTableIdGtdt, ///< GTDT Generator
+ EStdAcpiTableIdDbg2, ///< DBG2 Generator
+ EStdAcpiTableIdSpcr, ///< SPCR Generator
+ EStdAcpiTableIdMcfg, ///< MCFG Generator
+ EStdAcpiTableIdIort, ///< IORT Generator
+ EStdAcpiTableIdPptt, ///< PPTT Generator
+ EStdAcpiTableIdSrat, ///< SRAT Generator
+ EStdAcpiTableIdSsdtSerialPort, ///< SSDT Serial-Port Generator
+ EStdAcpiTableIdSsdtCmn600, ///< SSDT Cmn-600 Generator
+ EStdAcpiTableIdMax
+} ESTD_ACPI_TABLE_ID;
+
+/** This macro checks if the Table Generator ID is for an ACPI Table Generator.
+
+ @param [in] TableGeneratorId The table generator ID.
+
+ @return TRUE if the table generator ID is for an ACPI Table
+ Generator.
+**/
+#define IS_GENERATOR_TYPE_ACPI(TableGeneratorId) \
+ (GET_TABLE_TYPE (TableGeneratorId) == ETableGeneratorTypeAcpi)
+
+/** This macro checks if the Table Generator ID is for a standard ACPI
+ Table Generator.
+
+ @param [in] TableGeneratorId The table generator ID.
+
+ @return TRUE if the table generator ID is for a standard ACPI
+ Table Generator.
+**/
+#define IS_VALID_STD_ACPI_GENERATOR_ID(TableGeneratorId) \
+ ( \
+ IS_GENERATOR_NAMESPACE_STD (TableGeneratorId) && \
+ IS_GENERATOR_TYPE_ACPI (TableGeneratorId) && \
+ ((GET_TABLE_ID (GeneratorId) >= EStdAcpiTableIdRaw) && \
+ (GET_TABLE_ID (GeneratorId) < EStdAcpiTableIdMax)) \
+ )
+
+/** This macro creates a standard ACPI Table Generator ID.
+
+ @param [in] TableId The table generator ID.
+
+ @return a standard ACPI table generator ID.
+**/
+#define CREATE_STD_ACPI_TABLE_GEN_ID(TableId) \
+ CREATE_TABLE_GEN_ID ( \
+ ETableGeneratorTypeAcpi, \
+ ETableGeneratorNameSpaceStd, \
+ TableId \
+ )
+
+/** This macro creates an OEM ACPI Table Generator ID.
+
+ @param [in] TableId The table generator ID.
+
+ @return an OEM ACPI table generator ID.
+**/
+#define CREATE_OEM_ACPI_TABLE_GEN_ID(TableId) \
+ CREATE_TABLE_GEN_ID ( \
+ ETableGeneratorTypeAcpi, \
+ ETableGeneratorNameSpaceOem, \
+ TableId \
+ )
+
+/** The Creator ID for the ACPI tables generated using
+ the standard ACPI table generators.
+*/
+#define TABLE_GENERATOR_CREATOR_ID_ARM SIGNATURE_32('A', 'R', 'M', 'H')
+
+/** A macro to initialise the common header part of EFI ACPI tables as
+ defined by the EFI_ACPI_DESCRIPTION_HEADER structure.
+
+ @param [in] Signature The ACPI table signature.
+ @param [in] Type The ACPI table structure.
+ @param [in] Revision The ACPI table revision.
+**/
+#define ACPI_HEADER(Signature, Type, Revision) { \
+ Signature, /* UINT32 Signature */ \
+ sizeof (Type), /* UINT32 Length */ \
+ Revision, /* UINT8 Revision */ \
+ 0, /* UINT8 Checksum */ \
+ { 0, 0, 0, 0, 0, 0 }, /* UINT8 OemId[6] */ \
+ 0, /* UINT64 OemTableId */ \
+ 0, /* UINT32 OemRevision */ \
+ 0, /* UINT32 CreatorId */ \
+ 0 /* UINT32 CreatorRevision */\
+ }
+
+/** A macro to dump the common header part of EFI ACPI tables as
+ defined by the EFI_ACPI_DESCRIPTION_HEADER structure.
+
+ @param [in] AcpiHeader The pointer to the ACPI table header.
+**/
+#define DUMP_ACPI_TABLE_HEADER(AcpiHeader) \
+ DEBUG (( \
+ DEBUG_INFO, \
+ "ACPI TABLE %c%c%c%c : Rev 0x%x : Length : 0x%x\n", \
+ (AcpiHeader->Signature & 0xFF), \
+ ((AcpiHeader->Signature >> 8) & 0xFF), \
+ ((AcpiHeader->Signature >> 16) & 0xFF), \
+ ((AcpiHeader->Signature >> 24) & 0xFF), \
+ AcpiHeader->Revision, \
+ AcpiHeader->Length \
+ ));
+
+/** Forward declarations.
+*/
+typedef struct ConfigurationManagerProtocol EDKII_CONFIGURATION_MANAGER_PROTOCOL;
+typedef struct CmAStdObjAcpiTableInfo CM_STD_OBJ_ACPI_TABLE_INFO;
+typedef struct AcpiTableGenerator ACPI_TABLE_GENERATOR;
+
+/** This function pointer describes the interface to ACPI table build
+ functions provided by the ACPI table generator and called by the
+ Table Manager to build an 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 the generated ACPI table.
+
+ @return EFI_SUCCESS If the table is generated successfully or other
+ failure codes as returned by the generator.
+**/
+typedef EFI_STATUS (*ACPI_TABLE_GENERATOR_BUILD_TABLE) (
+ 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
+ );
+
+/** This function pointer describes the interface used by the
+ Table Manager to give the generator an opportunity to free
+ any resources allocated for building the ACPI table.
+
+ @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 the ACPI Table.
+
+ @return EFI_SUCCESS If freed successfully or other failure codes
+ as returned by the generator.
+**/
+typedef EFI_STATUS (*ACPI_TABLE_GENERATOR_FREE_TABLE) (
+ 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
+ );
+
+/** This function pointer describes an extended interface to build
+ ACPI Tables. The ACPI table generator can generate multiple
+ ACPI Tables and return a pointer to the list of ACPI tables.
+ The FreeTableResourcesEx() must be called to free any resources
+ that may have been allocated using this interface.
+
+ @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).
+
+ @return EFI_SUCCESS If the table is generated successfully or other
+ failure codes as returned by the generator.
+**/
+typedef EFI_STATUS (*ACPI_TABLE_GENERATOR_BUILD_TABLEEX) (
+ 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
+ );
+
+/** This function pointer describes an extended interface used by the
+ Table Manager to give the generator an opportunity to free
+ any resources allocated for building the ACPI table. This interface
+ must be used in conjunction with the BuildAcpiTableEx interface.
+
+ @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 the list of ACPI Table(s).
+ @param [in] TableCount Number of ACPI table(s).
+
+ @return EFI_SUCCESS If freed successfully or other failure codes
+ as returned by the generator.
+**/
+typedef EFI_STATUS (*ACPI_TABLE_GENERATOR_FREE_TABLEEX) (
+ 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
+ );
+
+/** The ACPI_TABLE_GENERATOR structure provides an interface that the
+ Table Manager can use to invoke the functions to build ACPI tables.
+
+ Note: Although the Generator is required to implement at least
+ one pair of interfaces (BuildAcpiTable & FreeTableResources or
+ BuildAcpiTableEx & FreeTableResourcesEx) for generating the ACPI
+ table(s), if both pair of interfaces are implemented the extended
+ version will take precedence.
+**/
+typedef struct AcpiTableGenerator {
+ /// The ACPI table generator ID.
+ ACPI_TABLE_GENERATOR_ID GeneratorID;
+
+ /// String describing the ACPI table generator.
+ CONST CHAR16 * Description;
+
+ /// The ACPI table signature.
+ UINT32 AcpiTableSignature;
+
+ /// The ACPI table revision.
+ UINT8 AcpiTableRevision;
+
+ /// The minimum supported ACPI table revision.
+ UINT8 MinAcpiTableRevision;
+
+ /// The ACPI table creator ID.
+ UINT32 CreatorId;
+
+ /// The ACPI table creator revision.
+ UINT32 CreatorRevision;
+
+ /// ACPI table build function pointer.
+ ACPI_TABLE_GENERATOR_BUILD_TABLE BuildAcpiTable;
+
+ /** The function to free any resources
+ allocated for building the ACPI table.
+ */
+ ACPI_TABLE_GENERATOR_FREE_TABLE FreeTableResources;
+
+ /// ACPI table extended build function pointer.
+ ACPI_TABLE_GENERATOR_BUILD_TABLEEX BuildAcpiTableEx;
+
+ /** The function to free any resources
+ allocated for building the ACPI table
+ using the extended interface.
+ */
+ ACPI_TABLE_GENERATOR_FREE_TABLEEX FreeTableResourcesEx;
+} ACPI_TABLE_GENERATOR;
+
+/** Register ACPI table factory generator.
+
+ The ACPI table factory maintains a list of the Standard and OEM ACPI
+ table generators.
+
+ @param [in] Generator Pointer to the ACPI table generator.
+
+ @retval EFI_SUCCESS The Generator was registered
+ successfully.
+ @retval EFI_INVALID_PARAMETER The Generator ID is invalid or
+ the Generator pointer is NULL.
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID is
+ already registered.
+**/
+EFI_STATUS
+EFIAPI
+RegisterAcpiTableGenerator (
+ IN CONST ACPI_TABLE_GENERATOR * CONST Generator
+ );
+
+/** Deregister ACPI generator.
+
+ This function is called by the ACPI table generator to deregister itself
+ from the ACPI table factory.
+
+ @param [in] Generator Pointer to the ACPI table generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER The generator is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+DeregisterAcpiTableGenerator (
+ IN CONST ACPI_TABLE_GENERATOR * CONST Generator
+ );
+
+#pragma pack()
+
+#endif // ACPI_TABLE_GENERATOR_H_
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ArmNameSpaceObjects.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ArmNameSpaceObjects.h
new file mode 100644
index 00000000..0bf48406
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ArmNameSpaceObjects.h
@@ -0,0 +1,883 @@
+/** @file
+
+ Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+ - Std or STD - Standard
+**/
+
+#ifndef ARM_NAMESPACE_OBJECTS_H_
+#define ARM_NAMESPACE_OBJECTS_H_
+
+#include <StandardNameSpaceObjects.h>
+
+#pragma pack(1)
+
+/** The EARM_OBJECT_ID enum describes the Object IDs
+ in the ARM Namespace
+*/
+typedef enum ArmObjectID {
+ EArmObjReserved, ///< 0 - Reserved
+ EArmObjBootArchInfo, ///< 1 - Boot Architecture Info
+ EArmObjCpuInfo, ///< 2 - CPU Info
+ EArmObjPowerManagementProfileInfo, ///< 3 - Power Management Profile Info
+ EArmObjGicCInfo, ///< 4 - GIC CPU Interface Info
+ EArmObjGicDInfo, ///< 5 - GIC Distributor Info
+ EArmObjGicMsiFrameInfo, ///< 6 - GIC MSI Frame Info
+ EArmObjGicRedistributorInfo, ///< 7 - GIC Redistributor Info
+ EArmObjGicItsInfo, ///< 8 - GIC ITS Info
+ EArmObjSerialConsolePortInfo, ///< 9 - Serial Console Port Info
+ EArmObjSerialDebugPortInfo, ///< 10 - Serial Debug Port Info
+ EArmObjGenericTimerInfo, ///< 11 - Generic Timer Info
+ EArmObjPlatformGTBlockInfo, ///< 12 - Platform GT Block Info
+ EArmObjGTBlockTimerFrameInfo, ///< 13 - Generic Timer Block Frame Info
+ EArmObjPlatformGenericWatchdogInfo, ///< 14 - Platform Generic Watchdog
+ EArmObjPciConfigSpaceInfo, ///< 15 - PCI Configuration Space Info
+ EArmObjHypervisorVendorIdentity, ///< 16 - Hypervisor Vendor Id
+ EArmObjFixedFeatureFlags, ///< 17 - Fixed feature flags for FADT
+ EArmObjItsGroup, ///< 18 - ITS Group
+ EArmObjNamedComponent, ///< 19 - Named Component
+ EArmObjRootComplex, ///< 20 - Root Complex
+ EArmObjSmmuV1SmmuV2, ///< 21 - SMMUv1 or SMMUv2
+ EArmObjSmmuV3, ///< 22 - SMMUv3
+ EArmObjPmcg, ///< 23 - PMCG
+ EArmObjGicItsIdentifierArray, ///< 24 - GIC ITS Identifier Array
+ EArmObjIdMappingArray, ///< 25 - ID Mapping Array
+ EArmObjSmmuInterruptArray, ///< 26 - SMMU Interrupt Array
+ EArmObjProcHierarchyInfo, ///< 27 - Processor Hierarchy Info
+ EArmObjCacheInfo, ///< 28 - Cache Info
+ EArmObjProcNodeIdInfo, ///< 29 - Processor Node ID Info
+ EArmObjCmRef, ///< 30 - CM Object Reference
+ EArmObjMemoryAffinityInfo, ///< 31 - Memory Affinity Info
+ EArmObjDeviceHandleAcpi, ///< 32 - Device Handle Acpi
+ EArmObjDeviceHandlePci, ///< 33 - Device Handle Pci
+ EArmObjGenericInitiatorAffinityInfo, ///< 34 - Generic Initiator Affinity
+ EArmObjSerialPortInfo, ///< 35 - Generic Serial Port Info
+ EArmObjCmn600Info, ///< 36 - CMN-600 Info
+ EArmObjMax
+} EARM_OBJECT_ID;
+
+/** A structure that describes the
+ ARM Boot Architecture flags.
+
+ ID: EArmObjBootArchInfo
+*/
+typedef struct CmArmBootArchInfo {
+ /** This is the ARM_BOOT_ARCH flags field of the FADT Table
+ described in the ACPI Table Specification.
+ */
+ UINT16 BootArchFlags;
+} CM_ARM_BOOT_ARCH_INFO;
+
+/** A structure that describes the
+ Power Management Profile Information for the Platform.
+
+ ID: EArmObjPowerManagementProfileInfo
+*/
+typedef struct CmArmPowerManagementProfileInfo {
+ /** This is the Preferred_PM_Profile field of the FADT Table
+ described in the ACPI Specification
+ */
+ UINT8 PowerManagementProfile;
+} CM_ARM_POWER_MANAGEMENT_PROFILE_INFO;
+
+/** A structure that describes the
+ GIC CPU Interface for the Platform.
+
+ ID: EArmObjGicCInfo
+*/
+typedef struct CmArmGicCInfo {
+ /// The GIC CPU Interface number.
+ UINT32 CPUInterfaceNumber;
+
+ /** The ACPI Processor UID. This must match the
+ _UID of the CPU Device object information described
+ in the DSDT/SSDT for the CPU.
+ */
+ UINT32 AcpiProcessorUid;
+
+ /** The flags field as described by the GICC structure
+ in the ACPI Specification.
+ */
+ UINT32 Flags;
+
+ /** The parking protocol version field as described by
+ the GICC structure in the ACPI Specification.
+ */
+ UINT32 ParkingProtocolVersion;
+
+ /** The Performance Interrupt field as described by
+ the GICC structure in the ACPI Specification.
+ */
+ UINT32 PerformanceInterruptGsiv;
+
+ /** The CPU Parked address field as described by
+ the GICC structure in the ACPI Specification.
+ */
+ UINT64 ParkedAddress;
+
+ /** The base address for the GIC CPU Interface
+ as described by the GICC structure in the
+ ACPI Specification.
+ */
+ UINT64 PhysicalBaseAddress;
+
+ /** The base address for GICV interface
+ as described by the GICC structure in the
+ ACPI Specification.
+ */
+ UINT64 GICV;
+
+ /** The base address for GICH interface
+ as described by the GICC structure in the
+ ACPI Specification.
+ */
+ UINT64 GICH;
+
+ /** The GICV maintenance interrupt
+ as described by the GICC structure in the
+ ACPI Specification.
+ */
+ UINT32 VGICMaintenanceInterrupt;
+
+ /** The base address for GICR interface
+ as described by the GICC structure in the
+ ACPI Specification.
+ */
+ UINT64 GICRBaseAddress;
+
+ /** The MPIDR for the CPU
+ as described by the GICC structure in the
+ ACPI Specification.
+ */
+ UINT64 MPIDR;
+
+ /** The Processor Power Efficiency class
+ as described by the GICC structure in the
+ ACPI Specification.
+ */
+ UINT8 ProcessorPowerEfficiencyClass;
+
+ /** Statistical Profiling Extension buffer overflow GSIV. Zero if
+ unsupported by this processor. This field was introduced in
+ ACPI 6.3 (MADT revision 5) and is therefore ignored when
+ generating MADT revision 4 or lower.
+ */
+ UINT16 SpeOverflowInterrupt;
+
+ /** The proximity domain to which the logical processor belongs.
+ This field is used to populate the GICC affinity structure
+ in the SRAT table.
+ */
+ UINT32 ProximityDomain;
+
+ /** The clock domain to which the logical processor belongs.
+ This field is used to populate the GICC affinity structure
+ in the SRAT table.
+ */
+ UINT32 ClockDomain;
+
+ /** The GICC Affinity flags field as described by the GICC Affinity structure
+ in the SRAT table.
+ */
+ UINT32 AffinityFlags;
+} CM_ARM_GICC_INFO;
+
+/** A structure that describes the
+ GIC Distributor information for the Platform.
+
+ ID: EArmObjGicDInfo
+*/
+typedef struct CmArmGicDInfo {
+ /// The Physical Base address for the GIC Distributor.
+ UINT64 PhysicalBaseAddress;
+
+ /** The global system interrupt
+ number where this GIC Distributor's
+ interrupt inputs start.
+ */
+ UINT32 SystemVectorBase;
+
+ /** The GIC version as described
+ by the GICD structure in the
+ ACPI Specification.
+ */
+ UINT8 GicVersion;
+} CM_ARM_GICD_INFO;
+
+/** A structure that describes the
+ GIC MSI Frame information for the Platform.
+
+ ID: EArmObjGicMsiFrameInfo
+*/
+typedef struct CmArmGicMsiFrameInfo {
+ /// The GIC MSI Frame ID
+ UINT32 GicMsiFrameId;
+
+ /// The Physical base address for the MSI Frame
+ UINT64 PhysicalBaseAddress;
+
+ /** The GIC MSI Frame flags
+ as described by the GIC MSI frame
+ structure in the ACPI Specification.
+ */
+ UINT32 Flags;
+
+ /// SPI Count used by this frame
+ UINT16 SPICount;
+
+ /// SPI Base used by this frame
+ UINT16 SPIBase;
+} CM_ARM_GIC_MSI_FRAME_INFO;
+
+/** A structure that describes the
+ GIC Redistributor information for the Platform.
+
+ ID: EArmObjGicRedistributorInfo
+*/
+typedef struct CmArmGicRedistInfo {
+ /** The physical address of a page range
+ containing all GIC Redistributors.
+ */
+ UINT64 DiscoveryRangeBaseAddress;
+
+ /// Length of the GIC Redistributor Discovery page range
+ UINT32 DiscoveryRangeLength;
+} CM_ARM_GIC_REDIST_INFO;
+
+/** A structure that describes the
+ GIC Interrupt Translation Service information for the Platform.
+
+ ID: EArmObjGicItsInfo
+*/
+typedef struct CmArmGicItsInfo {
+ /// The GIC ITS ID
+ UINT32 GicItsId;
+
+ /// The physical address for the Interrupt Translation Service
+ UINT64 PhysicalBaseAddress;
+
+ /** The proximity domain to which the logical processor belongs.
+ This field is used to populate the GIC ITS affinity structure
+ in the SRAT table.
+ */
+ UINT32 ProximityDomain;
+} CM_ARM_GIC_ITS_INFO;
+
+/** A structure that describes the
+ Serial Port information for the Platform.
+
+ ID: EArmObjSerialConsolePortInfo or
+ EArmObjSerialDebugPortInfo or
+ EArmObjSerialPortInfo
+*/
+typedef struct CmArmSerialPortInfo {
+ /// The physical base address for the serial port
+ UINT64 BaseAddress;
+
+ /// The serial port interrupt
+ UINT32 Interrupt;
+
+ /// The serial port baud rate
+ UINT64 BaudRate;
+
+ /// The serial port clock
+ UINT32 Clock;
+
+ /// Serial Port subtype
+ UINT16 PortSubtype;
+
+ /// The Base address length
+ UINT64 BaseAddressLength;
+
+ /// The access size
+ UINT8 AccessSize;
+} CM_ARM_SERIAL_PORT_INFO;
+
+/** A structure that describes the
+ Generic Timer information for the Platform.
+
+ ID: EArmObjGenericTimerInfo
+*/
+typedef struct CmArmGenericTimerInfo {
+ /// The physical base address for the counter control frame
+ UINT64 CounterControlBaseAddress;
+
+ /// The physical base address for the counter read frame
+ UINT64 CounterReadBaseAddress;
+
+ /// The secure PL1 timer interrupt
+ UINT32 SecurePL1TimerGSIV;
+
+ /// The secure PL1 timer flags
+ UINT32 SecurePL1TimerFlags;
+
+ /// The non-secure PL1 timer interrupt
+ UINT32 NonSecurePL1TimerGSIV;
+
+ /// The non-secure PL1 timer flags
+ UINT32 NonSecurePL1TimerFlags;
+
+ /// The virtual timer interrupt
+ UINT32 VirtualTimerGSIV;
+
+ /// The virtual timer flags
+ UINT32 VirtualTimerFlags;
+
+ /// The non-secure PL2 timer interrupt
+ UINT32 NonSecurePL2TimerGSIV;
+
+ /// The non-secure PL2 timer flags
+ UINT32 NonSecurePL2TimerFlags;
+
+ /// GSIV for the virtual EL2 timer
+ UINT32 VirtualPL2TimerGSIV;
+
+ /// Flags for the virtual EL2 timer
+ UINT32 VirtualPL2TimerFlags;
+} CM_ARM_GENERIC_TIMER_INFO;
+
+/** A structure that describes the
+ Platform Generic Block Timer Frame information for the Platform.
+
+ ID: EArmObjGTBlockTimerFrameInfo
+*/
+typedef struct CmArmGTBlockTimerFrameInfo {
+ /// The Generic Timer frame number
+ UINT8 FrameNumber;
+
+ /// The physical base address for the CntBase block
+ UINT64 PhysicalAddressCntBase;
+
+ /// The physical base address for the CntEL0Base block
+ UINT64 PhysicalAddressCntEL0Base;
+
+ /// The physical timer interrupt
+ UINT32 PhysicalTimerGSIV;
+
+ /** The physical timer flags as described by the GT Block
+ Timer frame Structure in the ACPI Specification.
+ */
+ UINT32 PhysicalTimerFlags;
+
+ /// The virtual timer interrupt
+ UINT32 VirtualTimerGSIV;
+
+ /** The virtual timer flags as described by the GT Block
+ Timer frame Structure in the ACPI Specification.
+ */
+ UINT32 VirtualTimerFlags;
+
+ /** The common timer flags as described by the GT Block
+ Timer frame Structure in the ACPI Specification.
+ */
+ UINT32 CommonFlags;
+} CM_ARM_GTBLOCK_TIMER_FRAME_INFO;
+
+/** A structure that describes the
+ Platform Generic Block Timer information for the Platform.
+
+ ID: EArmObjPlatformGTBlockInfo
+*/
+typedef struct CmArmGTBlockInfo {
+ /// The physical base address for the GT Block Timer structure
+ UINT64 GTBlockPhysicalAddress;
+
+ /// The number of timer frames implemented in the GT Block
+ UINT32 GTBlockTimerFrameCount;
+
+ /// Reference token for the GT Block timer frame list
+ CM_OBJECT_TOKEN GTBlockTimerFrameToken;
+} CM_ARM_GTBLOCK_INFO;
+
+/** A structure that describes the
+ SBSA Generic Watchdog information for the Platform.
+
+ ID: EArmObjPlatformGenericWatchdogInfo
+*/
+typedef struct CmArmGenericWatchdogInfo {
+ /// The physical base address of the SBSA Watchdog control frame
+ UINT64 ControlFrameAddress;
+
+ /// The physical base address of the SBSA Watchdog refresh frame
+ UINT64 RefreshFrameAddress;
+
+ /// The watchdog interrupt
+ UINT32 TimerGSIV;
+
+ /** The flags for the watchdog as described by the SBSA watchdog
+ structure in the ACPI specification.
+ */
+ UINT32 Flags;
+} CM_ARM_GENERIC_WATCHDOG_INFO;
+
+/** A structure that describes the
+ PCI Configuration Space information for the Platform.
+
+ ID: EArmObjPciConfigSpaceInfo
+*/
+typedef struct CmArmPciConfigSpaceInfo {
+ /// The physical base address for the PCI segment
+ UINT64 BaseAddress;
+
+ /// The PCI segment group number
+ UINT16 PciSegmentGroupNumber;
+
+ /// The start bus number
+ UINT8 StartBusNumber;
+
+ /// The end bus number
+ UINT8 EndBusNumber;
+} CM_ARM_PCI_CONFIG_SPACE_INFO;
+
+/** A structure that describes the
+ Hypervisor Vendor ID information for the Platform.
+
+ ID: EArmObjHypervisorVendorIdentity
+*/
+typedef struct CmArmHypervisorVendorId {
+ /// The hypervisor Vendor ID
+ UINT64 HypervisorVendorId;
+} CM_ARM_HYPERVISOR_VENDOR_ID;
+
+/** A structure that describes the
+ Fixed feature flags for the Platform.
+
+ ID: EArmObjFixedFeatureFlags
+*/
+typedef struct CmArmFixedFeatureFlags {
+ /// The Fixed feature flags
+ UINT32 Flags;
+} CM_ARM_FIXED_FEATURE_FLAGS;
+
+/** A structure that describes the
+ ITS Group node for the Platform.
+
+ ID: EArmObjItsGroup
+*/
+typedef struct CmArmItsGroupNode {
+ /// An unique token used to identify this object
+ CM_OBJECT_TOKEN Token;
+ /// The number of ITS identifiers in the ITS node
+ UINT32 ItsIdCount;
+ /// Reference token for the ITS identifier array
+ CM_OBJECT_TOKEN ItsIdToken;
+} CM_ARM_ITS_GROUP_NODE;
+
+/** A structure that describes the
+ Named component node for the Platform.
+
+ ID: EArmObjNamedComponent
+*/
+typedef struct CmArmNamedComponentNode {
+ /// An unique token used to identify this object
+ CM_OBJECT_TOKEN Token;
+ /// Number of ID mappings
+ UINT32 IdMappingCount;
+ /// Reference token for the ID mapping array
+ CM_OBJECT_TOKEN IdMappingToken;
+
+ /// Flags for the named component
+ UINT32 Flags;
+
+ /// Memory access properties : Cache coherent attributes
+ UINT32 CacheCoherent;
+ /// Memory access properties : Allocation hints
+ UINT8 AllocationHints;
+ /// Memory access properties : Memory access flags
+ UINT8 MemoryAccessFlags;
+
+ /// Memory access properties : Address size limit
+ UINT8 AddressSizeLimit;
+ /** ASCII Null terminated string with the full path to
+ the entry in the namespace for this object.
+ */
+ CHAR8* ObjectName;
+} CM_ARM_NAMED_COMPONENT_NODE;
+
+/** A structure that describes the
+ Root complex node for the Platform.
+
+ ID: EArmObjRootComplex
+*/
+typedef struct CmArmRootComplexNode {
+ /// An unique token used to identify this object
+ CM_OBJECT_TOKEN Token;
+ /// Number of ID mappings
+ UINT32 IdMappingCount;
+ /// Reference token for the ID mapping array
+ CM_OBJECT_TOKEN IdMappingToken;
+
+ /// Memory access properties : Cache coherent attributes
+ UINT32 CacheCoherent;
+ /// Memory access properties : Allocation hints
+ UINT8 AllocationHints;
+ /// Memory access properties : Memory access flags
+ UINT8 MemoryAccessFlags;
+
+ /// ATS attributes
+ UINT32 AtsAttribute;
+ /// PCI segment number
+ UINT32 PciSegmentNumber;
+ /// Memory address size limit
+ UINT8 MemoryAddressSize;
+} CM_ARM_ROOT_COMPLEX_NODE;
+
+/** A structure that describes the
+ SMMUv1 or SMMUv2 node for the Platform.
+
+ ID: EArmObjSmmuV1SmmuV2
+*/
+typedef struct CmArmSmmuV1SmmuV2Node {
+ /// An unique token used to identify this object
+ CM_OBJECT_TOKEN Token;
+ /// Number of ID mappings
+ UINT32 IdMappingCount;
+ /// Reference token for the ID mapping array
+ CM_OBJECT_TOKEN IdMappingToken;
+
+ /// SMMU Base Address
+ UINT64 BaseAddress;
+ /// Length of the memory range covered by the SMMU
+ UINT64 Span;
+ /// SMMU Model
+ UINT32 Model;
+ /// SMMU flags
+ UINT32 Flags;
+
+ /// Number of context interrupts
+ UINT32 ContextInterruptCount;
+ /// Reference token for the context interrupt array
+ CM_OBJECT_TOKEN ContextInterruptToken;
+
+ /// Number of PMU interrupts
+ UINT32 PmuInterruptCount;
+ /// Reference token for the PMU interrupt array
+ CM_OBJECT_TOKEN PmuInterruptToken;
+
+ /// GSIV of the SMMU_NSgIrpt interrupt
+ UINT32 SMMU_NSgIrpt;
+ /// SMMU_NSgIrpt interrupt flags
+ UINT32 SMMU_NSgIrptFlags;
+ /// GSIV of the SMMU_NSgCfgIrpt interrupt
+ UINT32 SMMU_NSgCfgIrpt;
+ /// SMMU_NSgCfgIrpt interrupt flags
+ UINT32 SMMU_NSgCfgIrptFlags;
+} CM_ARM_SMMUV1_SMMUV2_NODE;
+
+/** A structure that describes the
+ SMMUv3 node for the Platform.
+
+ ID: EArmObjSmmuV3
+*/
+typedef struct CmArmSmmuV3Node {
+ /// An unique token used to identify this object
+ CM_OBJECT_TOKEN Token;
+ /// Number of ID mappings
+ UINT32 IdMappingCount;
+ /// Reference token for the ID mapping array
+ CM_OBJECT_TOKEN IdMappingToken;
+
+ /// SMMU Base Address
+ UINT64 BaseAddress;
+ /// SMMU flags
+ UINT32 Flags;
+ /// VATOS address
+ UINT64 VatosAddress;
+ /// Model
+ UINT32 Model;
+ /// GSIV of the Event interrupt if SPI based
+ UINT32 EventInterrupt;
+ /// PRI Interrupt if SPI based
+ UINT32 PriInterrupt;
+ /// GERR interrupt if GSIV based
+ UINT32 GerrInterrupt;
+ /// Sync interrupt if GSIV based
+ UINT32 SyncInterrupt;
+
+ /// Proximity domain flag
+ UINT32 ProximityDomain;
+ /// Index into the array of ID mapping
+ UINT32 DeviceIdMappingIndex;
+} CM_ARM_SMMUV3_NODE;
+
+/** A structure that describes the
+ PMCG node for the Platform.
+
+ ID: EArmObjPmcg
+*/
+typedef struct CmArmPmcgNode {
+ /// An unique token used to identify this object
+ CM_OBJECT_TOKEN Token;
+ /// Number of ID mappings
+ UINT32 IdMappingCount;
+ /// Reference token for the ID mapping array
+ CM_OBJECT_TOKEN IdMappingToken;
+
+ /// Base Address for performance monitor counter group
+ UINT64 BaseAddress;
+ /// GSIV for the Overflow interrupt
+ UINT32 OverflowInterrupt;
+ /// Page 1 Base address
+ UINT64 Page1BaseAddress;
+
+ /// Reference token for the IORT node associated with this node
+ CM_OBJECT_TOKEN ReferenceToken;
+} CM_ARM_PMCG_NODE;
+
+/** A structure that describes the
+ GIC ITS Identifiers for an ITS Group node.
+
+ ID: EArmObjGicItsIdentifierArray
+*/
+typedef struct CmArmGicItsIdentifier {
+ /// The ITS Identifier
+ UINT32 ItsId;
+} CM_ARM_ITS_IDENTIFIER;
+
+/** A structure that describes the
+ ID Mappings for the Platform.
+
+ ID: EArmObjIdMappingArray
+*/
+typedef struct CmArmIdMapping {
+ /// Input base
+ UINT32 InputBase;
+ /// Number of input IDs
+ UINT32 NumIds;
+ /// Output Base
+ UINT32 OutputBase;
+ /// Reference token for the output node
+ CM_OBJECT_TOKEN OutputReferenceToken;
+ /// Flags
+ UINT32 Flags;
+} CM_ARM_ID_MAPPING;
+
+/** A structure that describes the Arm
+ Generic Interrupts.
+*/
+typedef struct CmArmGenericInterrupt {
+ /// Interrupt number
+ UINT32 Interrupt;
+
+ /// Flags
+ UINT32 Flags;
+} CM_ARM_GENERIC_INTERRUPT;
+
+/** A structure that describes the SMMU interrupts for the Platform.
+
+ Interrupt Interrupt number.
+ Flags Interrupt flags as defined for SMMU node.
+
+ ID: EArmObjSmmuInterruptArray
+*/
+typedef CM_ARM_GENERIC_INTERRUPT CM_ARM_SMMU_INTERRUPT;
+
+/** A structure that describes the AML Extended Interrupts.
+
+ Interrupt Interrupt number.
+ Flags Interrupt flags as defined by the Interrupt
+ Vector Flags (Byte 3) of the Extended Interrupt
+ resource descriptor.
+ See EFI_ACPI_EXTENDED_INTERRUPT_FLAG_xxx in Acpi10.h
+*/
+typedef CM_ARM_GENERIC_INTERRUPT CM_ARM_EXTENDED_INTERRUPT;
+
+/** A structure that describes the Processor Hierarchy Node (Type 0) in PPTT
+
+ ID: EArmObjProcHierarchyInfo
+*/
+typedef struct CmArmProcHierarchyInfo {
+ /// A unique token used to identify this object
+ CM_OBJECT_TOKEN Token;
+ /// Processor structure flags (ACPI 6.3 - January 2019, PPTT, Table 5-155)
+ UINT32 Flags;
+ /// Token for the parent CM_ARM_PROC_HIERARCHY_INFO object in the processor
+ /// topology. A value of CM_NULL_TOKEN means this node has no parent.
+ CM_OBJECT_TOKEN ParentToken;
+ /// Token of the associated CM_ARM_GICC_INFO object which has the
+ /// corresponding ACPI Processor ID. A value of CM_NULL_TOKEN means this
+ /// node represents a group of associated processors and it does not have an
+ /// associated GIC CPU interface.
+ CM_OBJECT_TOKEN GicCToken;
+ /// Number of resources private to this Node
+ UINT32 NoOfPrivateResources;
+ /// Token of the array which contains references to the resources private to
+ /// this CM_ARM_PROC_HIERARCHY_INFO instance. This field is ignored if
+ /// the NoOfPrivateResources is 0, in which case it is recommended to set
+ /// this field to CM_NULL_TOKEN.
+ CM_OBJECT_TOKEN PrivateResourcesArrayToken;
+} CM_ARM_PROC_HIERARCHY_INFO;
+
+/** A structure that describes the Cache Type Structure (Type 1) in PPTT
+
+ ID: EArmObjCacheInfo
+*/
+typedef struct CmArmCacheInfo {
+ /// A unique token used to identify this object
+ CM_OBJECT_TOKEN Token;
+ /// Reference token for the next level of cache that is private to the same
+ /// CM_ARM_PROC_HIERARCHY_INFO instance. A value of CM_NULL_TOKEN means this
+ /// entry represents the last cache level appropriate to the processor
+ /// hierarchy node structures using this entry.
+ CM_OBJECT_TOKEN NextLevelOfCacheToken;
+ /// Size of the cache in bytes
+ UINT32 Size;
+ /// Number of sets in the cache
+ UINT32 NumberOfSets;
+ /// Integer number of ways. The maximum associativity supported by
+ /// ACPI Cache type structure is limited to MAX_UINT8. However,
+ /// the maximum number of ways supported by the architecture is
+ /// PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX. Therfore this field
+ /// is 32-bit wide.
+ UINT32 Associativity;
+ /// Cache attributes (ACPI 6.3 - January 2019, PPTT, Table 5-156)
+ UINT8 Attributes;
+ /// Line size in bytes
+ UINT16 LineSize;
+} CM_ARM_CACHE_INFO;
+
+/** A structure that describes the ID Structure (Type 2) in PPTT
+
+ ID: EArmObjProcNodeIdInfo
+*/
+typedef struct CmArmProcNodeIdInfo {
+ /// A unique token used to identify this object
+ CM_OBJECT_TOKEN Token;
+ // Vendor ID (as described in ACPI ID registry)
+ UINT32 VendorId;
+ /// First level unique node ID
+ UINT64 Level1Id;
+ /// Second level unique node ID
+ UINT64 Level2Id;
+ /// Major revision of the node
+ UINT16 MajorRev;
+ /// Minor revision of the node
+ UINT16 MinorRev;
+ /// Spin revision of the node
+ UINT16 SpinRev;
+} CM_ARM_PROC_NODE_ID_INFO;
+
+/** A structure that describes a reference to another Configuration Manager
+ object.
+
+ This is useful for creating an array of reference tokens. The framework
+ can then query the configuration manager for these arrays using the
+ object ID EArmObjCmRef.
+
+ This can be used is to represent one-to-many relationships between objects.
+
+ ID: EArmObjCmRef
+*/
+typedef struct CmArmObjRef {
+ /// Token of the CM object being referenced
+ CM_OBJECT_TOKEN ReferenceToken;
+} CM_ARM_OBJ_REF;
+
+/** A structure that describes the Memory Affinity Structure (Type 1) in SRAT
+
+ ID: EArmObjMemoryAffinityInfo
+*/
+typedef struct CmArmMemoryAffinityInfo {
+ /// The proximity domain to which the "range of memory" belongs.
+ UINT32 ProximityDomain;
+
+ /// Base Address
+ UINT64 BaseAddress;
+
+ /// Length
+ UINT64 Length;
+
+ /// Flags
+ UINT32 Flags;
+} CM_ARM_MEMORY_AFFINITY_INFO;
+
+/** A structure that describes the ACPI Device Handle (Type 0) in the
+ Generic Initiator Affinity structure in SRAT
+
+ ID: EArmObjDeviceHandleAcpi
+*/
+typedef struct CmArmDeviceHandleAcpi {
+ /// Hardware ID
+ UINT64 Hid;
+
+ /// Unique Id
+ UINT32 Uid;
+} CM_ARM_DEVICE_HANDLE_ACPI;
+
+/** A structure that describes the PCI Device Handle (Type 1) in the
+ Generic Initiator Affinity structure in SRAT
+
+ ID: EArmObjDeviceHandlePci
+*/
+typedef struct CmArmDeviceHandlePci {
+ /// PCI Segment Number
+ UINT16 SegmentNumber;
+
+ /// PCI Bus Number - Max 256 busses (Bits 15:8 of BDF)
+ UINT8 BusNumber;
+
+ /// PCI Device Number - Max 32 devices (Bits 7:3 of BDF)
+ UINT8 DeviceNumber;
+
+ /// PCI Function Number - Max 8 functions (Bits 2:0 of BDF)
+ UINT8 FunctionNumber;
+} CM_ARM_DEVICE_HANDLE_PCI;
+
+/** A structure that describes the Generic Initiator Affinity structure in SRAT
+
+ ID: EArmObjGenericInitiatorAffinityInfo
+*/
+typedef struct CmArmGenericInitiatorAffinityInfo {
+ /// The proximity domain to which the generic initiator belongs.
+ UINT32 ProximityDomain;
+
+ /// Flags
+ UINT32 Flags;
+
+ /// Device Handle Type
+ UINT8 DeviceHandleType;
+
+ /// Reference Token for the Device Handle
+ CM_OBJECT_TOKEN DeviceHandleToken;
+} CM_ARM_GENERIC_INITIATOR_AFFINITY_INFO;
+
+/** A structure that describes the CMN-600 hardware.
+
+ ID: EArmObjCmn600Info
+*/
+typedef struct CmArmCmn600Info {
+ /// The PERIPHBASE address.
+ /// Corresponds to the Configuration Node Region (CFGR) base address.
+ UINT64 PeriphBaseAddress;
+
+ /// The PERIPHBASE address length.
+ /// Corresponds to the CFGR base address length.
+ UINT64 PeriphBaseAddressLength;
+
+ /// The ROOTNODEBASE address.
+ /// Corresponds to the Root node (ROOT) base address.
+ UINT64 RootNodeBaseAddress;
+
+ /// The Debug and Trace Logic Controller (DTC) count.
+ /// CMN-600 can have maximum 4 DTCs.
+ UINT8 DtcCount;
+
+ /// DTC Interrupt list.
+ /// The first interrupt resource descriptor pertains to
+ /// DTC[0], the second to DTC[1] and so on.
+ /// DtcCount determines the number of DTC Interrupts that
+ /// are populated. If DTC count is 2 then DtcInterrupt[2]
+ /// and DtcInterrupt[3] are ignored.
+ /// Note: The size of CM_ARM_CMN_600_INFO structure remains
+ /// constant and does not vary with the DTC count.
+ CM_ARM_EXTENDED_INTERRUPT DtcInterrupt[4];
+} CM_ARM_CMN_600_INFO;
+
+#pragma pack()
+
+#endif // ARM_NAMESPACE_OBJECTS_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ConfigurationManagerHelper.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ConfigurationManagerHelper.h
new file mode 100644
index 00000000..1712456b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ConfigurationManagerHelper.h
@@ -0,0 +1,126 @@
+/** @file
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+**/
+
+#ifndef CONFIGURATION_MANAGER_HELPER_H_
+#define CONFIGURATION_MANAGER_HELPER_H_
+
+/** The GET_OBJECT_LIST macro expands to a function that is used to retrieve
+ an object or an object list from the Configuration Manager using the
+ Configuration Manager Protocol interface.
+
+ The macro expands to a function which has the following prototype:
+
+ STATIC
+ EFI_STATUS
+ EFIAPI
+ Get<CmObjectId> (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST CM_OBJECT_TOKEN Token OPTIONAL,
+ OUT Type ** List,
+ OUT UINT32 * Count OPTIONAL
+ );
+
+ Generated function parameters:
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager protocol
+ interface.
+ @param [in] Token Reference token for the Object.
+ @param [out] List Pointer to the Object list.
+ @param [out] Count Count of the objects returned in the list.
+
+ Macro Parameters:
+ @param [in] CmObjectNameSpace The Object Namespace
+ @param [in] CmObjectId Object Id.
+ @param [in] Type Structure used to describe the Object.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object information is not found.
+ @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration
+ Manager is less than the Object size for the
+ requested object.
+**/
+#define GET_OBJECT_LIST(CmObjectNameSpace, CmObjectId, Type) \
+STATIC \
+EFI_STATUS \
+EFIAPI \
+Get##CmObjectId ( \
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, \
+ IN CONST CM_OBJECT_TOKEN Token OPTIONAL, \
+ OUT Type ** List, \
+ OUT UINT32 * CONST Count OPTIONAL \
+ ) \
+{ \
+ EFI_STATUS Status; \
+ CM_OBJ_DESCRIPTOR CmObjectDesc; \
+ UINT32 ObjCount = 0; \
+ if (List == NULL) { \
+ Status = EFI_INVALID_PARAMETER; \
+ DEBUG (( \
+ DEBUG_ERROR, \
+ "ERROR: Get" #CmObjectId ": Invalid out parameter for" \
+ " object list. Status = %r\n", \
+ Status \
+ )); \
+ goto error_handler; \
+ } \
+ Status = CfgMgrProtocol->GetObject ( \
+ CfgMgrProtocol, \
+ CREATE_CM_OBJECT_ID ( \
+ CmObjectNameSpace, \
+ CmObjectId \
+ ), \
+ Token, \
+ &CmObjectDesc \
+ ); \
+ if (EFI_ERROR (Status)) { \
+ DEBUG (( \
+ DEBUG_INFO, \
+ "INFO: Get" #CmObjectId ": Platform does not implement " \
+ #CmObjectId ". Status = %r\n", \
+ Status \
+ )); \
+ *List = NULL; \
+ goto error_handler; \
+ } \
+ if (CmObjectDesc.ObjectId != \
+ CREATE_CM_OBJECT_ID (CmObjectNameSpace, CmObjectId)) { \
+ DEBUG (( \
+ DEBUG_ERROR, \
+ "ERROR: Get" #CmObjectId ": " #CmObjectId \
+ ": Invalid ObjectId = 0x%x\n, expected Id = 0x%x\n", \
+ CmObjectDesc.ObjectId, \
+ CREATE_CM_OBJECT_ID (CmObjectNameSpace, CmObjectId) \
+ )); \
+ ASSERT (FALSE); \
+ Status = EFI_INVALID_PARAMETER; \
+ goto error_handler; \
+ } \
+ if (CmObjectDesc.Size < (sizeof (Type) * CmObjectDesc.Count)) { \
+ DEBUG (( \
+ DEBUG_ERROR, \
+ "ERROR: Get" #CmObjectId ": " #CmObjectId \
+ ": Buffer too small, size = 0x%x\n", \
+ CmObjectDesc.Size \
+ )); \
+ ASSERT (FALSE); \
+ Status = EFI_BAD_BUFFER_SIZE; \
+ goto error_handler; \
+ } \
+ ObjCount = CmObjectDesc.Count; \
+ *List = (Type*)CmObjectDesc.Data; \
+error_handler: \
+ if (Count != NULL) { \
+ *Count = ObjCount; \
+ } \
+ return Status; \
+}
+
+#endif // CONFIGURATION_MANAGER_HELPER_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ConfigurationManagerObject.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ConfigurationManagerObject.h
new file mode 100644
index 00000000..49bd7989
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/ConfigurationManagerObject.h
@@ -0,0 +1,190 @@
+/** @file
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+**/
+
+#ifndef CONFIGURATION_MANAGER_OBJECT_H_
+#define CONFIGURATION_MANAGER_OBJECT_H_
+
+#include <ArmNameSpaceObjects.h>
+#include <StandardNameSpaceObjects.h>
+
+#pragma pack(1)
+
+/** The CM_OBJECT_ID type is used to identify the Configuration Manager
+ objects.
+
+ Description of Configuration Manager Object ID
+_______________________________________________________________________________
+|31 |30 |29 |28 || 27 | 26 | 25 | 24 || 23 | 22 | 21 | 20 || 19 | 18 | 17 | 16|
+-------------------------------------------------------------------------------
+| Name Space ID || 0 | 0 | 0 | 0 || 0 | 0 | 0 | 0 || 0 | 0 | 0 | 0|
+_______________________________________________________________________________
+
+Bits: [31:28] - Name Space ID
+ 0000 - Standard
+ 0001 - ARM
+ 1000 - Custom/OEM
+ All other values are reserved.
+
+Bits: [27:16] - Reserved.
+_______________________________________________________________________________
+|15 |14 |13 |12 || 11 | 10 | 9 | 8 || 7 | 6 | 5 | 4 || 3 | 2 | 1 | 0|
+-------------------------------------------------------------------------------
+| 0 | 0 | 0 | 0 || 0 | 0 | 0 | 0 || Object ID |
+_______________________________________________________________________________
+
+Bits: [15:8] - Are reserved and must be zero.
+
+Bits: [7:0] - Object ID
+
+Object ID's in the Standard Namespace:
+ 0 - Configuration Manager Revision
+ 1 - ACPI Table List
+ 2 - SMBIOS Table List
+
+Object ID's in the ARM Namespace:
+ 0 - Reserved
+ 1 - Boot Architecture Info
+ 2 - CPU Info
+ 3 - Power Management Profile Info
+ 4 - GICC Info
+ 5 - GICD Info
+ 6 - GIC MSI Frame Info
+ 7 - GIC Redistributor Info
+ 8 - GIC ITS Info
+ 9 - Serial Console Port Info
+ 10 - Serial Debug Port Info
+ 11 - Generic Timer Info
+ 12 - Platform GT Block Info
+ 13 - Generic Timer Block Frame Info
+ 14 - Platform Generic Watchdog
+ 15 - PCI Configuration Space Info
+ 16 - Hypervisor Vendor Id
+ 17 - Fixed feature flags for FADT
+ 18 - ITS Group
+ 19 - Named Component
+ 20 - Root Complex
+ 21 - SMMUv1 or SMMUv2
+ 22 - SMMUv3
+ 23 - PMCG
+ 24 - GIC ITS Identifier Array
+ 25 - ID Mapping Array
+ 26 - SMMU Interrupt Array
+ 27 - Processor Hierarchy Info
+ 28 - Cache Info
+ 29 - Processor Hierarchy Node ID Info
+ 30 - CM Object Reference
+*/
+typedef UINT32 CM_OBJECT_ID;
+
+/** A mask for Object ID
+*/
+#define OBJECT_ID_MASK 0xFF
+
+/** A mask for Namespace ID
+*/
+#define NAMESPACE_ID_MASK 0xF
+
+/** Starting bit position for Namespace ID
+*/
+#define NAMESPACE_ID_BIT_SHIFT 28
+
+/** The EOBJECT_NAMESPACE_ID enum describes the defined namespaces
+ for the Configuration Manager Objects.
+*/
+typedef enum ObjectNameSpaceID {
+ EObjNameSpaceStandard, ///< Standard Objects Namespace
+ EObjNameSpaceArm, ///< ARM Objects Namespace
+ EObjNameSpaceOem = 0x8, ///< OEM Objects Namespace
+ EObjNameSpaceMax
+} EOBJECT_NAMESPACE_ID;
+
+/** A descriptor for Configuration Manager Objects.
+
+ The Configuration Manager Protocol interface uses this descriptor
+ to return the Configuration Manager Objects.
+*/
+typedef struct CmObjDescriptor {
+ /// Object Id
+ CM_OBJECT_ID ObjectId;
+
+ /// Size of the described Object or Object List
+ UINT32 Size;
+
+ /// Pointer to the described Object or Object List
+ VOID * Data;
+
+ /// Count of objects in the list
+ UINT32 Count;
+} CM_OBJ_DESCRIPTOR;
+
+#pragma pack()
+
+/** This macro returns the namespace ID from the CmObjectID.
+
+ @param [in] CmObjectId The Configuration Manager Object ID.
+
+ @retval Returns the Namespace ID corresponding to the CmObjectID.
+**/
+#define GET_CM_NAMESPACE_ID(CmObjectId) \
+ (((CmObjectId) >> NAMESPACE_ID_BIT_SHIFT) & \
+ NAMESPACE_ID_MASK)
+
+/** This macro returns the Object ID from the CmObjectID.
+
+ @param [in] CmObjectId The Configuration Manager Object ID.
+
+ @retval Returns the Object ID corresponding to the CmObjectID.
+**/
+#define GET_CM_OBJECT_ID(CmObjectId) ((CmObjectId) & OBJECT_ID_MASK)
+
+/** This macro returns a Configuration Manager Object ID
+ from the NameSpace ID and the ObjectID.
+
+ @param [in] NameSpaceId The namespace ID for the Object.
+ @param [in] ObjectId The Object ID.
+
+ @retval Returns the Configuration Manager Object ID.
+**/
+#define CREATE_CM_OBJECT_ID(NameSpaceId, ObjectId) \
+ ((((NameSpaceId) & NAMESPACE_ID_MASK) << NAMESPACE_ID_BIT_SHIFT) | \
+ ((ObjectId) & OBJECT_ID_MASK))
+
+/** This macro returns a Configuration Manager Object ID
+ in the Standard Object Namespace.
+
+ @param [in] ObjectId The Object ID.
+
+ @retval Returns a Standard Configuration Manager Object ID.
+**/
+#define CREATE_CM_STD_OBJECT_ID(ObjectId) \
+ (CREATE_CM_OBJECT_ID (EObjNameSpaceStandard, ObjectId))
+
+/** This macro returns a Configuration Manager Object ID
+ in the ARM Object Namespace.
+
+ @param [in] ObjectId The Object ID.
+
+ @retval Returns an ARM Configuration Manager Object ID.
+**/
+#define CREATE_CM_ARM_OBJECT_ID(ObjectId) \
+ (CREATE_CM_OBJECT_ID (EObjNameSpaceArm, ObjectId))
+
+/** This macro returns a Configuration Manager Object ID
+ in the OEM Object Namespace.
+
+ @param [in] ObjectId The Object ID.
+
+ @retval Returns an OEM Configuration Manager Object ID.
+**/
+#define CREATE_CM_OEM_OBJECT_ID(ObjectId) \
+ (CREATE_CM_OBJECT_ID (EObjNameSpaceOem, ObjectId))
+
+#endif // CONFIGURATION_MANAGER_OBJECT_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/DeviceTreeTableGenerator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/DeviceTreeTableGenerator.h
new file mode 100644
index 00000000..049e4307
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/DeviceTreeTableGenerator.h
@@ -0,0 +1,176 @@
+/** @file
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+ - Std or STD - Standard
+**/
+
+#ifndef DEVICETREE_TABLE_GENERATOR_H_
+#define DEVICETREE_TABLE_GENERATOR_H_
+
+#include <TableGenerator.h>
+
+#pragma pack(1)
+
+/** The DT_TABLE_GENERATOR_ID type describes Device Tree table generator ID.
+*/
+typedef TABLE_GENERATOR_ID DT_TABLE_GENERATOR_ID;
+
+/** The ESTD_DT_TABLE_ID enum describes the DT table IDs reserved for
+ the standard generators.
+*/
+typedef enum StdDtTableId {
+ EStdDtTableIdReserved = 0x0000, ///< Reserved.
+ EStdDtTableIdRaw, ///< RAW Generator.
+ EStdDtTableIdMax
+} ESTD_DT_TABLE_ID;
+
+/** This macro checks if the Table Generator ID is for an DT Table Generator.
+
+ @param [in] TableGeneratorId The table generator ID.
+
+ @return TRUE if the table generator ID is for an DT Table
+ Generator.
+**/
+#define IS_GENERATOR_TYPE_DT(TableGeneratorId) \
+ (GET_TABLE_TYPE(TableGeneratorId) == ETableGeneratorTypeDt)
+
+/** This macro checks if the Table Generator ID is for a standard DT
+ Table Generator.
+
+ @param [in] TableGeneratorId The table generator ID.
+
+ @return TRUE if the table generator ID is for a standard DT
+ Table Generator.
+**/
+#define IS_VALID_STD_DT_GENERATOR_ID(TableGeneratorId) \
+ ( \
+ IS_GENERATOR_NAMESPACE_STD(TableGeneratorId) && \
+ IS_GENERATOR_TYPE_DT(TableGeneratorId) && \
+ ((GET_TABLE_ID(GeneratorId) >= EStdDtTableIdRaw) && \
+ (GET_TABLE_ID(GeneratorId) < EStdDtTableIdMax)) \
+ )
+
+/** This macro creates a standard DT Table Generator ID.
+
+ @param [in] TableId The table generator ID.
+
+ @return a standard DT table generator ID.
+**/
+#define CREATE_STD_DT_TABLE_GEN_ID(TableId) \
+ CREATE_TABLE_GEN_ID ( \
+ ETableGeneratorTypeDt, \
+ ETableGeneratorNameSpaceStd, \
+ TableId \
+ )
+
+/** Forward declarations.
+*/
+typedef struct ConfigurationManagerProtocol EDKII_CONFIGURATION_MANAGER_PROTOCOL;
+typedef struct CmAStdObjDtTableInfo CM_STD_OBJ_DT_TABLE_INFO;
+typedef struct DtTableGenerator DT_TABLE_GENERATOR;
+
+/** This function pointer describes the interface to DT table build
+ functions provided by the DT table generator and called by the
+ Table Manager to build an DT table.
+
+ @param [in] Generator Pointer to the DT table generator.
+ @param [in] DtTableInfo Pointer to the DT table information.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol interface.
+ @param [out] Table Pointer to the generated DT table.
+
+ @return EFI_SUCCESS If the table is generated successfully or other
+ failure codes as returned by the generator.
+**/
+typedef EFI_STATUS (*DT_TABLE_GENERATOR_BUILD_TABLE) (
+ IN CONST DT_TABLE_GENERATOR * Generator,
+ IN CONST CM_STD_OBJ_DT_TABLE_INFO * CONST DtTableInfo,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ OUT VOID ** Table
+ );
+
+/** This function pointer describes the interface to used by the
+ Table Manager to give the generator an opportunity to free
+ any resources allocated for building the DT table.
+
+ @param [in] Generator Pointer to the DT table generator.
+ @param [in] DtTableInfo Pointer to the DT table information.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol interface.
+ @param [in] Table Pointer to the generated DT table.
+
+ @return EFI_SUCCESS If freed successfully or other failure codes
+ as returned by the generator.
+**/
+typedef EFI_STATUS (*DT_TABLE_GENERATOR_FREE_TABLE) (
+ IN CONST DT_TABLE_GENERATOR * Generator,
+ IN CONST CM_STD_OBJ_DT_TABLE_INFO * CONST DtTableInfo,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN VOID ** Table
+ );
+
+/** The DT_TABLE_GENERATOR structure provides an interface that the
+ Table Manager can use to invoke the functions to build DT tables.
+*/
+typedef struct DtTableGenerator {
+ /// The DT table generator ID.
+ DT_TABLE_GENERATOR_ID GeneratorID;
+
+ /// String describing the DT table generator.
+ CONST CHAR16 * Description;
+
+ /// DT table build function pointer.
+ DT_TABLE_GENERATOR_BUILD_TABLE BuildDtTable;
+
+ /// The function to free any resources allocated for building the DT table.
+ DT_TABLE_GENERATOR_FREE_TABLE FreeTableResources;
+} DT_TABLE_GENERATOR;
+
+/** Register DT table factory generator.
+
+ The DT table factory maintains a list of the Standard and OEM DT
+ table generators.
+
+ @param [in] Generator Pointer to the DT table generator.
+
+ @retval EFI_SUCCESS The Generator was registered
+ successfully.
+ @retval EFI_INVALID_PARAMETER The Generator ID is invalid or
+ the Generator pointer is NULL.
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID is
+ already registered.
+**/
+EFI_STATUS
+EFIAPI
+RegisterDtTableGenerator (
+ IN CONST DT_TABLE_GENERATOR * CONST Generator
+ );
+
+/** Deregister DT generator.
+
+ This function is called by the DT table generator to deregister itself
+ from the DT table factory.
+
+ @param [in] Generator Pointer to the DT table generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER The generator is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+DeregisterDtTableGenerator (
+ IN CONST DT_TABLE_GENERATOR * CONST Generator
+ );
+
+#pragma pack()
+
+#endif // DEVICETREE_TABLE_GENERATOR_H_
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h
new file mode 100644
index 00000000..51fbf40f
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Library/SsdtSerialPortFixupLib.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Library/SsdtSerialPortFixupLib.h
new file mode 100644
index 00000000..61eefdcf
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Library/TableHelperLib.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Library/TableHelperLib.h
new file mode 100644
index 00000000..80c44ef9
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Library/TableHelperLib.h
@@ -0,0 +1,159 @@
+/** @file
+
+ Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - PFN - Pointer to a Function
+
+**/
+
+#ifndef TABLE_HELPER_LIB_H_
+#define TABLE_HELPER_LIB_H_
+
+/** Is a character upper case
+*/
+#define IS_UPPER_CHAR(x) ((x >= 'A') && (x <= 'Z'))
+
+/** Is a character a decimal digit
+*/
+#define IS_DIGIT(x) ((x >= '0') && (x <= '9'))
+
+/** Is a character an upper case hexadecimal digit
+*/
+#define IS_UPPER_HEX(x) (((x >= 'A') && (x <= 'F')) || IS_DIGIT (x))
+
+/** The GetCgfMgrInfo function gets the CM_STD_OBJ_CONFIGURATION_MANAGER_INFO
+ object from the Configuration Manager.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager protocol
+ interface.
+ @param [out] CfgMfrInfo Pointer to the Configuration Manager Info
+ object structure.
+
+ @retval EFI_SUCCESS The object is returned.
+ @retval EFI_INVALID_PARAMETER The Object ID is invalid.
+ @retval EFI_NOT_FOUND The requested Object is not found.
+ @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration
+ Manager is less than the Object size.
+**/
+EFI_STATUS
+EFIAPI
+GetCgfMgrInfo (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ OUT CM_STD_OBJ_CONFIGURATION_MANAGER_INFO ** CfgMfrInfo
+ );
+
+/** The AddAcpiHeader function updates the ACPI header structure. It uses the
+ ACPI table Generator and the Configuration Manager protocol to obtain the
+ information required for constructing the header.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ protocol interface.
+ @param [in] Generator Pointer to the ACPI table Generator.
+ @param [in,out] AcpiHeader Pointer to the ACPI table header to be
+ updated.
+ @param [in] AcpiTableInfo Pointer to the ACPI table info structure.
+ @param [in] Length Length of the ACPI table.
+
+ @retval EFI_SUCCESS The ACPI table is updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object information is not found.
+ @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration
+ Manager is less than the Object size for the
+ requested object.
+**/
+EFI_STATUS
+EFIAPI
+AddAcpiHeader (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST ACPI_TABLE_GENERATOR * CONST Generator,
+ IN OUT EFI_ACPI_DESCRIPTION_HEADER * CONST AcpiHeader,
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo,
+ IN CONST UINT32 Length
+ );
+
+/**
+ Function prototype for testing if two arbitrary objects are equal.
+
+ @param [in] Object1 Pointer to the first object to compare.
+ @param [in] Object2 Pointer to the second object to compare.
+ @param [in] Index1 Index of Object1. This value is optional and
+ can be ignored by the specified implementation.
+ @param [in] Index2 Index of Object2. This value is optional and
+ can be ignored by the specified implementation.
+
+ @retval TRUE Object1 and Object2 are equal.
+ @retval FALSE Object1 and Object2 are NOT equal.
+**/
+typedef
+BOOLEAN
+(EFIAPI *PFN_IS_EQUAL)(
+ IN CONST VOID * Object1,
+ IN CONST VOID * Object2,
+ IN UINTN Index1 OPTIONAL,
+ IN UINTN Index2 OPTIONAL
+ );
+
+/**
+ Test and report if a duplicate entry exists in the given array of comparable
+ elements.
+
+ @param [in] Array Array of elements to test for duplicates.
+ @param [in] Count Number of elements in Array.
+ @param [in] ElementSize Size of an element in bytes
+ @param [in] EqualTestFunction The function to call to check if any two
+ elements are equal.
+
+ @retval TRUE A duplicate element was found or one of
+ the input arguments is invalid.
+ @retval FALSE Every element in Array is unique.
+**/
+BOOLEAN
+EFIAPI
+FindDuplicateValue (
+ IN CONST VOID * Array,
+ IN CONST UINTN Count,
+ IN CONST UINTN ElementSize,
+ 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
+ );
+
+/** Check if a HID is a valid PNP ID.
+
+ @param [in] Hid The Hid to validate.
+
+ @retval TRUE The Hid is a valid PNP ID.
+ @retval FALSE The Hid is not a valid PNP ID.
+**/
+BOOLEAN
+IsValidPnpId (
+ IN CONST CHAR8 * Hid
+ );
+
+/** Check if a HID is a valid ACPI ID.
+
+ @param [in] Hid The Hid to validate.
+
+ @retval TRUE The Hid is a valid ACPI ID.
+ @retval FALSE The Hid is not a valid ACPI ID.
+**/
+BOOLEAN
+IsValidAcpiId (
+ IN CONST CHAR8 * Hid
+ );
+
+#endif // TABLE_HELPER_LIB_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Protocol/ConfigurationManagerProtocol.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Protocol/ConfigurationManagerProtocol.h
new file mode 100644
index 00000000..e7f95663
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Protocol/ConfigurationManagerProtocol.h
@@ -0,0 +1,122 @@
+/** @file
+
+ Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+**/
+
+#ifndef CONFIGURATION_MANAGER_PROTOCOL_H_
+#define CONFIGURATION_MANAGER_PROTOCOL_H_
+
+#include <ConfigurationManagerObject.h>
+
+/** This macro defines the Configuration Manager Protocol GUID.
+
+ GUID: {D85A4835-5A82-4894-AC02-706F43D5978E}
+*/
+#define EDKII_CONFIGURATION_MANAGER_PROTOCOL_GUID \
+ { 0xd85a4835, 0x5a82, 0x4894, \
+ { 0xac, 0x2, 0x70, 0x6f, 0x43, 0xd5, 0x97, 0x8e } \
+ };
+
+/** This macro defines the Configuration Manager Protocol Revision.
+*/
+#define EDKII_CONFIGURATION_MANAGER_PROTOCOL_REVISION CREATE_REVISION (1, 0)
+
+#pragma pack(1)
+
+/**
+ Forward declarations:
+*/
+typedef struct ConfigurationManagerProtocol EDKII_CONFIGURATION_MANAGER_PROTOCOL;
+typedef struct PlatformRepositoryInfo EDKII_PLATFORM_REPOSITORY_INFO;
+
+/** The GetObject function defines the interface implemented by the
+ Configuration Manager Protocol for returning the Configuration
+ Manager Objects.
+
+ @param [in] This Pointer to the Configuration Manager Protocol.
+ @param [in] CmObjectId The Configuration Manager Object ID.
+ @param [in] Token An optional token identifying the object. If
+ unused this must be CM_NULL_TOKEN.
+ @param [out] CmObject Pointer to the Configuration Manager Object
+ descriptor describing the requested Object.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object information is not found.
+ @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration Manager
+ is less than the Object size for the requested
+ object.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_CONFIGURATION_MANAGER_GET_OBJECT) (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST This,
+ IN CONST CM_OBJECT_ID CmObjectId,
+ IN CONST CM_OBJECT_TOKEN Token OPTIONAL,
+ IN OUT CM_OBJ_DESCRIPTOR * CONST CmObject
+ );
+
+/** The SetObject function defines the interface implemented by the
+ Configuration Manager Protocol for updating the Configuration
+ Manager Objects.
+
+ @param [in] This Pointer to the Configuration Manager Protocol.
+ @param [in] CmObjectId The Configuration Manager Object ID.
+ @param [in] Token An optional token identifying the object. If
+ unused this must be CM_NULL_TOKEN.
+ @param [out] CmObject Pointer to the Configuration Manager Object
+ descriptor describing the Object.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object information is 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_UNSUPPORTED This operation is not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_CONFIGURATION_MANAGER_SET_OBJECT) (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST This,
+ IN CONST CM_OBJECT_ID CmObjectId,
+ IN CONST CM_OBJECT_TOKEN Token OPTIONAL,
+ IN CM_OBJ_DESCRIPTOR * CONST CmObject
+ );
+
+/** The EDKII_CONFIGURATION_MANAGER_PROTOCOL structure describes the
+ Configuration Manager Protocol interface.
+*/
+typedef struct ConfigurationManagerProtocol {
+ /// The Configuration Manager Protocol revision.
+ UINT32 Revision;
+
+ /** The interface used to request information about
+ the Configuration Manager Objects.
+ */
+ EDKII_CONFIGURATION_MANAGER_GET_OBJECT GetObject;
+
+ /** The interface used to update the information stored
+ in the Configuration Manager repository.
+ */
+ EDKII_CONFIGURATION_MANAGER_SET_OBJECT SetObject;
+
+ /** Pointer to an implementation defined abstract repository
+ provisioned by the Configuration Manager.
+ */
+ EDKII_PLATFORM_REPOSITORY_INFO * PlatRepoInfo;
+} EDKII_CONFIGURATION_MANAGER_PROTOCOL;
+
+/** The Configuration Manager Protocol GUID.
+*/
+extern EFI_GUID gEdkiiConfigurationManagerProtocolGuid;
+
+#pragma pack()
+
+#endif // CONFIGURATION_MANAGER_PROTOCOL_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Protocol/DynamicTableFactoryProtocol.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Protocol/DynamicTableFactoryProtocol.h
new file mode 100644
index 00000000..8f1dcedb
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/Protocol/DynamicTableFactoryProtocol.h
@@ -0,0 +1,254 @@
+/** @file
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - ACPI - Advanced Configuration and Power Interface
+ - SMBIOS - System Management BIOS
+ - DT - Device Tree
+**/
+
+#ifndef DYNAMIC_TABLE_FACTORY_PROTOCOL_H_
+#define DYNAMIC_TABLE_FACTORY_PROTOCOL_H_
+
+#include <AcpiTableGenerator.h>
+#include <SmbiosTableGenerator.h>
+#include <DeviceTreeTableGenerator.h>
+
+/** This macro defines the Dynamic Table Factory Protocol GUID.
+
+ GUID: {91D1E327-FE5A-49B8-AB65-0ECE2DDB45EC}
+*/
+#define EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL_GUID \
+ { 0x91d1e327, 0xfe5a, 0x49b8, \
+ { 0xab, 0x65, 0xe, 0xce, 0x2d, 0xdb, 0x45, 0xec } \
+ };
+
+/** This macro defines the Configuration Manager Protocol Revision.
+*/
+#define EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL_REVISION CREATE_REVISION (1, 0)
+
+#pragma pack(1)
+
+/**
+ Forward declarations:
+*/
+typedef struct DynamicTableFactoryProtocol EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL;
+typedef struct DynamicTableFactoryInfo EDKII_DYNAMIC_TABLE_FACTORY_INFO;
+
+/** Return a pointer to the ACPI table generator.
+
+ @param [in] This Pointer to the Dynamic Table Factory Protocol.
+ @param [in] TableId The ACPI table generator ID for the
+ requested generator.
+ @param [out] Generator Pointer to the requested ACPI table
+ generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_DYNAMIC_TABLE_FACTORY_GET_ACPI_TABLE_GENERATOR) (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST This,
+ IN CONST ACPI_TABLE_GENERATOR_ID GeneratorId,
+ OUT CONST ACPI_TABLE_GENERATOR ** CONST Generator
+ );
+
+/** Registers an ACPI table generator.
+
+ @param [in] Generator Pointer to the ACPI table generator.
+
+ @retval EFI_SUCCESS The Generator was registered
+ successfully.
+ @retval EFI_INVALID_PARAMETER The Generator ID is invalid or
+ the Generator pointer is NULL.
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID is
+ already registered.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_DYNAMIC_TABLE_FACTORY_REGISTER_ACPI_TABLE_GENERATOR) (
+ IN CONST ACPI_TABLE_GENERATOR * CONST Generator
+ );
+
+/** Deregister an ACPI table generator.
+
+ @param [in] Generator Pointer to the ACPI table generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER The generator is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_DYNAMIC_TABLE_FACTORY_DEREGISTER_ACPI_TABLE_GENERATOR) (
+ IN CONST ACPI_TABLE_GENERATOR * CONST Generator
+ );
+
+/** Return a pointer to the SMBIOS table generator.
+
+ @param [in] This Pointer to the Dynamic Table Factory Protocol.
+ @param [in] TableId The SMBIOS table generator ID for the
+ requested generator.
+ @param [out] Generator Pointer to the requested SMBIOS table
+ generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_DYNAMIC_TABLE_FACTORY_GET_SMBIOS_TABLE_GENERATOR) (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST This,
+ IN CONST SMBIOS_TABLE_GENERATOR_ID GeneratorId,
+ OUT CONST SMBIOS_TABLE_GENERATOR ** CONST Generator
+ );
+
+/** Register a SMBIOS table generator.
+
+ @param [in] Generator Pointer to the SMBIOS table generator.
+
+ @retval EFI_SUCCESS The Generator was registered
+ successfully.
+ @retval EFI_INVALID_PARAMETER The Generator ID is invalid or
+ the Generator pointer is NULL.
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID is
+ already registered.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_DYNAMIC_TABLE_FACTORY_REGISTER_SMBIOS_TABLE_GENERATOR) (
+ IN CONST SMBIOS_TABLE_GENERATOR * CONST Generator
+ );
+
+/** Deregister a SMBIOS table generator.
+
+ @param [in] Generator Pointer to the SMBIOS table generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER The generator is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_DYNAMIC_TABLE_FACTORY_DEREGISTER_SMBIOS_TABLE_GENERATOR) (
+ IN CONST SMBIOS_TABLE_GENERATOR * CONST Generator
+ );
+
+/** Return a pointer to the Device Tree table generator.
+
+ @param [in] This Pointer to the Dynamic Table Factory Protocol.
+ @param [in] TableId The Device Tree table generator ID for the
+ requested generator.
+ @param [out] Generator Pointer to the requested Device Tree table
+ generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_DYNAMIC_TABLE_FACTORY_GET_DT_TABLE_GENERATOR) (
+ IN CONST EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL * CONST This,
+ IN CONST DT_TABLE_GENERATOR_ID GeneratorId,
+ OUT CONST DT_TABLE_GENERATOR ** CONST Generator
+ );
+
+/** Register a DT table generator.
+
+ @param [in] Generator Pointer to the DT table generator.
+
+ @retval EFI_SUCCESS The Generator was registered
+ successfully.
+ @retval EFI_INVALID_PARAMETER The Generator ID is invalid or
+ the Generator pointer is NULL.
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID is
+ already registered.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_DYNAMIC_TABLE_FACTORY_REGISTER_DT_TABLE_GENERATOR) (
+ IN CONST DT_TABLE_GENERATOR * CONST Generator
+ );
+
+/** Deregister a DT table generator.
+
+ This function is called by the DT table generator to deregister itself
+ from the DT table factory.
+
+ @param [in] Generator Pointer to the DT table generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER The generator is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_DYNAMIC_TABLE_FACTORY_DEREGISTER_DT_TABLE_GENERATOR) (
+ IN CONST DT_TABLE_GENERATOR * CONST Generator
+ );
+
+/** A structure describing the Dynamic Table Factory Protocol interface.
+*/
+typedef struct DynamicTableFactoryProtocol {
+ /// The Dynamic Table Factory Protocol revision.
+ UINT32 Revision;
+
+ /// The interface used to request an ACPI Table Generator.
+ EDKII_DYNAMIC_TABLE_FACTORY_GET_ACPI_TABLE_GENERATOR GetAcpiTableGenerator;
+
+ /// Register an ACPI table Generator
+ EDKII_DYNAMIC_TABLE_FACTORY_REGISTER_ACPI_TABLE_GENERATOR
+ RegisterAcpiTableGenerator;
+
+ /// Deregister an ACPI table Generator
+ EDKII_DYNAMIC_TABLE_FACTORY_DEREGISTER_ACPI_TABLE_GENERATOR
+ DeregisterAcpiTableGenerator;
+
+ /// The interface used to request a SMBIOS Table Generator.
+ EDKII_DYNAMIC_TABLE_FACTORY_GET_SMBIOS_TABLE_GENERATOR GetSmbiosTableGenerator;
+
+ /// Register an SMBIOS table Generator
+ EDKII_DYNAMIC_TABLE_FACTORY_REGISTER_SMBIOS_TABLE_GENERATOR
+ RegisterSmbiosTableGenerator;
+
+ /// Deregister an SMBIOS table Generator
+ EDKII_DYNAMIC_TABLE_FACTORY_REGISTER_SMBIOS_TABLE_GENERATOR
+ DeregisterSmbiosTableGenerator;
+
+ /// The interface used to request a Device Tree Table Generator.
+ EDKII_DYNAMIC_TABLE_FACTORY_GET_DT_TABLE_GENERATOR GetDtTableGenerator;
+
+ /// Register a DT generator
+ EDKII_DYNAMIC_TABLE_FACTORY_REGISTER_DT_TABLE_GENERATOR
+ RegisterDtTableGenerator;
+
+ /// Deregister a DT generator
+ EDKII_DYNAMIC_TABLE_FACTORY_DEREGISTER_DT_TABLE_GENERATOR
+ DeregisterDtTableGenerator;
+
+ /** Pointer to the data structure that holds the
+ list of registered table generators
+ */
+ EDKII_DYNAMIC_TABLE_FACTORY_INFO * TableFactoryInfo;
+} EDKII_DYNAMIC_TABLE_FACTORY_PROTOCOL;
+
+/** The Dynamic Table Factory Protocol GUID.
+*/
+extern EFI_GUID gEdkiiDynamicTableFactoryProtocolGuid;
+
+#pragma pack()
+
+#endif // DYNAMIC_TABLE_FACTORY_PROTOCOL_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/SmbiosTableGenerator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/SmbiosTableGenerator.h
new file mode 100644
index 00000000..b347dc38
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/SmbiosTableGenerator.h
@@ -0,0 +1,234 @@
+/** @file
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SMBIOS_TABLE_GENERATOR_H_
+#define SMBIOS_TABLE_GENERATOR_H_
+
+#include <IndustryStandard/SmBios.h>
+
+#include <TableGenerator.h>
+
+#pragma pack(1)
+
+/** The SMBIOS_TABLE_GENERATOR_ID type describes SMBIOS table generator ID.
+*/
+typedef TABLE_GENERATOR_ID SMBIOS_TABLE_GENERATOR_ID;
+
+/** The ESTD_SMBIOS_TABLE_ID enum describes the SMBIOS table IDs reserved for
+ the standard generators.
+
+ NOTE: The SMBIOS Generator IDs do not match the table type numbers!
+ This allows 0 to be used to catch invalid parameters.
+*/
+typedef enum StdSmbiosTableGeneratorId {
+ EStdSmbiosTableIdReserved = 0x0000,
+ EStdSmbiosTableIdRAW,
+ EStdSmbiosTableIdType00,
+ EStdSmbiosTableIdType01,
+ EStdSmbiosTableIdType02,
+ EStdSmbiosTableIdType03,
+ EStdSmbiosTableIdType04,
+ EStdSmbiosTableIdType05,
+ EStdSmbiosTableIdType06,
+ EStdSmbiosTableIdType07,
+ EStdSmbiosTableIdType08,
+ EStdSmbiosTableIdType09,
+ EStdSmbiosTableIdType10,
+ EStdSmbiosTableIdType11,
+ EStdSmbiosTableIdType12,
+ EStdSmbiosTableIdType13,
+ EStdSmbiosTableIdType14,
+ EStdSmbiosTableIdType15,
+ EStdSmbiosTableIdType16,
+ EStdSmbiosTableIdType17,
+ EStdSmbiosTableIdType18,
+ EStdSmbiosTableIdType19,
+ EStdSmbiosTableIdType20,
+ EStdSmbiosTableIdType21,
+ EStdSmbiosTableIdType22,
+ EStdSmbiosTableIdType23,
+ EStdSmbiosTableIdType24,
+ EStdSmbiosTableIdType25,
+ EStdSmbiosTableIdType26,
+ EStdSmbiosTableIdType27,
+ EStdSmbiosTableIdType28,
+ EStdSmbiosTableIdType29,
+ EStdSmbiosTableIdType30,
+ EStdSmbiosTableIdType31,
+ EStdSmbiosTableIdType32,
+ EStdSmbiosTableIdType33,
+ EStdSmbiosTableIdType34,
+ EStdSmbiosTableIdType35,
+ EStdSmbiosTableIdType36,
+ EStdSmbiosTableIdType37,
+ EStdSmbiosTableIdType38,
+ EStdSmbiosTableIdType39,
+ EStdSmbiosTableIdType40,
+ EStdSmbiosTableIdType41,
+ EStdSmbiosTableIdType42,
+
+ // IDs 43 - 125 are reserved
+
+ EStdSmbiosTableIdType126 = (EStdSmbiosTableIdType00 + 126),
+ EStdSmbiosTableIdType127,
+ EStdSmbiosTableIdMax
+} ESTD_SMBIOS_TABLE_ID;
+
+/** This macro checks if the Table Generator ID is for an SMBIOS Table
+ Generator.
+
+ @param [in] TableGeneratorId The table generator ID.
+
+ @return TRUE if the table generator ID is for an SMBIOS Table
+ Generator.
+**/
+#define IS_GENERATOR_TYPE_SMBIOS(TableGeneratorId) \
+ ( \
+ GET_TABLE_TYPE (TableGeneratorId) == \
+ ETableGeneratorTypeSmbios \
+ )
+
+/** This macro checks if the Table Generator ID is for a standard SMBIOS
+ Table Generator.
+
+ @param [in] TableGeneratorId The table generator ID.
+
+ @return TRUE if the table generator ID is for a standard SMBIOS
+ Table Generator.
+**/
+#define IS_VALID_STD_SMBIOS_GENERATOR_ID(TableGeneratorId) \
+ ( \
+ IS_GENERATOR_NAMESPACE_STD(TableGeneratorId) && \
+ IS_GENERATOR_TYPE_SMBIOS(TableGeneratorId) && \
+ ((GET_TABLE_ID(GeneratorId) >= EStdSmbiosTableIdRaw) && \
+ (GET_TABLE_ID(GeneratorId) < EStdSmbiosTableIdMax)) \
+ )
+
+/** This macro creates a standard SMBIOS Table Generator ID.
+
+ @param [in] TableId The table generator ID.
+
+ @return a standard SMBIOS table generator ID.
+**/
+#define CREATE_STD_SMBIOS_TABLE_GEN_ID(TableId) \
+ CREATE_TABLE_GEN_ID ( \
+ ETableGeneratorTypeSmbios, \
+ ETableGeneratorNameSpaceStd, \
+ TableId \
+ )
+
+/** Forward declarations.
+*/
+typedef struct ConfigurationManagerProtocol EDKII_CONFIGURATION_MANAGER_PROTOCOL;
+typedef struct CmStdObjSmbiosTableInfo CM_STD_OBJ_SMBIOS_TABLE_INFO;
+typedef struct SmbiosTableGenerator SMBIOS_TABLE_GENERATOR;
+
+/** This function pointer describes the interface to SMBIOS table build
+ functions provided by the SMBIOS table generator and called by the
+ Table Manager to build an SMBIOS table.
+
+ @param [in] Generator Pointer to the SMBIOS table generator.
+ @param [in] SmbiosTableInfo Pointer to the SMBIOS table information.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol interface.
+ @param [out] Table Pointer to the generated SMBIOS table.
+
+ @return EFI_SUCCESS If the table is generated successfully or other
+ failure codes as returned by the generator.
+**/
+typedef EFI_STATUS (*SMBIOS_TABLE_GENERATOR_BUILD_TABLE) (
+ IN CONST SMBIOS_TABLE_GENERATOR * Generator,
+ IN CM_STD_OBJ_SMBIOS_TABLE_INFO * CONST SmbiosTableInfo,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ OUT SMBIOS_STRUCTURE ** Table
+ );
+
+/** This function pointer describes the interface to used by the
+ Table Manager to give the generator an opportunity to free
+ any resources allocated for building the SMBIOS table.
+
+ @param [in] Generator Pointer to the SMBIOS table generator.
+ @param [in] SmbiosTableInfo Pointer to the SMBIOS table information.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol interface.
+ @param [in] Table Pointer to the generated SMBIOS table.
+
+ @return EFI_SUCCESS If freed successfully or other failure codes
+ as returned by the generator.
+**/
+typedef EFI_STATUS (*SMBIOS_TABLE_GENERATOR_FREE_TABLE) (
+ IN CONST SMBIOS_TABLE_GENERATOR * Generator,
+ IN CONST CM_STD_OBJ_SMBIOS_TABLE_INFO * CONST SmbiosTableInfo,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN SMBIOS_STRUCTURE ** Table
+ );
+
+/** The SMBIOS_TABLE_GENERATOR structure provides an interface that the
+ Table Manager can use to invoke the functions to build SMBIOS tables.
+*/
+typedef struct SmbiosTableGenerator {
+ /// The SMBIOS table generator ID.
+ SMBIOS_TABLE_GENERATOR_ID GeneratorID;
+
+ /// String describing the DT table
+ /// generator.
+ CONST CHAR16* Description;
+
+ /// The SMBIOS table type.
+ SMBIOS_TYPE Type;
+
+ /// SMBIOS table build function pointer.
+ SMBIOS_TABLE_GENERATOR_BUILD_TABLE BuildSmbiosTable;
+
+ /** The function to free any resources
+ allocated for building the SMBIOS table.
+ */
+ SMBIOS_TABLE_GENERATOR_FREE_TABLE FreeTableResources;
+} SMBIOS_TABLE_GENERATOR;
+
+/** Register SMBIOS table factory generator.
+
+ The SMBIOS table factory maintains a list of the Standard and OEM SMBIOS
+ table generators.
+
+ @param [in] Generator Pointer to the SMBIOS table generator.
+
+ @retval EFI_SUCCESS The Generator was registered
+ successfully.
+ @retval EFI_INVALID_PARAMETER The Generator ID is invalid or
+ the Generator pointer is NULL.
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID is
+ already registered.
+**/
+EFI_STATUS
+EFIAPI
+RegisterSmbiosTableGenerator (
+ IN CONST SMBIOS_TABLE_GENERATOR * CONST Generator
+ );
+
+/** Deregister SMBIOS generator.
+
+ This function is called by the SMBIOS table generator to deregister itself
+ from the SMBIOS table factory.
+
+ @param [in] Generator Pointer to the SMBIOS table generator.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER The generator is invalid.
+ @retval EFI_NOT_FOUND The requested generator is not found
+ in the list of registered generators.
+**/
+EFI_STATUS
+EFIAPI
+DeregisterSmbiosTableGenerator (
+ IN CONST SMBIOS_TABLE_GENERATOR * CONST Generator
+ );
+#pragma pack()
+
+#endif // SMBIOS_TABLE_GENERATOR_H_
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/StandardNameSpaceObjects.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/StandardNameSpaceObjects.h
new file mode 100644
index 00000000..8090c0a1
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/StandardNameSpaceObjects.h
@@ -0,0 +1,131 @@
+/** @file
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+ - Std or STD - Standard
+**/
+
+#ifndef STANDARD_NAMESPACE_OBJECTS_H_
+#define STANDARD_NAMESPACE_OBJECTS_H_
+
+#include <AcpiTableGenerator.h>
+#include <SmbiosTableGenerator.h>
+
+#pragma pack(1)
+
+/** A macro defining a reserved zero/NULL token value that
+ does not identify any object.
+*/
+#define CM_NULL_TOKEN 0
+
+/** A reference token that the Configuration Manager can use
+ to identify a Configuration Manager object.
+
+ This can be used to differentiate between instances of
+ objects of the same types. The identification scheme is
+ implementation defined and is defined by the Configuration
+ Manager.
+
+ Typically the token is used to identify a specific instance
+ from a set of objects in a call to the GetObject()/SetObject(),
+ implemented by the Configuration Manager protocol.
+
+ Note: The token value 0 is reserved for a NULL token and does
+ not identify any object.
+**/
+typedef UINTN CM_OBJECT_TOKEN;
+
+/** The ESTD_OBJECT_ID enum describes the Object IDs
+ in the Standard Namespace.
+*/
+typedef enum StdObjectID {
+ EStdObjCfgMgrInfo = 0x00000000, ///< 0 - Configuration Manager Info
+ EStdObjAcpiTableList, ///< 1 - ACPI table Info List
+ EStdObjSmbiosTableList, ///< 2 - SMBIOS table Info List
+ EStdObjMax
+} ESTD_OBJECT_ID;
+
+/** A structure that describes the Configuration Manager Information.
+*/
+typedef struct CmStdObjConfigurationManagerInfo {
+ /// The Configuration Manager Revision.
+ UINT32 Revision;
+
+ /** The OEM ID. This information is used to
+ populate the ACPI table header information.
+ */
+ UINT8 OemId[6];
+} CM_STD_OBJ_CONFIGURATION_MANAGER_INFO;
+
+/** A structure used to describe the ACPI table generators to be invoked.
+
+ The AcpiTableData member of this structure may be used to directly provide
+ the binary ACPI table data which is required by the following standard
+ generators:
+ - RAW
+ - DSDT
+ - SSDT
+
+ Providing the ACPI table data is optional and depends on the generator
+ that is being invoked. If unused, set AcpiTableData to NULL.
+*/
+typedef struct CmAStdObjAcpiTableInfo {
+ /// The signature of the ACPI Table to be installed
+ UINT32 AcpiTableSignature;
+
+ /// The ACPI table revision
+ UINT8 AcpiTableRevision;
+
+ /// The ACPI Table Generator ID
+ ACPI_TABLE_GENERATOR_ID TableGeneratorId;
+
+ /// Optional pointer to the ACPI table data
+ EFI_ACPI_DESCRIPTION_HEADER * AcpiTableData;
+
+ /// An OEM-supplied string that the OEM uses to identify the particular
+ /// data table. This field is particularly useful when defining a definition
+ /// block to distinguish definition block functions. The OEM assigns each
+ /// dissimilar table a new OEM Table ID.
+ /// This field could be constructed using the SIGNATURE_64() macro.
+ /// e.g. SIGNATURE_64 ('A','R','M','H','G','T','D','T')
+ /// Note: If this field is not populated (has value of Zero), then the
+ /// Generators shall populate this information using part of the
+ /// CM_STD_OBJ_CONFIGURATION_MANAGER_INFO.OemId field and the
+ /// ACPI table signature.
+ UINT64 OemTableId;
+
+ /// An OEM-supplied revision number. Larger numbers are assumed to be
+ /// newer revisions.
+ /// Note: If this field is not populated (has value of Zero), then the
+ /// Generators shall populate this information using the revision of the
+ /// Configuration Manager (CM_STD_OBJ_CONFIGURATION_MANAGER_INFO.Revision).
+ UINT32 OemRevision;
+} CM_STD_OBJ_ACPI_TABLE_INFO;
+
+/** A structure used to describe the SMBIOS table generators to be invoked.
+
+ The SmbiosTableData member of this structure is used to provide
+ the SMBIOS table data which is required by the following standard
+ generator(s):
+ - RAW
+
+ Providing the SMBIOS table data is optional and depends on the
+ generator that is being invoked. If unused, set the SmbiosTableData
+ to NULL.
+*/
+typedef struct CmStdObjSmbiosTableInfo {
+ /// The SMBIOS Table Generator ID
+ SMBIOS_TABLE_GENERATOR_ID TableGeneratorId;
+
+ /// Optional pointer to the SMBIOS table data
+ SMBIOS_STRUCTURE * SmbiosTableData;
+} CM_STD_OBJ_SMBIOS_TABLE_INFO;
+
+#pragma pack()
+
+#endif // STANDARD_NAMESPACE_OBJECTS_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/TableGenerator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/TableGenerator.h
new file mode 100644
index 00000000..2b6eb354
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Include/TableGenerator.h
@@ -0,0 +1,247 @@
+/** @file
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - ACPI - Advanced Configuration and Power Interface
+ - SMBIOS - System Management BIOS
+ - DT - Device Tree
+**/
+
+#ifndef TABLE_GENERATOR_H_
+#define TABLE_GENERATOR_H_
+
+/** The TABLE_GENERATOR_ID type describes the Table Generator ID
+
+ Table Generator ID
+
+_______________________________________________________________________________
+| 31 | 30 |29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17| 16|
+-------------------------------------------------------------------------------
+|TNSID| 0 | TT | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0| 0|
+_______________________________________________________________________________
+_______________________________________________________________________________
+|15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0|
+-------------------------------------------------------------------------------
+| Table ID |
+_______________________________________________________________________________
+
+ Bit [31] - Table NameSpace ID (TNSID)
+ 0 - Standard
+ 1 - Custom/OEM
+
+ Bit [30] - Reserved, Must be Zero
+
+ Bit [29:28] - Table Type (TT)
+ 0 - ACPI Table
+ 1 - SMBIOS Table
+ 2 - DT (Device Tree) Table
+ 3 - Reserved (INVALID)
+
+ Bit [27:16] - Reserved, Must Be Zero
+
+ Bit [15:0] - Table ID
+
+ Standard ACPI Table IDs:
+ 0 - Reserved
+ 1 - RAW
+ 2 - FADT
+ 3 - DSDT
+ 4 - SSDT
+ 5 - MADT
+ 6 - GTDT
+ 7 - DBG2
+ 8 - SPCR
+ 9 - MCFG
+ 10 - PPTT
+
+ Standard SMBIOS Table IDs:
+ 0 - Reserved
+ 1 - RAW
+ 2 - Table Type00
+ 3 - Table Type01
+ 4 - Table Type02
+ 5 - Table Type03
+ 6 - Table Type04
+ 7 - Table Type05
+ 8 - Table Type06
+ 9 - Table Type07
+ 10 - Table Type08
+ 11 - Table Type09
+ 12 - Table Type10
+ 13 - Table Type11
+ 14 - Table Type12
+ 15 - Table Type13
+ 16 - Table Type14
+ 17 - Table Type15
+ 18 - Table Type16
+ 19 - Table Type17
+ 20 - Table Type18
+ 21 - Table Type19
+ 22 - Table Type20
+ 23 - Table Type21
+ 24 - Table Type22
+ 25 - Table Type23
+ 26 - Table Type24
+ 27 - Table Type25
+ 28 - Table Type26
+ 29 - Table Type27
+ 30 - Table Type28
+ 31 - Table Type29
+ 32 - Table Type30
+ 33 - Table Type31
+ 34 - Table Type32
+ 35 - Table Type33
+ 36 - Table Type34
+ 37 - Table Type35
+ 38 - Table Type36
+ 39 - Table Type37
+ 40 - Table Type38
+ 41 - Table Type39
+ 42 - Table Type40
+ 43 - Table Type41
+ 44 - Table Type42
+ 45-127 - Reserved
+ 128 - Table Type126
+ 129 - Table Type127
+**/
+typedef UINT32 TABLE_GENERATOR_ID;
+
+/** This enum lists the Table Generator Types.
+*/
+typedef enum TableGeneratorType {
+ ETableGeneratorTypeAcpi = 0, ///< ACPI Table Generator Type.
+ ETableGeneratorTypeSmbios, ///< SMBIOS Table Generator Type.
+ ETableGeneratorTypeDt, ///< Device Tree Table Generator Type.
+ ETableGeneratorTypeReserved
+} ETABLE_GENERATOR_TYPE;
+
+/** This enum lists the namespaces for the Table Generators.
+*/
+typedef enum TableGeneratorNameSpace {
+ ETableGeneratorNameSpaceStd = 0, ///< Standard Namespace.
+ ETableGeneratorNameSpaceOem ///< OEM Namespace.
+} ETABLE_GENERATOR_NAMESPACE;
+
+/** A mask for the Table ID bits of TABLE_GENERATOR_ID.
+*/
+#define TABLE_ID_MASK 0xFF
+
+/** A mask for the Namespace ID bits of TABLE_GENERATOR_ID.
+*/
+#define TABLE_NAMESPACEID_MASK (BIT31)
+
+/** A mask for the Table Type bits of TABLE_GENERATOR_ID.
+*/
+#define TABLE_TYPE_MASK (BIT29 | BIT28)
+
+/** Starting bit position for the Table Type bits
+*/
+#define TABLE_TYPE_BIT_SHIFT 28
+
+/** Starting bit position for the Table Namespace ID bit
+*/
+#define TABLE_NAMESPACE_ID_BIT_SHIFT 31
+
+/** This macro returns the Table ID from the TableGeneratorId.
+
+ @param [in] TableGeneratorId The table generator ID.
+
+ @return the Table ID described by the TableGeneratorId.
+**/
+#define GET_TABLE_ID(TableGeneratorId) \
+ ((TableGeneratorId) & TABLE_ID_MASK)
+
+/** This macro returns the Table type from the TableGeneratorId.
+
+ @param [in] TableGeneratorId The table generator ID.
+
+ @return the Table type described by the TableGeneratorId.
+**/
+#define GET_TABLE_TYPE(TableGeneratorId) \
+ (((TableGeneratorId) & TABLE_TYPE_MASK) >> TABLE_TYPE_BIT_SHIFT)
+
+/** This macro returns the Namespace ID from the TableGeneratorId.
+
+ @param [in] TableGeneratorId The table generator ID.
+
+ @return the Namespace described by the TableGeneratorId.
+**/
+#define GET_TABLE_NAMESPACEID(TableGeneratorId) \
+ (((TableGeneratorId) & TABLE_NAMESPACEID_MASK) >> \
+ TABLE_NAMESPACE_ID_BIT_SHIFT)
+
+/** This macro checks if the TableGeneratorId is in the Standard Namespace.
+
+ @param [in] TableGeneratorId The table generator ID.
+
+ @return TRUE if the TableGeneratorId is in the Standard Namespace.
+**/
+#define IS_GENERATOR_NAMESPACE_STD(TableGeneratorId) \
+ ( \
+ GET_TABLE_NAMESPACEID(TableGeneratorId) == \
+ ETableGeneratorNameSpaceStd \
+ )
+
+/** This macro creates a TableGeneratorId
+
+ @param [in] TableType The table type.
+ @param [in] TableNameSpaceId The namespace ID for the table.
+ @param [in] TableId The table ID.
+
+ @return a TableGeneratorId calculated from the inputs.
+**/
+#define CREATE_TABLE_GEN_ID(TableType, TableNameSpaceId, TableId) \
+ ((((TableType) << TABLE_TYPE_BIT_SHIFT) & TABLE_TYPE_MASK) | \
+ (((TableNameSpaceId) << TABLE_NAMESPACE_ID_BIT_SHIFT) & \
+ TABLE_NAMESPACEID_MASK) | ((TableId) & TABLE_ID_MASK))
+
+/** Starting bit position for MAJOR revision
+*/
+#define MAJOR_REVISION_BIT_SHIFT 16
+
+/** A mask for Major revision.
+*/
+#define MAJOR_REVISION_MASK 0xFFFF
+
+/** A mask for Minor revision.
+*/
+#define MINOR_REVISION_MASK 0xFFFF
+
+/** This macro generates a Major.Minor version
+ where the Major and Minor fields are 16 bit.
+
+ @param [in] Major The Major revision.
+ @param [in] Minor The Minor revision.
+
+ @return a 32 bit representation of the type Major.Minor.
+**/
+#define CREATE_REVISION(Major, Minor) \
+ ((((Major) & MAJOR_REVISION_MASK) << MAJOR_REVISION_BIT_SHIFT) | \
+ ((Minor) & MINOR_REVISION_MASK))
+
+/** This macro returns the Major revision
+
+ Extracts Major from the 32 bit representation of the type Major.Minor
+
+ @param [in] Revision The Revision value which is 32 bit.
+
+ @return the Major part of the revision.
+**/
+#define GET_MAJOR_REVISION(Revision) \
+ (((Revision) >> MAJOR_REVISION_BIT_SHIFT) & MAJOR_REVISION_MASK)
+
+/** This macro returns the Minor revision
+
+ Extracts Minor from the 32 bit representation of the type Major.Minor
+
+ @param [in] Revision The Revision value which is 32 bit.
+
+ @return the Minor part of the revision.
+**/
+#define GET_MINOR_REVISION(Revision) ((Revision) & MINOR_REVISION_MASK)
+
+#endif // TABLE_GENERATOR_H_
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf
new file mode 100644
index 00000000..d0c863e6
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf
@@ -0,0 +1,43 @@
+## @file
+# DBG2 Table Generator
+#
+# Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = AcpiDbg2LibArm
+ FILE_GUID = A17BA4F0-3DEB-4FE5-BD27-EC008E541B22
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiDbg2LibConstructor
+ DESTRUCTOR = AcpiDbg2LibDestructor
+
+[Sources]
+ Dbg2Generator.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ ArmPlatformPkg/ArmPlatformPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ PL011UartLib
+ SsdtSerialPortFixupLib
+
+[FixedPcd]
+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate
+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits
+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity
+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits
+
+[Protocols]
+
+[Guids]
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c
new file mode 100644
index 00000000..7b4e9f4a
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c
@@ -0,0 +1,577 @@
+/** @file
+ DBG2 Table Generator
+
+ Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - Microsoft Debug Port Table 2 (DBG2) Specification - December 10, 2015.
+
+**/
+
+#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>
+
+// 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 DBG2 Table Generator
+
+ Constructs the DBG2 table for PL011 or SBSA UART peripherals.
+
+Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArmObjSerialDebugPortInfo
+*/
+
+#pragma pack(1)
+
+/** The number of debug ports represented by the Table.
+*/
+#define DBG2_NUM_DEBUG_PORTS 1
+
+/** The number of Generic Address Registers
+ presented in the debug device information.
+*/
+#define DBG2_NUMBER_OF_GENERIC_ADDRESS_REGISTERS 1
+
+/** The index for the debug port 0 in the Debug port information list.
+*/
+#define INDEX_DBG_PORT0 0
+
+/** A string representing the name of the debug port 0.
+*/
+#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_DBG_PORT0)
+
+/** The PL011 UART address range length.
+*/
+#define PL011_UART_LENGTH 0x1000
+
+/** A structure that provides the OS with the required information
+ for initializing a debugger connection.
+*/
+typedef struct {
+ /// The debug device information for the platform
+ EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT Dbg2Device;
+
+ /// The base address register for the serial port
+ EFI_ACPI_6_2_GENERIC_ADDRESS_STRUCTURE BaseAddressRegister;
+
+ /// The address size
+ UINT32 AddressSize;
+
+ /// The debug port name string
+ UINT8 NameSpaceString[DBG2_NAMESPACESTRING_FIELD_SIZE];
+} DBG2_DEBUG_DEVICE_INFORMATION;
+
+/** A structure representing the information about the debug port(s)
+ available on the platform.
+*/
+typedef struct {
+ /// The DBG2 table header
+ EFI_ACPI_DEBUG_PORT_2_DESCRIPTION_TABLE Description;
+
+ /// Debug port information list
+ DBG2_DEBUG_DEVICE_INFORMATION Dbg2DeviceInfo[DBG2_NUM_DEBUG_PORTS];
+} DBG2_TABLE;
+
+/** A helper macro used for initializing the debug port device
+ information structure.
+
+ @param [in] SubType The DBG Port SubType.
+ @param [in] UartBase The UART port base address.
+ @param [in] UartAddrLen The UART port address range length.
+ @param [in] UartNameStr The UART port name string.
+**/
+#define DBG2_DEBUG_PORT_DDI( \
+ SubType, \
+ UartBase, \
+ UartAddrLen, \
+ UartNameStr \
+ ) { \
+ { \
+ /* UINT8 Revision */ \
+ EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT_REVISION, \
+ /* UINT16 Length */ \
+ sizeof (DBG2_DEBUG_DEVICE_INFORMATION), \
+ /* UINT8 NumberofGenericAddressRegisters */ \
+ DBG2_NUMBER_OF_GENERIC_ADDRESS_REGISTERS, \
+ /* UINT16 NameSpaceStringLength */ \
+ DBG2_NAMESPACESTRING_FIELD_SIZE, \
+ /* UINT16 NameSpaceStringOffset */ \
+ OFFSET_OF (DBG2_DEBUG_DEVICE_INFORMATION, NameSpaceString), \
+ /* UINT16 OemDataLength */ \
+ 0, \
+ /* UINT16 OemDataOffset */ \
+ 0, \
+ /* UINT16 Port Type */ \
+ EFI_ACPI_DBG2_PORT_TYPE_SERIAL, \
+ /* UINT16 Port Subtype */ \
+ SubType, \
+ /* UINT8 Reserved[2] */ \
+ {EFI_ACPI_RESERVED_BYTE, EFI_ACPI_RESERVED_BYTE}, \
+ /* UINT16 BaseAddressRegister Offset */ \
+ OFFSET_OF (DBG2_DEBUG_DEVICE_INFORMATION, BaseAddressRegister), \
+ /* UINT16 AddressSize Offset */ \
+ OFFSET_OF (DBG2_DEBUG_DEVICE_INFORMATION, AddressSize) \
+ }, \
+ /* EFI_ACPI_6_2_GENERIC_ADDRESS_STRUCTURE BaseAddressRegister */ \
+ ARM_GAS32 (UartBase), \
+ /* UINT32 AddressSize */ \
+ UartAddrLen, \
+ /* UINT8 NameSpaceString[MAX_DBG2_NAME_LEN] */ \
+ UartNameStr \
+ }
+
+/** The DBG2 Table template definition.
+
+ Note: fields marked with "{Template}" will be set dynamically
+*/
+STATIC
+DBG2_TABLE AcpiDbg2 = {
+ {
+ ACPI_HEADER (
+ EFI_ACPI_6_2_DEBUG_PORT_2_TABLE_SIGNATURE,
+ DBG2_TABLE,
+ EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT_REVISION
+ ),
+ OFFSET_OF (DBG2_TABLE, Dbg2DeviceInfo),
+ DBG2_NUM_DEBUG_PORTS
+ },
+ {
+ /*
+ * Debug port 1
+ */
+ DBG2_DEBUG_PORT_DDI (
+ 0, // {Template}: Serial Port Subtype
+ 0, // {Template}: Serial Port Base Address
+ PL011_UART_LENGTH,
+ NAME_STR_DBG_PORT0
+ )
+ }
+};
+
+#pragma pack()
+
+/** This macro expands to a function that retrieves the Serial
+ debug port information from the Configuration Manager
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjSerialDebugPortInfo,
+ CM_ARM_SERIAL_PORT_INFO
+ );
+
+/** Initialize the PL011/SBSA UART with the parameters obtained from
+ the Configuration Manager.
+
+ @param [in] SerialPortInfo Pointer to the Serial Port Information.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER The parameters for serial port initialization
+ are invalid.
+**/
+STATIC
+EFI_STATUS
+SetupDebugUart (
+ IN CONST CM_ARM_SERIAL_PORT_INFO * CONST SerialPortInfo
+ )
+{
+ EFI_STATUS Status;
+ UINT64 BaudRate;
+ UINT32 ReceiveFifoDepth;
+ EFI_PARITY_TYPE Parity;
+ UINT8 DataBits;
+ EFI_STOP_BITS_TYPE StopBits;
+
+ ASSERT (SerialPortInfo != NULL);
+
+ // Initialize the Serial Debug UART
+ DEBUG ((DEBUG_INFO, "Initializing Serial Debug UART...\n"));
+ ReceiveFifoDepth = 0; // Use the default value for FIFO depth
+ Parity = (EFI_PARITY_TYPE)FixedPcdGet8 (PcdUartDefaultParity);
+ DataBits = FixedPcdGet8 (PcdUartDefaultDataBits);
+ StopBits = (EFI_STOP_BITS_TYPE)FixedPcdGet8 (PcdUartDefaultStopBits);
+
+ BaudRate = SerialPortInfo->BaudRate;
+ Status = PL011UartInitializePort (
+ (UINTN)SerialPortInfo->BaseAddress,
+ SerialPortInfo->Clock,
+ &BaudRate,
+ &ReceiveFifoDepth,
+ &Parity,
+ &DataBits,
+ &StopBits
+ );
+
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
+
+/** 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;
+}
+
+/** 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 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
+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;
+ 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);
+
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: DBG2: Requested table revision = %d, is not supported."
+ "Supported table revision: Minimum = %d, Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Table = NULL;
+
+ Status = GetEArmObjSerialDebugPortInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &SerialPortInfo,
+ &SerialPortCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: DBG2: Failed to get serial port information. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ if (SerialPortCount == 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: DBG2: Serial port information not found. Status = %r\n",
+ EFI_NOT_FOUND
+ ));
+ return EFI_NOT_FOUND;
+ }
+
+ // Only use the first DBG2 port information.
+ Status = ValidateSerialPortInfo (SerialPortInfo, 1);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: DBG2: Invalid serial port information. Status = %r\n",
+ Status
+ ));
+ 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 (
+ CfgMgrProtocol,
+ This,
+ (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiDbg2,
+ AcpiTableInfo,
+ sizeof (DBG2_TABLE)
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: DBG2: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Update the base address
+ AcpiDbg2.Dbg2DeviceInfo[INDEX_DBG_PORT0].BaseAddressRegister.Address =
+ SerialPortInfo->BaseAddress;
+
+ // Set the access size
+ if (SerialPortInfo->AccessSize >= EFI_ACPI_6_3_QWORD) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: DBG2: Access size must be <= 3 (DWORD). Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ } else if (SerialPortInfo->AccessSize == EFI_ACPI_6_3_UNDEFINED) {
+ // 0 Undefined (legacy reasons)
+ // Default to DWORD access size as the access
+ // size field was introduced at a later date
+ // and some ConfigurationManager implementations
+ // may not be providing this field data
+ AcpiDbg2.Dbg2DeviceInfo[INDEX_DBG_PORT0].BaseAddressRegister.AccessSize =
+ EFI_ACPI_6_3_DWORD;
+ } else {
+ AcpiDbg2.Dbg2DeviceInfo[INDEX_DBG_PORT0].BaseAddressRegister.AccessSize =
+ SerialPortInfo->AccessSize;
+ }
+
+ // Update the serial port subtype
+ AcpiDbg2.Dbg2DeviceInfo[INDEX_DBG_PORT0].Dbg2Device.PortSubtype =
+ SerialPortInfo->PortSubtype;
+
+ 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)) {
+ // Initialize the serial port
+ Status = SetupDebugUart (SerialPortInfo);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: DBG2: Failed to configure debug serial port. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ 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;
+}
+
+/** This macro defines the DBG2 Table Generator revision.
+*/
+#define DBG2_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the DBG2 Table Generator.
+*/
+STATIC
+CONST
+ACPI_TABLE_GENERATOR Dbg2Generator = {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdDbg2),
+ // Generator Description
+ L"ACPI.STD.DBG2.GENERATOR",
+ // ACPI 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
+ EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ DBG2_GENERATOR_REVISION,
+ // Build table function. Use the extended version instead.
+ NULL,
+ // Free table function. Use the extended version instead.
+ NULL,
+ // Extended Build table function.
+ BuildDbg2TableEx,
+ // Extended free function.
+ FreeDbg2TableEx
+};
+
+/** 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
+AcpiDbg2LibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&Dbg2Generator);
+ DEBUG ((DEBUG_INFO, "DBG2: 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
+AcpiDbg2LibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&Dbg2Generator);
+ DEBUG ((DEBUG_INFO, "DBG2: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/AcpiFadtLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/AcpiFadtLibArm.inf
new file mode 100644
index 00000000..deaf7d20
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/AcpiFadtLibArm.inf
@@ -0,0 +1,36 @@
+## @file
+# FADT Table Generator
+#
+# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = AcpiFadtLibArm
+ FILE_GUID = 686FE5FE-B944-485F-8B1C-7D60E0056487
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiFadtLibConstructor
+ DESTRUCTOR = AcpiFadtLibDestructor
+
+[Sources]
+ FadtGenerator.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ BaseLib
+
+[Pcd]
+
+[Protocols]
+
+[Guids]
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/FadtGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/FadtGenerator.c
new file mode 100644
index 00000000..27b0257d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/FadtGenerator.c
@@ -0,0 +1,683 @@
+/** @file
+ FADT Table Generator
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.3 Specification, January 2019
+
+**/
+
+#include <Library/AcpiLib.h>
+#include <Library/DebugLib.h>
+#include <Protocol/AcpiTable.h>
+
+// Module specific include files.
+#include <AcpiTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <ConfigurationManagerHelper.h>
+#include <Library/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+/** ARM standard FADT Generator
+
+Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArmObjPowerManagementProfileInfo
+ - EArmObjBootArchInfo
+ - EArmObjHypervisorVendorIdentity (OPTIONAL)
+*/
+
+/** This macro defines the FADT flag options for ARM Platforms.
+*/
+#define FADT_FLAGS (EFI_ACPI_6_3_HW_REDUCED_ACPI | \
+ EFI_ACPI_6_3_LOW_POWER_S0_IDLE_CAPABLE)
+
+/** This macro defines the valid mask for the FADT flag option
+ if HW_REDUCED_ACPI flag in the table is set.
+
+ Invalid bits are: 1, 2, 3,7, 8, 13, 14,16, 17 and
+ 22-31 (reserved).
+
+ Valid bits are:
+ EFI_ACPI_6_3_WBINVD BIT0
+ EFI_ACPI_6_3_PWR_BUTTON BIT4
+ EFI_ACPI_6_3_SLP_BUTTON BIT5
+ EFI_ACPI_6_3_FIX_RTC BIT6
+ EFI_ACPI_6_3_DCK_CAP BIT9
+ EFI_ACPI_6_3_RESET_REG_SUP BIT10
+ EFI_ACPI_6_3_SEALED_CASE BIT11
+ EFI_ACPI_6_3_HEADLESS BIT12
+ EFI_ACPI_6_3_USE_PLATFORM_CLOCK BIT15
+ EFI_ACPI_6_3_FORCE_APIC_CLUSTER_MODEL BIT18
+ EFI_ACPI_6_3_FORCE_APIC_PHYSICAL_DESTINATION_MODE BIT19
+ EFI_ACPI_6_3_HW_REDUCED_ACPI BIT20
+ EFI_ACPI_6_3_LOW_POWER_S0_IDLE_CAPABLE BIT21
+*/
+#define VALID_HARDWARE_REDUCED_FLAG_MASK ( \
+ EFI_ACPI_6_3_WBINVD | \
+ EFI_ACPI_6_3_PWR_BUTTON | \
+ EFI_ACPI_6_3_SLP_BUTTON | \
+ EFI_ACPI_6_3_FIX_RTC | \
+ EFI_ACPI_6_3_DCK_CAP | \
+ EFI_ACPI_6_3_RESET_REG_SUP | \
+ EFI_ACPI_6_3_SEALED_CASE | \
+ EFI_ACPI_6_3_HEADLESS | \
+ EFI_ACPI_6_3_USE_PLATFORM_CLOCK | \
+ EFI_ACPI_6_3_FORCE_APIC_CLUSTER_MODEL | \
+ EFI_ACPI_6_3_FORCE_APIC_PHYSICAL_DESTINATION_MODE | \
+ EFI_ACPI_6_3_HW_REDUCED_ACPI | \
+ EFI_ACPI_6_3_LOW_POWER_S0_IDLE_CAPABLE)
+
+#pragma pack(1)
+
+/** The AcpiFadt is a template EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE
+ structure used for generating the FADT Table.
+ Note: fields marked with "{Template}" will be updated dynamically.
+*/
+STATIC
+EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE AcpiFadt = {
+ ACPI_HEADER (
+ EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE,
+ EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE,
+ EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_REVISION
+ ),
+ // UINT32 FirmwareCtrl
+ 0,
+ // UINT32 Dsdt
+ 0,
+ // UINT8 Reserved0
+ EFI_ACPI_RESERVED_BYTE,
+ // UINT8 PreferredPmProfile
+ EFI_ACPI_6_3_PM_PROFILE_UNSPECIFIED, // {Template}: Power Management Profile
+ // UINT16 SciInt
+ 0,
+ // UINT32 SmiCmd
+ 0,
+ // UINT8 AcpiEnable
+ 0,
+ // UINT8 AcpiDisable
+ 0,
+ // UINT8 S4BiosReq
+ 0,
+ // UINT8 PstateCnt
+ 0,
+ // UINT32 Pm1aEvtBlk
+ 0,
+ // UINT32 Pm1bEvtBlk
+ 0,
+ // UINT32 Pm1aCntBlk
+ 0,
+ // UINT32 Pm1bCntBlk
+ 0,
+ // UINT32 Pm2CntBlk
+ 0,
+ // UINT32 PmTmrBlk
+ 0,
+ // UINT32 Gpe0Blk
+ 0,
+ // UINT32 Gpe1Blk
+ 0,
+ // UINT8 Pm1EvtLen
+ 0,
+ // UINT8 Pm1CntLen
+ 0,
+ // UINT8 Pm2CntLen
+ 0,
+ // UINT8 PmTmrLen
+ 0,
+ // UINT8 Gpe0BlkLen
+ 0,
+ // UINT8 Gpe1BlkLen
+ 0,
+ // UINT8 Gpe1Base
+ 0,
+ // UINT8 CstCnt
+ 0,
+ // UINT16 PLvl2Lat
+ 0,
+ // UINT16 PLvl3Lat
+ 0,
+ // UINT16 FlushSize
+ 0,
+ // UINT16 FlushStride
+ 0,
+ // UINT8 DutyOffset
+ 0,
+ // UINT8 DutyWidth
+ 0,
+ // UINT8 DayAlrm
+ 0,
+ // UINT8 MonAlrm
+ 0,
+ // UINT8 Century
+ 0,
+ // UINT16 IaPcBootArch
+ 0,
+ // UINT8 Reserved1
+ 0,
+ // UINT32 Flags
+ FADT_FLAGS,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE ResetReg
+ NULL_GAS,
+ // UINT8 ResetValue
+ 0,
+ // UINT16 ArmBootArch
+ EFI_ACPI_6_3_ARM_PSCI_COMPLIANT, // {Template}: ARM Boot Architecture Flags
+ // UINT8 MinorRevision
+ EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_MINOR_REVISION,
+ // UINT64 XFirmwareCtrl
+ 0,
+ // UINT64 XDsdt
+ 0,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1aEvtBlk
+ NULL_GAS,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1bEvtBlk
+ NULL_GAS,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1aCntBlk
+ NULL_GAS,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1bCntBlk
+ NULL_GAS,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm2CntBlk
+ NULL_GAS,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPmTmrBlk
+ NULL_GAS,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XGpe0Blk
+ NULL_GAS,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XGpe1Blk
+ NULL_GAS,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE SleepControlReg
+ NULL_GAS,
+ // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE SleepStatusReg
+ NULL_GAS,
+ // UINT64 HypervisorVendorIdentity
+ EFI_ACPI_RESERVED_QWORD // {Template}: Hypervisor Vendor ID
+};
+
+#pragma pack()
+
+/** This macro expands to a function that retrieves the Power
+ Management Profile Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjPowerManagementProfileInfo,
+ CM_ARM_POWER_MANAGEMENT_PROFILE_INFO
+ );
+
+/** This macro expands to a function that retrieves the Boot
+ Architecture Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjBootArchInfo,
+ CM_ARM_BOOT_ARCH_INFO
+ );
+
+/** This macro expands to a function that retrieves the Hypervisor
+ Vendor ID from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjHypervisorVendorIdentity,
+ CM_ARM_HYPERVISOR_VENDOR_ID
+ );
+
+/** This macro expands to a function that retrieves the Fixed
+ feature flags for the platform from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjFixedFeatureFlags,
+ CM_ARM_FIXED_FEATURE_FLAGS
+ );
+
+/** Update the Power Management Profile information in the FADT Table.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+
+ @retval EFI_SUCCESS Success.
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FadtAddPmProfileInfo (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol
+)
+{
+ EFI_STATUS Status;
+ CM_ARM_POWER_MANAGEMENT_PROFILE_INFO * PmProfile;
+
+ ASSERT (CfgMgrProtocol != NULL);
+
+ // Get the Power Management Profile from the Platform Configuration Manager
+ Status = GetEArmObjPowerManagementProfileInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &PmProfile,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: FADT: Failed to get Power Management Profile information." \
+ " Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "FADT: PreferredPmProfile = 0x%x\n",
+ PmProfile->PowerManagementProfile
+ ));
+
+ AcpiFadt.PreferredPmProfile = PmProfile->PowerManagementProfile;
+
+error_handler:
+ return Status;
+}
+
+/** Updates the Boot Architecture information in the FADT Table.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+
+ @retval EFI_SUCCESS Success.
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FadtAddBootArchInfo (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol
+)
+{
+ EFI_STATUS Status;
+ CM_ARM_BOOT_ARCH_INFO * BootArchInfo;
+
+ ASSERT (CfgMgrProtocol != NULL);
+
+ // Get the Boot Architecture flags from the Platform Configuration Manager
+ Status = GetEArmObjBootArchInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &BootArchInfo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: FADT: Failed to get Boot Architecture flags. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "FADT BootArchFlag = 0x%x\n",
+ BootArchInfo->BootArchFlags
+ ));
+
+ AcpiFadt.ArmBootArch = BootArchInfo->BootArchFlags;
+
+error_handler:
+ return Status;
+}
+
+/** Update the Hypervisor Vendor ID in the FADT Table.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+
+ @retval EFI_SUCCESS Success.
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FadtAddHypervisorVendorId (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol
+)
+{
+ EFI_STATUS Status;
+ CM_ARM_HYPERVISOR_VENDOR_ID * HypervisorVendorInfo;
+
+ ASSERT (CfgMgrProtocol != NULL);
+
+ // Get the Hypervisor Vendor ID from the Platform Configuration Manager
+ Status = GetEArmObjHypervisorVendorIdentity (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &HypervisorVendorInfo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: FADT: Platform does not have a Hypervisor Vendor ID."
+ "Status = %r\n",
+ Status
+ ));
+ } else {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: FADT: Failed to get Hypervisor Vendor ID. Status = %r\n",
+ Status
+ ));
+ }
+ goto error_handler;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "FADT: EArmObjHypervisorVendorIdentity = 0x%lx\n",
+ HypervisorVendorInfo->HypervisorVendorId
+ ));
+
+ AcpiFadt.HypervisorVendorIdentity = HypervisorVendorInfo->HypervisorVendorId;
+
+error_handler:
+ return Status;
+}
+
+/** Update the Fixed Feature Flags in the FADT Table.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+
+ @retval EFI_SUCCESS Success.
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FadtAddFixedFeatureFlags (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol
+)
+{
+ EFI_STATUS Status;
+ CM_ARM_FIXED_FEATURE_FLAGS * FixedFeatureFlags;
+
+ ASSERT (CfgMgrProtocol != NULL);
+
+ // Get the Fixed feature flags from the Platform Configuration Manager
+ Status = GetEArmObjFixedFeatureFlags (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &FixedFeatureFlags,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: FADT: Platform does not define additional Fixed feature flags."
+ "Status = %r\n",
+ Status
+ ));
+ } else {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: FADT: Failed to get Fixed feature flags. Status = %r\n",
+ Status
+ ));
+ }
+ goto error_handler;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "FADT: EArmObjFixedFeatureFlags = 0x%x\n",
+ FixedFeatureFlags->Flags
+ ));
+
+ if ((FixedFeatureFlags->Flags & ~(VALID_HARDWARE_REDUCED_FLAG_MASK)) != 0) {
+ DEBUG ((
+ DEBUG_WARN,
+ "FADT: Invalid Fixed feature flags defined by platform,"
+ "Invalid Flags bits are = 0x%x\n",
+ (FixedFeatureFlags->Flags & ~(VALID_HARDWARE_REDUCED_FLAG_MASK))
+ ));
+ }
+
+ AcpiFadt.Flags |= (FixedFeatureFlags->Flags &
+ VALID_HARDWARE_REDUCED_FLAG_MASK);
+
+error_handler:
+ return Status;
+}
+
+/** Construct the FADT 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.
+
+ @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.
+
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildFadtTable (
+ 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
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (Table != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: FADT: Requested table revision = %d, is not supported."
+ "Supported table revision: Minimum = %d, Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Table = NULL;
+
+ Status = AddAcpiHeader (
+ CfgMgrProtocol,
+ This,
+ (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiFadt,
+ AcpiTableInfo,
+ sizeof (EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE)
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: FADT: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Update PmProfile Info
+ Status = FadtAddPmProfileInfo (CfgMgrProtocol);
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ // Update BootArch Info
+ Status = FadtAddBootArchInfo (CfgMgrProtocol);
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ // Add the Hypervisor Vendor Id if present
+ // Note if no hypervisor is present the zero bytes
+ // will be placed in this field.
+ Status = FadtAddHypervisorVendorId (CfgMgrProtocol);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: FADT: No Hypervisor Vendor ID found," \
+ " assuming no Hypervisor is present in the firmware.\n"
+ ));
+ } else {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: FADT: Error reading Hypervisor Vendor ID, Status = %r",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ Status = FadtAddFixedFeatureFlags (CfgMgrProtocol);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: FADT: No Fixed feature flags found," \
+ " assuming no additional flags are defined for the platform.\n"
+ ));
+ Status = EFI_SUCCESS;
+ } else {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: FADT: Error reading Fixed feature flags, Status = %r",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiFadt;
+error_handler:
+ return Status;
+}
+
+/** This macro defines the FADT Table Generator revision.
+*/
+#define FADT_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the FADT Table Generator.
+*/
+STATIC
+CONST
+ACPI_TABLE_GENERATOR FadtGenerator = {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdFadt),
+ // Generator Description
+ L"ACPI.STD.FADT.GENERATOR",
+ // ACPI Table Signature
+ EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_REVISION,
+ // Minimum supported ACPI Table Revision
+ EFI_ACPI_6_2_FIXED_ACPI_DESCRIPTION_TABLE_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ FADT_GENERATOR_REVISION,
+ // Build Table function
+ BuildFadtTable,
+ // No additional resources are allocated by the generator.
+ // Hence the Free Resource function is not required.
+ NULL,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+};
+
+/** 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
+AcpiFadtLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&FadtGenerator);
+ DEBUG ((DEBUG_INFO, "FADT: 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
+AcpiFadtLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&FadtGenerator);
+ DEBUG ((DEBUG_INFO, "FADT: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/AcpiGtdtLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/AcpiGtdtLibArm.inf
new file mode 100644
index 00000000..ef449603
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/AcpiGtdtLibArm.inf
@@ -0,0 +1,36 @@
+## @file
+# GTDT Table Generator
+#
+# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = AcpiGtdtLibArm
+ FILE_GUID = 26490F7A-7FA2-423C-8939-C6206329BC37
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiGtdtLibConstructor
+ DESTRUCTOR = AcpiGtdtLibDestructor
+
+[Sources]
+ GtdtGenerator.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ BaseLib
+
+[FixedPcd]
+
+[Protocols]
+
+[Guids]
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/GtdtGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/GtdtGenerator.c
new file mode 100644
index 00000000..27dccab5
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/GtdtGenerator.c
@@ -0,0 +1,780 @@
+/** @file
+ GTDT Table Generator
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.3 Specification - January 2019
+
+**/
+
+#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/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+/** ARM standard GTDT Generator
+
+Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArmObjGenericTimerInfo
+ - EArmObjPlatformGenericWatchdogInfo (OPTIONAL)
+ - EArmObjPlatformGTBlockInfo (OPTIONAL)
+ - EArmObjGTBlockTimerFrameInfo (OPTIONAL)
+*/
+
+/** This macro expands to a function that retrieves the Generic
+ Timer Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGenericTimerInfo,
+ CM_ARM_GENERIC_TIMER_INFO
+ );
+
+/** This macro expands to a function that retrieves the SBSA Generic
+ Watchdog Timer Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjPlatformGenericWatchdogInfo,
+ CM_ARM_GENERIC_WATCHDOG_INFO
+ );
+
+/** This macro expands to a function that retrieves the Platform Generic
+ Timer Block Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjPlatformGTBlockInfo,
+ CM_ARM_GTBLOCK_INFO
+ );
+
+/** This macro expands to a function that retrieves the Generic
+ Timer Block Timer Frame Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGTBlockTimerFrameInfo,
+ CM_ARM_GTBLOCK_TIMER_FRAME_INFO
+ );
+
+/** Add the Generic Timer Information to the GTDT table.
+
+ Also update the Platform Timer offset information if the platform
+ implements platform timers.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Gtdt Pointer to the GTDT Table.
+ @param [in] PlatformTimerCount Platform timer count.
+ @param [in] AcpiTableRevision Acpi Revision targeted by the platform.
+
+ @retval EFI_SUCCESS Success.
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AddGenericTimerInfo (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE * CONST Gtdt,
+ IN CONST UINT32 PlatformTimerCount,
+ IN CONST UINT32 AcpiTableRevision
+)
+{
+ EFI_STATUS Status;
+ CM_ARM_GENERIC_TIMER_INFO * GenericTimerInfo;
+
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (Gtdt != NULL);
+
+ Status = GetEArmObjGenericTimerInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GenericTimerInfo,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Failed to get GenericTimerInfo. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Gtdt->CntControlBasePhysicalAddress =
+ GenericTimerInfo->CounterControlBaseAddress;
+ Gtdt->Reserved = EFI_ACPI_RESERVED_DWORD;
+ Gtdt->SecurePL1TimerGSIV = GenericTimerInfo->SecurePL1TimerGSIV;
+ Gtdt->SecurePL1TimerFlags = GenericTimerInfo->SecurePL1TimerFlags;
+ Gtdt->NonSecurePL1TimerGSIV = GenericTimerInfo->NonSecurePL1TimerGSIV;
+ Gtdt->NonSecurePL1TimerFlags = GenericTimerInfo->NonSecurePL1TimerFlags;
+ Gtdt->VirtualTimerGSIV = GenericTimerInfo->VirtualTimerGSIV;
+ Gtdt->VirtualTimerFlags = GenericTimerInfo->VirtualTimerFlags;
+ Gtdt->NonSecurePL2TimerGSIV = GenericTimerInfo->NonSecurePL2TimerGSIV;
+ Gtdt->NonSecurePL2TimerFlags = GenericTimerInfo->NonSecurePL2TimerFlags;
+ Gtdt->CntReadBasePhysicalAddress =
+ GenericTimerInfo->CounterReadBaseAddress;
+ Gtdt->PlatformTimerCount = PlatformTimerCount;
+ Gtdt->PlatformTimerOffset = (PlatformTimerCount == 0) ? 0 :
+ sizeof (EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE);
+
+ if (AcpiTableRevision > EFI_ACPI_6_2_GENERIC_TIMER_DESCRIPTION_TABLE_REVISION) {
+ Gtdt->VirtualPL2TimerGSIV = GenericTimerInfo->VirtualPL2TimerGSIV;
+ Gtdt->VirtualPL2TimerFlags = GenericTimerInfo->VirtualPL2TimerFlags;
+ }
+
+ return Status;
+}
+
+/** Add the SBSA Generic Watchdog Timers to the GTDT table.
+
+ @param [in] Gtdt Pointer to the GTDT Table.
+ @param [in] WatchdogOffset Offset to the watchdog information in the
+ GTDT Table.
+ @param [in] WatchdogInfoList Pointer to the watchdog information list.
+ @param [in] WatchdogCount Platform timer count.
+**/
+STATIC
+VOID
+AddGenericWatchdogList (
+ IN EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE * CONST Gtdt,
+ IN CONST UINT32 WatchdogOffset,
+ IN CONST CM_ARM_GENERIC_WATCHDOG_INFO * WatchdogInfoList,
+ IN UINT32 WatchdogCount
+ )
+{
+ EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_STRUCTURE * Watchdog;
+
+ ASSERT (Gtdt != NULL);
+ ASSERT (WatchdogInfoList != NULL);
+
+ Watchdog = (EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_STRUCTURE *)
+ ((UINT8*)Gtdt + WatchdogOffset);
+
+ while (WatchdogCount-- != 0) {
+ // Add watchdog entry
+ DEBUG ((DEBUG_INFO, "GTDT: Watchdog = 0x%p\n", Watchdog));
+ Watchdog->Type = EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG;
+ Watchdog->Length =
+ sizeof (EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_STRUCTURE);
+ Watchdog->Reserved = EFI_ACPI_RESERVED_BYTE;
+ Watchdog->RefreshFramePhysicalAddress =
+ WatchdogInfoList->RefreshFrameAddress;
+ Watchdog->WatchdogControlFramePhysicalAddress =
+ WatchdogInfoList->ControlFrameAddress;
+ Watchdog->WatchdogTimerGSIV = WatchdogInfoList->TimerGSIV;
+ Watchdog->WatchdogTimerFlags = WatchdogInfoList->Flags;
+ Watchdog++;
+ WatchdogInfoList++;
+ } // for
+}
+
+/**
+ Function to test if two Generic Timer Block Frame Info structures have the
+ same frame number.
+
+ @param [in] Frame1 Pointer to the first GT Block Frame Info
+ structure.
+ @param [in] Frame2 Pointer to the second GT Block Frame Info
+ structure.
+ @param [in] Index1 Index of Frame1 in the shared GT Block Frame
+ Information List.
+ @param [in] Index2 Index of Frame2 in the shared GT Block Frame
+ Information List.
+
+ @retval TRUE Frame1 and Frame2 have the same frame number.
+ @return FALSE Frame1 and Frame2 have different frame numbers.
+
+**/
+BOOLEAN
+EFIAPI
+IsGtFrameNumberEqual (
+ IN CONST VOID * Frame1,
+ IN CONST VOID * Frame2,
+ IN UINTN Index1,
+ IN UINTN Index2
+ )
+{
+ UINT8 FrameNumber1;
+ UINT8 FrameNumber2;
+
+ ASSERT ((Frame1 != NULL) && (Frame2 != NULL));
+
+ FrameNumber1 = ((CM_ARM_GTBLOCK_TIMER_FRAME_INFO*)Frame1)->FrameNumber;
+ FrameNumber2 = ((CM_ARM_GTBLOCK_TIMER_FRAME_INFO*)Frame2)->FrameNumber;
+
+ if (FrameNumber1 == FrameNumber2) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: GT Block Frame Info Structures %d and %d have the same " \
+ "frame number: 0x%x.\n",
+ Index1,
+ Index2,
+ FrameNumber1
+ ));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/** Update the GT Block Timer Frame lists in the GTDT Table.
+
+ @param [in] GtBlockFrame Pointer to the GT Block Frames
+ list to be updated.
+ @param [in] GTBlockTimerFrameList Pointer to the GT Block Frame
+ Information List.
+ @param [in] GTBlockFrameCount Number of GT Block Frames.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+**/
+STATIC
+EFI_STATUS
+AddGTBlockTimerFrames (
+ IN EFI_ACPI_6_3_GTDT_GT_BLOCK_TIMER_STRUCTURE * GtBlockFrame,
+ IN CONST CM_ARM_GTBLOCK_TIMER_FRAME_INFO * GTBlockTimerFrameList,
+ IN UINT32 GTBlockFrameCount
+)
+{
+ BOOLEAN IsFrameNumberDuplicated;
+
+ ASSERT (GtBlockFrame != NULL);
+ ASSERT (GTBlockTimerFrameList != NULL);
+
+ IsFrameNumberDuplicated = FindDuplicateValue (
+ GTBlockTimerFrameList,
+ GTBlockFrameCount,
+ sizeof (CM_ARM_GTBLOCK_TIMER_FRAME_INFO),
+ IsGtFrameNumberEqual
+ );
+ // Duplicate entry was found so timer frame numbers provided are invalid
+ if (IsFrameNumberDuplicated) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ while (GTBlockFrameCount-- != 0) {
+ DEBUG ((
+ DEBUG_INFO,
+ "GTDT: GtBlockFrame = 0x%p\n",
+ GtBlockFrame
+ ));
+
+ if (GTBlockTimerFrameList->FrameNumber >= 8) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Frame number %d is not in the range 0-7\n",
+ GTBlockTimerFrameList->FrameNumber
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ GtBlockFrame->GTFrameNumber = GTBlockTimerFrameList->FrameNumber;
+ GtBlockFrame->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
+ GtBlockFrame->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
+ GtBlockFrame->Reserved[2] = EFI_ACPI_RESERVED_BYTE;
+
+ GtBlockFrame->CntBaseX = GTBlockTimerFrameList->PhysicalAddressCntBase;
+ GtBlockFrame->CntEL0BaseX =
+ GTBlockTimerFrameList->PhysicalAddressCntEL0Base;
+
+ GtBlockFrame->GTxPhysicalTimerGSIV =
+ GTBlockTimerFrameList->PhysicalTimerGSIV;
+ GtBlockFrame->GTxPhysicalTimerFlags =
+ GTBlockTimerFrameList->PhysicalTimerFlags;
+
+ GtBlockFrame->GTxVirtualTimerGSIV = GTBlockTimerFrameList->VirtualTimerGSIV;
+ GtBlockFrame->GTxVirtualTimerFlags =
+ GTBlockTimerFrameList->VirtualTimerFlags;
+
+ GtBlockFrame->GTxCommonFlags = GTBlockTimerFrameList->CommonFlags;
+ GtBlockFrame++;
+ GTBlockTimerFrameList++;
+ } // for
+ return EFI_SUCCESS;
+}
+
+/** Add the GT Block Timers in the GTDT Table.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Gtdt Pointer to the GTDT Table.
+ @param [in] GTBlockOffset Offset of the GT Block
+ information in the GTDT Table.
+ @param [in] GTBlockInfo Pointer to the GT Block
+ Information List.
+ @param [in] BlockTimerCount Number of GT Block Timers.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+**/
+STATIC
+EFI_STATUS
+AddGTBlockList (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE * CONST Gtdt,
+ IN CONST UINT32 GTBlockOffset,
+ IN CONST CM_ARM_GTBLOCK_INFO * GTBlockInfo,
+ IN UINT32 BlockTimerCount
+)
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE * GTBlock;
+ EFI_ACPI_6_3_GTDT_GT_BLOCK_TIMER_STRUCTURE * GtBlockFrame;
+ CM_ARM_GTBLOCK_TIMER_FRAME_INFO * GTBlockTimerFrameList;
+ UINT32 GTBlockTimerFrameCount;
+ UINTN Length;
+
+ ASSERT (Gtdt != NULL);
+ ASSERT (GTBlockInfo != NULL);
+
+ GTBlock = (EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE *)((UINT8*)Gtdt +
+ GTBlockOffset);
+
+ while (BlockTimerCount-- != 0) {
+ DEBUG ((DEBUG_INFO, "GTDT: GTBlock = 0x%p\n", GTBlock));
+
+ Status = GetEArmObjGTBlockTimerFrameInfo (
+ CfgMgrProtocol,
+ GTBlockInfo->GTBlockTimerFrameToken,
+ &GTBlockTimerFrameList,
+ &GTBlockTimerFrameCount
+ );
+ if (EFI_ERROR (Status) ||
+ (GTBlockTimerFrameCount != GTBlockInfo->GTBlockTimerFrameCount)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Failed to get Generic Timer Frames. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Length = sizeof (EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE) +
+ (sizeof (EFI_ACPI_6_3_GTDT_GT_BLOCK_TIMER_STRUCTURE) *
+ GTBlockInfo->GTBlockTimerFrameCount);
+
+ // Check that the length of the GT block does not
+ // exceed MAX_UINT16
+ if (Length > MAX_UINT16) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Too many GT Frames. Count = %d. " \
+ "Maximum supported GT Block size exceeded. " \
+ "Status = %r\n",
+ GTBlockInfo->GTBlockTimerFrameCount,
+ Status
+ ));
+ return Status;
+ }
+
+ GTBlock->Type = EFI_ACPI_6_3_GTDT_GT_BLOCK;
+ GTBlock->Length = (UINT16)Length;
+ GTBlock->Reserved = EFI_ACPI_RESERVED_BYTE;
+ GTBlock->CntCtlBase = GTBlockInfo->GTBlockPhysicalAddress;
+ GTBlock->GTBlockTimerCount = GTBlockInfo->GTBlockTimerFrameCount;
+ GTBlock->GTBlockTimerOffset =
+ sizeof (EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE);
+
+ GtBlockFrame = (EFI_ACPI_6_3_GTDT_GT_BLOCK_TIMER_STRUCTURE*)
+ ((UINT8*)GTBlock + GTBlock->GTBlockTimerOffset);
+
+ // Add GT Block Timer frames
+ Status = AddGTBlockTimerFrames (
+ GtBlockFrame,
+ GTBlockTimerFrameList,
+ GTBlockTimerFrameCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Failed to add Generic Timer Frames. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // Next GTBlock
+ GTBlock = (EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE *)((UINT8*)GTBlock +
+ GTBlock->Length);
+ GTBlockInfo++;
+ }// for
+ return EFI_SUCCESS;
+}
+
+/** Construct the GTDT ACPI table.
+
+ Called by the Dynamic Table Manager, 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.
+
+ @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.
+
+ @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_OUT_OF_RESOURCES Memory allocation failed.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildGtdtTable (
+ 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
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TableSize;
+ UINT32 PlatformTimerCount;
+ UINT32 WatchdogCount;
+ UINT32 BlockTimerCount;
+ CM_ARM_GENERIC_WATCHDOG_INFO * WatchdogInfoList;
+ CM_ARM_GTBLOCK_INFO * GTBlockInfo;
+ EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE * Gtdt;
+ UINT32 Idx;
+ UINT32 GTBlockOffset;
+ UINT32 WatchdogOffset;
+
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (Table != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Requested table revision = %d, is not supported."
+ "Supported table revision: Minimum = %d, Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Table = NULL;
+ Status = GetEArmObjPlatformGTBlockInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GTBlockInfo,
+ &BlockTimerCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Failed to Get Platform GT Block Information." \
+ " Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Status = GetEArmObjPlatformGenericWatchdogInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &WatchdogInfoList,
+ &WatchdogCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Failed to Get Platform Generic Watchdog Information." \
+ " Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "GTDT: BlockTimerCount = %d, WatchdogCount = %d\n",
+ BlockTimerCount,
+ WatchdogCount
+ ));
+
+ // Calculate the GTDT Table Size
+ PlatformTimerCount = 0;
+ TableSize = sizeof (EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE);
+ if (BlockTimerCount != 0) {
+ GTBlockOffset = TableSize;
+ PlatformTimerCount += BlockTimerCount;
+ TableSize += (sizeof (EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE) *
+ BlockTimerCount);
+
+ for (Idx = 0; Idx < BlockTimerCount; Idx++) {
+ if (GTBlockInfo[Idx].GTBlockTimerFrameCount > 8) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "GTDT: GTBockFrameCount cannot be more than 8." \
+ " GTBockFrameCount = %d, Status = %r\n",
+ GTBlockInfo[Idx].GTBlockTimerFrameCount,
+ Status
+ ));
+ goto error_handler;
+ }
+ TableSize += (sizeof (EFI_ACPI_6_3_GTDT_GT_BLOCK_TIMER_STRUCTURE) *
+ GTBlockInfo[Idx].GTBlockTimerFrameCount);
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "GTDT: GTBockOffset = 0x%x, PLATFORM_TIMER_COUNT = %d\n",
+ GTBlockOffset,
+ PlatformTimerCount
+ ));
+ }
+
+ WatchdogOffset = 0;
+ if (WatchdogCount != 0) {
+ WatchdogOffset = TableSize;
+ PlatformTimerCount += WatchdogCount;
+ TableSize += (sizeof (EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_STRUCTURE) *
+ WatchdogCount);
+ DEBUG ((
+ DEBUG_INFO,
+ "GTDT: WatchdogOffset = 0x%x, PLATFORM_TIMER_COUNT = %d\n",
+ WatchdogOffset,
+ PlatformTimerCount
+ ));
+ }
+
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize);
+ if (*Table == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Failed to allocate memory for GTDT Table, Size = %d," \
+ " Status = %r\n",
+ TableSize,
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Gtdt = (EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE*)*Table;
+ DEBUG ((
+ DEBUG_INFO,
+ "GTDT: Gtdt = 0x%p TableSize = 0x%x\n",
+ Gtdt,
+ TableSize
+ ));
+
+ Status = AddAcpiHeader (
+ CfgMgrProtocol,
+ This,
+ &Gtdt->Header,
+ AcpiTableInfo,
+ TableSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Status = AddGenericTimerInfo (
+ CfgMgrProtocol,
+ Gtdt,
+ PlatformTimerCount,
+ AcpiTableInfo->AcpiTableRevision
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Failed to add Generic Timer Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ if (BlockTimerCount != 0) {
+ Status = AddGTBlockList (
+ CfgMgrProtocol,
+ Gtdt,
+ GTBlockOffset,
+ GTBlockInfo,
+ BlockTimerCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: GTDT: Failed to add GT Block timers. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ if (WatchdogCount != 0) {
+ AddGenericWatchdogList (
+ Gtdt,
+ WatchdogOffset,
+ WatchdogInfoList,
+ WatchdogCount
+ );
+ }
+
+ return Status;
+
+error_handler:
+ if (*Table != NULL) {
+ FreePool (*Table);
+ *Table = NULL;
+ }
+ return Status;
+}
+
+/** Free any resources allocated for constructing the GTDT.
+
+ @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 [in, out] Table Pointer to the ACPI Table.
+
+ @retval EFI_SUCCESS The resources were freed successfully.
+ @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
+**/
+STATIC
+EFI_STATUS
+FreeGtdtTableResources (
+ 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
+)
+{
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ if ((Table == NULL) || (*Table == NULL)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: GTDT: Invalid Table Pointer\n"));
+ ASSERT ((Table != NULL) && (*Table != NULL));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (*Table);
+ *Table = NULL;
+ return EFI_SUCCESS;
+}
+
+/** This macro defines the GTDT Table Generator revision.
+*/
+#define GTDT_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the GTDT Table Generator.
+*/
+STATIC
+CONST
+ACPI_TABLE_GENERATOR GtdtGenerator = {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdGtdt),
+ // Generator Description
+ L"ACPI.STD.GTDT.GENERATOR",
+ // ACPI Table Signature
+ EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE_REVISION,
+ // Minimum ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_2_GENERIC_TIMER_DESCRIPTION_TABLE_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ GTDT_GENERATOR_REVISION,
+ // Build Table function
+ BuildGtdtTable,
+ // Free Resource function
+ FreeGtdtTableResources,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+};
+
+/** 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
+AcpiGtdtLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&GtdtGenerator);
+ DEBUG ((DEBUG_INFO, "GTDT: 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
+AcpiGtdtLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&GtdtGenerator);
+ DEBUG ((DEBUG_INFO, "GTDT: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/AcpiIortLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/AcpiIortLibArm.inf
new file mode 100644
index 00000000..f7fa6e2d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/AcpiIortLibArm.inf
@@ -0,0 +1,37 @@
+## @file
+# IORT Table Generator
+#
+# Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = AcpiIortLibArm
+ FILE_GUID = 25682BA8-B41D-4403-B034-253769E0DAD5
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiIortLibConstructor
+ DESTRUCTOR = AcpiIortLibDestructor
+
+[Sources]
+ IortGenerator.c
+ IortGenerator.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ BaseLib
+
+[Pcd]
+
+[Protocols]
+
+[Guids]
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c
new file mode 100644
index 00000000..3dac52c0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c
@@ -0,0 +1,2229 @@
+/** @file
+ IORT Table Generator
+
+ Copyright (c) 2017 - 2020, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - IO Remapping Table, Platform Design Document,
+ Document number: ARM DEN 0049D, Issue D, March 2018
+
+**/
+
+#include <IndustryStandard/IoRemappingTable.h>
+#include <Library/AcpiLib.h>
+#include <Library/BaseLib.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/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+#include "IortGenerator.h"
+
+/** ARM standard IORT Generator
+
+Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArmObjItsGroup
+ - EArmObjNamedComponent
+ - EArmObjRootComplex
+ - EArmObjSmmuV1SmmuV2
+ - EArmObjSmmuV3
+ - EArmObjPmcg
+ - EArmObjGicItsIdentifierArray
+ - EArmObjIdMappingArray
+ - EArmObjGicItsIdentifierArray
+*/
+
+/** This macro expands to a function that retrieves the ITS
+ Group node information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjItsGroup,
+ CM_ARM_ITS_GROUP_NODE
+ );
+
+/** This macro expands to a function that retrieves the
+ Named Component node information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjNamedComponent,
+ CM_ARM_NAMED_COMPONENT_NODE
+ );
+
+/** This macro expands to a function that retrieves the
+ Root Complex node information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjRootComplex,
+ CM_ARM_ROOT_COMPLEX_NODE
+ );
+
+/** This macro expands to a function that retrieves the
+ SMMU v1/v2 node information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjSmmuV1SmmuV2,
+ CM_ARM_SMMUV1_SMMUV2_NODE
+ );
+
+/** This macro expands to a function that retrieves the
+ SMMU v3 node information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjSmmuV3,
+ CM_ARM_SMMUV3_NODE
+ );
+
+/** This macro expands to a function that retrieves the
+ PMCG node information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjPmcg,
+ CM_ARM_PMCG_NODE
+ );
+
+/** This macro expands to a function that retrieves the
+ ITS Identifier Array information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGicItsIdentifierArray,
+ CM_ARM_ITS_IDENTIFIER
+ );
+
+/** This macro expands to a function that retrieves the
+ Id Mapping Array information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjIdMappingArray,
+ CM_ARM_ID_MAPPING
+ );
+
+/** This macro expands to a function that retrieves the
+ SMMU Interrupt Array information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjSmmuInterruptArray,
+ CM_ARM_SMMU_INTERRUPT
+ );
+
+/** Returns the size of the ITS Group node.
+
+ @param [in] Node Pointer to ITS Group node.
+
+ @retval Size of the ITS Group Node.
+**/
+STATIC
+UINT32
+GetItsGroupNodeSize (
+ IN CONST CM_ARM_ITS_GROUP_NODE * Node
+ )
+{
+ ASSERT (Node != NULL);
+
+ /* Size of ITS Group Node +
+ Size of ITS Identifier array
+ */
+ return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE) +
+ (Node->ItsIdCount * sizeof (UINT32)));
+}
+
+/** Returns the total size required for the ITS Group nodes and
+ updates the Node Indexer.
+
+ This function calculates the size required for the node group
+ and also populates the Node Indexer array with offsets for the
+ individual nodes.
+
+ @param [in] NodeStartOffset Offset from the start of the
+ IORT where this node group starts.
+ @param [in] NodeList Pointer to ITS Group node list.
+ @param [in] NodeCount Count of the ITS Group nodes.
+ @param [in, out] NodeIndexer Pointer to the next Node Indexer.
+
+ @retval Total size of the ITS Group Nodes.
+**/
+STATIC
+UINT64
+GetSizeofItsGroupNodes (
+ IN CONST UINT32 NodeStartOffset,
+ IN CONST CM_ARM_ITS_GROUP_NODE * NodeList,
+ IN UINT32 NodeCount,
+ IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer
+ )
+{
+ UINT64 Size;
+
+ ASSERT (NodeList != NULL);
+
+ Size = 0;
+ while (NodeCount-- != 0) {
+ (*NodeIndexer)->Token = NodeList->Token;
+ (*NodeIndexer)->Object = (VOID*)NodeList;
+ (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
+ *NodeIndexer,
+ (*NodeIndexer)->Token,
+ (*NodeIndexer)->Object,
+ (*NodeIndexer)->Offset
+ ));
+
+ Size += GetItsGroupNodeSize (NodeList);
+ (*NodeIndexer)++;
+ NodeList++;
+ }
+ return Size;
+}
+
+/** Returns the size of the Named Component node.
+
+ @param [in] Node Pointer to Named Component node.
+
+ @retval Size of the Named Component node.
+**/
+STATIC
+UINT32
+GetNamedComponentNodeSize (
+ IN CONST CM_ARM_NAMED_COMPONENT_NODE * Node
+ )
+{
+ ASSERT (Node != NULL);
+
+ /* Size of Named Component node +
+ Size of ID mapping array +
+ Size of ASCII string + 'padding to 32-bit word aligned'.
+ */
+ return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE) +
+ (Node->IdMappingCount *
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)) +
+ ALIGN_VALUE (AsciiStrSize (Node->ObjectName), 4));
+}
+
+/** Returns the total size required for the Named Component nodes and
+ updates the Node Indexer.
+
+ This function calculates the size required for the node group
+ and also populates the Node Indexer array with offsets for the
+ individual nodes.
+
+ @param [in] NodeStartOffset Offset from the start of the
+ IORT where this node group starts.
+ @param [in] NodeList Pointer to Named Component node list.
+ @param [in] NodeCount Count of the Named Component nodes.
+ @param [in, out] NodeIndexer Pointer to the next Node Indexer.
+
+ @retval Total size of the Named Component nodes.
+**/
+STATIC
+UINT64
+GetSizeofNamedComponentNodes (
+ IN CONST UINT32 NodeStartOffset,
+ IN CONST CM_ARM_NAMED_COMPONENT_NODE * NodeList,
+ IN UINT32 NodeCount,
+ IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer
+ )
+{
+ UINT64 Size;
+
+ ASSERT (NodeList != NULL);
+
+ Size = 0;
+ while (NodeCount-- != 0) {
+ (*NodeIndexer)->Token = NodeList->Token;
+ (*NodeIndexer)->Object = (VOID*)NodeList;
+ (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
+ *NodeIndexer,
+ (*NodeIndexer)->Token,
+ (*NodeIndexer)->Object,
+ (*NodeIndexer)->Offset
+ ));
+
+ Size += GetNamedComponentNodeSize (NodeList);
+ (*NodeIndexer)++;
+ NodeList++;
+ }
+
+ return Size;
+}
+
+/** Returns the size of the Root Complex node.
+
+ @param [in] Node Pointer to Root Complex node.
+
+ @retval Size of the Root Complex node.
+**/
+STATIC
+UINT32
+GetRootComplexNodeSize (
+ IN CONST CM_ARM_ROOT_COMPLEX_NODE * Node
+ )
+{
+ ASSERT (Node != NULL);
+
+ /* Size of Root Complex node +
+ Size of ID mapping array
+ */
+ return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE) +
+ (Node->IdMappingCount *
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)));
+}
+
+/** Returns the total size required for the Root Complex nodes and
+ updates the Node Indexer.
+
+ This function calculates the size required for the node group
+ and also populates the Node Indexer array with offsets for the
+ individual nodes.
+
+ @param [in] NodeStartOffset Offset from the start of the
+ IORT where this node group starts.
+ @param [in] NodeList Pointer to Root Complex node list.
+ @param [in] NodeCount Count of the Root Complex nodes.
+ @param [in, out] NodeIndexer Pointer to the next Node Indexer.
+
+ @retval Total size of the Root Complex nodes.
+**/
+STATIC
+UINT64
+GetSizeofRootComplexNodes (
+ IN CONST UINT32 NodeStartOffset,
+ IN CONST CM_ARM_ROOT_COMPLEX_NODE * NodeList,
+ IN UINT32 NodeCount,
+ IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer
+ )
+{
+ UINT64 Size;
+
+ ASSERT (NodeList != NULL);
+
+ Size = 0;
+ while (NodeCount-- != 0) {
+ (*NodeIndexer)->Token = NodeList->Token;
+ (*NodeIndexer)->Object = (VOID*)NodeList;
+ (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
+ *NodeIndexer,
+ (*NodeIndexer)->Token,
+ (*NodeIndexer)->Object,
+ (*NodeIndexer)->Offset
+ ));
+
+ Size += GetRootComplexNodeSize (NodeList);
+ (*NodeIndexer)++;
+ NodeList++;
+ }
+
+ return Size;
+}
+
+/** Returns the size of the SMMUv1/SMMUv2 node.
+
+ @param [in] Node Pointer to SMMUv1/SMMUv2 node list.
+
+ @retval Size of the SMMUv1/SMMUv2 node.
+**/
+STATIC
+UINT32
+GetSmmuV1V2NodeSize (
+ IN CONST CM_ARM_SMMUV1_SMMUV2_NODE * Node
+ )
+{
+ ASSERT (Node != NULL);
+
+ /* Size of SMMU v1/SMMU v2 node +
+ Size of ID mapping array +
+ Size of context interrupt array +
+ Size of PMU interrupt array
+ */
+ return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE) +
+ (Node->IdMappingCount *
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)) +
+ (Node->ContextInterruptCount *
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT)) +
+ (Node->PmuInterruptCount *
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT)));
+}
+
+/** Returns the total size required for the SMMUv1/SMMUv2 nodes and
+ updates the Node Indexer.
+
+ This function calculates the size required for the node group
+ and also populates the Node Indexer array with offsets for the
+ individual nodes.
+
+ @param [in] NodeStartOffset Offset from the start of the
+ IORT where this node group starts.
+ @param [in] NodeList Pointer to SMMUv1/SMMUv2 node list.
+ @param [in] NodeCount Count of the SMMUv1/SMMUv2 nodes.
+ @param [in, out] NodeIndexer Pointer to the next Node Indexer.
+
+ @retval Total size of the SMMUv1/SMMUv2 nodes.
+**/
+STATIC
+UINT64
+GetSizeofSmmuV1V2Nodes (
+ IN CONST UINT32 NodeStartOffset,
+ IN CONST CM_ARM_SMMUV1_SMMUV2_NODE * NodeList,
+ IN UINT32 NodeCount,
+ IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer
+ )
+{
+ UINT64 Size;
+
+ ASSERT (NodeList != NULL);
+
+ Size = 0;
+ while (NodeCount-- != 0) {
+ (*NodeIndexer)->Token = NodeList->Token;
+ (*NodeIndexer)->Object = (VOID*)NodeList;
+ (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
+ *NodeIndexer,
+ (*NodeIndexer)->Token,
+ (*NodeIndexer)->Object,
+ (*NodeIndexer)->Offset
+ ));
+
+ Size += GetSmmuV1V2NodeSize (NodeList);
+ (*NodeIndexer)++;
+ NodeList++;
+ }
+ return Size;
+}
+
+/** Returns the size of the SMMUv3 node.
+
+ @param [in] Node Pointer to SMMUv3 node list.
+
+ @retval Total size of the SMMUv3 nodes.
+**/
+STATIC
+UINT32
+GetSmmuV3NodeSize (
+ IN CONST CM_ARM_SMMUV3_NODE * Node
+ )
+{
+ ASSERT (Node != NULL);
+
+ /* Size of SMMU v1/SMMU v2 node +
+ Size of ID mapping array
+ */
+ return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE) +
+ (Node->IdMappingCount *
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)));
+}
+
+/** Returns the total size required for the SMMUv3 nodes and
+ updates the Node Indexer.
+
+ This function calculates the size required for the node group
+ and also populates the Node Indexer array with offsets for the
+ individual nodes.
+
+ @param [in] NodeStartOffset Offset from the start of the
+ IORT where this node group starts.
+ @param [in] NodeList Pointer to SMMUv3 node list.
+ @param [in] NodeCount Count of the SMMUv3 nodes.
+ @param [in, out] NodeIndexer Pointer to the next Node Indexer.
+
+ @retval Total size of the SMMUv3 nodes.
+**/
+STATIC
+UINT64
+GetSizeofSmmuV3Nodes (
+ IN CONST UINT32 NodeStartOffset,
+ IN CONST CM_ARM_SMMUV3_NODE * NodeList,
+ IN UINT32 NodeCount,
+ IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer
+ )
+{
+ UINT64 Size;
+
+ ASSERT (NodeList != NULL);
+
+ Size = 0;
+ while (NodeCount-- != 0) {
+ (*NodeIndexer)->Token = NodeList->Token;
+ (*NodeIndexer)->Object = (VOID*)NodeList;
+ (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
+ *NodeIndexer,
+ (*NodeIndexer)->Token,
+ (*NodeIndexer)->Object,
+ (*NodeIndexer)->Offset
+ ));
+
+ Size += GetSmmuV3NodeSize (NodeList);
+ (*NodeIndexer)++;
+ NodeList++;
+ }
+ return Size;
+}
+
+/** Returns the size of the PMCG node.
+
+ @param [in] Node Pointer to PMCG node.
+
+ @retval Size of the PMCG node.
+**/
+STATIC
+UINT32
+GetPmcgNodeSize (
+ IN CONST CM_ARM_PMCG_NODE * Node
+ )
+{
+ ASSERT (Node != NULL);
+
+ /* Size of PMCG node +
+ Size of ID mapping array
+ */
+ return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE) +
+ (Node->IdMappingCount *
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)));
+}
+
+/** Returns the total size required for the PMCG nodes and
+ updates the Node Indexer.
+
+ This function calculates the size required for the node group
+ and also populates the Node Indexer array with offsets for the
+ individual nodes.
+
+ @param [in] NodeStartOffset Offset from the start of the
+ IORT where this node group starts.
+ @param [in] NodeList Pointer to PMCG node list.
+ @param [in] NodeCount Count of the PMCG nodes.
+ @param [in, out] NodeIndexer Pointer to the next Node Indexer.
+
+ @retval Total size of the PMCG nodes.
+**/
+STATIC
+UINT64
+GetSizeofPmcgNodes (
+ IN CONST UINT32 NodeStartOffset,
+ IN CONST CM_ARM_PMCG_NODE * NodeList,
+ IN UINT32 NodeCount,
+ IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer
+ )
+{
+ UINT64 Size;
+
+ ASSERT (NodeList != NULL);
+
+ Size = 0;
+ while (NodeCount-- != 0) {
+ (*NodeIndexer)->Token = NodeList->Token;
+ (*NodeIndexer)->Object = (VOID*)NodeList;
+ (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
+ *NodeIndexer,
+ (*NodeIndexer)->Token,
+ (*NodeIndexer)->Object,
+ (*NodeIndexer)->Offset
+ ));
+
+ Size += GetPmcgNodeSize (NodeList);
+ (*NodeIndexer)++;
+ NodeList++;
+ }
+ return Size;
+}
+
+/** Returns the offset of the Node referenced by the Token.
+
+ @param [in] NodeIndexer Pointer to node indexer array.
+ @param [in] NodeCount Count of the nodes.
+ @param [in] Token Reference token for the node.
+ @param [out] NodeOffset Offset of the node from the
+ start of the IORT table.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_NOT_FOUND No matching token reference
+ found in node indexer array.
+**/
+STATIC
+EFI_STATUS
+GetNodeOffsetReferencedByToken (
+ IN IORT_NODE_INDEXER * NodeIndexer,
+ IN UINT32 NodeCount,
+ IN CM_OBJECT_TOKEN Token,
+ OUT UINT32 * NodeOffset
+ )
+{
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Node Indexer: Search Token = %p\n",
+ Token
+ ));
+ while (NodeCount-- != 0) {
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Node Indexer: NodeIndexer->Token = %p, Offset = %d\n",
+ NodeIndexer->Token,
+ NodeIndexer->Offset
+ ));
+ if (NodeIndexer->Token == Token) {
+ *NodeOffset = NodeIndexer->Offset;
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Node Indexer: Token = %p, Found\n",
+ Token
+ ));
+ return EFI_SUCCESS;
+ }
+ NodeIndexer++;
+ }
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Node Indexer: Token = %p, Not Found\n",
+ Token
+ ));
+ return EFI_NOT_FOUND;
+}
+
+/** Update the Id Mapping Array.
+
+ This function retrieves the Id Mapping Array object referenced by the
+ IdMappingToken and updates the IdMapArray.
+
+ @param [in] This Pointer to the table Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] IdMapArray Pointer to an array of Id Mappings.
+ @param [in] IdCount Number of Id Mappings.
+ @param [in] IdMappingToken Reference Token for retrieving the
+ Id Mapping Array object.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddIdMappingArray (
+ IN CONST ACPI_TABLE_GENERATOR * CONST This,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray,
+ IN UINT32 IdCount,
+ IN CONST CM_OBJECT_TOKEN IdMappingToken
+ )
+{
+ EFI_STATUS Status;
+ CM_ARM_ID_MAPPING * IdMappings;
+ UINT32 IdMappingCount;
+ ACPI_IORT_GENERATOR * Generator;
+
+ ASSERT (IdMapArray != NULL);
+
+ Generator = (ACPI_IORT_GENERATOR*)This;
+
+ // Get the Id Mapping Array
+ Status = GetEArmObjIdMappingArray (
+ CfgMgrProtocol,
+ IdMappingToken,
+ &IdMappings,
+ &IdMappingCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get Id Mapping array. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ if (IdMappingCount < IdCount) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get the required number of Id Mappings.\n"
+ ));
+ return EFI_NOT_FOUND;
+ }
+
+ // Populate the Id Mapping array
+ while (IdCount-- != 0) {
+ Status = GetNodeOffsetReferencedByToken (
+ Generator->NodeIndexer,
+ Generator->IortNodeCount,
+ IdMappings->OutputReferenceToken,
+ &IdMapArray->OutputReference
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get Output Reference for ITS Identifier array."
+ "Reference Token = %p"
+ " Status = %r\n",
+ IdMappings->OutputReferenceToken,
+ Status
+ ));
+ return Status;
+ }
+
+ IdMapArray->InputBase = IdMappings->InputBase;
+ IdMapArray->NumIds = IdMappings->NumIds;
+ IdMapArray->OutputBase = IdMappings->OutputBase;
+ IdMapArray->Flags = IdMappings->Flags;
+
+ IdMapArray++;
+ IdMappings++;
+ } // Id Mapping array
+
+ return EFI_SUCCESS;
+}
+
+/** Update the ITS Group Node Information.
+
+ @param [in] This Pointer to the table Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Iort Pointer to IORT table structure.
+ @param [in] NodesStartOffset Offset for the start of the ITS Group
+ Nodes.
+ @param [in] NodeList Pointer to an array of ITS Group Node
+ Objects.
+ @param [in] NodeCount Number of ITS Group Node Objects.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddItsGroupNodes (
+ IN CONST ACPI_TABLE_GENERATOR * CONST This,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort,
+ IN CONST UINT32 NodesStartOffset,
+ IN CONST CM_ARM_ITS_GROUP_NODE * NodeList,
+ IN UINT32 NodeCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE * ItsGroupNode;
+ UINT32 * ItsIds;
+ CM_ARM_ITS_IDENTIFIER * ItsIdentifier;
+ UINT32 ItsIdentifierCount;
+ UINT32 IdIndex;
+ UINT64 NodeLength;
+
+ ASSERT (Iort != NULL);
+
+ ItsGroupNode = (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE*)((UINT8*)Iort +
+ NodesStartOffset);
+
+ while (NodeCount-- != 0) {
+ NodeLength = GetItsGroupNodeSize (NodeList);
+ if (NodeLength > MAX_UINT16) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: ITS Id Array Node length 0x%lx > MAX_UINT16."
+ " Status = %r\n",
+ NodeLength,
+ Status
+ ));
+ return Status;
+ }
+
+ // Populate the node header
+ ItsGroupNode->Node.Type = EFI_ACPI_IORT_TYPE_ITS_GROUP;
+ ItsGroupNode->Node.Length = (UINT16)NodeLength;
+ ItsGroupNode->Node.Revision = 0;
+ ItsGroupNode->Node.Reserved = EFI_ACPI_RESERVED_DWORD;
+ ItsGroupNode->Node.NumIdMappings = 0;
+ ItsGroupNode->Node.IdReference = 0;
+
+ // IORT specific data
+ ItsGroupNode->NumItsIdentifiers = NodeList->ItsIdCount;
+ ItsIds = (UINT32*)((UINT8*)ItsGroupNode +
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE));
+
+ Status = GetEArmObjGicItsIdentifierArray (
+ CfgMgrProtocol,
+ NodeList->ItsIdToken,
+ &ItsIdentifier,
+ &ItsIdentifierCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get ITS Identifier array. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ if (ItsIdentifierCount < ItsGroupNode->NumItsIdentifiers) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get the required number of ITS Identifiers.\n"
+ ));
+ return EFI_NOT_FOUND;
+ }
+
+ // Populate the ITS identifier array
+ for (IdIndex = 0; IdIndex < ItsGroupNode->NumItsIdentifiers; IdIndex++) {
+ ItsIds[IdIndex] = ItsIdentifier[IdIndex].ItsId;
+ } // ITS identifier array
+
+ // Next IORT Group Node
+ ItsGroupNode = (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE*)((UINT8*)ItsGroupNode +
+ ItsGroupNode->Node.Length);
+ NodeList++;
+ } // IORT Group Node
+
+ return EFI_SUCCESS;
+}
+
+/** Update the Named Component Node Information.
+
+ This function updates the Named Component node information in the IORT
+ table.
+
+ @param [in] This Pointer to the table Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Iort Pointer to IORT table structure.
+ @param [in] NodesStartOffset Offset for the start of the Named
+ Component Nodes.
+ @param [in] NodeList Pointer to an array of Named Component
+ Node Objects.
+ @param [in] NodeCount Number of Named Component Node Objects.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddNamedComponentNodes (
+ IN CONST ACPI_TABLE_GENERATOR * CONST This,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort,
+ IN CONST UINT32 NodesStartOffset,
+ IN CONST CM_ARM_NAMED_COMPONENT_NODE * NodeList,
+ IN UINT32 NodeCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE * NcNode;
+ EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray;
+ CHAR8 * ObjectName;
+ UINTN ObjectNameLength;
+ UINT64 NodeLength;
+
+ ASSERT (Iort != NULL);
+
+ NcNode = (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE*)((UINT8*)Iort +
+ NodesStartOffset);
+
+ while (NodeCount-- != 0) {
+ NodeLength = GetNamedComponentNodeSize (NodeList);
+ if (NodeLength > MAX_UINT16) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Named Component Node length 0x%lx > MAX_UINT16."
+ " Status = %r\n",
+ NodeLength,
+ Status
+ ));
+ return Status;
+ }
+
+ // Populate the node header
+ NcNode->Node.Type = EFI_ACPI_IORT_TYPE_NAMED_COMP;
+ NcNode->Node.Length = (UINT16)NodeLength;
+ NcNode->Node.Revision = 2;
+ NcNode->Node.Reserved = EFI_ACPI_RESERVED_DWORD;
+ NcNode->Node.NumIdMappings = NodeList->IdMappingCount;
+
+ ObjectNameLength = AsciiStrLen (NodeList->ObjectName) + 1;
+ NcNode->Node.IdReference =
+ (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE) +
+ (ALIGN_VALUE (ObjectNameLength, 4)));
+
+ // Named Component specific data
+ NcNode->Flags = NodeList->Flags;
+ NcNode->CacheCoherent = NodeList->CacheCoherent;
+ NcNode->AllocationHints = NodeList->AllocationHints;
+ NcNode->Reserved = EFI_ACPI_RESERVED_WORD;
+ NcNode->MemoryAccessFlags = NodeList->MemoryAccessFlags;
+ NcNode->AddressSizeLimit = NodeList->AddressSizeLimit;
+
+ // Copy the object name
+ ObjectName = (CHAR8*)((UINT8*)NcNode +
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE));
+ Status = AsciiStrCpyS (
+ ObjectName,
+ ObjectNameLength,
+ NodeList->ObjectName
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to copy Object Name. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ if ((NodeList->IdMappingCount > 0) &&
+ (NodeList->IdMappingToken != CM_NULL_TOKEN)) {
+ // Ids for Named Component
+ IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE*)((UINT8*)NcNode +
+ NcNode->Node.IdReference);
+
+ Status = AddIdMappingArray (
+ This,
+ CfgMgrProtocol,
+ IdMapArray,
+ NodeList->IdMappingCount,
+ NodeList->IdMappingToken
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+ }
+
+ // Next Named Component Node
+ NcNode = (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE*)((UINT8*)NcNode +
+ NcNode->Node.Length);
+ NodeList++;
+ } // Named Component Node
+
+ return EFI_SUCCESS;
+}
+
+/** Update the Root Complex Node Information.
+
+ This function updates the Root Complex node information in the IORT table.
+
+ @param [in] This Pointer to the table Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Iort Pointer to IORT table structure.
+ @param [in] NodesStartOffset Offset for the start of the Root Complex
+ Nodes.
+ @param [in] NodeList Pointer to an array of Root Complex Node
+ Objects.
+ @param [in] NodeCount Number of Root Complex Node Objects.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddRootComplexNodes (
+ IN CONST ACPI_TABLE_GENERATOR * CONST This,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort,
+ IN CONST UINT32 NodesStartOffset,
+ IN CONST CM_ARM_ROOT_COMPLEX_NODE * NodeList,
+ IN UINT32 NodeCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_0_IO_REMAPPING_RC_NODE * RcNode;
+ EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray;
+ UINT64 NodeLength;
+
+ ASSERT (Iort != NULL);
+
+ RcNode = (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE*)((UINT8*)Iort +
+ NodesStartOffset);
+
+ while (NodeCount-- != 0) {
+ NodeLength = GetRootComplexNodeSize (NodeList);
+ if (NodeLength > MAX_UINT16) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Root Complex Node length 0x%lx > MAX_UINT16."
+ " Status = %r\n",
+ NodeLength,
+ Status
+ ));
+ return Status;
+ }
+
+ // Populate the node header
+ RcNode->Node.Type = EFI_ACPI_IORT_TYPE_ROOT_COMPLEX;
+ RcNode->Node.Length = (UINT16)NodeLength;
+ RcNode->Node.Revision = 1;
+ RcNode->Node.Reserved = EFI_ACPI_RESERVED_DWORD;
+ RcNode->Node.NumIdMappings = NodeList->IdMappingCount;
+ RcNode->Node.IdReference = sizeof (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE);
+
+ // Root Complex specific data
+ RcNode->CacheCoherent = NodeList->CacheCoherent;
+ RcNode->AllocationHints = NodeList->AllocationHints;
+ RcNode->Reserved = EFI_ACPI_RESERVED_WORD;
+ RcNode->MemoryAccessFlags = NodeList->MemoryAccessFlags;
+ RcNode->AtsAttribute = NodeList->AtsAttribute;
+ RcNode->PciSegmentNumber = NodeList->PciSegmentNumber;
+ RcNode->MemoryAddressSize = NodeList->MemoryAddressSize;
+ RcNode->Reserved1[0] = EFI_ACPI_RESERVED_BYTE;
+ RcNode->Reserved1[1] = EFI_ACPI_RESERVED_BYTE;
+ RcNode->Reserved1[2] = EFI_ACPI_RESERVED_BYTE;
+
+ if ((NodeList->IdMappingCount > 0) &&
+ (NodeList->IdMappingToken != CM_NULL_TOKEN)) {
+ // Ids for Root Complex
+ IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE*)((UINT8*)RcNode +
+ RcNode->Node.IdReference);
+ Status = AddIdMappingArray (
+ This,
+ CfgMgrProtocol,
+ IdMapArray,
+ NodeList->IdMappingCount,
+ NodeList->IdMappingToken
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+ }
+
+ // Next Root Complex Node
+ RcNode = (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE*)((UINT8*)RcNode +
+ RcNode->Node.Length);
+ NodeList++;
+ } // Root Complex Node
+
+ return EFI_SUCCESS;
+}
+
+/** Update the SMMU Interrupt Array.
+
+ This function retrieves the InterruptArray object referenced by the
+ InterruptToken and updates the SMMU InterruptArray.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in, out] InterruptArray Pointer to an array of Interrupts.
+ @param [in] InterruptCount Number of entries in the InterruptArray.
+ @param [in] InterruptToken Reference Token for retrieving the SMMU
+ InterruptArray object.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddSmmuInterruptArray (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN OUT EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT * InterruptArray,
+ IN UINT32 InterruptCount,
+ IN CONST CM_OBJECT_TOKEN InterruptToken
+ )
+{
+ EFI_STATUS Status;
+ CM_ARM_SMMU_INTERRUPT * SmmuInterrupt;
+ UINT32 SmmuInterruptCount;
+
+ ASSERT (InterruptArray != NULL);
+
+ // Get the SMMU Interrupt Array
+ Status = GetEArmObjSmmuInterruptArray (
+ CfgMgrProtocol,
+ InterruptToken,
+ &SmmuInterrupt,
+ &SmmuInterruptCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get SMMU Interrupt array. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ if (SmmuInterruptCount < InterruptCount) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get the required number of SMMU Interrupts.\n"
+ ));
+ return EFI_NOT_FOUND;
+ }
+
+ // Populate the Id Mapping array
+ while (InterruptCount-- != 0) {
+ InterruptArray->Interrupt = SmmuInterrupt->Interrupt;
+ InterruptArray->InterruptFlags = SmmuInterrupt->Flags;
+ InterruptArray++;
+ SmmuInterrupt++;
+ } // Id Mapping array
+
+ return EFI_SUCCESS;
+}
+
+/** Update the SMMU v1/v2 Node Information.
+
+ @param [in] This Pointer to the table Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Iort Pointer to IORT table structure.
+ @param [in] NodesStartOffset Offset for the start of the SMMU v1/v2
+ Nodes.
+ @param [in] NodeList Pointer to an array of SMMU v1/v2 Node
+ Objects.
+ @param [in] NodeCount Number of SMMU v1/v2 Node Objects.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddSmmuV1V2Nodes (
+ IN CONST ACPI_TABLE_GENERATOR * CONST This,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort,
+ IN CONST UINT32 NodesStartOffset,
+ IN CONST CM_ARM_SMMUV1_SMMUV2_NODE * NodeList,
+ IN UINT32 NodeCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE * SmmuNode;
+ EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray;
+
+ EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT * ContextInterruptArray;
+ EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT * PmuInterruptArray;
+ UINT64 NodeLength;
+
+ ASSERT (Iort != NULL);
+
+ SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE*)((UINT8*)Iort +
+ NodesStartOffset);
+
+ while (NodeCount-- != 0) {
+ NodeLength = GetSmmuV1V2NodeSize (NodeList);
+ if (NodeLength > MAX_UINT16) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: SMMU V1/V2 Node length 0x%lx > MAX_UINT16. Status = %r\n",
+ NodeLength,
+ Status
+ ));
+ return Status;
+ }
+
+ // Populate the node header
+ SmmuNode->Node.Type = EFI_ACPI_IORT_TYPE_SMMUv1v2;
+ SmmuNode->Node.Length = (UINT16)NodeLength;
+ SmmuNode->Node.Revision = 0;
+ SmmuNode->Node.Reserved = EFI_ACPI_RESERVED_DWORD;
+ SmmuNode->Node.NumIdMappings = NodeList->IdMappingCount;
+ SmmuNode->Node.IdReference = sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE) +
+ (NodeList->ContextInterruptCount *
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT)) +
+ (NodeList->PmuInterruptCount *
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT));
+
+ // SMMU v1/v2 specific data
+ SmmuNode->Base = NodeList->BaseAddress;
+ SmmuNode->Span = NodeList->Span;
+ SmmuNode->Model = NodeList->Model;
+ SmmuNode->Flags = NodeList->Flags;
+
+ // Reference to Global Interrupt Array
+ SmmuNode->GlobalInterruptArrayRef =
+ OFFSET_OF (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE, SMMU_NSgIrpt);
+
+ // Context Interrupt
+ SmmuNode->NumContextInterrupts = NodeList->ContextInterruptCount;
+ SmmuNode->ContextInterruptArrayRef =
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE);
+ ContextInterruptArray =
+ (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT*)((UINT8*)SmmuNode +
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE));
+
+ // PMU Interrupt
+ SmmuNode->NumPmuInterrupts = NodeList->PmuInterruptCount;
+ SmmuNode->PmuInterruptArrayRef = SmmuNode->ContextInterruptArrayRef +
+ (NodeList->ContextInterruptCount *
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT));
+ PmuInterruptArray =
+ (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT*)((UINT8*)SmmuNode +
+ SmmuNode->PmuInterruptArrayRef);
+
+ SmmuNode->SMMU_NSgIrpt = NodeList->SMMU_NSgIrpt;
+ SmmuNode->SMMU_NSgIrptFlags = NodeList->SMMU_NSgIrptFlags;
+ SmmuNode->SMMU_NSgCfgIrpt = NodeList->SMMU_NSgCfgIrpt;
+ SmmuNode->SMMU_NSgCfgIrptFlags = NodeList->SMMU_NSgCfgIrptFlags;
+
+ // Add Context Interrupt Array
+ Status = AddSmmuInterruptArray (
+ CfgMgrProtocol,
+ ContextInterruptArray,
+ SmmuNode->NumContextInterrupts,
+ NodeList->ContextInterruptToken
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to Context Interrupt Array. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // Add PMU Interrupt Array
+ if ((SmmuNode->NumPmuInterrupts > 0) &&
+ (NodeList->PmuInterruptToken != CM_NULL_TOKEN)) {
+ Status = AddSmmuInterruptArray (
+ CfgMgrProtocol,
+ PmuInterruptArray,
+ SmmuNode->NumPmuInterrupts,
+ NodeList->PmuInterruptToken
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to PMU Interrupt Array. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+ }
+
+ if ((NodeList->IdMappingCount > 0) &&
+ (NodeList->IdMappingToken != CM_NULL_TOKEN)) {
+ // Ids for SMMU v1/v2 Node
+ IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE*)((UINT8*)SmmuNode +
+ SmmuNode->Node.IdReference);
+ Status = AddIdMappingArray (
+ This,
+ CfgMgrProtocol,
+ IdMapArray,
+ NodeList->IdMappingCount,
+ NodeList->IdMappingToken
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+ }
+ // Next SMMU v1/v2 Node
+ SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE*)((UINT8*)SmmuNode +
+ SmmuNode->Node.Length);
+ NodeList++;
+ } // SMMU v1/v2 Node
+
+ return EFI_SUCCESS;
+}
+
+/** Update the SMMUv3 Node Information.
+
+ This function updates the SMMUv3 node information in the IORT table.
+
+ @param [in] This Pointer to the table Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Iort Pointer to IORT table structure.
+ @param [in] NodesStartOffset Offset for the start of the SMMUv3 Nodes.
+ @param [in] NodeList Pointer to an array of SMMUv3 Node Objects.
+ @param [in] NodeCount Number of SMMUv3 Node Objects.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddSmmuV3Nodes (
+ IN CONST ACPI_TABLE_GENERATOR * CONST This,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort,
+ IN CONST UINT32 NodesStartOffset,
+ IN CONST CM_ARM_SMMUV3_NODE * NodeList,
+ IN UINT32 NodeCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE * SmmuV3Node;
+ EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray;
+ UINT64 NodeLength;
+
+ ASSERT (Iort != NULL);
+
+ SmmuV3Node = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE*)((UINT8*)Iort +
+ NodesStartOffset);
+
+ while (NodeCount-- != 0) {
+ NodeLength = GetSmmuV3NodeSize (NodeList);
+ if (NodeLength > MAX_UINT16) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: SMMU V3 Node length 0x%lx > MAX_UINT16. Status = %r\n",
+ NodeLength,
+ Status
+ ));
+ return Status;
+ }
+
+ // Populate the node header
+ SmmuV3Node->Node.Type = EFI_ACPI_IORT_TYPE_SMMUv3;
+ SmmuV3Node->Node.Length = (UINT16)NodeLength;
+ SmmuV3Node->Node.Revision = 2;
+ SmmuV3Node->Node.Reserved = EFI_ACPI_RESERVED_DWORD;
+ SmmuV3Node->Node.NumIdMappings = NodeList->IdMappingCount;
+ SmmuV3Node->Node.IdReference =
+ sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE);
+
+ // SMMUv3 specific data
+ SmmuV3Node->Base = NodeList->BaseAddress;
+ SmmuV3Node->Flags = NodeList->Flags;
+ SmmuV3Node->Reserved = EFI_ACPI_RESERVED_WORD;
+ SmmuV3Node->VatosAddress = NodeList->VatosAddress;
+ SmmuV3Node->Model = NodeList->Model;
+ SmmuV3Node->Event = NodeList->EventInterrupt;
+ SmmuV3Node->Pri = NodeList->PriInterrupt;
+ SmmuV3Node->Gerr = NodeList->GerrInterrupt;
+ SmmuV3Node->Sync = NodeList->SyncInterrupt;
+
+ if ((SmmuV3Node->Flags & EFI_ACPI_IORT_SMMUv3_FLAG_PROXIMITY_DOMAIN) != 0) {
+ // The Proximity Domain Valid flag is set to 1
+ SmmuV3Node->ProximityDomain = NodeList->ProximityDomain;
+ } else {
+ SmmuV3Node->ProximityDomain = 0;
+ }
+
+ if ((SmmuV3Node->Event != 0) && (SmmuV3Node->Pri != 0) &&
+ (SmmuV3Node->Gerr != 0) && (SmmuV3Node->Sync != 0)) {
+ // If all the SMMU control interrupts are GSIV based,
+ // the DeviceID mapping index field is ignored.
+ SmmuV3Node->DeviceIdMappingIndex = 0;
+ } else {
+ SmmuV3Node->DeviceIdMappingIndex = NodeList->DeviceIdMappingIndex;
+ }
+
+ if ((NodeList->IdMappingCount > 0) &&
+ (NodeList->IdMappingToken != CM_NULL_TOKEN)) {
+ // Ids for SMMUv3 node
+ IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE*)((UINT8*)SmmuV3Node +
+ SmmuV3Node->Node.IdReference);
+ Status = AddIdMappingArray (
+ This,
+ CfgMgrProtocol,
+ IdMapArray,
+ NodeList->IdMappingCount,
+ NodeList->IdMappingToken
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+ }
+
+ // Next SMMUv3 Node
+ SmmuV3Node = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE*)((UINT8*)SmmuV3Node +
+ SmmuV3Node->Node.Length);
+ NodeList++;
+ } // SMMUv3 Node
+
+ return EFI_SUCCESS;
+}
+
+/** Update the PMCG Node Information.
+
+ This function updates the PMCG node information in the IORT table.
+
+ @param [in] This Pointer to the table Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Iort Pointer to IORT table structure.
+ @param [in] NodesStartOffset Offset for the start of the PMCG Nodes.
+ @param [in] NodeList Pointer to an array of PMCG Node Objects.
+ @param [in] NodeCount Number of PMCG Node Objects.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddPmcgNodes (
+ IN CONST ACPI_TABLE_GENERATOR * CONST This,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort,
+ IN CONST UINT32 NodesStartOffset,
+ IN CONST CM_ARM_PMCG_NODE * NodeList,
+ IN UINT32 NodeCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE * PmcgNode;
+ EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray;
+ ACPI_IORT_GENERATOR * Generator;
+ UINT64 NodeLength;
+
+ ASSERT (Iort != NULL);
+
+ Generator = (ACPI_IORT_GENERATOR*)This;
+ PmcgNode = (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE*)((UINT8*)Iort +
+ NodesStartOffset);
+
+ while (NodeCount-- != 0) {
+ NodeLength = GetPmcgNodeSize (NodeList);
+ if (NodeLength > MAX_UINT16) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: PMCG Node length 0x%lx > MAX_UINT16. Status = %r\n",
+ NodeLength,
+ Status
+ ));
+ return Status;
+ }
+
+ // Populate the node header
+ PmcgNode->Node.Type = EFI_ACPI_IORT_TYPE_PMCG;
+ PmcgNode->Node.Length = (UINT16)NodeLength;
+ PmcgNode->Node.Revision = 1;
+ PmcgNode->Node.Reserved = EFI_ACPI_RESERVED_DWORD;
+ PmcgNode->Node.NumIdMappings = NodeList->IdMappingCount;
+ PmcgNode->Node.IdReference = sizeof (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE);
+
+ // PMCG specific data
+ PmcgNode->Base = NodeList->BaseAddress;
+ PmcgNode->OverflowInterruptGsiv = NodeList->OverflowInterrupt;
+ PmcgNode->Page1Base = NodeList->Page1BaseAddress;
+
+ Status = GetNodeOffsetReferencedByToken (
+ Generator->NodeIndexer,
+ Generator->IortNodeCount,
+ NodeList->ReferenceToken,
+ &PmcgNode->NodeReference
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get Output Reference for PMCG Node."
+ "Reference Token = %p"
+ " Status = %r\n",
+ NodeList->ReferenceToken,
+ Status
+ ));
+ return Status;
+ }
+
+ if ((NodeList->IdMappingCount > 0) &&
+ (NodeList->IdMappingToken != CM_NULL_TOKEN)) {
+ // Ids for PMCG node
+ IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE*)((UINT8*)PmcgNode +
+ PmcgNode->Node.IdReference);
+
+ Status = AddIdMappingArray (
+ This,
+ CfgMgrProtocol,
+ IdMapArray,
+ NodeList->IdMappingCount,
+ NodeList->IdMappingToken
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+ }
+
+ // Next PMCG Node
+ PmcgNode = (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE*)((UINT8*)PmcgNode +
+ PmcgNode->Node.Length);
+ NodeList++;
+ } // PMCG Node
+
+ return EFI_SUCCESS;
+}
+
+/** Construct the IORT ACPI 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.
+
+ @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.
+
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildIortTable (
+ 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
+ )
+{
+ EFI_STATUS Status;
+
+ UINT64 TableSize;
+ UINT64 NodeSize;
+
+ UINT32 IortNodeCount;
+ UINT32 ItsGroupNodeCount;
+ UINT32 NamedComponentNodeCount;
+ UINT32 RootComplexNodeCount;
+ UINT32 SmmuV1V2NodeCount;
+ UINT32 SmmuV3NodeCount;
+ UINT32 PmcgNodeCount;
+
+ UINT32 ItsGroupOffset;
+ UINT32 NamedComponentOffset;
+ UINT32 RootComplexOffset;
+ UINT32 SmmuV1V2Offset;
+ UINT32 SmmuV3Offset;
+ UINT32 PmcgOffset;
+
+ CM_ARM_ITS_GROUP_NODE * ItsGroupNodeList;
+ CM_ARM_NAMED_COMPONENT_NODE * NamedComponentNodeList;
+ CM_ARM_ROOT_COMPLEX_NODE * RootComplexNodeList;
+ CM_ARM_SMMUV1_SMMUV2_NODE * SmmuV1V2NodeList;
+ CM_ARM_SMMUV3_NODE * SmmuV3NodeList;
+ CM_ARM_PMCG_NODE * PmcgNodeList;
+
+ EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort;
+ IORT_NODE_INDEXER * NodeIndexer;
+ ACPI_IORT_GENERATOR * Generator;
+
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (Table != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Requested table revision = %d, is not supported."
+ "Supported table revision: Minimum = %d, Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Generator = (ACPI_IORT_GENERATOR*)This;
+ *Table = NULL;
+
+ // Get the ITS group node info
+ Status = GetEArmObjItsGroup (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &ItsGroupNodeList,
+ &ItsGroupNodeCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get ITS Group Node Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Add the ITS group node count
+ IortNodeCount = ItsGroupNodeCount;
+
+ // Get the Named component node info
+ Status = GetEArmObjNamedComponent (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &NamedComponentNodeList,
+ &NamedComponentNodeCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get Named Component Node Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Add the Named Component group count
+ IortNodeCount += NamedComponentNodeCount;
+
+ // Get the Root complex node info
+ Status = GetEArmObjRootComplex (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &RootComplexNodeList,
+ &RootComplexNodeCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get Root Complex Node Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Add the Root Complex node count
+ IortNodeCount += RootComplexNodeCount;
+
+ // Get the SMMU v1/v2 node info
+ Status = GetEArmObjSmmuV1SmmuV2 (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &SmmuV1V2NodeList,
+ &SmmuV1V2NodeCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get SMMUv1/SMMUv2 Node Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Add the SMMU v1/v2 node count
+ IortNodeCount += SmmuV1V2NodeCount;
+
+ // Get the SMMUv3 node info
+ Status = GetEArmObjSmmuV3 (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &SmmuV3NodeList,
+ &SmmuV3NodeCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get SMMUv3 Node Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Add the SMMUv3 node count
+ IortNodeCount += SmmuV3NodeCount;
+
+ // Get the PMCG node info
+ Status = GetEArmObjPmcg (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &PmcgNodeList,
+ &PmcgNodeCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to get PMCG Node Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Add the PMCG node count
+ IortNodeCount += PmcgNodeCount;
+
+ // Allocate Node Indexer array
+ NodeIndexer = (IORT_NODE_INDEXER*)AllocateZeroPool (
+ (sizeof (IORT_NODE_INDEXER) *
+ IortNodeCount)
+ );
+ if (NodeIndexer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to allocate memory for Node Indexer" \
+ " Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ DEBUG ((DEBUG_INFO, "INFO: NodeIndexer = %p\n", NodeIndexer));
+ Generator->IortNodeCount = IortNodeCount;
+ Generator->NodeIndexer = NodeIndexer;
+
+ // Calculate the size of the IORT table
+ TableSize = sizeof (EFI_ACPI_6_0_IO_REMAPPING_TABLE);
+
+ // ITS Group Nodes
+ if (ItsGroupNodeCount > 0) {
+ ItsGroupOffset = (UINT32)TableSize;
+ // Size of ITS Group node list.
+ NodeSize = GetSizeofItsGroupNodes (
+ ItsGroupOffset,
+ ItsGroupNodeList,
+ ItsGroupNodeCount,
+ &NodeIndexer
+ );
+ if (NodeSize > MAX_UINT32) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Invalid Size of Group Nodes. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ TableSize += NodeSize;
+
+ DEBUG ((
+ DEBUG_INFO,
+ " ItsGroupNodeCount = %d\n" \
+ " ItsGroupOffset = %d\n",
+ ItsGroupNodeCount,
+ ItsGroupOffset
+ ));
+ }
+
+ // Named Component Nodes
+ if (NamedComponentNodeCount > 0) {
+ NamedComponentOffset = (UINT32)TableSize;
+ // Size of Named Component node list.
+ NodeSize = GetSizeofNamedComponentNodes (
+ NamedComponentOffset,
+ NamedComponentNodeList,
+ NamedComponentNodeCount,
+ &NodeIndexer
+ );
+ if (NodeSize > MAX_UINT32) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Invalid Size of Named Component Nodes. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ TableSize += NodeSize;
+
+ DEBUG ((
+ DEBUG_INFO,
+ " NamedComponentNodeCount = %d\n" \
+ " NamedComponentOffset = %d\n",
+ NamedComponentNodeCount,
+ NamedComponentOffset
+ ));
+ }
+
+ // Root Complex Nodes
+ if (RootComplexNodeCount > 0) {
+ RootComplexOffset = (UINT32)TableSize;
+ // Size of Root Complex node list.
+ NodeSize = GetSizeofRootComplexNodes (
+ RootComplexOffset,
+ RootComplexNodeList,
+ RootComplexNodeCount,
+ &NodeIndexer
+ );
+ if (NodeSize > MAX_UINT32) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Invalid Size of Root Complex Nodes. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ TableSize += NodeSize;
+
+ DEBUG ((
+ DEBUG_INFO,
+ " RootComplexNodeCount = %d\n" \
+ " RootComplexOffset = %d\n",
+ RootComplexNodeCount,
+ RootComplexOffset
+ ));
+ }
+
+ // SMMUv1/SMMUv2 Nodes
+ if (SmmuV1V2NodeCount > 0) {
+ SmmuV1V2Offset = (UINT32)TableSize;
+ // Size of SMMUv1/SMMUv2 node list.
+ NodeSize = GetSizeofSmmuV1V2Nodes (
+ SmmuV1V2Offset,
+ SmmuV1V2NodeList,
+ SmmuV1V2NodeCount,
+ &NodeIndexer
+ );
+ if (NodeSize > MAX_UINT32) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Invalid Size of SMMUv1/v2 Nodes. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ TableSize += NodeSize;
+
+ DEBUG ((
+ DEBUG_INFO,
+ " SmmuV1V2NodeCount = %d\n" \
+ " SmmuV1V2Offset = %d\n",
+ SmmuV1V2NodeCount,
+ SmmuV1V2Offset
+ ));
+ }
+
+ // SMMUv3 Nodes
+ if (SmmuV3NodeCount > 0) {
+ SmmuV3Offset = (UINT32)TableSize;
+ // Size of SMMUv3 node list.
+ NodeSize = GetSizeofSmmuV3Nodes (
+ SmmuV3Offset,
+ SmmuV3NodeList,
+ SmmuV3NodeCount,
+ &NodeIndexer
+ );
+ if (NodeSize > MAX_UINT32) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Invalid Size of SMMUv3 Nodes. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ TableSize += NodeSize;
+
+ DEBUG ((
+ DEBUG_INFO,
+ " SmmuV3NodeCount = %d\n" \
+ " SmmuV3Offset = %d\n",
+ SmmuV3NodeCount,
+ SmmuV3Offset
+ ));
+ }
+
+ // PMCG Nodes
+ if (PmcgNodeCount > 0) {
+ PmcgOffset = (UINT32)TableSize;
+ // Size of PMCG node list.
+ NodeSize = GetSizeofPmcgNodes (
+ PmcgOffset,
+ PmcgNodeList,
+ PmcgNodeCount,
+ &NodeIndexer
+ );
+ if (NodeSize > MAX_UINT32) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Invalid Size of PMCG Nodes. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ TableSize += NodeSize;
+
+ DEBUG ((
+ DEBUG_INFO,
+ " PmcgNodeCount = %d\n" \
+ " PmcgOffset = %d\n",
+ PmcgNodeCount,
+ PmcgOffset
+ ));
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: IORT:\n" \
+ " IortNodeCount = %d\n" \
+ " TableSize = 0x%lx\n",
+ IortNodeCount,
+ TableSize
+ ));
+
+ if (TableSize > MAX_UINT32) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: IORT Table Size 0x%lx > MAX_UINT32," \
+ " Status = %r\n",
+ TableSize,
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Allocate the Buffer for IORT table
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize);
+ if (*Table == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to allocate memory for IORT Table, Size = %d," \
+ " Status = %r\n",
+ TableSize,
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Iort = (EFI_ACPI_6_0_IO_REMAPPING_TABLE*)*Table;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "IORT: Iort = 0x%p TableSize = 0x%lx\n",
+ Iort,
+ TableSize
+ ));
+
+ Status = AddAcpiHeader (
+ CfgMgrProtocol,
+ This,
+ &Iort->Header,
+ AcpiTableInfo,
+ (UINT32)TableSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Update IORT table
+ Iort->NumNodes = IortNodeCount;
+ Iort->NodeOffset = sizeof (EFI_ACPI_6_0_IO_REMAPPING_TABLE);
+ Iort->Reserved = EFI_ACPI_RESERVED_DWORD;
+
+ if (ItsGroupNodeCount > 0) {
+ Status = AddItsGroupNodes (
+ This,
+ CfgMgrProtocol,
+ Iort,
+ ItsGroupOffset,
+ ItsGroupNodeList,
+ ItsGroupNodeCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add ITS Group Node. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ if (NamedComponentNodeCount > 0) {
+ Status = AddNamedComponentNodes (
+ This,
+ CfgMgrProtocol,
+ Iort,
+ NamedComponentOffset,
+ NamedComponentNodeList,
+ NamedComponentNodeCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add Named Component Node. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ if (RootComplexNodeCount > 0) {
+ Status = AddRootComplexNodes (
+ This,
+ CfgMgrProtocol,
+ Iort,
+ RootComplexOffset,
+ RootComplexNodeList,
+ RootComplexNodeCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add Root Complex Node. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ if (SmmuV1V2NodeCount > 0) {
+ Status = AddSmmuV1V2Nodes (
+ This,
+ CfgMgrProtocol,
+ Iort,
+ SmmuV1V2Offset,
+ SmmuV1V2NodeList,
+ SmmuV1V2NodeCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add SMMU v1/v2 Node. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ if (SmmuV3NodeCount > 0) {
+ Status = AddSmmuV3Nodes (
+ This,
+ CfgMgrProtocol,
+ Iort,
+ SmmuV3Offset,
+ SmmuV3NodeList,
+ SmmuV3NodeCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add SMMUv3 Node. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ if (PmcgNodeCount > 0) {
+ Status = AddPmcgNodes (
+ This,
+ CfgMgrProtocol,
+ Iort,
+ PmcgOffset,
+ PmcgNodeList,
+ PmcgNodeCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: IORT: Failed to add SMMUv3 Node. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ return EFI_SUCCESS;
+
+error_handler:
+ if (Generator->NodeIndexer != NULL) {
+ FreePool (Generator->NodeIndexer);
+ Generator->NodeIndexer = NULL;
+ }
+
+ if (*Table != NULL) {
+ FreePool (*Table);
+ *Table = NULL;
+ }
+ return Status;
+}
+
+/** Free any resources allocated for constructing the IORT
+
+ @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 [in, out] Table Pointer to the ACPI Table.
+
+ @retval EFI_SUCCESS The resources were freed successfully.
+ @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
+**/
+STATIC
+EFI_STATUS
+FreeIortTableResources (
+ 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
+ )
+{
+ ACPI_IORT_GENERATOR * Generator;
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ Generator = (ACPI_IORT_GENERATOR*)This;
+
+ // Free any memory allocated by the generator
+ if (Generator->NodeIndexer != NULL) {
+ FreePool (Generator->NodeIndexer);
+ Generator->NodeIndexer = NULL;
+ }
+
+ if ((Table == NULL) || (*Table == NULL)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: IORT: Invalid Table Pointer\n"));
+ ASSERT ((Table != NULL) && (*Table != NULL));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (*Table);
+ *Table = NULL;
+ return EFI_SUCCESS;
+}
+
+/** The IORT Table Generator revision.
+*/
+#define IORT_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the MADT Table Generator.
+*/
+STATIC
+ACPI_IORT_GENERATOR IortGenerator = {
+ // ACPI table generator header
+ {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdIort),
+ // Generator Description
+ L"ACPI.STD.IORT.GENERATOR",
+ // ACPI Table Signature
+ EFI_ACPI_6_2_IO_REMAPPING_TABLE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_IO_REMAPPING_TABLE_REVISION,
+ // Minimum supported ACPI Table Revision
+ EFI_ACPI_IO_REMAPPING_TABLE_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ IORT_GENERATOR_REVISION,
+ // Build Table function
+ BuildIortTable,
+ // Free Resource function
+ FreeIortTableResources,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+ },
+
+ // IORT Generator private data
+
+ // Iort Node count
+ 0,
+ // Pointer to Iort node indexer
+ NULL
+};
+
+/** 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
+AcpiIortLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&IortGenerator.Header);
+ DEBUG ((DEBUG_INFO, "IORT: 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
+AcpiIortLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&IortGenerator.Header);
+ DEBUG ((DEBUG_INFO, "Iort: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.h
new file mode 100644
index 00000000..7b61f0b8
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.h
@@ -0,0 +1,44 @@
+/** @file
+
+ Copyright (c) 2018, ARM Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+ - Std or STD - Standard
+**/
+
+#ifndef IORT_GENERATOR_H_
+#define IORT_GENERATOR_H_
+
+#pragma pack(1)
+
+/** A structure that describes the Node indexer
+ used for indexing the IORT nodes.
+*/
+typedef struct IortNodeIndexer {
+ /// Index token for the Node
+ CM_OBJECT_TOKEN Token;
+ /// Pointer to the node
+ VOID * Object;
+ /// Node offset from the start of the IORT table
+ UINT32 Offset;
+} IORT_NODE_INDEXER;
+
+typedef struct AcpiIortGenerator {
+ /// ACPI Table generator header
+ ACPI_TABLE_GENERATOR Header;
+
+ // IORT Generator private data
+
+ /// IORT node count
+ UINT32 IortNodeCount;
+ /// Pointer to the node indexer array
+ IORT_NODE_INDEXER * NodeIndexer;
+} ACPI_IORT_GENERATOR;
+
+#pragma pack()
+
+#endif // IORT_GENERATOR_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/AcpiMadtLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/AcpiMadtLibArm.inf
new file mode 100644
index 00000000..850c3f6b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/AcpiMadtLibArm.inf
@@ -0,0 +1,36 @@
+## @file
+# MADT Table Generator
+#
+# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = AcpiMadtLibArm
+ FILE_GUID = AF76C93B-41B5-454D-83CD-D2A80A1C1E38
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiMadtLibConstructor
+ DESTRUCTOR = AcpiMadtLibDestructor
+
+[Sources]
+ MadtGenerator.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ BaseLib
+
+[Pcd]
+
+[Protocols]
+
+[Guids]
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/MadtGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/MadtGenerator.c
new file mode 100644
index 00000000..fa7654a4
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/MadtGenerator.c
@@ -0,0 +1,830 @@
+/** @file
+ MADT Table Generator
+
+ Copyright (c) 2017 - 2020, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.3 Specification - January 2019
+
+**/
+
+#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/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+/** ARM standard MADT Generator
+
+Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArmObjGicCInfo
+ - EArmObjGicDInfo
+ - EArmObjGicMsiFrameInfo (OPTIONAL)
+ - EArmObjGicRedistributorInfo (OPTIONAL)
+ - EArmObjGicItsInfo (OPTIONAL)
+*/
+
+/** This macro expands to a function that retrieves the GIC
+ CPU interface Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGicCInfo,
+ CM_ARM_GICC_INFO
+ );
+
+/** This macro expands to a function that retrieves the GIC
+ Distributor Information from the Configuration Manager.
+*/
+
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGicDInfo,
+ CM_ARM_GICD_INFO
+ );
+
+/** This macro expands to a function that retrieves the GIC
+ MSI Frame Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGicMsiFrameInfo,
+ CM_ARM_GIC_MSI_FRAME_INFO
+ );
+
+/** This macro expands to a function that retrieves the GIC
+ Redistributor Information from the Configuration Manager.
+*/
+
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGicRedistributorInfo,
+ CM_ARM_GIC_REDIST_INFO
+ );
+
+/** This macro expands to a function that retrieves the GIC
+ Interrupt Translation Service Information from the
+ Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGicItsInfo,
+ CM_ARM_GIC_ITS_INFO
+ );
+
+/** This function updates the GIC CPU Interface Information in the
+ EFI_ACPI_6_3_GIC_STRUCTURE structure.
+
+ @param [in] Gicc Pointer to GIC CPU Interface structure.
+ @param [in] GicCInfo Pointer to the GIC CPU Interface Information.
+ @param [in] MadtRev MADT table revision.
+**/
+STATIC
+VOID
+AddGICC (
+ IN EFI_ACPI_6_3_GIC_STRUCTURE * CONST Gicc,
+ IN CONST CM_ARM_GICC_INFO * CONST GicCInfo,
+ IN CONST UINT8 MadtRev
+ )
+{
+ ASSERT (Gicc != NULL);
+ ASSERT (GicCInfo != NULL);
+
+ // UINT8 Type
+ Gicc->Type = EFI_ACPI_6_3_GIC;
+ // UINT8 Length
+ Gicc->Length = sizeof (EFI_ACPI_6_3_GIC_STRUCTURE);
+ // UINT16 Reserved
+ Gicc->Reserved = EFI_ACPI_RESERVED_WORD;
+
+ // UINT32 CPUInterfaceNumber
+ Gicc->CPUInterfaceNumber = GicCInfo->CPUInterfaceNumber;
+ // UINT32 AcpiProcessorUid
+ Gicc->AcpiProcessorUid = GicCInfo->AcpiProcessorUid;
+ // UINT32 Flags
+ Gicc->Flags = GicCInfo->Flags;
+ // UINT32 ParkingProtocolVersion
+ Gicc->ParkingProtocolVersion = GicCInfo->ParkingProtocolVersion;
+ // UINT32 PerformanceInterruptGsiv
+ Gicc->PerformanceInterruptGsiv = GicCInfo->PerformanceInterruptGsiv;
+ // UINT64 ParkedAddress
+ Gicc->ParkedAddress = GicCInfo->ParkedAddress;
+
+ // UINT64 PhysicalBaseAddress
+ Gicc->PhysicalBaseAddress = GicCInfo->PhysicalBaseAddress;
+ // UINT64 GICV
+ Gicc->GICV = GicCInfo->GICV;
+ // UINT64 GICH
+ Gicc->GICH = GicCInfo->GICH;
+
+ // UINT32 VGICMaintenanceInterrupt
+ Gicc->VGICMaintenanceInterrupt = GicCInfo->VGICMaintenanceInterrupt;
+ // UINT64 GICRBaseAddress
+ Gicc->GICRBaseAddress = GicCInfo->GICRBaseAddress;
+
+ // UINT64 MPIDR
+ Gicc->MPIDR = GicCInfo->MPIDR;
+ // UINT8 ProcessorPowerEfficiencyClass
+ Gicc->ProcessorPowerEfficiencyClass =
+ GicCInfo->ProcessorPowerEfficiencyClass;
+ // UINT8 Reserved2
+ Gicc->Reserved2 = EFI_ACPI_RESERVED_BYTE;
+
+ // UINT16 SpeOverflowInterrupt
+ if (MadtRev > EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION) {
+ Gicc->SpeOverflowInterrupt = GicCInfo->SpeOverflowInterrupt;
+ } else {
+ // Setting SpeOverflowInterrupt to 0 ensures backward compatibility with
+ // ACPI 6.2 by also clearing the Reserved2[1] and Reserved2[2] fields
+ // in EFI_ACPI_6_2_GIC_STRUCTURE.
+ Gicc->SpeOverflowInterrupt = 0;
+ }
+}
+
+/**
+ Function to test if two GIC CPU Interface information structures have the
+ same ACPI Processor UID.
+
+ @param [in] GicCInfo1 Pointer to the first GICC info structure.
+ @param [in] GicCInfo2 Pointer to the second GICC info structure.
+ @param [in] Index1 Index of GicCInfo1 in the shared list of GIC
+ CPU Interface Info structures.
+ @param [in] Index2 Index of GicCInfo2 in the shared list of GIC
+ CPU Interface Info structures.
+
+ @retval TRUE GicCInfo1 and GicCInfo2 have the same UID.
+ @retval FALSE GicCInfo1 and GicCInfo2 have different UIDs.
+**/
+BOOLEAN
+EFIAPI
+IsAcpiUidEqual (
+ IN CONST VOID * GicCInfo1,
+ IN CONST VOID * GicCInfo2,
+ IN UINTN Index1,
+ IN UINTN Index2
+ )
+{
+ UINT32 Uid1;
+ UINT32 Uid2;
+
+ ASSERT ((GicCInfo1 != NULL) && (GicCInfo2 != NULL));
+
+ Uid1 = ((CM_ARM_GICC_INFO*)GicCInfo1)->AcpiProcessorUid;
+ Uid2 = ((CM_ARM_GICC_INFO*)GicCInfo2)->AcpiProcessorUid;
+
+ if (Uid1 == Uid2) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: GICC Info Structures %d and %d have the same ACPI " \
+ "Processor UID: 0x%x.\n",
+ Index1,
+ Index2,
+ Uid1
+ ));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/** Add the GIC CPU Interface Information to the MADT Table.
+
+ This function also checks for duplicate ACPI Processor UIDs.
+
+ @param [in] Gicc Pointer to GIC CPU Interface structure list.
+ @param [in] GicCInfo Pointer to the GIC CPU Information list.
+ @param [in] GicCCount Count of GIC CPU Interfaces.
+ @param [in] MadtRev MADT table revision.
+
+ @retval EFI_SUCCESS GIC CPU Interface Information was added
+ successfully.
+ @retval EFI_INVALID_PARAMETER One or more invalid GIC CPU Info values were
+ provided and the generator failed to add the
+ information to the table.
+**/
+STATIC
+EFI_STATUS
+AddGICCList (
+ IN EFI_ACPI_6_3_GIC_STRUCTURE * Gicc,
+ IN CONST CM_ARM_GICC_INFO * GicCInfo,
+ IN UINT32 GicCCount,
+ IN CONST UINT8 MadtRev
+ )
+{
+ BOOLEAN IsAcpiProcUidDuplicated;
+
+ ASSERT (Gicc != NULL);
+ ASSERT (GicCInfo != NULL);
+
+ IsAcpiProcUidDuplicated = FindDuplicateValue (
+ GicCInfo,
+ GicCCount,
+ sizeof (CM_ARM_GICC_INFO),
+ IsAcpiUidEqual
+ );
+ // Duplicate ACPI Processor UID was found so the GICC info provided
+ // is invalid
+ if (IsAcpiProcUidDuplicated) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ while (GicCCount-- != 0) {
+ AddGICC (Gicc++, GicCInfo++, MadtRev);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Update the GIC Distributor Information in the MADT Table.
+
+ @param [in] Gicd Pointer to GIC Distributor structure.
+ @param [in] GicDInfo Pointer to the GIC Distributor Information.
+**/
+STATIC
+VOID
+AddGICD (
+ EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE * CONST Gicd,
+ CONST CM_ARM_GICD_INFO * CONST GicDInfo
+)
+{
+ ASSERT (Gicd != NULL);
+ ASSERT (GicDInfo != NULL);
+
+ // UINT8 Type
+ Gicd->Type = EFI_ACPI_6_3_GICD;
+ // UINT8 Length
+ Gicd->Length = sizeof (EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE);
+ // UINT16 Reserved
+ Gicd->Reserved1 = EFI_ACPI_RESERVED_WORD;
+ // UINT32 Identifier
+ // One, and only one, GIC distributor structure must be present
+ // in the MADT for an ARM based system
+ Gicd->GicId = 0;
+ // UINT64 PhysicalBaseAddress
+ Gicd->PhysicalBaseAddress = GicDInfo->PhysicalBaseAddress;
+ // UINT32 VectorBase
+ Gicd->SystemVectorBase = EFI_ACPI_RESERVED_DWORD;
+ // UINT8 GicVersion
+ Gicd->GicVersion = GicDInfo->GicVersion;
+ // UINT8 Reserved2[3]
+ Gicd->Reserved2[0] = EFI_ACPI_RESERVED_BYTE;
+ Gicd->Reserved2[1] = EFI_ACPI_RESERVED_BYTE;
+ Gicd->Reserved2[2] = EFI_ACPI_RESERVED_BYTE;
+}
+
+/** Update the GIC MSI Frame Information.
+
+ @param [in] GicMsiFrame Pointer to GIC MSI Frame structure.
+ @param [in] GicMsiFrameInfo Pointer to the GIC MSI Frame Information.
+**/
+STATIC
+VOID
+AddGICMsiFrame (
+ IN EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE * CONST GicMsiFrame,
+ IN CONST CM_ARM_GIC_MSI_FRAME_INFO * CONST GicMsiFrameInfo
+)
+{
+ ASSERT (GicMsiFrame != NULL);
+ ASSERT (GicMsiFrameInfo != NULL);
+
+ GicMsiFrame->Type = EFI_ACPI_6_3_GIC_MSI_FRAME;
+ GicMsiFrame->Length = sizeof (EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE);
+ GicMsiFrame->Reserved1 = EFI_ACPI_RESERVED_WORD;
+ GicMsiFrame->GicMsiFrameId = GicMsiFrameInfo->GicMsiFrameId;
+ GicMsiFrame->PhysicalBaseAddress = GicMsiFrameInfo->PhysicalBaseAddress;
+
+ GicMsiFrame->Flags = GicMsiFrameInfo->Flags;
+ GicMsiFrame->SPICount = GicMsiFrameInfo->SPICount;
+ GicMsiFrame->SPIBase = GicMsiFrameInfo->SPIBase;
+}
+
+/** Add the GIC MSI Frame Information to the MADT Table.
+
+ @param [in] GicMsiFrame Pointer to GIC MSI Frame structure list.
+ @param [in] GicMsiFrameInfo Pointer to the GIC MSI Frame info list.
+ @param [in] GicMsiFrameCount Count of GIC MSI Frames.
+**/
+STATIC
+VOID
+AddGICMsiFrameInfoList (
+ IN EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE * GicMsiFrame,
+ IN CONST CM_ARM_GIC_MSI_FRAME_INFO * GicMsiFrameInfo,
+ IN UINT32 GicMsiFrameCount
+)
+{
+ ASSERT (GicMsiFrame != NULL);
+ ASSERT (GicMsiFrameInfo != NULL);
+
+ while (GicMsiFrameCount-- != 0) {
+ AddGICMsiFrame (GicMsiFrame++, GicMsiFrameInfo++);
+ }
+}
+
+/** Update the GIC Redistributor Information.
+
+ @param [in] Gicr Pointer to GIC Redistributor structure.
+ @param [in] GicRedistributorInfo Pointer to the GIC Redistributor Info.
+**/
+STATIC
+VOID
+AddGICRedistributor (
+ IN EFI_ACPI_6_3_GICR_STRUCTURE * CONST Gicr,
+ IN CONST CM_ARM_GIC_REDIST_INFO * CONST GicRedistributorInfo
+ )
+{
+ ASSERT (Gicr != NULL);
+ ASSERT (GicRedistributorInfo != NULL);
+
+ Gicr->Type = EFI_ACPI_6_3_GICR;
+ Gicr->Length = sizeof (EFI_ACPI_6_3_GICR_STRUCTURE);
+ Gicr->Reserved = EFI_ACPI_RESERVED_WORD;
+ Gicr->DiscoveryRangeBaseAddress =
+ GicRedistributorInfo->DiscoveryRangeBaseAddress;
+ Gicr->DiscoveryRangeLength = GicRedistributorInfo->DiscoveryRangeLength;
+}
+
+/** Add the GIC Redistributor Information to the MADT Table.
+
+ @param [in] Gicr Pointer to GIC Redistributor structure list.
+ @param [in] GicRInfo Pointer to the GIC Distributor info list.
+ @param [in] GicRCount Count of GIC Distributors.
+**/
+STATIC
+VOID
+AddGICRedistributorList (
+ IN EFI_ACPI_6_3_GICR_STRUCTURE * Gicr,
+ IN CONST CM_ARM_GIC_REDIST_INFO * GicRInfo,
+ IN UINT32 GicRCount
+)
+{
+ ASSERT (Gicr != NULL);
+ ASSERT (GicRInfo != NULL);
+
+ while (GicRCount-- != 0) {
+ AddGICRedistributor (Gicr++, GicRInfo++);
+ }
+}
+
+/** Update the GIC Interrupt Translation Service Information
+
+ @param [in] GicIts Pointer to GIC ITS structure.
+ @param [in] GicItsInfo Pointer to the GIC ITS Information.
+**/
+STATIC
+VOID
+AddGICInterruptTranslationService (
+ IN EFI_ACPI_6_3_GIC_ITS_STRUCTURE * CONST GicIts,
+ IN CONST CM_ARM_GIC_ITS_INFO * CONST GicItsInfo
+)
+{
+ ASSERT (GicIts != NULL);
+ ASSERT (GicItsInfo != NULL);
+
+ GicIts->Type = EFI_ACPI_6_3_GIC_ITS;
+ GicIts->Length = sizeof (EFI_ACPI_6_3_GIC_ITS_STRUCTURE);
+ GicIts->Reserved = EFI_ACPI_RESERVED_WORD;
+ GicIts->GicItsId = GicItsInfo->GicItsId;
+ GicIts->PhysicalBaseAddress = GicItsInfo->PhysicalBaseAddress;
+ GicIts->Reserved2 = EFI_ACPI_RESERVED_DWORD;
+}
+
+/** Add the GIC Interrupt Translation Service Information
+ to the MADT Table.
+
+ @param [in] GicIts Pointer to GIC ITS structure list.
+ @param [in] GicItsInfo Pointer to the GIC ITS list.
+ @param [in] GicItsCount Count of GIC ITS.
+**/
+STATIC
+VOID
+AddGICItsList (
+ IN EFI_ACPI_6_3_GIC_ITS_STRUCTURE * GicIts,
+ IN CONST CM_ARM_GIC_ITS_INFO * GicItsInfo,
+ IN UINT32 GicItsCount
+)
+{
+ ASSERT (GicIts != NULL);
+ ASSERT (GicItsInfo != NULL);
+
+ while (GicItsCount-- != 0) {
+ AddGICInterruptTranslationService (GicIts++, GicItsInfo++);
+ }
+}
+
+/** Construct the MADT ACPI 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.
+
+ @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.
+
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildMadtTable (
+ 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
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TableSize;
+ UINT32 GicCCount;
+ UINT32 GicDCount;
+ UINT32 GicMSICount;
+ UINT32 GicRedistCount;
+ UINT32 GicItsCount;
+ CM_ARM_GICC_INFO * GicCInfo;
+ CM_ARM_GICD_INFO * GicDInfo;
+ CM_ARM_GIC_MSI_FRAME_INFO * GicMSIInfo;
+ CM_ARM_GIC_REDIST_INFO * GicRedistInfo;
+ CM_ARM_GIC_ITS_INFO * GicItsInfo;
+ UINT32 GicCOffset;
+ UINT32 GicDOffset;
+ UINT32 GicMSIOffset;
+ UINT32 GicRedistOffset;
+ UINT32 GicItsOffset;
+
+ EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER * Madt;
+
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (Table != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: Requested table revision = %d, is not supported."
+ "Supported table revision: Minimum = %d, Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Table = NULL;
+
+ Status = GetEArmObjGicCInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GicCInfo,
+ &GicCCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: Failed to get GICC Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ if (GicCCount == 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: GIC CPU Interface information not provided.\n"
+ ));
+ ASSERT (GicCCount != 0);
+ Status = EFI_INVALID_PARAMETER;
+ goto error_handler;
+ }
+
+ Status = GetEArmObjGicDInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GicDInfo,
+ &GicDCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: Failed to get GICD Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ if (GicDCount == 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: GIC Distributor information not provided.\n"
+ ));
+ ASSERT (GicDCount != 0);
+ Status = EFI_INVALID_PARAMETER;
+ goto error_handler;
+ }
+
+ if (GicDCount > 1) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: One, and only one, GIC distributor must be present."
+ "GicDCount = %d\n",
+ GicDCount
+ ));
+ ASSERT (GicDCount <= 1);
+ Status = EFI_INVALID_PARAMETER;
+ goto error_handler;
+ }
+
+ Status = GetEArmObjGicMsiFrameInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GicMSIInfo,
+ &GicMSICount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: Failed to get GIC MSI Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Status = GetEArmObjGicRedistributorInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GicRedistInfo,
+ &GicRedistCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: Failed to get GIC Redistributor Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Status = GetEArmObjGicItsInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GicItsInfo,
+ &GicItsCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: Failed to get GIC ITS Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ TableSize = sizeof (EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER);
+
+ GicCOffset = TableSize;
+ TableSize += (sizeof (EFI_ACPI_6_3_GIC_STRUCTURE) * GicCCount);
+
+ GicDOffset = TableSize;
+ TableSize += (sizeof (EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE) * GicDCount);
+
+ GicMSIOffset = TableSize;
+ TableSize += (sizeof (EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE) * GicMSICount);
+
+ GicRedistOffset = TableSize;
+ TableSize += (sizeof (EFI_ACPI_6_3_GICR_STRUCTURE) * GicRedistCount);
+
+ GicItsOffset = TableSize;
+ TableSize += (sizeof (EFI_ACPI_6_3_GIC_ITS_STRUCTURE) * GicItsCount);
+
+ // Allocate the Buffer for MADT table
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize);
+ if (*Table == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: Failed to allocate memory for MADT Table, Size = %d," \
+ " Status = %r\n",
+ TableSize,
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Madt = (EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER*)*Table;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "MADT: Madt = 0x%p TableSize = 0x%x\n",
+ Madt,
+ TableSize
+ ));
+
+ Status = AddAcpiHeader (
+ CfgMgrProtocol,
+ This,
+ &Madt->Header,
+ AcpiTableInfo,
+ TableSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Status = AddGICCList (
+ (EFI_ACPI_6_3_GIC_STRUCTURE*)((UINT8*)Madt + GicCOffset),
+ GicCInfo,
+ GicCCount,
+ Madt->Header.Revision
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MADT: Failed to add GICC structures. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ AddGICD (
+ (EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE*)((UINT8*)Madt + GicDOffset),
+ GicDInfo
+ );
+
+ if (GicMSICount != 0) {
+ AddGICMsiFrameInfoList (
+ (EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE*)((UINT8*)Madt + GicMSIOffset),
+ GicMSIInfo,
+ GicMSICount
+ );
+ }
+
+ if (GicRedistCount != 0) {
+ AddGICRedistributorList (
+ (EFI_ACPI_6_3_GICR_STRUCTURE*)((UINT8*)Madt + GicRedistOffset),
+ GicRedistInfo,
+ GicRedistCount
+ );
+ }
+
+ if (GicItsCount != 0) {
+ AddGICItsList (
+ (EFI_ACPI_6_3_GIC_ITS_STRUCTURE*)((UINT8*)Madt + GicItsOffset),
+ GicItsInfo,
+ GicItsCount
+ );
+ }
+
+ return EFI_SUCCESS;
+
+error_handler:
+ if (*Table != NULL) {
+ FreePool (*Table);
+ *Table = NULL;
+ }
+ return Status;
+}
+
+/** Free any resources allocated for constructing the MADT
+
+ @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 [in, out] Table Pointer to the ACPI Table.
+
+ @retval EFI_SUCCESS The resources were freed successfully.
+ @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
+**/
+STATIC
+EFI_STATUS
+FreeMadtTableResources (
+ 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
+ )
+{
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ if ((Table == NULL) || (*Table == NULL)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: MADT: Invalid Table Pointer\n"));
+ ASSERT ((Table != NULL) && (*Table != NULL));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (*Table);
+ *Table = NULL;
+ return EFI_SUCCESS;
+}
+
+/** The MADT Table Generator revision.
+*/
+#define MADT_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the MADT Table Generator.
+*/
+STATIC
+CONST
+ACPI_TABLE_GENERATOR MadtGenerator = {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdMadt),
+ // Generator Description
+ L"ACPI.STD.MADT.GENERATOR",
+ // ACPI Table Signature
+ EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION,
+ // Minimum supported ACPI Table Revision
+ EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ MADT_GENERATOR_REVISION,
+ // Build Table function
+ BuildMadtTable,
+ // Free Resource function
+ FreeMadtTableResources,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+};
+
+/** 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
+AcpiMadtLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&MadtGenerator);
+ DEBUG ((DEBUG_INFO, "MADT: 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
+AcpiMadtLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&MadtGenerator);
+ DEBUG ((DEBUG_INFO, "MADT: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/AcpiMcfgLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/AcpiMcfgLibArm.inf
new file mode 100644
index 00000000..60cf8f3d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/AcpiMcfgLibArm.inf
@@ -0,0 +1,36 @@
+## @file
+# MCFG Table Generator
+#
+# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = AcpiMcfgLibArm
+ FILE_GUID = 8C9BDCB2-72D4-4F30-A12D-1145C3807FF7
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiMcfgLibConstructor
+ DESTRUCTOR = AcpiMcfgLibDestructor
+
+[Sources]
+ McfgGenerator.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ BaseLib
+
+[Pcd]
+
+[Protocols]
+
+[Guids]
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/McfgGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/McfgGenerator.c
new file mode 100644
index 00000000..9412b423
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/McfgGenerator.c
@@ -0,0 +1,364 @@
+/** @file
+ MCFG Table Generator
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - PCI Firmware Specification - Revision 3.2, January 26, 2015.
+
+**/
+
+#include <IndustryStandard/MemoryMappedConfigurationSpaceAccessTable.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/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+/** ARM standard MCFG Generator
+
+Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArmObjPciConfigSpaceInfo
+*/
+
+#pragma pack(1)
+
+/** This typedef is used to shorten the name of the MCFG Table
+ header structure.
+*/
+typedef
+ EFI_ACPI_MEMORY_MAPPED_CONFIGURATION_BASE_ADDRESS_TABLE_HEADER
+ MCFG_TABLE;
+
+/** This typedef is used to shorten the name of the Enhanced
+ Configuration Space address structure.
+*/
+typedef
+ EFI_ACPI_MEMORY_MAPPED_ENHANCED_CONFIGURATION_SPACE_BASE_ADDRESS_ALLOCATION_STRUCTURE
+ MCFG_CFG_SPACE_ADDR;
+
+#pragma pack()
+
+/** Retrieve the PCI Configuration Space Information.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjPciConfigSpaceInfo,
+ CM_ARM_PCI_CONFIG_SPACE_INFO
+ );
+
+/** Add the PCI Enhanced Configuration Space Information to the MCFG Table.
+
+ @param [in] Mcfg Pointer to MCFG Table.
+ @param [in] PciCfgSpaceOffset Offset for the PCI Configuration Space
+ Info structure in the MCFG Table.
+ @param [in] PciCfgSpaceInfoList Pointer to the PCI Configuration Space
+ Info List.
+ @param [in] PciCfgSpaceCount Count of PCI Configuration Space Info.
+**/
+STATIC
+VOID
+AddPciConfigurationSpaceList (
+ IN MCFG_TABLE * CONST Mcfg,
+ IN CONST UINT32 PciCfgSpaceOffset,
+ IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO * PciCfgSpaceInfoList,
+ IN UINT32 PciCfgSpaceCount
+)
+{
+ MCFG_CFG_SPACE_ADDR * PciCfgSpace;
+
+ ASSERT (Mcfg != NULL);
+ ASSERT (PciCfgSpaceInfoList != NULL);
+
+ PciCfgSpace = (MCFG_CFG_SPACE_ADDR *)((UINT8*)Mcfg + PciCfgSpaceOffset);
+
+ while (PciCfgSpaceCount-- != 0) {
+ // Add PCI Configuration Space entry
+ PciCfgSpace->BaseAddress = PciCfgSpaceInfoList->BaseAddress;
+ PciCfgSpace->PciSegmentGroupNumber =
+ PciCfgSpaceInfoList->PciSegmentGroupNumber;
+ PciCfgSpace->StartBusNumber = PciCfgSpaceInfoList->StartBusNumber;
+ PciCfgSpace->EndBusNumber = PciCfgSpaceInfoList->EndBusNumber;
+ PciCfgSpace->Reserved = EFI_ACPI_RESERVED_DWORD;
+ PciCfgSpace++;
+ PciCfgSpaceInfoList++;
+ }
+}
+
+/** Construct the MCFG ACPI 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.
+
+ @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.
+
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildMcfgTable (
+ 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
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TableSize;
+ UINT32 ConfigurationSpaceCount;
+ CM_ARM_PCI_CONFIG_SPACE_INFO * PciConfigSpaceInfoList;
+ MCFG_TABLE * Mcfg;
+
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (Table != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MCFG: Requested table revision = %d, is not supported."
+ "Supported table revision: Minimum = %d, Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Table = NULL;
+ Status = GetEArmObjPciConfigSpaceInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &PciConfigSpaceInfoList,
+ &ConfigurationSpaceCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "ERROR: MCFG: Failed to get PCI Configuration Space Information." \
+ " Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ if (ConfigurationSpaceCount == 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MCFG: Configuration Space Count = %d\n",
+ ConfigurationSpaceCount
+ ));
+ Status = EFI_INVALID_PARAMETER;
+ ASSERT (ConfigurationSpaceCount != 0);
+ goto error_handler;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "MCFG: Configuration Space Count = %d\n",
+ ConfigurationSpaceCount
+ ));
+
+ // Calculate the MCFG Table Size
+ TableSize = sizeof (MCFG_TABLE) +
+ ((sizeof (MCFG_CFG_SPACE_ADDR) * ConfigurationSpaceCount));
+
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize);
+ if (*Table == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MCFG: Failed to allocate memory for MCFG Table, Size = %d," \
+ " Status = %r\n",
+ TableSize,
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Mcfg = (MCFG_TABLE*)*Table;
+ DEBUG ((
+ DEBUG_INFO,
+ "MCFG: Mcfg = 0x%p TableSize = 0x%x\n",
+ Mcfg,
+ TableSize
+ ));
+
+ Status = AddAcpiHeader (
+ CfgMgrProtocol,
+ This,
+ &Mcfg->Header,
+ AcpiTableInfo,
+ TableSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: MCFG: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Mcfg->Reserved = EFI_ACPI_RESERVED_QWORD;
+
+ AddPciConfigurationSpaceList (
+ Mcfg,
+ sizeof (MCFG_TABLE),
+ PciConfigSpaceInfoList,
+ ConfigurationSpaceCount
+ );
+
+ return EFI_SUCCESS;
+
+error_handler:
+ if (*Table != NULL) {
+ FreePool (*Table);
+ *Table = NULL;
+ }
+ return Status;
+}
+
+/** Free any resources allocated for constructing the MCFG
+
+ @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 [in, out] Table Pointer to the ACPI Table.
+
+ @retval EFI_SUCCESS The resources were freed successfully.
+ @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
+**/
+STATIC
+EFI_STATUS
+FreeMcfgTableResources (
+ 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
+ )
+{
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ if ((Table == NULL) || (*Table == NULL)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: MCFG: Invalid Table Pointer\n"));
+ ASSERT ((Table != NULL) && (*Table != NULL));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (*Table);
+ *Table = NULL;
+ return EFI_SUCCESS;
+}
+
+/** This macro defines the MCFG Table Generator revision.
+*/
+#define MCFG_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the MCFG Table Generator.
+*/
+STATIC
+CONST
+ACPI_TABLE_GENERATOR McfgGenerator = {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdMcfg),
+ // Generator Description
+ L"ACPI.STD.MCFG.GENERATOR",
+ // ACPI Table Signature
+ EFI_ACPI_6_2_PCI_EXPRESS_MEMORY_MAPPED_CONFIGURATION_SPACE_BASE_ADDRESS_DESCRIPTION_TABLE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_MEMORY_MAPPED_CONFIGURATION_SPACE_ACCESS_TABLE_REVISION,
+ // Minimum supported ACPI Table Revision
+ EFI_ACPI_MEMORY_MAPPED_CONFIGURATION_SPACE_ACCESS_TABLE_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ MCFG_GENERATOR_REVISION,
+ // Build Table function
+ BuildMcfgTable,
+ // Free Resource function
+ FreeMcfgTableResources,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+};
+
+/** 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
+AcpiMcfgLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&McfgGenerator);
+ DEBUG ((DEBUG_INFO, "MCFG: 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
+AcpiMcfgLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&McfgGenerator);
+ DEBUG ((DEBUG_INFO, "MCFG: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf
new file mode 100644
index 00000000..8c56efde
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf
@@ -0,0 +1,30 @@
+## @file
+# PPTT Table Generator
+#
+# Copyright (c) 2019, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = AcpiPpttLibArm
+ FILE_GUID = FA102D52-5A92-4F95-A097-1D53F9CF5959
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiPpttLibConstructor
+ DESTRUCTOR = AcpiPpttLibDestructor
+
+[Sources]
+ PpttGenerator.c
+ PpttGenerator.h
+
+[Packages]
+ EmbeddedPkg/EmbeddedPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c
new file mode 100644
index 00000000..13c932dd
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c
@@ -0,0 +1,1540 @@
+/** @file
+ PPTT Table Generator
+
+ Copyright (c) 2019, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.3 Specification, January 2019
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+**/
+
+#include <Library/AcpiLib.h>
+#include <Library/BaseLib.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/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+#include "PpttGenerator.h"
+
+/**
+ ARM standard PPTT Generator
+
+ Requirements:
+ The following Configuration Manager Object(s) are used by this Generator:
+ - EArmObjProcHierarchyInfo (REQUIRED)
+ - EArmObjCacheInfo
+ - EArmObjProcNodeIdInfo
+ - EArmObjCmRef
+ - EArmObjGicCInfo (REQUIRED)
+*/
+
+/**
+ This macro expands to a function that retrieves the Processor Hierarchy
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjProcHierarchyInfo,
+ CM_ARM_PROC_HIERARCHY_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the cache information
+ from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjCacheInfo,
+ CM_ARM_CACHE_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the ID information for
+ Processor Hierarchy Nodes from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjProcNodeIdInfo,
+ CM_ARM_PROC_NODE_ID_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the cross-CM-object-
+ reference information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjCmRef,
+ CM_ARM_OBJ_REF
+ );
+
+/**
+ This macro expands to a function that retrieves the GIC CPU interface
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGicCInfo,
+ CM_ARM_GICC_INFO
+ );
+
+/**
+ Returns the size of the PPTT Processor Hierarchy Node (Type 0) given a
+ Processor Hierarchy Info CM object.
+
+ @param [in] Node Pointer to Processor Hierarchy Info CM object which
+ represents the Processor Hierarchy Node to be generated.
+
+ @retval Size of the Processor Hierarchy Node in bytes.
+**/
+STATIC
+UINT32
+GetProcHierarchyNodeSize (
+ IN CONST CM_ARM_PROC_HIERARCHY_INFO * Node
+ )
+{
+ ASSERT (Node != NULL);
+
+ // <size of Processor Hierarchy Node> + <size of Private Resources array>
+ return sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR) +
+ (Node->NoOfPrivateResources * sizeof (UINT32));
+}
+
+/**
+ This macro expands to a function that retrieves the amount of memory required
+ to store the Processor Hierarchy Nodes (Type 0) and updates the Node Indexer.
+*/
+GET_SIZE_OF_PPTT_STRUCTS (
+ ProcHierarchyNodes,
+ GetProcHierarchyNodeSize (NodesToIndex),
+ CM_ARM_PROC_HIERARCHY_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the amount of memory required
+ to store the Cache Type Structures (Type 1) and updates the Node Indexer.
+*/
+GET_SIZE_OF_PPTT_STRUCTS (
+ CacheTypeStructs,
+ sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE),
+ CM_ARM_CACHE_INFO
+ );
+
+/** This macro expands to a function that retrieves the amount of memory
+ required to store the ID Structures (Type 2) and updates the Node Indexer.
+*/
+GET_SIZE_OF_PPTT_STRUCTS (
+ IdStructs,
+ sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_ID),
+ CM_ARM_PROC_NODE_ID_INFO
+ );
+
+/**
+ Search the Node Indexer and return the indexed PPTT node with the given
+ Token.
+
+ @param [in] NodeIndexer Pointer to the Node Indexer array.
+ @param [in] NodeCount Number of elements in Node Indexer.
+ @param [in] SearchToken Token used for Node Indexer lookup.
+ @param [out] IndexedNodeFound Pointer to the Node Indexer array element
+ with the given Token.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_NOT_FOUND No element with a matching token was
+ found in the Node Indexer array.
+**/
+STATIC
+EFI_STATUS
+GetPpttNodeReferencedByToken (
+ IN PPTT_NODE_INDEXER * NodeIndexer,
+ IN UINT32 NodeCount,
+ IN CONST CM_OBJECT_TOKEN SearchToken,
+ OUT PPTT_NODE_INDEXER ** IndexedNodeFound
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (NodeIndexer != NULL);
+
+ DEBUG ((
+ DEBUG_INFO,
+ "PPTT: Node Indexer: SearchToken = %p\n",
+ SearchToken
+ ));
+
+ while (NodeCount-- != 0) {
+ DEBUG ((
+ DEBUG_INFO,
+ "PPTT: Node Indexer: NodeIndexer->Token = %p. Offset = %d\n",
+ NodeIndexer->Token,
+ NodeIndexer->Offset
+ ));
+
+ if (NodeIndexer->Token == SearchToken) {
+ *IndexedNodeFound = NodeIndexer;
+ Status = EFI_SUCCESS;
+ DEBUG ((
+ DEBUG_INFO,
+ "PPTT: Node Indexer: Token = %p. Found, Status = %r\n",
+ SearchToken,
+ Status
+ ));
+ return Status;
+ }
+ NodeIndexer++;
+ }
+
+ Status = EFI_NOT_FOUND;
+ DEBUG ((
+ DEBUG_ERROR,
+ "PPTT: Node Indexer: SearchToken = %p. Status = %r\n",
+ SearchToken,
+ Status
+ ));
+
+ return Status;
+}
+
+/**
+ Detect cycles in the processor and cache topology graph represented in
+ the PPTT table.
+
+ @param [in] Generator Pointer to the PPTT Generator.
+
+ @retval EFI_SUCCESS There are no cyclic references in the graph.
+ @retval EFI_INVALID_PARAMETER Processor or cache references form a cycle.
+**/
+STATIC
+EFI_STATUS
+DetectCyclesInTopology (
+ IN CONST ACPI_PPTT_GENERATOR * CONST Generator
+ )
+{
+ EFI_STATUS Status;
+ PPTT_NODE_INDEXER * Iterator;
+ PPTT_NODE_INDEXER * CycleDetector;
+ UINT32 NodesRemaining;
+
+ ASSERT (Generator != NULL);
+
+ Iterator = Generator->NodeIndexer;
+ NodesRemaining = Generator->ProcTopologyStructCount;
+
+ while (NodesRemaining != 0) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: PPTT: Cycle detection for element with index %d\n",
+ Generator->ProcTopologyStructCount - NodesRemaining
+ ));
+
+ CycleDetector = Iterator;
+
+ // Walk the topology tree
+ while (CycleDetector->TopologyParent != NULL) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: PPTT: %p -> %p\n",
+ CycleDetector->Token,
+ CycleDetector->TopologyParent->Token
+ ));
+
+ // Check if we have already visited this node
+ if (CycleDetector->CycleDetectionStamp == NodesRemaining) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Cycle in processor and cache topology detected for " \
+ "a chain of references originating from a node with: Token = %p " \
+ "Status = %r\n",
+ Iterator->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ // Stamp the visited node
+ CycleDetector->CycleDetectionStamp = NodesRemaining;
+ CycleDetector = CycleDetector->TopologyParent;
+ } // Continue topology tree walk
+
+ Iterator++;
+ NodesRemaining--;
+ } // Next Node Indexer
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Update the array of private resources for a given Processor Hierarchy Node.
+
+ @param [in] Generator Pointer to the PPTT Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] PrivResArray Pointer to the array of private resources.
+ @param [in] PrivResCount Number of private resources.
+ @param [in] PrivResArrayToken Reference Token for the CM_ARM_OBJ_REF
+ array describing node's private resources.
+
+ @retval EFI_SUCCESS Array updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND A private resource was not found.
+**/
+STATIC
+EFI_STATUS
+AddPrivateResources (
+ IN CONST ACPI_PPTT_GENERATOR * CONST Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN UINT32 * PrivResArray,
+ IN UINT32 PrivResCount,
+ IN CONST CM_OBJECT_TOKEN PrivResArrayToken
+ )
+{
+ EFI_STATUS Status;
+ CM_ARM_OBJ_REF * CmObjRefs;
+ UINT32 CmObjRefCount;
+ PPTT_NODE_INDEXER * PpttNodeFound;
+
+ ASSERT (
+ (Generator != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (PrivResArray != NULL) &&
+ (PrivResCount != 0)
+ );
+
+ // Validate input arguments
+ if (PrivResArrayToken == CM_NULL_TOKEN) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The number of private resources is %d while " \
+ "PrivResToken = CM_NULL_TOKEN. Status = %r\n",
+ PrivResCount,
+ Status
+ ));
+ return Status;
+ }
+
+ CmObjRefCount = 0;
+ // Get the CM Object References
+ Status = GetEArmObjCmRef (
+ CfgMgrProtocol,
+ PrivResArrayToken,
+ &CmObjRefs,
+ &CmObjRefCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get CM Object References. " \
+ "PrivResToken = %p. Status = %r\n",
+ PrivResArrayToken,
+ Status
+ ));
+ return Status;
+ }
+
+ if (CmObjRefCount != PrivResCount) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The number of CM Object References retrieved and the " \
+ "number of private resources don't match. CmObjRefCount = %d. " \
+ "PrivResourceCount = %d. PrivResToken = %p. Status = %r\n",
+ CmObjRefCount,
+ PrivResCount,
+ PrivResArrayToken,
+ Status
+ ));
+ return Status;
+ }
+
+ while (PrivResCount-- != 0) {
+ if (CmObjRefs->ReferenceToken == CM_NULL_TOKEN) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: CM_NULL_TOKEN provided as reference token for a " \
+ "private resource. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // The Node indexer has the Processor hierarchy nodes at the begining
+ // followed by the cache structs and Id structs. Therefore we can
+ // skip the Processor hierarchy nodes in the node indexer search.
+ Status = GetPpttNodeReferencedByToken (
+ Generator->CacheStructIndexedList,
+ (Generator->ProcTopologyStructCount -
+ Generator->ProcHierarchyNodeCount),
+ CmObjRefs->ReferenceToken,
+ &PpttNodeFound
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get a private resource with Token = %p from " \
+ "Node Indexer. Status = %r\n",
+ CmObjRefs->ReferenceToken,
+ Status
+ ));
+ return Status;
+ }
+
+ // Update the offset of the private resources in the Processor
+ // Hierarchy Node structure
+ *(PrivResArray++) = PpttNodeFound->Offset;
+ CmObjRefs++;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Function to test if two indexed Processor Hierarchy Info objects map to the
+ same GIC CPU Interface Info object.
+
+ This is a callback function that can be invoked by FindDuplicateValue ().
+
+ @param [in] Object1 Pointer to the first indexed Processor Hierarchy
+ Info object.
+ @param [in] Object2 Pointer to the second indexed Processor Hierarchy
+ Info object.
+ @param [in] Index1 Index of Object1 to be displayed for debugging
+ purposes.
+ @param [in] Index2 Index of Object2 to be displayed for debugging
+ purposes.
+
+ @retval TRUE Object1 and Object2 have the same GicCToken.
+ @retval FALSE Object1 and Object2 have different GicCTokens.
+**/
+BOOLEAN
+EFIAPI
+IsGicCTokenEqual (
+ IN CONST VOID * Object1,
+ IN CONST VOID * Object2,
+ IN UINTN Index1,
+ IN UINTN Index2
+ )
+{
+ PPTT_NODE_INDEXER * IndexedObject1;
+ PPTT_NODE_INDEXER * IndexedObject2;
+ CM_ARM_PROC_HIERARCHY_INFO * ProcNode1;
+ CM_ARM_PROC_HIERARCHY_INFO * ProcNode2;
+
+ ASSERT (
+ (Object1 != NULL) &&
+ (Object2 != NULL)
+ );
+
+ IndexedObject1 = (PPTT_NODE_INDEXER*)Object1;
+ IndexedObject2 = (PPTT_NODE_INDEXER*)Object2;
+ ProcNode1 = (CM_ARM_PROC_HIERARCHY_INFO*)IndexedObject1->Object;
+ ProcNode2 = (CM_ARM_PROC_HIERARCHY_INFO*)IndexedObject2->Object;
+
+ if (IS_ACPI_PROC_ID_VALID (ProcNode1) &&
+ IS_ACPI_PROC_ID_VALID (ProcNode2) &&
+ (ProcNode1->GicCToken != CM_NULL_TOKEN) &&
+ (ProcNode2->GicCToken != CM_NULL_TOKEN) &&
+ (ProcNode1->GicCToken == ProcNode2->GicCToken)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Two Processor Hierarchy Info objects (%d and %d) map to " \
+ "the same GICC Info object. ACPI Processor IDs are not unique. " \
+ "GicCToken = %p.\n",
+ Index1,
+ IndexedObject1->Token,
+ Index2,
+ ProcNode1->GicCToken
+ ));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Update the Processor Hierarchy Node (Type 0) information.
+
+ This function populates the Processor Hierarchy Nodes with information from
+ the Configuration Manager and adds this information to the PPTT table.
+
+ @param [in] Generator Pointer to the PPTT Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Pptt Pointer to PPTT table structure.
+ @param [in] NodesStartOffset Offset from the start of PPTT table to the
+ start of Processor Hierarchy Nodes.
+
+ @retval EFI_SUCCESS Node updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddProcHierarchyNodes (
+ IN CONST ACPI_PPTT_GENERATOR * CONST Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,
+ IN CONST UINT32 NodesStartOffset
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR * ProcStruct;
+ UINT32 * PrivateResources;
+ BOOLEAN IsGicCTokenDuplicated;
+
+ CM_ARM_GICC_INFO * GicCInfoList;
+ UINT32 GicCInfoCount;
+ UINT32 UniqueGicCRefCount;
+
+ PPTT_NODE_INDEXER * PpttNodeFound;
+ CM_ARM_PROC_HIERARCHY_INFO * ProcInfoNode;
+
+ PPTT_NODE_INDEXER * ProcNodeIterator;
+ UINT32 NodeCount;
+ UINT32 Length;
+
+ ASSERT (
+ (Generator != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Pptt != NULL)
+ );
+
+ ProcStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR*)((UINT8*)Pptt +
+ NodesStartOffset);
+
+ ProcNodeIterator = Generator->ProcHierarchyNodeIndexedList;
+ NodeCount = Generator->ProcHierarchyNodeCount;
+
+ // Check if every GICC Object is referenced by onlu one Proc Node
+ IsGicCTokenDuplicated = FindDuplicateValue (
+ ProcNodeIterator,
+ NodeCount,
+ sizeof (PPTT_NODE_INDEXER),
+ IsGicCTokenEqual
+ );
+ // Duplicate GIC CPU Interface Token was found so two PPTT Processor Hierarchy
+ // Nodes map to the same MADT GICC structure
+ if (IsGicCTokenDuplicated) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ UniqueGicCRefCount = 0;
+
+ while (NodeCount-- != 0) {
+ ProcInfoNode = (CM_ARM_PROC_HIERARCHY_INFO*)ProcNodeIterator->Object;
+
+ // Check if the private resource count is within the size limit
+ // imposed on the Processor Hierarchy node by the specification.
+ // Note: The length field is 8 bit wide while the number of private
+ // resource field is 32 bit wide.
+ Length = GetProcHierarchyNodeSize (ProcInfoNode);
+ if (Length > MAX_UINT8) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Too many private resources. Count = %d. " \
+ "Maximum supported Processor Node size exceeded. " \
+ "Token = %p. Status = %r\n",
+ ProcInfoNode->NoOfPrivateResources,
+ ProcInfoNode->ParentToken,
+ Status
+ ));
+ return Status;
+ }
+
+ // Populate the node header
+ ProcStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_PROCESSOR;
+ ProcStruct->Length = (UINT8)Length;
+ ProcStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
+ ProcStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
+
+ // Populate the flags
+ ProcStruct->Flags.PhysicalPackage = ProcInfoNode->Flags & BIT0;
+ ProcStruct->Flags.AcpiProcessorIdValid = (ProcInfoNode->Flags & BIT1) >> 1;
+ ProcStruct->Flags.ProcessorIsAThread = (ProcInfoNode->Flags & BIT2) >> 2;
+ ProcStruct->Flags.NodeIsALeaf = (ProcInfoNode->Flags & BIT3) >> 3;
+ ProcStruct->Flags.IdenticalImplementation =
+ (ProcInfoNode->Flags & BIT4) >> 4;
+ ProcStruct->Flags.Reserved = 0;
+
+ // Populate the parent reference
+ if (ProcInfoNode->ParentToken == CM_NULL_TOKEN) {
+ ProcStruct->Parent = 0;
+ } else {
+ Status = GetPpttNodeReferencedByToken (
+ Generator->ProcHierarchyNodeIndexedList,
+ Generator->ProcHierarchyNodeCount,
+ ProcInfoNode->ParentToken,
+ &PpttNodeFound
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get parent processor hierarchy node " \
+ "reference. Token = %p, Status = %r\n",
+ ProcInfoNode->ParentToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ // Test if the reference is to a 'leaf' node
+ if (IS_PROC_NODE_LEAF (
+ ((CM_ARM_PROC_HIERARCHY_INFO*)PpttNodeFound->Object))) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Reference to a leaf Processor Hierarchy Node. " \
+ "ParentToken = %p. ChildToken = %p. Status = %r\n",
+ ProcInfoNode->ParentToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ // Update Proc Structure with the offset of the parent node
+ ProcStruct->Parent = PpttNodeFound->Offset;
+
+ // Store the reference for the parent node in the Node Indexer
+ // so that this can be used later for cycle detection
+ ProcNodeIterator->TopologyParent = PpttNodeFound;
+ }
+
+ // Populate ACPI Processor ID
+ if (!IS_ACPI_PROC_ID_VALID (ProcInfoNode)) {
+ // Default invalid ACPI Processor ID to 0
+ ProcStruct->AcpiProcessorId = 0;
+ } else if (ProcInfoNode->GicCToken == CM_NULL_TOKEN) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The 'ACPI Processor ID valid' flag is set but no GICC " \
+ "structure token was provided. GicCToken = %p. RequestorToken = %p. " \
+ "Status = %r\n",
+ ProcInfoNode->GicCToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ } else {
+ Status = GetEArmObjGicCInfo (
+ CfgMgrProtocol,
+ ProcInfoNode->GicCToken,
+ &GicCInfoList,
+ &GicCInfoCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get GICC structure. ACPI Processor ID " \
+ "can't be populated. GicCToken = %p. RequestorToken = %p. " \
+ "Status = %r\n",
+ ProcInfoNode->GicCToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ if (GicCInfoCount != 1) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to find a unique GICC structure. " \
+ "ACPI Processor ID can't be populated. " \
+ "GICC Structure Count = %d. GicCToken = %p. RequestorToken = %p " \
+ "Status = %r\n",
+ GicCInfoCount,
+ ProcInfoNode->GicCToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ // Update the ACPI Processor Id
+ ProcStruct->AcpiProcessorId = GicCInfoList->AcpiProcessorUid;
+
+ // Increment the reference count for the number of
+ // Unique GICC objects that were retrieved.
+ UniqueGicCRefCount++;
+ }
+
+ ProcStruct->NumberOfPrivateResources = ProcInfoNode->NoOfPrivateResources;
+ PrivateResources = (UINT32*)((UINT8*)ProcStruct +
+ sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR));
+
+ if (ProcStruct->NumberOfPrivateResources != 0) {
+ // Populate the private resources array
+ Status = AddPrivateResources (
+ Generator,
+ CfgMgrProtocol,
+ PrivateResources,
+ ProcStruct->NumberOfPrivateResources,
+ ProcInfoNode->PrivateResourcesArrayToken
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to populate the private resources array. " \
+ "Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+ }
+
+ // Next Processor Hierarchy Node
+ ProcStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR*)((UINT8*)ProcStruct +
+ ProcStruct->Length);
+ ProcNodeIterator++;
+ } // Processor Hierarchy Node
+
+ // Knowing the total number of GICC references made and that all GICC Token
+ // references are unique, we can test if no GICC instances have been left out.
+ Status = GetEArmObjGicCInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GicCInfoList,
+ &GicCInfoCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get GICC Info. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // MADT - PPTT cross validation
+ // This checks that one and only one GICC structure is referenced by a
+ // Processor Hierarchy Node in the PPTT.
+ // Since we have already checked that the GICC objects referenced by the
+ // Proc Nodes are unique, the UniqueGicCRefCount cannot be greater than
+ // the total number of GICC objects in the platform.
+ if (GicCInfoCount > UniqueGicCRefCount) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: %d GICC structure(s) exposed by MADT don't have " \
+ "a corresponding Processor Hierarchy Node. Status = %r\n",
+ GicCInfoCount - UniqueGicCRefCount,
+ Status
+ ));
+ }
+
+ return Status;
+}
+
+/**
+ Update the Cache Type Structure (Type 1) information.
+
+ This function populates the Cache Type Structures with information from
+ the Configuration Manager and adds this information to the PPTT table.
+
+ @param [in] Generator Pointer to the PPTT Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Pptt Pointer to PPTT table structure.
+ @param [in] NodesStartOffset Offset from the start of PPTT table to the
+ start of Cache Type Structures.
+
+ @retval EFI_SUCCESS Structures updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND A required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddCacheTypeStructures (
+ IN CONST ACPI_PPTT_GENERATOR * CONST Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,
+ IN CONST UINT32 NodesStartOffset
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE * CacheStruct;
+ PPTT_NODE_INDEXER * PpttNodeFound;
+ CM_ARM_CACHE_INFO * CacheInfoNode;
+ PPTT_NODE_INDEXER * CacheNodeIterator;
+ UINT32 NodeCount;
+
+ ASSERT (
+ (Generator != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Pptt != NULL)
+ );
+
+ CacheStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE*)((UINT8*)Pptt +
+ NodesStartOffset);
+
+ CacheNodeIterator = Generator->CacheStructIndexedList;
+ NodeCount = Generator->CacheStructCount;
+
+ while (NodeCount-- != 0) {
+ CacheInfoNode = (CM_ARM_CACHE_INFO*)CacheNodeIterator->Object;
+
+ // Populate the node header
+ CacheStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_CACHE;
+ CacheStruct->Length = sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE);
+ CacheStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
+ CacheStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
+
+ // "On Arm-based systems, all cache properties must be provided in the
+ // table." (ACPI 6.3, Section 5.2.29.2)
+ CacheStruct->Flags.SizePropertyValid = 1;
+ CacheStruct->Flags.NumberOfSetsValid = 1;
+ CacheStruct->Flags.AssociativityValid = 1;
+ CacheStruct->Flags.AllocationTypeValid = 1;
+ CacheStruct->Flags.CacheTypeValid = 1;
+ CacheStruct->Flags.WritePolicyValid = 1;
+ CacheStruct->Flags.LineSizeValid = 1;
+ CacheStruct->Flags.Reserved = 0;
+
+ // Populate the reference to the next level of cache
+ if (CacheInfoNode->NextLevelOfCacheToken == CM_NULL_TOKEN) {
+ CacheStruct->NextLevelOfCache = 0;
+ } else {
+ Status = GetPpttNodeReferencedByToken (
+ Generator->CacheStructIndexedList,
+ Generator->CacheStructCount,
+ CacheInfoNode->NextLevelOfCacheToken,
+ &PpttNodeFound
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get the reference to the Next Level of " \
+ "Cache. NextLevelOfCacheToken = %p. RequestorToken = %p. " \
+ "Status = %r\n",
+ CacheInfoNode->NextLevelOfCacheToken,
+ CacheInfoNode->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ // Update Cache Structure with the offset for the next level of cache
+ CacheStruct->NextLevelOfCache = PpttNodeFound->Offset;
+
+ // Store the next level of cache information in the Node Indexer
+ // so that this can be used later for cycle detection
+ CacheNodeIterator->TopologyParent = PpttNodeFound;
+ }
+
+ CacheStruct->Size = CacheInfoNode->Size;
+
+ // Validate and populate the 'Number of sets' field
+ if (CacheInfoNode->NumberOfSets > PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: When ARMv8.3-CCIDX is implemented the maximum number " \
+ "of sets can be %d. NumberOfSets = %d. Status = %r\n",
+ PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX,
+ CacheInfoNode->NumberOfSets,
+ Status
+ ));
+ return Status;
+ }
+
+ if (CacheInfoNode->NumberOfSets > PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: PPTT: When ARMv8.3-CCIDX is not implemented the maximum " \
+ "number of sets can be %d. NumberOfSets = %d\n",
+ PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX,
+ CacheInfoNode->NumberOfSets
+ ));
+ }
+
+ CacheStruct->NumberOfSets = CacheInfoNode->NumberOfSets;
+
+ // Validate Associativity field based on maximum associativity
+ // supported by ACPI Cache type structure.
+ if (CacheInfoNode->Associativity > MAX_UINT8) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The maximum associativity supported by ACPI " \
+ "Cache type structure is %d. Associativity = %d, Status = %r\n",
+ MAX_UINT8,
+ CacheInfoNode->Associativity,
+ Status
+ ));
+ return Status;
+ }
+
+ // Validate the Associativity field based on the architecture specification
+ // The architecture supports much larger associativity values than the
+ // current ACPI specification.
+ // These checks will be needed in the future when the ACPI specification
+ // is extended. Disabling this code for now.
+#if 0
+ if (CacheInfoNode->Associativity > PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: When ARMv8.3-CCIDX is implemented the maximum cache " \
+ "associativity can be %d. Associativity = %d. Status = %r\n",
+ PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX,
+ CacheInfoNode->Associativity,
+ Status
+ ));
+ return Status;
+ }
+
+ if (CacheInfoNode->Associativity > PPTT_ARM_CACHE_ASSOCIATIVITY_MAX) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: PPTT: When ARMv8.3-CCIDX is not implemented the maximum " \
+ "cache associativity can be %d. Associativity = %d\n",
+ PPTT_ARM_CACHE_ASSOCIATIVITY_MAX,
+ CacheInfoNode->Associativity
+ ));
+ }
+#endif
+
+ // Note a typecast is needed as the maximum associativity
+ // supported by ACPI Cache type structure is MAX_UINT8.
+ CacheStruct->Associativity = (UINT8)CacheInfoNode->Associativity;
+
+ // Populate cache attributes
+ CacheStruct->Attributes.AllocationType =
+ CacheInfoNode->Attributes & (BIT0 | BIT1);
+ CacheStruct->Attributes.CacheType =
+ (CacheInfoNode->Attributes & (BIT2 | BIT3)) >> 2;
+ CacheStruct->Attributes.WritePolicy =
+ (CacheInfoNode->Attributes & BIT4) >> 4;
+ CacheStruct->Attributes.Reserved = 0;
+
+ // Validate and populate cache line size
+ if ((CacheInfoNode->LineSize < PPTT_ARM_CACHE_LINE_SIZE_MIN) ||
+ (CacheInfoNode->LineSize > PPTT_ARM_CACHE_LINE_SIZE_MAX)) {
+
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The cache line size must be between %d and %d bytes " \
+ "on ARM Platforms. LineSize = %d. Status = %r\n" ,
+ PPTT_ARM_CACHE_LINE_SIZE_MIN,
+ PPTT_ARM_CACHE_LINE_SIZE_MAX,
+ CacheInfoNode->LineSize,
+ Status
+ ));
+ return Status;
+ }
+
+ if ((CacheInfoNode->LineSize & (CacheInfoNode->LineSize - 1)) != 0) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The cache line size is not a power of 2. " \
+ "LineSize = %d. Status = %r\n" ,
+ CacheInfoNode->LineSize,
+ Status
+ ));
+ return Status;
+ }
+
+ CacheStruct->LineSize = CacheInfoNode->LineSize;
+
+ // Next Cache Type Structure
+ CacheStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE*)((UINT8*)CacheStruct +
+ CacheStruct->Length);
+ CacheNodeIterator++;
+ } // Cache Type Structure
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Update the ID Type Structure (Type 2) information.
+
+ This function populates the ID Type Structures with information from
+ the Configuration Manager and and adds this information to the PPTT table.
+
+ @param [in] Generator Pointer to the PPTT Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Pptt Pointer to PPTT table structure.
+ @param [in] NodesStartOffset Offset from the start of PPTT table to the
+ start of ID Type Structures.
+
+ @retval EFI_SUCCESS Structures updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND A required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddIdTypeStructures (
+ IN CONST ACPI_PPTT_GENERATOR * CONST Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,
+ IN CONST UINT32 NodesStartOffset
+ )
+{
+ EFI_ACPI_6_3_PPTT_STRUCTURE_ID * IdStruct;
+ CM_ARM_PROC_NODE_ID_INFO * ProcIdInfoNode;
+ PPTT_NODE_INDEXER * IdStructIterator;
+ UINT32 NodeCount;
+
+
+ ASSERT (
+ (Generator != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Pptt != NULL)
+ );
+
+ IdStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_ID*)((UINT8*)Pptt + NodesStartOffset);
+
+ IdStructIterator = Generator->IdStructIndexedList;
+ NodeCount = Generator->IdStructCount;
+ while (NodeCount-- != 0) {
+ ProcIdInfoNode = (CM_ARM_PROC_NODE_ID_INFO*)IdStructIterator->Object;
+
+ // Populate the node
+ IdStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_ID;
+ IdStruct->Length = sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_ID);
+ IdStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
+ IdStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
+ IdStruct->VendorId = ProcIdInfoNode->VendorId;
+ IdStruct->Level1Id = ProcIdInfoNode->Level1Id;
+ IdStruct->Level2Id = ProcIdInfoNode->Level2Id;
+ IdStruct->MajorRev = ProcIdInfoNode->MajorRev;
+ IdStruct->MinorRev = ProcIdInfoNode->MinorRev;
+ IdStruct->SpinRev = ProcIdInfoNode->SpinRev;
+
+ // Next ID Type Structure
+ IdStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_ID*)((UINT8*)IdStruct +
+ IdStruct->Length);
+ IdStructIterator++;
+ } // ID Type Structure
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Construct the PPTT ACPI 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.
+
+ @param [in] This Pointer to the table generator.
+ @param [in] AcpiTableInfo Pointer to the ACPI table generator to be used.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [out] Table Pointer to the constructed ACPI Table.
+
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildPpttTable (
+ 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
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TableSize;
+ UINT32 ProcTopologyStructCount;
+ UINT32 ProcHierarchyNodeCount;
+ UINT32 CacheStructCount;
+ UINT32 IdStructCount;
+
+ UINT32 ProcHierarchyNodeOffset;
+ UINT32 CacheStructOffset;
+ UINT32 IdStructOffset;
+
+ CM_ARM_PROC_HIERARCHY_INFO * ProcHierarchyNodeList;
+ CM_ARM_CACHE_INFO * CacheStructList;
+ CM_ARM_PROC_NODE_ID_INFO * IdStructList;
+
+ ACPI_PPTT_GENERATOR * Generator;
+
+ // Pointer to the Node Indexer array
+ PPTT_NODE_INDEXER * NodeIndexer;
+
+ EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt;
+
+ ASSERT (
+ (This != NULL) &&
+ (AcpiTableInfo != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Table != NULL) &&
+ (AcpiTableInfo->TableGeneratorId == This->GeneratorID) &&
+ (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature)
+ );
+
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Requested table revision = %d is not supported. "
+ "Supported table revisions: Minimum = %d. Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Generator = (ACPI_PPTT_GENERATOR*)This;
+ *Table = NULL;
+
+ // Get the processor hierarchy info and update the processor topology
+ // structure count with Processor Hierarchy Nodes (Type 0)
+ Status = GetEArmObjProcHierarchyInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &ProcHierarchyNodeList,
+ &ProcHierarchyNodeCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get processor hierarchy info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ ProcTopologyStructCount = ProcHierarchyNodeCount;
+ Generator->ProcHierarchyNodeCount = ProcHierarchyNodeCount;
+
+ // Get the cache info and update the processor topology structure count with
+ // Cache Type Structures (Type 1)
+ Status = GetEArmObjCacheInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &CacheStructList,
+ &CacheStructCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get cache info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ ProcTopologyStructCount += CacheStructCount;
+ Generator->CacheStructCount = CacheStructCount;
+
+ // Get the processor hierarchy node ID info and update the processor topology
+ // structure count with ID Structures (Type 2)
+ Status = GetEArmObjProcNodeIdInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &IdStructList,
+ &IdStructCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get processor hierarchy node ID info. " \
+ "Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ ProcTopologyStructCount += IdStructCount;
+ Generator->IdStructCount = IdStructCount;
+
+ // Allocate Node Indexer array
+ NodeIndexer = (PPTT_NODE_INDEXER*)AllocateZeroPool (
+ sizeof (PPTT_NODE_INDEXER) *
+ ProcTopologyStructCount
+ );
+ if (NodeIndexer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to allocate memory for Node Indexer. Status = %r\n ",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ DEBUG ((DEBUG_INFO, "INFO: NodeIndexer = %p\n", NodeIndexer));
+ Generator->ProcTopologyStructCount = ProcTopologyStructCount;
+ Generator->NodeIndexer = NodeIndexer;
+
+ // Calculate the size of the PPTT table
+ TableSize = sizeof (EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER);
+
+ // Include the size of Processor Hierarchy Nodes and index them
+ if (Generator->ProcHierarchyNodeCount != 0) {
+ ProcHierarchyNodeOffset = TableSize;
+ Generator->ProcHierarchyNodeIndexedList = NodeIndexer;
+ TableSize += GetSizeofProcHierarchyNodes (
+ ProcHierarchyNodeOffset,
+ ProcHierarchyNodeList,
+ Generator->ProcHierarchyNodeCount,
+ &NodeIndexer
+ );
+
+ DEBUG ((
+ DEBUG_INFO,
+ " ProcHierarchyNodeCount = %d\n" \
+ " ProcHierarchyNodeOffset = 0x%x\n" \
+ " ProcHierarchyNodeIndexedList = 0x%p\n",
+ Generator->ProcHierarchyNodeCount,
+ ProcHierarchyNodeOffset,
+ Generator->ProcHierarchyNodeIndexedList
+ ));
+
+ }
+
+ // Include the size of Cache Type Structures and index them
+ if (Generator->CacheStructCount != 0) {
+ CacheStructOffset = TableSize;
+ Generator->CacheStructIndexedList = NodeIndexer;
+ TableSize += GetSizeofCacheTypeStructs (
+ CacheStructOffset,
+ CacheStructList,
+ Generator->CacheStructCount,
+ &NodeIndexer
+ );
+ DEBUG ((
+ DEBUG_INFO,
+ " CacheStructCount = %d\n" \
+ " CacheStructOffset = 0x%x\n" \
+ " CacheStructIndexedList = 0x%p\n",
+ Generator->CacheStructCount,
+ CacheStructOffset,
+ Generator->CacheStructIndexedList
+ ));
+ }
+
+ // Include the size of ID Type Structures and index them
+ if (Generator->IdStructCount != 0) {
+ IdStructOffset = TableSize;
+ Generator->IdStructIndexedList = NodeIndexer;
+ TableSize += GetSizeofIdStructs (
+ IdStructOffset,
+ IdStructList,
+ Generator->IdStructCount,
+ &NodeIndexer
+ );
+ DEBUG ((
+ DEBUG_INFO,
+ " IdStructCount = %d\n" \
+ " IdStructOffset = 0x%x\n" \
+ " IdStructIndexedList = 0x%p\n",
+ Generator->IdStructCount,
+ IdStructOffset,
+ Generator->IdStructIndexedList
+ ));
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: PPTT:\n" \
+ " ProcTopologyStructCount = %d\n" \
+ " TableSize = %d\n",
+ ProcTopologyStructCount,
+ TableSize
+ ));
+
+ // Allocate the Buffer for the PPTT table
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize);
+ if (*Table == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to allocate memory for PPTT Table. " \
+ "Size = %d. Status = %r\n",
+ TableSize,
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Pptt = (EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER*)*Table;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "PPTT: Pptt = 0x%p. TableSize = 0x%x\n",
+ Pptt,
+ TableSize
+ ));
+
+ // Add ACPI header
+ Status = AddAcpiHeader (
+ CfgMgrProtocol,
+ This,
+ &Pptt->Header,
+ AcpiTableInfo,
+ TableSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Add Processor Hierarchy Nodes (Type 0) to the generated table
+ if (Generator->ProcHierarchyNodeCount != 0) {
+ Status = AddProcHierarchyNodes (
+ Generator,
+ CfgMgrProtocol,
+ Pptt,
+ ProcHierarchyNodeOffset
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to add Processor Hierarchy Nodes. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ // Add Cache Type Structures (Type 1) to the generated table
+ if (Generator->CacheStructCount != 0) {
+ Status = AddCacheTypeStructures (
+ Generator,
+ CfgMgrProtocol,
+ Pptt,
+ CacheStructOffset
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to add Cache Type Structures. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ // Add ID Type Structures (Type 2) to the generated table
+ if (Generator->IdStructCount != 0) {
+ Status = AddIdTypeStructures (
+ Generator,
+ CfgMgrProtocol,
+ Pptt,
+ IdStructOffset
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to add ID Type Structures. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ // Validate CM object cross-references in PPTT
+ Status = DetectCyclesInTopology (Generator);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Invalid processor and cache topology. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ return Status;
+
+error_handler:
+ if (Generator->NodeIndexer != NULL) {
+ FreePool (Generator->NodeIndexer);
+ Generator->NodeIndexer = NULL;
+ }
+
+ if (*Table != NULL) {
+ FreePool (*Table);
+ *Table = NULL;
+ }
+
+ return Status;
+}
+
+/**
+ Free any resources allocated for constructing the PPTT
+
+ @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 [in, out] Table Pointer to the ACPI Table.
+
+ @retval EFI_SUCCESS The resources were freed successfully.
+ @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
+**/
+STATIC
+EFI_STATUS
+FreePpttTableResources (
+ 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
+ )
+{
+ ACPI_PPTT_GENERATOR * Generator;
+
+ ASSERT (
+ (This != NULL) &&
+ (AcpiTableInfo != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (AcpiTableInfo->TableGeneratorId == This->GeneratorID) &&
+ (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature)
+ );
+
+ Generator = (ACPI_PPTT_GENERATOR*)This;
+
+ // Free any memory allocated by the generator
+ if (Generator->NodeIndexer != NULL) {
+ FreePool (Generator->NodeIndexer);
+ Generator->NodeIndexer = NULL;
+ }
+
+ if ((Table == NULL) || (*Table == NULL)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: PPTT: Invalid Table Pointer\n"));
+ ASSERT (
+ (Table != NULL) &&
+ (*Table != NULL)
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (*Table);
+ *Table = NULL;
+ return EFI_SUCCESS;
+}
+
+/** The PPTT Table Generator revision.
+*/
+#define PPTT_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the PPTT Table Generator.
+*/
+STATIC
+ACPI_PPTT_GENERATOR PpttGenerator = {
+ // ACPI table generator header
+ {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdPptt),
+ // Generator Description
+ L"ACPI.STD.PPTT.GENERATOR",
+ // ACPI Table Signature
+ EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_STRUCTURE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_REVISION,
+ // Minimum supported ACPI Table Revision
+ EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ PPTT_GENERATOR_REVISION,
+ // Build Table function
+ BuildPpttTable,
+ // Free Resource function
+ FreePpttTableResources,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+ },
+
+ // PPTT Generator private data
+
+ // Processor topology node count
+ 0,
+ // Count of Processor Hierarchy Nodes
+ 0,
+ // Count of Cache Structures
+ 0,
+ // Count of Id Structures
+ 0,
+ // Pointer to PPTT Node Indexer
+ NULL
+};
+
+/**
+ 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
+AcpiPpttLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&PpttGenerator.Header);
+ DEBUG ((DEBUG_INFO, "PPTT: 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
+AcpiPpttLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&PpttGenerator.Header);
+ DEBUG ((DEBUG_INFO, "PPTT: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.h
new file mode 100644
index 00000000..1e9cf543
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.h
@@ -0,0 +1,189 @@
+/** @file
+ Header file for the dynamic PPTT generator
+
+ Copyright (c) 2019, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.3 Specification, January 2019
+ - ARM Architecture Reference Manual ARMv8 (D.a)
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+**/
+
+#ifndef PPTT_GENERATOR_H_
+#define PPTT_GENERATOR_H_
+
+#pragma pack(1)
+
+/// Cache parameters allowed by the architecture with
+/// ARMv8.3-CCIDX (Cache extended number of sets)
+/// Derived from CCSIDR_EL1 when ID_AA64MMFR2_EL1.CCIDX==0001
+#define PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX (1 << 24)
+#define PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX (1 << 21)
+
+/// Cache parameters allowed by the architecture without
+/// ARMv8.3-CCIDX (Cache extended number of sets)
+/// Derived from CCSIDR_EL1 when ID_AA64MMFR2_EL1.CCIDX==0000
+#define PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX (1 << 15)
+#define PPTT_ARM_CACHE_ASSOCIATIVITY_MAX (1 << 10)
+
+/// Common cache parameters
+/// Derived from CCSIDR_EL1
+/// The LineSize is represented by bits 2:0
+/// (Log2(Number of bytes in cache line)) - 4 is used to represent
+/// the LineSize bits.
+#define PPTT_ARM_CACHE_LINE_SIZE_MAX (1 << 11)
+#define PPTT_ARM_CACHE_LINE_SIZE_MIN (1 << 4)
+
+/// Test if the given Processor Hierarchy Info object has the 'Node is a Leaf'
+/// flag set
+#define IS_PROC_NODE_LEAF(Node) ((Node->Flags & BIT3) != 0)
+
+/// Test if the given Processor Hierarchy Info object has the 'ACPI Processor
+/// ID valid' flag set
+#define IS_ACPI_PROC_ID_VALID(Node) ((Node->Flags & BIT1) != 0)
+
+/**
+ The GET_SIZE_OF_PPTT_STRUCTS macro expands to a function that is used to
+ calculate the total memory requirement for the PPTT structures represented
+ by the given list of Configuration Manager Objects of the same type. This
+ function also indexes the input CM objects so that various other CM objects
+ (possibly of different type) can reference them.
+
+ The size of memory needed for the specified type of PPTT structures is based
+ on the number and type of CM objects provided. The macro assumes that the
+ ACPI object PpttObjName has fixed size.
+
+ The macro expands to a function which has the following prototype:
+
+ STATIC
+ UINT32
+ EFIAPI
+ GetSizeof<PpttObjName> (
+ IN CONST UINT32 StartOffset,
+ IN CONST CmObjectType * Nodes,
+ IN UINT32 NodeCount,
+ IN OUT PPTT_NODE_INDEXER ** CONST NodeIndexer
+ )
+
+ Generated function parameters:
+ @param [in] StartOffset Offset from the start of PPTT to where
+ the PPTT structures will be placed.
+ @param [in] NodesToIndex Pointer to the list of CM objects to be
+ indexed and size-estimated.
+ @param [out] NodeCount Number of CM objects in NodesToIndex.
+ @param [in, out] NodeIndexer Pointer to the list of Node Indexer
+ elements to populate.
+ @retval Size Total memory requirement for the PPTT
+ structures described in NodesToIndex.
+
+ Macro Parameters:
+ @param [in] PpttObjName Name for the type of PPTT structures which
+ size is estimated.
+ @param [in] PpttObjSize Expression to use to calculate the size of
+ of a single instance of the PPTT structure
+ which corresponds to the CM object being
+ indexed.
+ @param [in] CmObjectType Data type of the CM nodes in NodesToIndex.
+**/
+#define GET_SIZE_OF_PPTT_STRUCTS( \
+ PpttObjName, \
+ PpttObjSize, \
+ CmObjectType \
+) \
+STATIC \
+UINT32 \
+GetSizeof##PpttObjName ( \
+ IN CONST UINT32 StartOffset, \
+ IN CONST CmObjectType * NodesToIndex, \
+ IN UINT32 NodeCount, \
+ IN OUT PPTT_NODE_INDEXER ** CONST NodeIndexer \
+ ) \
+{ \
+ UINT32 Size; \
+ \
+ ASSERT ( \
+ (NodesToIndex != NULL) && \
+ (NodeIndexer != NULL) \
+ ); \
+ \
+ Size = 0; \
+ while (NodeCount-- != 0) { \
+ (*NodeIndexer)->Token = NodesToIndex->Token; \
+ (*NodeIndexer)->Object = (VOID*)NodesToIndex; \
+ (*NodeIndexer)->Offset = Size + StartOffset; \
+ (*NodeIndexer)->CycleDetectionStamp = 0; \
+ (*NodeIndexer)->TopologyParent = NULL; \
+ DEBUG (( \
+ DEBUG_INFO, \
+ "PPTT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n", \
+ *NodeIndexer, \
+ (*NodeIndexer)->Token, \
+ (*NodeIndexer)->Object, \
+ (*NodeIndexer)->Offset \
+ )); \
+ \
+ Size += PpttObjSize; \
+ (*NodeIndexer)++; \
+ NodesToIndex++; \
+ } \
+ return Size; \
+}
+
+/**
+ A structure for indexing CM objects (nodes) used in PPTT generation.
+
+ PPTT_NODE_INDEXER is a wrapper around CM objects which augments these objects
+ with additional information that enables generating PPTT structures with
+ correct cross-references.
+
+ PPTT_NODE_INDEXER keeps track of each structure's offset from the base
+ address of the generated table. It also caches certain information and makes
+ PPTT cyclic reference detection possible.
+*/
+typedef struct PpttNodeIndexer {
+ /// Unique identifier for the node
+ CM_OBJECT_TOKEN Token;
+ /// Pointer to the CM object being indexed
+ VOID * Object;
+ /// Offset from the start of the PPTT table to the PPTT structure which is
+ /// represented by Object
+ UINT32 Offset;
+ /// Field used to mark nodes as 'visited' when detecting cycles in processor
+ /// and cache topology
+ UINT32 CycleDetectionStamp;
+ /// Reference to a Node Indexer element which is the parent of this Node
+ /// Indexer element in the processor and cache topology
+ /// e.g For a hardware thread the TopologyParent would point to a CPU node
+ /// For a L1 cache the TopologyParent would point to a L2 cache
+ struct PpttNodeIndexer * TopologyParent;
+} PPTT_NODE_INDEXER;
+
+typedef struct AcpiPpttGenerator {
+ /// ACPI Table generator header
+ ACPI_TABLE_GENERATOR Header;
+ /// PPTT structure count
+ UINT32 ProcTopologyStructCount;
+ /// Count of Processor Hierarchy Nodes
+ UINT32 ProcHierarchyNodeCount;
+ /// Count of Cache Structures
+ UINT32 CacheStructCount;
+ /// Count of Id Structures
+ UINT32 IdStructCount;
+ /// List of indexed CM objects for PPTT generation
+ PPTT_NODE_INDEXER * NodeIndexer;
+ /// Pointer to the start of Processor Hierarchy nodes in
+ /// the Node Indexer array
+ PPTT_NODE_INDEXER * ProcHierarchyNodeIndexedList;
+ /// Pointer to the start of Cache Structures in the Node Indexer array
+ PPTT_NODE_INDEXER * CacheStructIndexedList;
+ /// Pointer to the start of Id Structures in the Node Indexer array
+ PPTT_NODE_INDEXER * IdStructIndexedList;
+} ACPI_PPTT_GENERATOR;
+
+#pragma pack()
+
+#endif // PPTT_GENERATOR_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf
new file mode 100644
index 00000000..53d2b1ac
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf
@@ -0,0 +1,36 @@
+## @file
+# Raw Table Generator
+#
+# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = AcpiRawLibArm
+ FILE_GUID = 20F31568-D687-49BA-B326-CCD9D38EDE16
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiRawLibConstructor
+ DESTRUCTOR = AcpiRawLibDestructor
+
+[Sources]
+ RawGenerator.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ BaseLib
+
+[Pcd]
+
+[Protocols]
+
+[Guids]
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/RawGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/RawGenerator.c
new file mode 100644
index 00000000..39c684ef
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/RawGenerator.c
@@ -0,0 +1,144 @@
+/** @file
+ MCFG Table Generator
+
+ Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/AcpiLib.h>
+#include <Library/DebugLib.h>
+#include <Protocol/AcpiTable.h>
+
+// Module specific include files.
+#include <AcpiTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <ConfigurationManagerHelper.h>
+#include <Library/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+/** Construct the ACPI table using the ACPI table data provided.
+
+ 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.
+
+ @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.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildRawTable (
+ 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
+ )
+{
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (Table != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableData != NULL);
+
+ if (AcpiTableInfo->AcpiTableData == NULL) {
+ *Table = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Table = AcpiTableInfo->AcpiTableData;
+
+ return EFI_SUCCESS;
+}
+
+/** This macro defines the Raw Generator revision.
+*/
+#define RAW_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the Raw Table Generator.
+*/
+STATIC
+CONST
+ACPI_TABLE_GENERATOR RawGenerator = {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdRaw),
+ // Generator Description
+ L"ACPI.STD.RAW.GENERATOR",
+ // ACPI Table Signature - Unused
+ 0,
+ // ACPI Table Revision - Unused
+ 0,
+ // Minimum ACPI Table Revision - Unused
+ 0,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ RAW_GENERATOR_REVISION,
+ // Build Table function
+ BuildRawTable,
+ // No additional resources are allocated by the generator.
+ // Hence the Free Resource function is not required.
+ NULL,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+};
+
+/** 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
+AcpiRawLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&RawGenerator);
+ DEBUG ((DEBUG_INFO, "RAW: 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
+AcpiRawLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&RawGenerator);
+ DEBUG ((DEBUG_INFO, "RAW: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf
new file mode 100644
index 00000000..bc3b0d3e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf
@@ -0,0 +1,37 @@
+## @file
+# SPCR Table Generator
+#
+# Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = AcpiSpcrLibArm
+ FILE_GUID = 55088136-7B78-4974-B1EE-F630150D0DE7
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiSpcrLibConstructor
+ DESTRUCTOR = AcpiSpcrLibDestructor
+
+[Sources]
+ SpcrGenerator.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ SsdtSerialPortFixupLib
+
+[Pcd]
+
+[Protocols]
+
+[Guids]
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c
new file mode 100644
index 00000000..6c07bfdc
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c
@@ -0,0 +1,477 @@
+/** @file
+ SPCR Table Generator
+
+ Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - Microsoft Serial Port Console Redirection Table
+ Specification - Version 1.03 - August 10, 2015.
+
+**/
+
+#include <IndustryStandard/DebugPort2Table.h>
+#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>
+
+/** ARM standard SPCR Table Generator
+
+ Constructs the SPCR table for PL011 or SBSA UART peripherals.
+
+Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArmObjSerialConsolePortInfo
+
+NOTE: This implementation ignores the possibility that the Serial settings may
+ be modified from the UEFI Shell. A more complex handler would be needed
+ to (e.g.) recover serial port settings from the UART, or non-volatile
+ storage.
+*/
+
+#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
+
+/**A template for generating the SPCR Table.
+
+ Note: fields marked "{Template}" will be updated dynamically.
+*/
+STATIC
+EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE AcpiSpcr = {
+ ACPI_HEADER (
+ EFI_ACPI_6_2_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE,
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE,
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_REVISION
+ ),
+ 0, // {Template}: Serial Port Subtype
+ {
+ EFI_ACPI_RESERVED_BYTE,
+ EFI_ACPI_RESERVED_BYTE,
+ EFI_ACPI_RESERVED_BYTE
+ },
+ ARM_GAS32 (0), // {Template}: Serial Port Base Address
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERRUPT_TYPE_GIC,
+ 0, // Not used on ARM
+ 0, // {Template}: Serial Port Interrupt
+ 0, // {Template}: Serial Port Baudrate
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_PARITY_NO_PARITY,
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_STOP_BITS_1,
+ SPCR_FLOW_CONTROL_NONE,
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_TERMINAL_TYPE_ANSI,
+ EFI_ACPI_RESERVED_BYTE,
+ 0xFFFF,
+ 0xFFFF,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00000000,
+ 0x00,
+ EFI_ACPI_RESERVED_DWORD
+};
+
+#pragma pack()
+
+/** This macro expands to a function that retrieves the Serial
+ Port Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjSerialConsolePortInfo,
+ CM_ARM_SERIAL_PORT_INFO
+ )
+
+/** 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 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
+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;
+ 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);
+
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SPCR: Requested table revision = %d, is not supported."
+ "Supported table revision: Minimum = %d, Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Table = NULL;
+
+ Status = GetEArmObjSerialConsolePortInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &SerialPortInfo,
+ &SerialPortCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SPCR: Failed to get serial port information. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ if (SerialPortCount == 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SPCR: Serial port information not found. Status = %r\n",
+ EFI_NOT_FOUND
+ ));
+ return EFI_NOT_FOUND;
+ }
+
+ // 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: Invalid serial port information. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // 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,
+ (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiSpcr,
+ AcpiTableInfo,
+ sizeof (EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE)
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SPCR: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // The SPCR InterfaceType uses the same encoding as that of the
+ // DBG2 table Port Subtype field. However InterfaceType is 8-bit
+ // while the Port Subtype field in the DBG2 table is 16-bit.
+ if ((SerialPortInfo->PortSubtype & 0xFF00) != 0) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SPCR: Invalid Port subtype (must be < 256). Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Update the serial port subtype
+ AcpiSpcr.InterfaceType = (UINT8)SerialPortInfo->PortSubtype;
+
+ // Update the base address
+ AcpiSpcr.BaseAddress.Address = SerialPortInfo->BaseAddress;
+
+ // Set the access size
+ if (SerialPortInfo->AccessSize >= EFI_ACPI_6_3_QWORD) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SPCR: Access size must be <= 3 (DWORD). Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ } else if (SerialPortInfo->AccessSize == EFI_ACPI_6_3_UNDEFINED) {
+ // 0 Undefined (legacy reasons)
+ // Default to DWORD access size as the access
+ // size field was introduced at a later date
+ // and some ConfigurationManager implementations
+ // may not be providing this field data
+ AcpiSpcr.BaseAddress.AccessSize = EFI_ACPI_6_3_DWORD;
+ } else {
+ AcpiSpcr.BaseAddress.AccessSize = SerialPortInfo->AccessSize;
+ }
+
+ // Update the UART interrupt
+ AcpiSpcr.GlobalSystemInterrupt = SerialPortInfo->Interrupt;
+
+ switch (SerialPortInfo->BaudRate) {
+ case 9600:
+ AcpiSpcr.BaudRate =
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_9600;
+ break;
+ case 19200:
+ AcpiSpcr.BaudRate =
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_19200;
+ break;
+ case 57600:
+ AcpiSpcr.BaudRate =
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_57600;
+ break;
+ case 115200:
+ AcpiSpcr.BaudRate =
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_115200;
+ break;
+ default:
+ Status = EFI_UNSUPPORTED;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SPCR: Invalid Baud Rate %ld, Status = %r\n",
+ SerialPortInfo->BaudRate,
+ Status
+ ));
+ goto error_handler;
+ } // switch
+
+ 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;
+}
+
+/** This macro defines the SPCR Table Generator revision.
+*/
+#define SPCR_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the SPCR Table Generator.
+*/
+STATIC
+CONST
+ACPI_TABLE_GENERATOR SpcrGenerator = {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSpcr),
+ // Generator Description
+ L"ACPI.STD.SPCR.GENERATOR",
+ // ACPI 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
+ EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ SPCR_GENERATOR_REVISION,
+ // Build table function. Use the extended version instead.
+ NULL,
+ // Free table function. Use the extended version instead.
+ NULL,
+ // Extended Build table function.
+ BuildSpcrTableEx,
+ // Extended free function.
+ FreeSpcrTableEx
+};
+
+/** 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
+AcpiSpcrLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&SpcrGenerator);
+ DEBUG ((DEBUG_INFO, "SPCR: 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
+AcpiSpcrLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&SpcrGenerator);
+ DEBUG ((DEBUG_INFO, "SPCR: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf
new file mode 100644
index 00000000..ac7e0ffa
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf
@@ -0,0 +1,29 @@
+## @file
+# SRAT Table Generator
+#
+# Copyright (c) 2019, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = AcpiSratLibArm
+ FILE_GUID = 2CE21E0A-A39C-4B26-BC0E-526178036ACD
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiSratLibConstructor
+ DESTRUCTOR = AcpiSratLibDestructor
+
+[Sources]
+ SratGenerator.c
+
+[Packages]
+ EmbeddedPkg/EmbeddedPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/SratGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/SratGenerator.c
new file mode 100644
index 00000000..1d723939
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/SratGenerator.c
@@ -0,0 +1,835 @@
+/** @file
+ SRAT Table Generator
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.3 Specification, January 2019
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+**/
+
+#include <Library/AcpiLib.h>
+#include <Library/BaseLib.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/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+/**
+ ARM standard SRAT Generator
+
+ Requirements:
+ The following Configuration Manager Object(s) are used by this Generator:
+ - EArmObjGicCInfo (REQUIRED)
+ - EArmObjGicItsInfo (OPTIONAL)
+ - EArmObjMemoryAffinityInfo (OPTIONAL)
+ - EArmObjGenericInitiatorAffinityInfo (OPTIONAL)
+ - EArmObjDeviceHandleAcpi (OPTIONAL)
+ - EArmObjDeviceHandlePci (OPTIONAL)
+*/
+
+/** This macro expands to a function that retrieves the GIC
+ CPU interface Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGicCInfo,
+ CM_ARM_GICC_INFO
+ );
+
+/** This macro expands to a function that retrieves the GIC
+ Interrupt Translation Service Information from the
+ Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGicItsInfo,
+ CM_ARM_GIC_ITS_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the Memory Affinity
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjMemoryAffinityInfo,
+ CM_ARM_MEMORY_AFFINITY_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the Generic Initiator Affinity
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGenericInitiatorAffinityInfo,
+ CM_ARM_GENERIC_INITIATOR_AFFINITY_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the ACPI Device Handle
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjDeviceHandleAcpi,
+ CM_ARM_DEVICE_HANDLE_ACPI
+ );
+
+/**
+ This macro expands to a function that retrieves the PCI Device Handle
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjDeviceHandlePci,
+ CM_ARM_DEVICE_HANDLE_PCI
+ );
+
+
+/** Return the PCI Device information in BDF format
+
+ PCI Bus Number - Max 256 busses (Bits 15:8 of BDF)
+ PCI Device Number - Max 32 devices (Bits 7:3 of BDF)
+ PCI Function Number - Max 8 functions (Bits 2:0 of BDF)
+
+ @param [in] DeviceHandlePci Pointer to the PCI Device Handle.
+
+ @retval BDF value corresponding to the PCI Device Handle.
+**/
+STATIC
+UINT16
+GetBdf (
+ IN CONST CM_ARM_DEVICE_HANDLE_PCI * DeviceHandlePci
+ )
+{
+ UINT16 Bdf;
+ Bdf = (UINT16)DeviceHandlePci->BusNumber << 8;
+ Bdf |= (DeviceHandlePci->DeviceNumber & 0x1F) << 3;
+ Bdf |= DeviceHandlePci->FunctionNumber & 0x7;
+ return Bdf;
+}
+
+/** Add the GICC Affinity Structures in the SRAT Table.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Srat Pointer to the SRAT Table.
+ @param [in] GicCAffOffset Offset of the GICC Affinity
+ information in the SRAT Table.
+ @param [in] GicCInfo Pointer to the GIC CPU Information list.
+ @param [in] GicCCount Count of GIC CPU Interfaces.
+
+ @retval EFI_SUCCESS Table generated successfully.
+**/
+STATIC
+EFI_STATUS
+AddGICCAffinity (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER * CONST Srat,
+ IN CONST UINT32 GicCAffOffset,
+ IN CONST CM_ARM_GICC_INFO * GicCInfo,
+ IN UINT32 GicCCount
+ )
+{
+ EFI_ACPI_6_3_GICC_AFFINITY_STRUCTURE * GicCAff;
+
+ ASSERT (Srat != NULL);
+ ASSERT (GicCInfo != NULL);
+
+ GicCAff = (EFI_ACPI_6_3_GICC_AFFINITY_STRUCTURE *)((UINT8*)Srat +
+ GicCAffOffset);
+
+ while (GicCCount-- != 0) {
+ DEBUG ((DEBUG_INFO, "SRAT: GicCAff = 0x%p\n", GicCAff));
+
+ GicCAff->Type = EFI_ACPI_6_3_GICC_AFFINITY;
+ GicCAff->Length = sizeof (EFI_ACPI_6_3_GICC_AFFINITY_STRUCTURE);
+ GicCAff->ProximityDomain = GicCInfo->ProximityDomain;
+ GicCAff->AcpiProcessorUid = GicCInfo->AcpiProcessorUid;
+ GicCAff->Flags = GicCInfo->AffinityFlags;
+ GicCAff->ClockDomain = GicCInfo->ClockDomain;
+
+ // Next
+ GicCAff++;
+ GicCInfo++;
+ }// while
+ return EFI_SUCCESS;
+}
+
+/** Add the GIC ITS Affinity Structures in the SRAT Table.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Srat Pointer to the SRAT Table.
+ @param [in] GicItsAffOffset Offset of the GIC ITS Affinity
+ information in the SRAT Table.
+ @param [in] GicItsInfo Pointer to the GIC ITS Information list.
+ @param [in] GicItsCount Count of GIC ITS.
+
+ @retval EFI_SUCCESS Table generated successfully.
+**/
+STATIC
+EFI_STATUS
+AddGICItsAffinity (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER * CONST Srat,
+ IN CONST UINT32 GicItsAffOffset,
+ IN CONST CM_ARM_GIC_ITS_INFO * GicItsInfo,
+ IN UINT32 GicItsCount
+ )
+{
+ EFI_ACPI_6_3_GIC_ITS_AFFINITY_STRUCTURE * GicItsAff;
+
+ ASSERT (Srat != NULL);
+ ASSERT (GicItsInfo != NULL);
+
+ GicItsAff = (EFI_ACPI_6_3_GIC_ITS_AFFINITY_STRUCTURE *)((UINT8*)Srat +
+ GicItsAffOffset);
+
+ while (GicItsCount-- != 0) {
+ DEBUG ((DEBUG_INFO, "SRAT: GicItsAff = 0x%p\n", GicItsAff));
+
+ GicItsAff->Type = EFI_ACPI_6_3_GIC_ITS_AFFINITY;
+ GicItsAff->Length = sizeof (EFI_ACPI_6_3_GIC_ITS_AFFINITY_STRUCTURE);
+ GicItsAff->ProximityDomain = GicItsInfo->ProximityDomain;
+ GicItsAff->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
+ GicItsAff->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
+ GicItsAff->ItsId = GicItsInfo->GicItsId;
+
+ // Next
+ GicItsAff++;
+ GicItsInfo++;
+ }// while
+ return EFI_SUCCESS;
+}
+
+/** Add the Memory Affinity Structures in the SRAT Table.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Srat Pointer to the SRAT Table.
+ @param [in] MemAffOffset Offset of the Memory Affinity
+ information in the SRAT Table.
+ @param [in] MemAffInfo Pointer to the Memory Affinity Information list.
+ @param [in] MemAffCount Count of Memory Affinity objects.
+
+ @retval EFI_SUCCESS Table generated successfully.
+**/
+STATIC
+EFI_STATUS
+AddMemoryAffinity (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER * CONST Srat,
+ IN CONST UINT32 MemAffOffset,
+ IN CONST CM_ARM_MEMORY_AFFINITY_INFO * MemAffInfo,
+ IN UINT32 MemAffCount
+ )
+{
+ EFI_ACPI_6_3_MEMORY_AFFINITY_STRUCTURE * MemAff;
+
+ ASSERT (Srat != NULL);
+ ASSERT (MemAffInfo != NULL);
+
+ MemAff = (EFI_ACPI_6_3_MEMORY_AFFINITY_STRUCTURE *)((UINT8*)Srat +
+ MemAffOffset);
+
+ while (MemAffCount-- != 0) {
+ DEBUG ((DEBUG_INFO, "SRAT: MemAff = 0x%p\n", MemAff));
+
+ MemAff->Type = EFI_ACPI_6_3_MEMORY_AFFINITY;
+ MemAff->Length = sizeof (EFI_ACPI_6_3_MEMORY_AFFINITY_STRUCTURE);
+ MemAff->ProximityDomain = MemAffInfo->ProximityDomain;
+ MemAff->Reserved1 = EFI_ACPI_RESERVED_WORD;
+ MemAff->AddressBaseLow = (UINT32)(MemAffInfo->BaseAddress & MAX_UINT32);
+ MemAff->AddressBaseHigh = (UINT32)(MemAffInfo->BaseAddress >> 32);
+ MemAff->LengthLow = (UINT32)(MemAffInfo->Length & MAX_UINT32);
+ MemAff->LengthHigh = (UINT32)(MemAffInfo->Length >> 32);
+ MemAff->Reserved2 = EFI_ACPI_RESERVED_DWORD;
+ MemAff->Flags = MemAffInfo->Flags;
+ MemAff->Reserved3 = EFI_ACPI_RESERVED_QWORD;
+
+ // Next
+ MemAff++;
+ MemAffInfo++;
+ }// while
+ return EFI_SUCCESS;
+}
+
+
+/** Add the Generic Initiator Affinity Structures in the SRAT Table.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Srat Pointer to the SRAT Table.
+ @param [in] GenInitAffOff Offset of the Generic Initiator Affinity
+ information in the SRAT Table.
+ @param [in] GenInitAffInfo Pointer to the Generic Initiator Affinity
+ Information list.
+ @param [in] GenInitAffCount Count of Generic Initiator Affinity
+ objects.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object information is not found.
+ @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration
+ Manager is less than the Object size for the
+ requested object.
+**/
+STATIC
+EFI_STATUS
+AddGenericInitiatorAffinity (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER * CONST Srat,
+ IN CONST UINT32 GenInitAffOff,
+ IN CONST CM_ARM_GENERIC_INITIATOR_AFFINITY_INFO * GenInitAffInfo,
+ IN UINT32 GenInitAffCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY_STRUCTURE * GenInitAff;
+ CM_ARM_DEVICE_HANDLE_ACPI * DeviceHandleAcpi;
+ CM_ARM_DEVICE_HANDLE_PCI * DeviceHandlePci;
+ UINT32 DeviceHandleCount;
+
+ ASSERT (Srat != NULL);
+ ASSERT (GenInitAffInfo != NULL);
+
+ GenInitAff = (EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY_STRUCTURE *)(
+ (UINT8*)Srat + GenInitAffOff);
+
+ while (GenInitAffCount-- != 0) {
+ DEBUG ((DEBUG_INFO, "SRAT: GenInitAff = 0x%p\n", GenInitAff));
+
+ GenInitAff->Type = EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY;
+ GenInitAff->Length =
+ sizeof (EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY_STRUCTURE);
+ GenInitAff->Reserved1 = EFI_ACPI_RESERVED_WORD;
+ GenInitAff->DeviceHandleType = GenInitAffInfo->DeviceHandleType;
+ GenInitAff->ProximityDomain = GenInitAffInfo->ProximityDomain;
+
+ if (GenInitAffInfo->DeviceHandleToken == CM_NULL_TOKEN) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Invalid Device Handle Token.\n"
+ ));
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (GenInitAffInfo->DeviceHandleType == EFI_ACPI_6_3_ACPI_DEVICE_HANDLE) {
+ Status = GetEArmObjDeviceHandleAcpi (
+ CfgMgrProtocol,
+ GenInitAffInfo->DeviceHandleToken,
+ &DeviceHandleAcpi,
+ &DeviceHandleCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to get ACPI Device Handle Inf."
+ " DeviceHandleToken = %p."
+ " Status = %r\n",
+ GenInitAffInfo->DeviceHandleToken,
+ Status
+ ));
+ return Status;
+ }
+
+ // We are expecting only one device handle.
+ ASSERT (DeviceHandleCount == 1);
+
+ // Populate the ACPI device handle information.
+ GenInitAff->DeviceHandle.Acpi.AcpiHid = DeviceHandleAcpi->Hid;
+ GenInitAff->DeviceHandle.Acpi.AcpiUid = DeviceHandleAcpi->Uid;
+ GenInitAff->DeviceHandle.Acpi.Reserved[0] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Acpi.Reserved[1] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Acpi.Reserved[2] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Acpi.Reserved[3] = EFI_ACPI_RESERVED_BYTE;
+ } else if (GenInitAffInfo->DeviceHandleType ==
+ EFI_ACPI_6_3_PCI_DEVICE_HANDLE) {
+ Status = GetEArmObjDeviceHandlePci (
+ CfgMgrProtocol,
+ GenInitAffInfo->DeviceHandleToken,
+ &DeviceHandlePci,
+ &DeviceHandleCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to get ACPI Device Handle Inf."
+ " DeviceHandleToken = %p."
+ " Status = %r\n",
+ GenInitAffInfo->DeviceHandleToken,
+ Status
+ ));
+ return Status;
+ }
+
+ // We are expecting only one device handle
+ ASSERT (DeviceHandleCount == 1);
+
+ // Populate the ACPI device handle information.
+ GenInitAff->DeviceHandle.Pci.PciSegment = DeviceHandlePci->SegmentNumber;
+ GenInitAff->DeviceHandle.Pci.PciBdfNumber = GetBdf (DeviceHandlePci);
+
+ GenInitAff->DeviceHandle.Pci.Reserved[0] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[1] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[2] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[3] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[4] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[5] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[6] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[7] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[8] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[9] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[10] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->DeviceHandle.Pci.Reserved[11] = EFI_ACPI_RESERVED_BYTE;
+ } else {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Invalid Device Handle Type.\n"
+ ));
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ GenInitAff->Flags = GenInitAffInfo->Flags;
+ GenInitAff->Reserved2[0] = EFI_ACPI_RESERVED_BYTE;
+ GenInitAff->Reserved2[1] = EFI_ACPI_RESERVED_BYTE;
+
+ // Next
+ GenInitAff++;
+ GenInitAffInfo++;
+ }// while
+ return EFI_SUCCESS;
+}
+
+/** Construct the SRAT ACPI table.
+
+ Called by the Dynamic Table Manager, 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.
+
+ @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.
+
+ @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_OUT_OF_RESOURCES Memory allocation failed.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildSratTable (
+ 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
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TableSize;
+ UINT32 GicCCount;
+ UINT32 GicItsCount;
+ UINT32 MemAffCount;
+ UINT32 GenInitiatorAffCount;
+
+ UINT32 GicCAffOffset;
+ UINT32 GicItsAffOffset;
+ UINT32 MemAffOffset;
+ UINT32 GenInitiatorAffOffset;
+
+ CM_ARM_GICC_INFO * GicCInfo;
+ CM_ARM_GIC_ITS_INFO * GicItsInfo;
+ CM_ARM_MEMORY_AFFINITY_INFO * MemAffInfo;
+ CM_ARM_GENERIC_INITIATOR_AFFINITY_INFO * GenInitiatorAffInfo;
+
+ EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER * Srat;
+
+ ASSERT (
+ (This != NULL) &&
+ (AcpiTableInfo != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Table != NULL) &&
+ (AcpiTableInfo->TableGeneratorId == This->GeneratorID) &&
+ (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature)
+ );
+
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Requested table revision = %d is not supported. "
+ "Supported table revisions: Minimum = %d. Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Table = NULL;
+
+ Status = GetEArmObjGicCInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GicCInfo,
+ &GicCCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to get GICC Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ if (GicCCount == 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: GIC CPU Interface information not provided.\n"
+ ));
+ ASSERT (0);
+ Status = EFI_INVALID_PARAMETER;
+ goto error_handler;
+ }
+
+ Status = GetEArmObjGicItsInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GicItsInfo,
+ &GicItsCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to get GIC ITS Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Status = GetEArmObjMemoryAffinityInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &MemAffInfo,
+ &MemAffCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to get Memory Affinity Info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Status = GetEArmObjGenericInitiatorAffinityInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GenInitiatorAffInfo,
+ &GenInitiatorAffCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to get Generic Initiator Affinity Info."
+ " Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Calculate the size of the SRAT table
+ TableSize = sizeof (EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER);
+
+ GicCAffOffset = TableSize;
+ TableSize += (sizeof (EFI_ACPI_6_3_GICC_AFFINITY_STRUCTURE) * GicCCount);
+
+ if (GicItsCount != 0) {
+ GicItsAffOffset = TableSize;
+ TableSize += (sizeof (EFI_ACPI_6_3_GIC_ITS_AFFINITY_STRUCTURE) *
+ GicItsCount);
+ }
+
+ if (MemAffCount != 0) {
+ MemAffOffset = TableSize;
+ TableSize += (sizeof (EFI_ACPI_6_3_MEMORY_AFFINITY_STRUCTURE) *
+ MemAffCount);
+ }
+
+ if (GenInitiatorAffCount != 0) {
+ GenInitiatorAffOffset = TableSize;
+ TableSize += (sizeof (EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY_STRUCTURE) *
+ GenInitiatorAffCount);
+ }
+
+ // Allocate the Buffer for SRAT table
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize);
+ if (*Table == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to allocate memory for SRAT Table, Size = %d," \
+ " Status = %r\n",
+ TableSize,
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Srat = (EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER*)*Table;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "SRAT: Srat = 0x%p TableSize = 0x%x\n",
+ Srat,
+ TableSize
+ ));
+
+ Status = AddAcpiHeader (
+ CfgMgrProtocol,
+ This,
+ &Srat->Header,
+ AcpiTableInfo,
+ TableSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Setup the Reserved fields
+ // Reserved1 must be set to 1 for backward compatibility
+ Srat->Reserved1 = 1;
+ Srat->Reserved2 = EFI_ACPI_RESERVED_QWORD;
+
+ Status = AddGICCAffinity (
+ CfgMgrProtocol,
+ Srat,
+ GicCAffOffset,
+ GicCInfo,
+ GicCCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to add GICC Affinity structures. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ if (GicItsCount != 0) {
+ Status = AddGICItsAffinity (
+ CfgMgrProtocol,
+ Srat,
+ GicItsAffOffset,
+ GicItsInfo,
+ GicItsCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to add GIC ITS Affinity structures. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ if (MemAffCount != 0) {
+ Status = AddMemoryAffinity (
+ CfgMgrProtocol,
+ Srat,
+ MemAffOffset,
+ MemAffInfo,
+ MemAffCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to add Memory Affinity structures. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ if (GenInitiatorAffCount != 0) {
+ Status = AddGenericInitiatorAffinity (
+ CfgMgrProtocol,
+ Srat,
+ GenInitiatorAffOffset,
+ GenInitiatorAffInfo,
+ GenInitiatorAffCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SRAT: Failed to add Generic Initiator Affinity structures."
+ " Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ return Status;
+
+error_handler:
+
+ if (*Table != NULL) {
+ FreePool (*Table);
+ *Table = NULL;
+ }
+
+ return Status;
+}
+
+/** Free any resources allocated for constructing the SRAT.
+
+ @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 [in, out] Table Pointer to the ACPI Table.
+
+ @retval EFI_SUCCESS The resources were freed successfully.
+ @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
+**/
+STATIC
+EFI_STATUS
+FreeSratTableResources (
+ 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
+ )
+{
+ ASSERT (
+ (This != NULL) &&
+ (AcpiTableInfo != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (AcpiTableInfo->TableGeneratorId == This->GeneratorID) &&
+ (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature)
+ );
+
+ if ((Table == NULL) || (*Table == NULL)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: SRAT: Invalid Table Pointer\n"));
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (*Table);
+ *Table = NULL;
+ return EFI_SUCCESS;
+}
+
+/** The SRAT Table Generator revision.
+*/
+#define SRAT_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the SRAT Table Generator.
+*/
+STATIC
+CONST
+ACPI_TABLE_GENERATOR SratGenerator = {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSrat),
+ // Generator Description
+ L"ACPI.STD.SRAT.GENERATOR",
+ // ACPI Table Signature
+ EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_REVISION,
+ // Minimum supported ACPI Table Revision
+ EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ SRAT_GENERATOR_REVISION,
+ // Build Table function
+ BuildSratTable,
+ // Free Resource function
+ FreeSratTableResources,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+};
+
+/** 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
+AcpiSratLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&SratGenerator);
+ DEBUG ((DEBUG_INFO, "SRAT: 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
+AcpiSratLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&SratGenerator);
+ DEBUG ((DEBUG_INFO, "SRAT: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c
new file mode 100644
index 00000000..a0c4a7fa
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c
@@ -0,0 +1,708 @@
+/** @file
+ SSDT CMN-600 AML Table Generator.
+
+ Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - Arm CoreLink CMN-600 Coherent Mesh Network Technical Reference Manual r3p0
+ - Generic ACPI for Arm Components 1.0 Platform Design Document
+**/
+
+#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>
+#include "SsdtCmn600Generator.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 ssdtcmn600template_aml_code[];
+
+/** SSDT CMN-600 Table Generator.
+
+ Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArmObjCmn600Info
+*/
+
+/** This macro expands to a function that retrieves the CMN-600
+ Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjCmn600Info,
+ CM_ARM_CMN_600_INFO
+ );
+
+/** Check the CMN-600 Information.
+
+ @param [in] Cmn600InfoList Array of CMN-600 information structure.
+ @param [in] Cmn600Count Count of CMN-600 information structure.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ValidateCmn600Info (
+ IN CONST CM_ARM_CMN_600_INFO * Cmn600InfoList,
+ IN CONST UINT32 Cmn600Count
+ )
+{
+ UINT32 Index;
+ UINT32 DtcIndex;
+ CONST CM_ARM_CMN_600_INFO * Cmn600Info;
+ CONST CM_ARM_GENERIC_INTERRUPT * DtcInterrupt;
+
+ if ((Cmn600InfoList == NULL) ||
+ (Cmn600Count == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Validate each Cmn600Info structure.
+ for (Index = 0; Index < Cmn600Count; Index++) {
+ Cmn600Info = &Cmn600InfoList[Index];
+
+ // At least one DTC is required.
+ if ((Cmn600Info->DtcCount == 0) ||
+ (Cmn600Info->DtcCount > MAX_DTC_COUNT)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: Invalid DTC configuration:\n"
+ ));
+ goto error_handler;
+ }
+
+ // Check PERIPHBASE and ROOTNODEBASE address spaces are initialized.
+ if ((Cmn600Info->PeriphBaseAddress == 0) ||
+ (Cmn600Info->RootNodeBaseAddress == 0)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: Invalid PERIPHBASE or ROOTNODEBASE.\n"
+ ));
+ goto error_handler;
+ }
+
+ // The PERIPHBASE address must be 64MB aligned for a (X < 4) && (Y < 4)
+ // dimension mesh, and 256MB aligned otherwise.
+ // Check it is a least 64MB aligned.
+ if ((Cmn600Info->PeriphBaseAddress &
+ (PERIPHBASE_MIN_ADDRESS_LENGTH - 1)) != 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: PERIPHBASE address must be 64MB aligned.\n"
+ ));
+ goto error_handler;
+ }
+
+ // The PERIPHBASE address is at most 64MB for a (X < 4) && (Y < 4)
+ // dimension mesh, and 256MB otherwise. Check it is not more than 256MB.
+ if (Cmn600Info->PeriphBaseAddressLength > PERIPHBASE_MAX_ADDRESS_LENGTH) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: PERIPHBASE address range must be < 256MB.\n"
+ ));
+ goto error_handler;
+ }
+
+ // Check the 16 KB alignment of the ROOTNODEBASE address.
+ if ((Cmn600Info->PeriphBaseAddress &
+ (ROOTNODEBASE_ADDRESS_LENGTH - 1)) != 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: Root base address must be 16KB aligned.\n"
+ ));
+ goto error_handler;
+ }
+
+ // The ROOTNODEBASE address space should be included in the PERIPHBASE
+ // address space.
+ if ((Cmn600Info->PeriphBaseAddress > Cmn600Info->RootNodeBaseAddress) ||
+ ((Cmn600Info->PeriphBaseAddress + Cmn600Info->PeriphBaseAddressLength) <
+ (Cmn600Info->RootNodeBaseAddress + ROOTNODEBASE_ADDRESS_LENGTH))) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600:"
+ " ROOTNODEBASE address space not in PERIPHBASE address space.\n"
+ ));
+ goto error_handler;
+ }
+
+ for (DtcIndex = 0; DtcIndex < Cmn600Info->DtcCount; DtcIndex++) {
+ DtcInterrupt = &Cmn600Info->DtcInterrupt[DtcIndex];
+ if (((DtcInterrupt->Flags &
+ EFI_ACPI_EXTENDED_INTERRUPT_FLAG_PRODUCER_CONSUMER_MASK) == 0)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: DTC Interrupt must be consumer.\n"
+ ));
+ goto error_handler;
+ }
+ } // for DTC Interrupt
+
+ } //for Cmn600InfoList
+
+ return EFI_SUCCESS;
+
+error_handler:
+
+ DEBUG ((
+ DEBUG_ERROR,
+ "PeriphBaseAddress = 0x%llx\n"
+ "PeriphBaseAddressLength = 0x%llx\n"
+ "RootNodeBaseAddress = 0x%llx\n"
+ "DtcCount = %u\n",
+ Cmn600Info->PeriphBaseAddress,
+ Cmn600Info->PeriphBaseAddressLength,
+ Cmn600Info->RootNodeBaseAddress,
+ Cmn600Info->DtcCount
+ ));
+
+ DEBUG_CODE (
+ for (DtcIndex = 0; DtcIndex < Cmn600Info->DtcCount; DtcIndex++) {
+ DtcInterrupt = &Cmn600Info->DtcInterrupt[DtcIndex];
+ DEBUG ((
+ DEBUG_ERROR,
+ " DTC[%d]:\n",
+ DtcIndex
+ ));
+ DEBUG ((
+ DEBUG_ERROR,
+ " Interrupt = 0x%lx\n",
+ DtcInterrupt->Interrupt
+ ));
+ DEBUG ((
+ DEBUG_ERROR,
+ " Flags = 0x%lx\n",
+ DtcInterrupt->Flags
+ ));
+ } // for
+ );
+
+ return EFI_INVALID_PARAMETER;
+}
+
+/** Build a SSDT table describing the CMN-600 device.
+
+ The table created by this function must be freed by FreeSsdtCmn600Table.
+
+ @param [in] Cmn600Info Pointer to a Cmn600 structure.
+ @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 CMN600 device.
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FixupCmn600Info (
+ IN CONST CM_ARM_CMN_600_INFO * Cmn600Info,
+ IN CONST CHAR8 * Name,
+ IN CONST UINT64 Uid,
+ OUT EFI_ACPI_DESCRIPTION_HEADER ** Table
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS Status1;
+ UINT8 Index;
+ CONST CM_ARM_GENERIC_INTERRUPT * DtcInt;
+
+ EFI_ACPI_DESCRIPTION_HEADER * SsdtCmn600Template;
+ AML_ROOT_NODE_HANDLE RootNodeHandle;
+ AML_OBJECT_NODE_HANDLE NameOpIdNode;
+ AML_OBJECT_NODE_HANDLE NameOpCrsNode;
+ AML_DATA_NODE_HANDLE CmnPeriphBaseRdNode;
+ AML_DATA_NODE_HANDLE CmnRootNodeBaseRdNode;
+ AML_OBJECT_NODE_HANDLE DeviceNode;
+
+ // Parse the Ssdt CMN-600 Template.
+ SsdtCmn600Template = (EFI_ACPI_DESCRIPTION_HEADER*)
+ ssdtcmn600template_aml_code;
+
+ RootNodeHandle = NULL;
+ Status = AmlParseDefinitionBlock (
+ SsdtCmn600Template,
+ &RootNodeHandle
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: Failed to parse SSDT CMN-600 Template."
+ " Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // Get the _UID NameOp object defined by the "Name ()" statement,
+ // and update its value.
+ Status = AmlFindNode (
+ RootNodeHandle,
+ "\\_SB_.CMN0._UID",
+ &NameOpIdNode
+ );
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ Status = AmlNameOpUpdateInteger (NameOpIdNode, (UINT64)Uid);
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ // Get the _CRS object defined by the "Name ()" statement.
+ Status = AmlFindNode (
+ RootNodeHandle,
+ "\\_SB.CMN0._CRS",
+ &NameOpCrsNode
+ );
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ // Get the first Rd node in the "_CRS" object.
+ // This is the PERIPHBASE node.
+ Status = AmlNameOpCrsGetFirstRdNode (NameOpCrsNode, &CmnPeriphBaseRdNode);
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ if (CmnPeriphBaseRdNode == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ goto error_handler;
+ }
+
+ // Update the PERIPHBASE base address and length.
+ Status = AmlUpdateRdQWord (
+ CmnPeriphBaseRdNode,
+ Cmn600Info->PeriphBaseAddress,
+ Cmn600Info->PeriphBaseAddressLength
+ );
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ // Get the QWord node corresponding to the ROOTNODEBASE.
+ // It is the second Resource Data element in the BufferNode's
+ // variable list of arguments.
+ Status = AmlNameOpCrsGetNextRdNode (
+ CmnPeriphBaseRdNode,
+ &CmnRootNodeBaseRdNode
+ );
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ if (CmnRootNodeBaseRdNode == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ goto error_handler;
+ }
+
+ // Update the ROOTNODEBASE base address and length.
+ Status = AmlUpdateRdQWord (
+ CmnRootNodeBaseRdNode,
+ Cmn600Info->RootNodeBaseAddress,
+ ROOTNODEBASE_ADDRESS_LENGTH
+ );
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ // Add the Interrupt node(s).
+ // Generate Resource Data node(s) corresponding to the "Interrupt ()"
+ // ASL function and add it at the last position in the list of
+ // Resource Data nodes.
+ for (Index = 0; Index < Cmn600Info->DtcCount; Index++) {
+ DtcInt = &Cmn600Info->DtcInterrupt[Index];
+ Status = AmlCodeGenCrsAddRdInterrupt (
+ NameOpCrsNode,
+ ((DtcInt->Flags &
+ EFI_ACPI_EXTENDED_INTERRUPT_FLAG_PRODUCER_CONSUMER_MASK) != 0),
+ ((DtcInt->Flags &
+ EFI_ACPI_EXTENDED_INTERRUPT_FLAG_MODE_MASK) != 0),
+ ((DtcInt->Flags &
+ EFI_ACPI_EXTENDED_INTERRUPT_FLAG_POLARITY_MASK) != 0),
+ ((DtcInt->Flags &
+ EFI_ACPI_EXTENDED_INTERRUPT_FLAG_SHARABLE_MASK) != 0),
+ (UINT32*)&DtcInt->Interrupt,
+ 1
+ );
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+ } // for
+
+ // Fixup the CMN600 device name.
+ // This MUST be done at the end, otherwise AML paths won't be valid anymore.
+ // Get the CMN0 variable defined by the "Device ()" statement.
+ Status = AmlFindNode (RootNodeHandle, "\\_SB_.CMN0", &DeviceNode);
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ // Update the CMN600 Device's name.
+ Status = AmlDeviceOpUpdateName (DeviceNode, (CHAR8*)Name);
+ if (EFI_ERROR (Status)) {
+ goto error_handler;
+ }
+
+ // Serialise the definition block
+ Status = AmlSerializeDefinitionBlock (
+ RootNodeHandle,
+ Table
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: Failed to Serialize SSDT Table Data."
+ " Status = %r\n",
+ Status
+ ));
+ }
+
+error_handler:
+ // Cleanup
+ if (RootNodeHandle != NULL) {
+ Status1 = AmlDeleteTree (RootNodeHandle);
+ if (EFI_ERROR (Status1)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: 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;
+}
+
+/** Free any resources allocated for constructing the SSDT tables for CMN-600.
+
+ @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
+FreeSsdtCmn600TableResourcesEx (
+ 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_ACPI_DESCRIPTION_HEADER ** TableList;
+ UINTN Index;
+
+ 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 == 0)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: SSDT-CMN-600: 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)) {
+ FreePool (TableList[Index]);
+ } else {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: Could not free SSDT table at index %d."
+ " Status = %r\n",
+ Index,
+ EFI_INVALID_PARAMETER
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+ } //for
+
+ // Free the table list.
+ FreePool (*Table);
+ *Table = NULL;
+ return EFI_SUCCESS;
+}
+
+/** Construct SSDT tables for describing CMN-600 meshes.
+
+ 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
+BuildSsdtCmn600TableEx (
+ 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;
+ UINT64 Index;
+ CM_ARM_CMN_600_INFO * Cmn600Info;
+ UINT32 Cmn600Count;
+ CHAR8 NewName[5];
+ 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;
+
+ // Get CMN-600 information.
+ Status = GetEArmObjCmn600Info (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &Cmn600Info,
+ &Cmn600Count
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: Failed to get the CMN-600 information."
+ " Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ if ((Cmn600Count == 0) || (Cmn600Count > MAX_CMN600_DEVICES_SUPPORTED)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: CMN600 peripheral count = %d."
+ " This must be between 1 to 16.\n",
+ Cmn600Count
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Validate the CMN-600 Info.
+ Status = ValidateCmn600Info (Cmn600Info, Cmn600Count);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: Invalid CMN600 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*) * Cmn600Count)
+ );
+ if (TableList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: 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;
+
+ NewName[0] = 'C';
+ NewName[1] = 'M';
+ NewName[2] = 'N';
+ NewName[4] = '\0';
+ for (Index = 0; Index < Cmn600Count; Index++) {
+ NewName[3] = AsciiFromHex ((UINT8)(Index));
+
+ // Build a SSDT table describing the CMN600 device.
+ Status = FixupCmn600Info (
+ &Cmn600Info[Index],
+ NewName,
+ Index,
+ &TableList[Index]
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CMN-600: Failed to build associated SSDT table."
+ " Status = %r\n",
+ Status
+ ));
+ break;
+ }
+
+ // Increment the table count here so that appropriate clean-up
+ // can be done in case of failure.
+ *TableCount += 1;
+ } // for
+
+ // Note: Table list and CMN600 device count has been setup. The
+ // framework will invoke FreeSsdtCmn600TableResourcesEx() even
+ // on failure, so appropriate clean-up will be done.
+ return Status;
+}
+
+/** This macro defines the Raw Generator revision.
+*/
+#define SSDT_CMN_600_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the Raw Table Generator.
+*/
+STATIC
+CONST
+ACPI_TABLE_GENERATOR SsdtCmn600Generator = {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtCmn600),
+ // Generator Description
+ L"ACPI.STD.SSDT.CMN600.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_CMN_600_GENERATOR_REVISION,
+ // Build table function. Use the extended version instead.
+ NULL,
+ // Free table function. Use the extended version instead.
+ NULL,
+ // Build Table function
+ BuildSsdtCmn600TableEx,
+ // Free Resource function
+ FreeSsdtCmn600TableResourcesEx
+};
+
+/** 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
+AcpiSsdtCmn600LibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = RegisterAcpiTableGenerator (&SsdtCmn600Generator);
+ DEBUG ((
+ DEBUG_INFO,
+ "SSDT-CMN-600: 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
+AcpiSsdtCmn600LibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = DeregisterAcpiTableGenerator (&SsdtCmn600Generator);
+ DEBUG ((
+ DEBUG_INFO,
+ "SSDT-CMN-600: Deregister Generator. Status = %r\n",
+ Status
+ ));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h
new file mode 100644
index 00000000..a7ecb940
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h
@@ -0,0 +1,51 @@
+/** @file
+
+ Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+ - Std or STD - Standard
+
+ @par Reference(s):
+ - Arm CoreLink CMN-600 Coherent Mesh Network Technical Reference Manual r3p0
+ - Generic ACPI for Arm Components 1.0 Platform Design Document
+**/
+
+#ifndef SSDT_CMN600_GENERATOR_H_
+#define SSDT_CMN600_GENERATOR_H_
+
+/** PeriphBase maximum address length is 256MB (0x10000000)
+ for a (X >= 4) || (Y >= 4) dimensions mesh.
+*/
+#define PERIPHBASE_MAX_ADDRESS_LENGTH SIZE_256MB
+
+/** PeriphBase minimum address length is 64MB (0x04000000)
+ for a (X < 4) && (Y < 4) dimensions mesh.
+*/
+#define PERIPHBASE_MIN_ADDRESS_LENGTH SIZE_64MB
+
+/** RootNodeBase address length is 16KB (0x00004000).
+*/
+#define ROOTNODEBASE_ADDRESS_LENGTH SIZE_16KB
+
+/** Maximum number of CMN-600 Debug and Trace Logic Controllers (DTC).
+*/
+#define MAX_DTC_COUNT 4
+
+/** Starting value for the UID to represent the CMN600 devices.
+*/
+#define CMN600_DEVICE_START_UID 0
+
+/** Maximum CMN-600 devices supported by this generator.
+ This generator supports a maximum of 16 CMN-600 devices.
+ 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_CMN600_DEVICES_SUPPORTED 16
+
+#endif // SSDT_CMN600_GENERATOR_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf
new file mode 100644
index 00000000..9f10c987
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf
@@ -0,0 +1,34 @@
+## @file
+# Ssdt CMN-600 Table Generator
+#
+# Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = SsdtCmn600LibArm
+ FILE_GUID = CEDB450D-8F0E-4ACC-8FB7-F72EC7D216A4
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiSsdtCmn600LibConstructor
+ DESTRUCTOR = AcpiSsdtCmn600LibDestructor
+
+[Sources]
+ SsdtCmn600Generator.c
+ SsdtCmn600Generator.h
+ SsdtCmn600Template.asl
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ ArmPlatformPkg/ArmPlatformPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ AmlLib
+ BaseLib
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl
new file mode 100644
index 00000000..33c49f8f
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl
@@ -0,0 +1,81 @@
+/** @file
+ SSDT CMN-600 Template
+
+ Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - Arm CoreLink CMN-600 Coherent Mesh Network Technical Reference Manual r3p0
+ - Generic ACPI for Arm Components 1.0 Platform Design Document
+
+ @par Glossary:
+ - {template} - Data fixed up using AML Fixup APIs.
+ - {codegen} - Data generated using AML Codegen APIs.
+**/
+
+DefinitionBlock ("SsdtCmn600.aml", "SSDT", 2, "ARMLTD", "CMN-600", 1) {
+ Scope (_SB) {
+ // CMN-600 device object for a X * Y mesh, where (X >= 4) || (Y >= 4).
+ Device (CMN0) { // {template}
+ Name (_HID, "ARMHC600")
+ Name (_UID, 0x0) // {template}
+
+ Name (_CRS, ResourceTemplate () {
+ // Descriptor for 256 MB of the CFG region at offset PERIPHBASE.
+ QWordMemory (
+ ResourceConsumer, // bit 0 of general flags is 0.
+ PosDecode,
+ MinFixed, // Range is fixed.
+ MaxFixed, // Range is Fixed.
+ NonCacheable,
+ ReadWrite,
+ 0x00000000, // Granularity
+ 0xA0000000, // MinAddress // {template}
+ 0xAFFFFFFF, // MaxAddress // {template}
+ 0x00000000, // Translation
+ 0x10000000, // RangeLength // {template}
+ , // ResourceSourceIndex
+ , // ResourceSource
+ CFGR // DescriptorName
+ ) // QWordMemory
+
+ // Descriptor for the root node. This is a 16 KB region at offset
+ // ROOTNODEBASE. In this example, ROOTNODEBASE starts at the 16 KB
+ // aligned offset of PERIPHBASE.
+ QWordMemory (
+ ResourceConsumer, // bit 0 of general flags is 0.
+ PosDecode,
+ MinFixed, // Range is fixed.
+ MaxFixed, // Range is Fixed.
+ NonCacheable,
+ ReadWrite,
+ 0x00000000, // Granularity
+ 0xA0000000, // MinAddress // {template}
+ 0xAFFFFFFF, // MaxAddress // {template}
+ 0x00000000, // Translation
+ 0x10000000, // RangeLength // {template}
+ , // ResourceSourceIndex
+ , // ResourceSource
+ ROOT // DescriptorName
+ ) // QWordMemory
+
+ // The Interrupt information is generated using AmlCodegen.
+ // Interrupt on PMU0 overflow, attached to DTC [0], with GSIV = <gsiv0>.
+ //
+ // Interrupt ( // {codegen}
+ // ResourceConsumer, // ResourceUsage
+ // Level, // EdgeLevel
+ // ActiveHigh, // ActiveLevel
+ // Exclusive, // Shared
+ // , // ResourceSourceIndex
+ // , // ResourceSource
+ // // DescriptorName
+ // ) {
+ // 0xA5 // <gsiv0 >
+ // } // Interrupt
+
+ }) // Name
+ } // Device
+ } // _SB
+} // DefinitionBlock
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c
new file mode 100644
index 00000000..0af74613
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c
@@ -0,0 +1,371 @@
+/** @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 != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ 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[5];
+ 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;
+
+ NewName[0] = 'C';
+ NewName[1] = 'O';
+ NewName[2] = 'M';
+ NewName[4] = '\0';
+ 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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
new file mode 100644
index 00000000..36907c79
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h
new file mode 100644
index 00000000..cd363ccf
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c
new file mode 100644
index 00000000..9986d428
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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) {
+ AMLDBG_PRINT_NAMESEG (Buffer);
+ Buffer += AML_NAME_SEG_SIZE;
+ for (Index = 0; Index < SegCount - 1; Index++) {
+ DEBUG ((DEBUG_INFO, "."));
+ AMLDBG_PRINT_NAMESEG (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)) {
+ AMLDBG_PRINT_CHARS (
+ 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)) {
+ AMLDBG_PRINT_NAMESTR (
+ 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
+AmlDbgDumpRaw (
+ 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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h
new file mode 100644
index 00000000..5e43f011
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h
@@ -0,0 +1,178 @@
+/** @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
+AmlDbgDumpRaw (
+ 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
+ );
+
+/* Macros to encapsulate Aml Debug Print APIs.
+*/
+
+#define AMLDBG_DUMP_RAW(Ptr, Length) \
+ AmlDbgDumpRaw (Ptr, Length)
+
+#define AMLDBG_PRINT_CHARS(ErrorLevel, Buffer, Size) \
+ AmlDbgPrintChars (ErrorLevel, Buffer, Size)
+
+#define AMLDBG_PRINT_NAMESEG(Buffer) \
+ AmlDbgPrintNameSeg (Buffer)
+
+#define AMLDBG_PRINT_NAMESTR(Buffer,NewLine) \
+ AmlDbgPrintNameString (Buffer,NewLine)
+
+#define AMLDBG_PRINT_NODE(Node) \
+ AmlDbgPrintNode (Node)
+
+#define AMLDBG_PRINT_TREE(Node) \
+ AmlDbgPrintTree (Node)
+
+#define AMLDBG_PRINT_NAMESPACE(RootNode) \
+ AmlDbgPrintNameSpace (RootNode)
+
+#else
+
+#define AMLDBG_DUMP_RAW(Ptr, Length)
+
+#define AMLDBG_PRINT_CHARS(ErrorLevel, Buffer, Size)
+
+#define AMLDBG_PRINT_NAMESEG(Buffer)
+
+#define AMLDBG_PRINT_NAMESTR(Buffer,NewLine)
+
+#define AMLDBG_PRINT_NODE(Node)
+
+#define AMLDBG_PRINT_TREE(Node)
+
+#define AMLDBG_PRINT_NAMESPACE(RootNode)
+
+#endif // MDEPKG_NDEBUG
+
+#endif // AML_PRINT_H_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h
new file mode 100644
index 00000000..b58f323e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c
new file mode 100644
index 00000000..2027d5d5
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_OPCODE_DEF ("ZeroOp", AML_ZERO_OP), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x01 */ {AML_OPCODE_DEF ("OneOp", AML_ONE_OP), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x06 */ {AML_OPCODE_DEF ("AliasOp", AML_ALIAS_OP), 0, 2, 1, {EAmlName, EAmlName, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x08 */ {AML_OPCODE_DEF ("NameOp", AML_NAME_OP), 0, 2, 0, {EAmlName, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x0A */ {AML_OPCODE_DEF ("BytePrefix", AML_BYTE_PREFIX), 0, 1, 0, {EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x0B */ {AML_OPCODE_DEF ("WordPrefix", AML_WORD_PREFIX), 0, 1, 0, {EAmlUInt16, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x0C */ {AML_OPCODE_DEF ("DWordPrefix", AML_DWORD_PREFIX), 0, 1, 0, {EAmlUInt32, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x0D */ {AML_OPCODE_DEF ("StringPrefix", AML_STRING_PREFIX), 0, 1, 0, {EAmlString, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x0E */ {AML_OPCODE_DEF ("QWordPrefix", AML_QWORD_PREFIX), 0, 1, 0, {EAmlUInt64, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x10 */ {AML_OPCODE_DEF ("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_OPCODE_DEF ("BufferOp", AML_BUFFER_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_BYTE_LIST},
+ /* 0x12 */ {AML_OPCODE_DEF ("PackageOp", AML_PACKAGE_OP), 0, 1, 0, {EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ},
+ /* 0x13 */ {AML_OPCODE_DEF ("VarPackageOp", AML_VAR_PACKAGE_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ},
+ /* 0x14 */ {AML_OPCODE_DEF ("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_OPCODE_DEF ("ExternalOp", AML_EXTERNAL_OP), 0, 3, 0, {EAmlName, EAmlUInt8, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x2E */ {AML_OPCODE_DEF ("DualNamePrefix", AML_DUAL_NAME_PREFIX), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x2F */ {AML_OPCODE_DEF ("MultiNamePrefix", AML_MULTI_NAME_PREFIX), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x41 */ {AML_OPCODE_DEF ("NameChar_A", 'A'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x42 */ {AML_OPCODE_DEF ("NameChar_B", 'B'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x43 */ {AML_OPCODE_DEF ("NameChar_C", 'C'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x44 */ {AML_OPCODE_DEF ("NameChar_D", 'D'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x45 */ {AML_OPCODE_DEF ("NameChar_E", 'E'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x46 */ {AML_OPCODE_DEF ("NameChar_F", 'F'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x47 */ {AML_OPCODE_DEF ("NameChar_G", 'G'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x48 */ {AML_OPCODE_DEF ("NameChar_H", 'H'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x49 */ {AML_OPCODE_DEF ("NameChar_I", 'I'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x4A */ {AML_OPCODE_DEF ("NameChar_J", 'J'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x4B */ {AML_OPCODE_DEF ("NameChar_K", 'K'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x4C */ {AML_OPCODE_DEF ("NameChar_L", 'L'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x4D */ {AML_OPCODE_DEF ("NameChar_M", 'M'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x4E */ {AML_OPCODE_DEF ("NameChar_N", 'N'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x4F */ {AML_OPCODE_DEF ("NameChar_O", 'O'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x50 */ {AML_OPCODE_DEF ("NameChar_P", 'P'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x51 */ {AML_OPCODE_DEF ("NameChar_Q", 'Q'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x52 */ {AML_OPCODE_DEF ("NameChar_R", 'R'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x53 */ {AML_OPCODE_DEF ("NameChar_S", 'S'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x54 */ {AML_OPCODE_DEF ("NameChar_T", 'T'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x55 */ {AML_OPCODE_DEF ("NameChar_U", 'U'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x56 */ {AML_OPCODE_DEF ("NameChar_V", 'V'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x57 */ {AML_OPCODE_DEF ("NameChar_W", 'W'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x58 */ {AML_OPCODE_DEF ("NameChar_X", 'X'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x59 */ {AML_OPCODE_DEF ("NameChar_Y", 'Y'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x5A */ {AML_OPCODE_DEF ("NameChar_Z", 'Z'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x5B 0x01 */ {AML_OPCODE_DEF ("MutexOp", AML_EXT_OP), AML_EXT_MUTEX_OP, 2, 0, {EAmlName, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x5B 0x02 */ {AML_OPCODE_DEF ("EventOp", AML_EXT_OP), AML_EXT_EVENT_OP, 1, 0, {EAmlName, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x5B 0x12 */ {AML_OPCODE_DEF ("CondRefOfOp", AML_EXT_OP), AML_EXT_COND_REF_OF_OP, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x13 */ {AML_OPCODE_DEF ("CreateFieldOp", AML_EXT_OP), AML_EXT_CREATE_FIELD_OP,4, 3, {EAmlObject, EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x5B 0x1F */ {AML_OPCODE_DEF ("LoadTableOp", AML_EXT_OP), AML_EXT_LOAD_TABLE_OP, 6, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlObject, EAmlObject, EAmlObject}, 0},
+ /* 0x5B 0x20 */ {AML_OPCODE_DEF ("LoadOp", AML_EXT_OP), AML_EXT_LOAD_OP, 2, 0, {EAmlName, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x21 */ {AML_OPCODE_DEF ("StallOp", AML_EXT_OP), AML_EXT_STALL_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x22 */ {AML_OPCODE_DEF ("SleepOp", AML_EXT_OP), AML_EXT_SLEEP_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x23 */ {AML_OPCODE_DEF ("AcquireOp", AML_EXT_OP), AML_EXT_ACQUIRE_OP, 2, 0, {EAmlObject, EAmlUInt16, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x24 */ {AML_OPCODE_DEF ("SignalOp", AML_EXT_OP), AML_EXT_SIGNAL_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x25 */ {AML_OPCODE_DEF ("WaitOp", AML_EXT_OP), AML_EXT_WAIT_OP, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x26 */ {AML_OPCODE_DEF ("ResetOp", AML_EXT_OP), AML_EXT_RESET_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x27 */ {AML_OPCODE_DEF ("ReleaseOp", AML_EXT_OP), AML_EXT_RELEASE_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x28 */ {AML_OPCODE_DEF ("FromBCDOp", AML_EXT_OP), AML_EXT_FROM_BCD_OP, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x29 */ {AML_OPCODE_DEF ("ToBCDOp", AML_EXT_OP), AML_EXT_TO_BCD_OP, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x2A */ {AML_OPCODE_DEF ("UnloadOp", AML_EXT_OP), AML_EXT_UNLOAD_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x30 */ {AML_OPCODE_DEF ("RevisionOp", AML_EXT_OP), AML_EXT_REVISION_OP, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x31 */ {AML_OPCODE_DEF ("DebugOp", AML_EXT_OP), AML_EXT_DEBUG_OP, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x32 */ {AML_OPCODE_DEF ("FatalOp", AML_EXT_OP), AML_EXT_FATAL_OP, 3, 0, {EAmlUInt8, EAmlUInt32, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x33 */ {AML_OPCODE_DEF ("TimerOp", AML_EXT_OP), AML_EXT_TIMER_OP, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x5B 0x80 */ {AML_OPCODE_DEF ("OpRegionOp", AML_EXT_OP), AML_EXT_REGION_OP, 4, 0, {EAmlName, EAmlUInt8, EAmlObject, EAmlObject, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x5B 0x81 */ {AML_OPCODE_DEF ("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_OPCODE_DEF ("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_OPCODE_DEF ("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_OPCODE_DEF ("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_OPCODE_DEF ("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_OPCODE_DEF ("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_OPCODE_DEF ("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_OPCODE_DEF ("DataRegionOp", AML_EXT_OP), AML_EXT_DATA_REGION_OP, 4, 0, {EAmlName, EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x5C */ {AML_OPCODE_DEF ("RootChar", AML_ROOT_CHAR), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x5E */ {AML_OPCODE_DEF ("ParentPrefixChar", AML_PARENT_PREFIX_CHAR), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x5F */ {AML_OPCODE_DEF ("NameChar", '_'), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR},
+ /* 0x60 */ {AML_OPCODE_DEF ("Local0Op", AML_LOCAL0), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x61 */ {AML_OPCODE_DEF ("Local1Op", AML_LOCAL1), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x62 */ {AML_OPCODE_DEF ("Local2Op", AML_LOCAL2), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x63 */ {AML_OPCODE_DEF ("Local3Op", AML_LOCAL3), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x64 */ {AML_OPCODE_DEF ("Local4Op", AML_LOCAL4), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x65 */ {AML_OPCODE_DEF ("Local5Op", AML_LOCAL5), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x66 */ {AML_OPCODE_DEF ("Local6Op", AML_LOCAL6), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x67 */ {AML_OPCODE_DEF ("Local7Op", AML_LOCAL7), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x68 */ {AML_OPCODE_DEF ("Arg0Op", AML_ARG0), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x69 */ {AML_OPCODE_DEF ("Arg1Op", AML_ARG1), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x6A */ {AML_OPCODE_DEF ("Arg2Op", AML_ARG2), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x6B */ {AML_OPCODE_DEF ("Arg3Op", AML_ARG3), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x6C */ {AML_OPCODE_DEF ("Arg4Op", AML_ARG4), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x6D */ {AML_OPCODE_DEF ("Arg5Op", AML_ARG5), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x6E */ {AML_OPCODE_DEF ("Arg6Op", AML_ARG6), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x70 */ {AML_OPCODE_DEF ("StoreOp", AML_STORE_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x71 */ {AML_OPCODE_DEF ("RefOfOp", AML_REF_OF_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x72 */ {AML_OPCODE_DEF ("AddOp", AML_ADD_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x73 */ {AML_OPCODE_DEF ("ConcatOp", AML_CONCAT_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x74 */ {AML_OPCODE_DEF ("SubtractOp", AML_SUBTRACT_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x75 */ {AML_OPCODE_DEF ("IncrementOp", AML_INCREMENT_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x76 */ {AML_OPCODE_DEF ("DecrementOp", AML_DECREMENT_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x77 */ {AML_OPCODE_DEF ("MultiplyOp", AML_MULTIPLY_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x78 */ {AML_OPCODE_DEF ("DivideOp", AML_DIVIDE_OP), 0, 4, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone}, 0},
+ /* 0x79 */ {AML_OPCODE_DEF ("ShiftLeftOp", AML_SHIFT_LEFT_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x7A */ {AML_OPCODE_DEF ("ShiftRightOp", AML_SHIFT_RIGHT_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x7B */ {AML_OPCODE_DEF ("AndOp", AML_AND_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x7C */ {AML_OPCODE_DEF ("NAndOp", AML_NAND_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x7D */ {AML_OPCODE_DEF ("OrOp", AML_OR_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x7E */ {AML_OPCODE_DEF ("NorOp", AML_NOR_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x7F */ {AML_OPCODE_DEF ("XOrOp", AML_XOR_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x80 */ {AML_OPCODE_DEF ("NotOp", AML_NOT_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x81 */ {AML_OPCODE_DEF ("FindSetLeftBitOp", AML_FIND_SET_LEFT_BIT_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x82 */ {AML_OPCODE_DEF ("FindSetRightBitOp", AML_FIND_SET_RIGHT_BIT_OP),0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x83 */ {AML_OPCODE_DEF ("DerefOfOp", AML_DEREF_OF_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x84 */ {AML_OPCODE_DEF ("ConcatResOp", AML_CONCAT_RES_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x85 */ {AML_OPCODE_DEF ("ModOp", AML_MOD_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x86 */ {AML_OPCODE_DEF ("NotifyOp", AML_NOTIFY_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x87 */ {AML_OPCODE_DEF ("SizeOfOp", AML_SIZE_OF_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x88 */ {AML_OPCODE_DEF ("IndexOp", AML_INDEX_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x89 */ {AML_OPCODE_DEF ("MatchOp", AML_MATCH_OP), 0, 6, 0, {EAmlObject, EAmlUInt8, EAmlObject, EAmlUInt8, EAmlObject, EAmlObject}, 0},
+ /* 0x8A */ {AML_OPCODE_DEF ("CreateDWordFieldOp", AML_CREATE_DWORD_FIELD_OP),0, 3, 2, {EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x8B */ {AML_OPCODE_DEF ("CreateWordFieldOp", AML_CREATE_WORD_FIELD_OP), 0, 3, 2, {EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x8C */ {AML_OPCODE_DEF ("CreateByteFieldOp", AML_CREATE_BYTE_FIELD_OP), 0, 3, 2, {EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x8D */ {AML_OPCODE_DEF ("CreateBitFieldOp", AML_CREATE_BIT_FIELD_OP), 0, 3, 2, {EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x8E */ {AML_OPCODE_DEF ("ObjectTypeOp", AML_OBJECT_TYPE_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x8F */ {AML_OPCODE_DEF ("CreateQWordFieldOp", AML_CREATE_QWORD_FIELD_OP),0, 3, 2, {EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE},
+ /* 0x90 */ {AML_OPCODE_DEF ("LAndOp", AML_LAND_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x91 */ {AML_OPCODE_DEF ("LOrOp", AML_LOR_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x92 */ {AML_OPCODE_DEF ("LNotOp", AML_LNOT_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x93 */ {AML_OPCODE_DEF ("LEqualOp", AML_LEQUAL_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x94 */ {AML_OPCODE_DEF ("LGreaterOp", AML_LGREATER_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x95 */ {AML_OPCODE_DEF ("LLessOp", AML_LLESS_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x96 */ {AML_OPCODE_DEF ("ToBufferOp", AML_TO_BUFFER_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x97 */ {AML_OPCODE_DEF ("ToDecimalStringOp", AML_TO_DEC_STRING_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x98 */ {AML_OPCODE_DEF ("ToHexStringOp", AML_TO_HEX_STRING_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x99 */ {AML_OPCODE_DEF ("ToIntegerOp", AML_TO_INTEGER_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x9C */ {AML_OPCODE_DEF ("ToStringOp", AML_TO_STRING_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x9D */ {AML_OPCODE_DEF ("CopyObjectOp", AML_COPY_OBJECT_OP), 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x9E */ {AML_OPCODE_DEF ("MidOp", AML_MID_OP), 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0x9F */ {AML_OPCODE_DEF ("ContinueOp", AML_CONTINUE_OP), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0xA0 */ {AML_OPCODE_DEF ("IfOp", AML_IF_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ},
+ /* 0xA1 */ {AML_OPCODE_DEF ("ElseOp", AML_ELSE_OP), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ},
+ /* 0xA2 */ {AML_OPCODE_DEF ("WhileOp", AML_WHILE_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ},
+ /* 0xA3 */ {AML_OPCODE_DEF ("NoopOp", AML_NOOP_OP), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0xA4 */ {AML_OPCODE_DEF ("ReturnOp", AML_RETURN_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0xA5 */ {AML_OPCODE_DEF ("BreakOp", AML_BREAK_OP), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0xCC */ {AML_OPCODE_DEF ("BreakPointOp", AML_BREAK_POINT_OP), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0},
+ /* 0xD0 */ {AML_OPCODE_DEF ("MethodInvocOp", AML_METHOD_INVOC_OP), 0, 2, 0, {EAmlName, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_PSEUDO_OPCODE | AML_HAS_CHILD_OBJ},
+ /* 0xFF */ {AML_OPCODE_DEF ("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_OPCODE_DEF ("FieldReservedOp", AML_FIELD_RESERVED_OP), 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_FIELD_ELEMENT | AML_HAS_PKG_LENGTH},
+ /* 0x01 */ {AML_OPCODE_DEF ("FieldAccessOp", AML_FIELD_ACCESS_OP), 0, 2, 0, {EAmlUInt8, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_FIELD_ELEMENT},
+ /* 0x02 */ {AML_OPCODE_DEF ("FieldConnectionOp", AML_FIELD_CONNECTION_OP), 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_FIELD_ELEMENT},
+ /* 0x03 */ {AML_OPCODE_DEF ("FieldExtAccessOp", AML_FIELD_EXT_ACCESS_OP), 0, 3, 0, {EAmlUInt8, EAmlUInt8, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone}, AML_IS_FIELD_ELEMENT},
+ /* 0x04 */ {AML_OPCODE_DEF ("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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h
new file mode 100644
index 00000000..6b563db0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_OPCODE_DEF(str, OpCode) str, OpCode
+#else
+#define AML_OPCODE_DEF(str, OpCode) OpCode
+#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_
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h
new file mode 100644
index 00000000..9fd94fc3
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf
new file mode 100644
index 00000000..5e657ef3
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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]
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h
new file mode 100644
index 00000000..79736ad5
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c
new file mode 100644
index 00000000..076e8216
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c
new file mode 100644
index 00000000..16904115
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h
new file mode 100644
index 00000000..f682d0ec
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c
new file mode 100644
index 00000000..8371a862
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c
new file mode 100644
index 00000000..d7ea7bbe
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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,
+ OUT AML_OBJECT_NODE ** NewObjectNode
+ )
+{
+ EFI_STATUS Status;
+
+ if (NewObjectNode != NULL) {
+ *NewObjectNode = Node;
+ }
+
+ // Add RdNode as the last element.
+ if (ParentNode != NULL) {
+ Status = AmlVarListAddTail (ParentNode, (AML_NODE_HEADER*)Node);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** AML code generation for DefinitionBlock.
+
+ Create a Root Node handle.
+ It is the caller's responsibility to free the allocated memory
+ with the AmlDeleteTree function.
+
+ AmlCodeGenDefinitionBlock (TableSignature, OemID, TableID, OEMRevision) is
+ equivalent to the following ASL code:
+ DefinitionBlock (AMLFileName, TableSignature, ComplianceRevision,
+ OemID, TableID, OEMRevision) {}
+ with the ComplianceRevision set to 2 and the AMLFileName is ignored.
+
+ @param[in] TableSignature 4-character ACPI signature.
+ Must be 'DSDT' or 'SSDT'.
+ @param[in] OemId 6-character string OEM identifier.
+ @param[in] OemTableId 8-character string OEM table identifier.
+ @param[in] OemRevision OEM revision number.
+ @param[out] NewRootNode Pointer to the root node representing a
+ Definition Block.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCodeGenDefinitionBlock (
+ IN CONST CHAR8 * TableSignature,
+ IN CONST CHAR8 * OemId,
+ IN CONST CHAR8 * OemTableId,
+ IN UINT32 OemRevision,
+ OUT AML_ROOT_NODE ** NewRootNode
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_DESCRIPTION_HEADER AcpiHeader;
+
+ if ((TableSignature == NULL) ||
+ (OemId == NULL) ||
+ (OemTableId == NULL) ||
+ (NewRootNode == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (&AcpiHeader.Signature, TableSignature, 4);
+ AcpiHeader.Length = sizeof (EFI_ACPI_DESCRIPTION_HEADER);
+ AcpiHeader.Revision = 2;
+ CopyMem (&AcpiHeader.OemId, OemId, 6);
+ CopyMem (&AcpiHeader.OemTableId, OemTableId, 8);
+ AcpiHeader.OemRevision = OemRevision;
+ AcpiHeader.CreatorId = TABLE_GENERATOR_CREATOR_ID_ARM;
+ AcpiHeader.CreatorRevision = CREATE_REVISION (1, 0);
+
+ Status = AmlCreateRootNode (&AcpiHeader, NewRootNode);
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+/** AML code generation for a String object node.
+
+ @param [in] String Pointer to a NULL terminated string.
+ @param [out] NewObjectNode If success, contains the created
+ String object node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlCodeGenString (
+ IN CHAR8 * String,
+ OUT AML_OBJECT_NODE ** NewObjectNode
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE * ObjectNode;
+ AML_DATA_NODE * DataNode;
+
+ if ((String == NULL) ||
+ (NewObjectNode == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ObjectNode = NULL;
+ DataNode = NULL;
+
+ Status = AmlCreateObjectNode (
+ AmlGetByteEncodingByOpCode (AML_STRING_PREFIX, 0),
+ 0,
+ &ObjectNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCreateDataNode (
+ EAmlNodeDataTypeString,
+ (UINT8*)String,
+ (UINT32)AsciiStrLen (String) + 1,
+ &DataNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ Status = AmlSetFixedArgument (
+ ObjectNode,
+ EAmlParseIndexTerm0,
+ (AML_NODE_HEADER*)DataNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ AmlDeleteTree ((AML_NODE_HEADER*)DataNode);
+ goto error_handler;
+ }
+
+ *NewObjectNode = ObjectNode;
+ return Status;
+
+error_handler:
+ if (ObjectNode != NULL) {
+ AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
+ }
+
+ return Status;
+}
+
+/** AML code generation for an Integer object node.
+
+ @param [in] Integer Integer of the Integer object node.
+ @param [out] NewObjectNode If success, contains the created
+ Integer object node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlCodeGenInteger (
+ IN UINT64 Integer,
+ OUT AML_OBJECT_NODE ** NewObjectNode
+ )
+{
+ EFI_STATUS Status;
+ INT8 ValueWidthDiff;
+
+ if (NewObjectNode == NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Create an object node containing Zero.
+ Status = AmlCreateObjectNode (
+ AmlGetByteEncodingByOpCode (AML_ZERO_OP, 0),
+ 0,
+ NewObjectNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Update the object node with integer value.
+ Status = AmlNodeSetIntegerValue (*NewObjectNode, Integer, &ValueWidthDiff);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ AmlDeleteTree ((AML_NODE_HEADER*)*NewObjectNode);
+ }
+
+ return Status;
+}
+
+/** AML code generation for a Name object node.
+
+ @param [in] NameString The new variable name.
+ Must be a NULL-terminated ASL NameString
+ e.g.: "DEV0", "DV15.DEV0", etc.
+ This input string is copied.
+ @param [in] Object Object associated to the NameString.
+ @param [in] ParentNode If provided, set ParentNode as the parent
+ of the node created.
+ @param [out] NewObjectNode If success, contains the created node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlCodeGenName (
+ IN CONST CHAR8 * NameString,
+ IN AML_OBJECT_NODE * Object,
+ IN AML_NODE_HEADER * ParentNode, OPTIONAL
+ OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE * ObjectNode;
+ AML_DATA_NODE * DataNode;
+ CHAR8 * AmlNameString;
+ UINT32 AmlNameStringSize;
+
+ if ((NameString == NULL) ||
+ (Object == NULL) ||
+ ((ParentNode == NULL) && (NewObjectNode == NULL))) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ObjectNode = NULL;
+ DataNode = NULL;
+ AmlNameString = NULL;
+
+ Status = ConvertAslNameToAmlName (NameString, &AmlNameString);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlGetNameStringSize (AmlNameString, &AmlNameStringSize);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler1;
+ }
+
+ Status = AmlCreateObjectNode (
+ AmlGetByteEncodingByOpCode (AML_NAME_OP, 0),
+ 0,
+ &ObjectNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler1;
+ }
+
+ Status = AmlCreateDataNode (
+ EAmlNodeDataTypeNameString,
+ (UINT8*)AmlNameString,
+ AmlNameStringSize,
+ &DataNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler2;
+ }
+
+ Status = AmlSetFixedArgument (
+ ObjectNode,
+ EAmlParseIndexTerm0,
+ (AML_NODE_HEADER*)DataNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ AmlDeleteTree ((AML_NODE_HEADER*)DataNode);
+ goto error_handler2;
+ }
+
+ Status = AmlSetFixedArgument (
+ ObjectNode,
+ EAmlParseIndexTerm1,
+ (AML_NODE_HEADER*)Object
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler2;
+ }
+
+ Status = LinkNode (
+ ObjectNode,
+ ParentNode,
+ NewObjectNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler2;
+ }
+
+ // Free AmlNameString before returning as it is copied
+ // in the call to AmlCreateDataNode().
+ goto error_handler1;
+
+error_handler2:
+ if (ObjectNode != NULL) {
+ AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
+ }
+
+error_handler1:
+ if (AmlNameString != NULL) {
+ FreePool (AmlNameString);
+ }
+
+ return Status;
+}
+
+/** AML code generation for a Name object node, containing a String.
+
+ AmlCodeGenNameString ("_HID", "HID0000", ParentNode, NewObjectNode) is
+ equivalent of the following ASL code:
+ Name(_HID, "HID0000")
+
+ @param [in] NameString The new variable name.
+ Must be a NULL-terminated ASL NameString
+ e.g.: "DEV0", "DV15.DEV0", etc.
+ The input string is copied.
+ @param [in] String NULL terminated String to associate to the
+ NameString.
+ @param [in] ParentNode If provided, set ParentNode as the parent
+ of the node created.
+ @param [out] NewObjectNode If success, contains the created node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCodeGenNameString (
+ IN CONST CHAR8 * NameString,
+ IN CHAR8 * String,
+ IN AML_NODE_HEADER * ParentNode, OPTIONAL
+ OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE * ObjectNode;
+
+ if ((NameString == NULL) ||
+ (String == NULL) ||
+ ((ParentNode == NULL) && (NewObjectNode == NULL))) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = AmlCodeGenString (String, &ObjectNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenName (
+ NameString,
+ ObjectNode,
+ ParentNode,
+ NewObjectNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
+ }
+
+ return Status;
+}
+
+/** AML code generation for a Name object node, containing an Integer.
+
+ AmlCodeGenNameInteger ("_UID", 1, ParentNode, NewObjectNode) is
+ equivalent of the following ASL code:
+ Name(_UID, One)
+
+ @param [in] NameString The new variable name.
+ Must be a NULL-terminated ASL NameString
+ e.g.: "DEV0", "DV15.DEV0", etc.
+ The input string is copied.
+ @param [in] Integer Integer to associate to the NameString.
+ @param [in] ParentNode If provided, set ParentNode as the parent
+ of the node created.
+ @param [out] NewObjectNode If success, contains the created node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCodeGenNameInteger (
+ IN CONST CHAR8 * NameString,
+ IN UINT64 Integer,
+ IN AML_NODE_HEADER * ParentNode, OPTIONAL
+ OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE * ObjectNode;
+
+ if ((NameString == NULL) ||
+ ((ParentNode == NULL) && (NewObjectNode == NULL))) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = AmlCodeGenInteger (Integer, &ObjectNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenName (
+ NameString,
+ ObjectNode,
+ ParentNode,
+ NewObjectNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
+ }
+
+ return Status;
+}
+
+/** AML code generation for a Device object node.
+
+ AmlCodeGenDevice ("COM0", ParentNode, NewObjectNode) is
+ equivalent of the following ASL code:
+ Device(COM0) {}
+
+ @param [in] NameString The new Device's name.
+ Must be a NULL-terminated ASL NameString
+ e.g.: "DEV0", "DV15.DEV0", etc.
+ The input string is copied.
+ @param [in] ParentNode If provided, set ParentNode as the parent
+ of the node created.
+ @param [out] NewObjectNode If success, contains the created node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCodeGenDevice (
+ IN CONST CHAR8 * NameString,
+ IN AML_NODE_HEADER * ParentNode, OPTIONAL
+ OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE * ObjectNode;
+ AML_DATA_NODE * DataNode;
+ CHAR8 * AmlNameString;
+ UINT32 AmlNameStringSize;
+
+ if ((NameString == NULL) ||
+ ((ParentNode == NULL) && (NewObjectNode == NULL))) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ObjectNode = NULL;
+ DataNode = NULL;
+ AmlNameString = NULL;
+
+ Status = ConvertAslNameToAmlName (NameString, &AmlNameString);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlGetNameStringSize (AmlNameString, &AmlNameStringSize);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler1;
+ }
+
+ Status = AmlCreateObjectNode (
+ AmlGetByteEncodingByOpCode (AML_EXT_OP, AML_EXT_DEVICE_OP),
+ AmlNameStringSize + AmlComputePkgLengthWidth (AmlNameStringSize),
+ &ObjectNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler1;
+ }
+
+ Status = AmlCreateDataNode (
+ EAmlNodeDataTypeNameString,
+ (UINT8*)AmlNameString,
+ AmlNameStringSize,
+ &DataNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler2;
+ }
+
+ Status = AmlSetFixedArgument (
+ ObjectNode,
+ EAmlParseIndexTerm0,
+ (AML_NODE_HEADER*)DataNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ AmlDeleteTree ((AML_NODE_HEADER*)DataNode);
+ goto error_handler2;
+ }
+
+ Status = LinkNode (
+ ObjectNode,
+ ParentNode,
+ NewObjectNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler2;
+ }
+
+ // Free AmlNameString before returning as it is copied
+ // in the call to AmlCreateDataNode().
+ goto error_handler1;
+
+error_handler2:
+ if (ObjectNode != NULL) {
+ AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
+ }
+
+error_handler1:
+ if (AmlNameString != NULL) {
+ FreePool (AmlNameString);
+ }
+
+ return Status;
+}
+
+/** AML code generation for a Scope object node.
+
+ AmlCodeGenScope ("_SB", ParentNode, NewObjectNode) is
+ equivalent of the following ASL code:
+ Scope(_SB) {}
+
+ @param [in] NameString The new Scope's name.
+ Must be a NULL-terminated ASL NameString
+ e.g.: "DEV0", "DV15.DEV0", etc.
+ The input string is copied.
+ @param [in] ParentNode If provided, set ParentNode as the parent
+ of the node created.
+ @param [out] NewObjectNode If success, contains the created node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCodeGenScope (
+ IN CONST CHAR8 * NameString,
+ IN AML_NODE_HEADER * ParentNode, OPTIONAL
+ OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE * ObjectNode;
+ AML_DATA_NODE * DataNode;
+ CHAR8 * AmlNameString;
+ UINT32 AmlNameStringSize;
+
+ if ((NameString == NULL) ||
+ ((ParentNode == NULL) && (NewObjectNode == NULL))) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ObjectNode = NULL;
+ DataNode = NULL;
+ AmlNameString = NULL;
+
+ Status = ConvertAslNameToAmlName (NameString, &AmlNameString);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlGetNameStringSize (AmlNameString, &AmlNameStringSize);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler1;
+ }
+
+ Status = AmlCreateObjectNode (
+ AmlGetByteEncodingByOpCode (AML_SCOPE_OP, 0),
+ AmlNameStringSize + AmlComputePkgLengthWidth (AmlNameStringSize),
+ &ObjectNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler1;
+ }
+
+ Status = AmlCreateDataNode (
+ EAmlNodeDataTypeNameString,
+ (UINT8*)AmlNameString,
+ AmlNameStringSize,
+ &DataNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler2;
+ }
+
+ Status = AmlSetFixedArgument (
+ ObjectNode,
+ EAmlParseIndexTerm0,
+ (AML_NODE_HEADER*)DataNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ AmlDeleteTree ((AML_NODE_HEADER*)DataNode);
+ goto error_handler2;
+ }
+
+ Status = LinkNode (
+ ObjectNode,
+ ParentNode,
+ NewObjectNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler2;
+ }
+
+ // Free AmlNameString before returning as it is copied
+ // in the call to AmlCreateDataNode().
+ goto error_handler1;
+
+error_handler2:
+ if (ObjectNode != NULL) {
+ AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode);
+ }
+
+error_handler1:
+ if (AmlNameString != NULL) {
+ FreePool (AmlNameString);
+ }
+
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c
new file mode 100644
index 00000000..9db919a2
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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,
+ OUT 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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h
new file mode 100644
index 00000000..cf4a98a3
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c
new file mode 100644
index 00000000..a85e58e0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c
@@ -0,0 +1,1496 @@
+/** @file
+ AML NameSpace.
+
+ Copyright (c) 2019 - 2021, 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;
+
+ AML_STREAM * CurrNodePathBStream;
+ 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;
+ }
+
+ // 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:"));
+ AMLDBG_PRINT_CHARS (
+ DEBUG_VERBOSE,
+ (CHAR8*)AmlStreamGetCurrPos (SearchPathBStream),
+ AmlStreamGetIndex (SearchPathBStream)
+ );
+ DEBUG ((DEBUG_VERBOSE, "\nPath of the current node: "));
+ AMLDBG_PRINT_CHARS (
+ 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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h
new file mode 100644
index 00000000..1148cf02
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c
new file mode 100644
index 00000000..3b1bde99
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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.
+ AMLDBG_DUMP_RAW (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.
+ AMLDBG_DUMP_RAW (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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h
new file mode 100644
index 00000000..ddd196b5
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c
new file mode 100644
index 00000000..28fe51b2
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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;
+
+ AMLDBG_PRINT_CHARS (
+ 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: "));
+ AMLDBG_PRINT_CHARS (
+ 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.
+ AMLDBG_PRINT_CHARS (
+ 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: "
+ ));
+ AMLDBG_PRINT_CHARS (
+ 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"
+ ));
+ AMLDBG_PRINT_CHARS (
+ 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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h
new file mode 100644
index 00000000..9d6291f7
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c
new file mode 100644
index 00000000..3a77049d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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;
+ }
+
+ AMLDBG_DUMP_RAW (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;
+ }
+
+ AMLDBG_DUMP_RAW (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');
+
+ AMLDBG_DUMP_RAW (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.
+ AMLDBG_DUMP_RAW (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.
+ AMLDBG_DUMP_RAW (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;
+ }
+
+ AMLDBG_DUMP_RAW (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;
+ }
+
+ AMLDBG_DUMP_RAW (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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h
new file mode 100644
index 00000000..d512f703
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c
new file mode 100644
index 00000000..6209fc65
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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;
+ }
+
+ AMLDBG_DUMP_RAW (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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h
new file mode 100644
index 00000000..b0eb7084
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c
new file mode 100644
index 00000000..06446feb
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h
new file mode 100644
index 00000000..757c2a44
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Serialize/AmlSerialize.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Serialize/AmlSerialize.c
new file mode 100644
index 00000000..7f86d646
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c
new file mode 100644
index 00000000..db5a9a13
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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 [out] 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 [out] 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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h
new file mode 100644
index 00000000..7b72e1d6
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c
new file mode 100644
index 00000000..41e626c8
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h
new file mode 100644
index 00000000..a5d0a67a
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c
new file mode 100644
index 00000000..5e2b09f9
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c
new file mode 100644
index 00000000..00dd77bc
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h
new file mode 100644
index 00000000..6e680949
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c
new file mode 100644
index 00000000..97e65e71
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c
new file mode 100644
index 00000000..a6cd229a
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h
new file mode 100644
index 00000000..101cb08c
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c
new file mode 100644
index 00000000..297c0fe8
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c
new file mode 100644
index 00000000..f8b84d67
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h
new file mode 100644
index 00000000..797ec129
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c
new file mode 100644
index 00000000..ea4d0db0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h
new file mode 100644
index 00000000..4a9208f2
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c
new file mode 100644
index 00000000..5da5f644
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h
new file mode 100644
index 00000000..ddb764aa
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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_
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c
new file mode 100644
index 00000000..ebd8f791
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c
@@ -0,0 +1,536 @@
+/** @file
+ SSDT Serial Port Fixup Library.
+
+ Copyright (c) 2019 - 2021, 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 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;
+ CONST CHAR8 * NonBsaHid;
+
+ // Get the _CID and _HID value to write.
+ switch (SerialPortInfo->PortSubtype) {
+ case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550:
+ {
+ // If there is a non-BSA compliant HID, use that.
+ NonBsaHid = (CONST CHAR8*)PcdGetPtr (PcdNonBsaCompliant16550SerialHid);
+ if ((NonBsaHid != NULL) && (AsciiStrLen (NonBsaHid) != 0)) {
+ if (!(IsValidPnpId (NonBsaHid) || IsValidAcpiId (NonBsaHid))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HidString = NonBsaHid;
+ CidString = "";
+ } else {
+ 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 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 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 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/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf
new file mode 100644
index 00000000..dcf96194
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf
@@ -0,0 +1,32 @@
+## @file
+# SSDT Serial Port fixup Library
+#
+# Copyright (c) 2020 - 2021, 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
+
+[Pcd]
+ gEdkiiDynamicTablesPkgTokenSpaceGuid.PcdNonBsaCompliant16550SerialHid
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortTemplate.asl b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortTemplate.asl
new file mode 100644
index 00000000..7666e34e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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)
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelper.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelper.c
new file mode 100644
index 00000000..8f3fdaa5
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelper.c
@@ -0,0 +1,343 @@
+/** @file
+ Table Helper
+
+ Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Protocol/AcpiTable.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+
+// Module specific include files.
+#include <AcpiTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <Library/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+/** The GetCgfMgrInfo function gets the CM_STD_OBJ_CONFIGURATION_MANAGER_INFO
+ object from the Configuration Manager.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager protocol
+ interface.
+ @param [out] CfgMfrInfo Pointer to the Configuration Manager Info
+ object structure.
+
+ @retval EFI_SUCCESS The object is returned.
+ @retval EFI_INVALID_PARAMETER The Object ID is invalid.
+ @retval EFI_NOT_FOUND The requested Object is not found.
+ @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration
+ Manager is less than the Object size.
+**/
+EFI_STATUS
+EFIAPI
+GetCgfMgrInfo (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ OUT CM_STD_OBJ_CONFIGURATION_MANAGER_INFO ** CfgMfrInfo
+ )
+{
+ EFI_STATUS Status;
+ CM_OBJ_DESCRIPTOR CmObjectDesc;
+
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (CfgMfrInfo != NULL);
+
+ *CfgMfrInfo = NULL;
+ Status = CfgMgrProtocol->GetObject (
+ CfgMgrProtocol,
+ CREATE_CM_STD_OBJECT_ID (EStdObjCfgMgrInfo),
+ CM_NULL_TOKEN,
+ &CmObjectDesc
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to Get Configuration Manager Info. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ if (CmObjectDesc.ObjectId != CREATE_CM_STD_OBJECT_ID (EStdObjCfgMgrInfo)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: EStdObjCfgMgrInfo: Invalid ObjectId = 0x%x, expected Id = 0x%x\n",
+ CmObjectDesc.ObjectId,
+ CREATE_CM_STD_OBJECT_ID (EStdObjCfgMgrInfo)
+ ));
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (CmObjectDesc.Size <
+ (sizeof (CM_STD_OBJ_CONFIGURATION_MANAGER_INFO) * CmObjectDesc.Count)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: EStdObjCfgMgrInfo: Buffer too small, size = 0x%x\n",
+ CmObjectDesc.Size
+ ));
+ ASSERT (FALSE);
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ *CfgMfrInfo = (CM_STD_OBJ_CONFIGURATION_MANAGER_INFO*)CmObjectDesc.Data;
+ return Status;
+}
+
+/** The AddAcpiHeader function updates the ACPI header structure pointed by
+ the AcpiHeader. It utilizes the ACPI table Generator and the Configuration
+ Manager protocol to obtain any information required for constructing the
+ header.
+
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ protocol interface.
+ @param [in] Generator Pointer to the ACPI table Generator.
+ @param [in,out] AcpiHeader Pointer to the ACPI table header to be
+ updated.
+ @param [in] AcpiTableInfo Pointer to the ACPI table info structure.
+ @param [in] Length Length of the ACPI table.
+
+ @retval EFI_SUCCESS The ACPI table is updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object information is not found.
+ @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration
+ Manager is less than the Object size for the
+ requested object.
+**/
+EFI_STATUS
+EFIAPI
+AddAcpiHeader (
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST ACPI_TABLE_GENERATOR * CONST Generator,
+ IN OUT EFI_ACPI_DESCRIPTION_HEADER * CONST AcpiHeader,
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo,
+ IN CONST UINT32 Length
+ )
+{
+ EFI_STATUS Status;
+ CM_STD_OBJ_CONFIGURATION_MANAGER_INFO * CfgMfrInfo;
+
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (Generator != NULL);
+ ASSERT (AcpiHeader != NULL);
+ ASSERT (Length >= sizeof (EFI_ACPI_DESCRIPTION_HEADER));
+
+ if ((CfgMgrProtocol == NULL) ||
+ (Generator == NULL) ||
+ (AcpiHeader == NULL) ||
+ (Length < sizeof (EFI_ACPI_DESCRIPTION_HEADER))
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetCgfMgrInfo (CfgMgrProtocol, &CfgMfrInfo);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to get Configuration Manager info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // UINT32 Signature
+ AcpiHeader->Signature = Generator->AcpiTableSignature;
+ // UINT32 Length
+ AcpiHeader->Length = Length;
+ // UINT8 Revision
+ AcpiHeader->Revision = AcpiTableInfo->AcpiTableRevision;
+ // UINT8 Checksum
+ AcpiHeader->Checksum = 0;
+
+ // UINT8 OemId[6]
+ CopyMem (AcpiHeader->OemId, CfgMfrInfo->OemId, sizeof (AcpiHeader->OemId));
+
+ // UINT64 OemTableId
+ if (AcpiTableInfo->OemTableId != 0) {
+ AcpiHeader->OemTableId = AcpiTableInfo->OemTableId;
+ } else {
+ AcpiHeader->OemTableId = SIGNATURE_32 (
+ CfgMfrInfo->OemId[0],
+ CfgMfrInfo->OemId[1],
+ CfgMfrInfo->OemId[2],
+ CfgMfrInfo->OemId[3]
+ ) |
+ ((UINT64)Generator->AcpiTableSignature << 32);
+ }
+
+ // UINT32 OemRevision
+ if (AcpiTableInfo->OemRevision != 0) {
+ AcpiHeader->OemRevision = AcpiTableInfo->OemRevision;
+ } else {
+ AcpiHeader->OemRevision = CfgMfrInfo->Revision;
+ }
+
+ // UINT32 CreatorId
+ AcpiHeader->CreatorId = Generator->CreatorId;
+ // UINT32 CreatorRevision
+ AcpiHeader->CreatorRevision = Generator->CreatorRevision;
+
+error_handler:
+ return Status;
+}
+
+/**
+ Test and report if a duplicate entry exists in the given array of comparable
+ elements.
+
+ @param [in] Array Array of elements to test for duplicates.
+ @param [in] Count Number of elements in Array.
+ @param [in] ElementSize Size of an element in bytes
+ @param [in] EqualTestFunction The function to call to check if any two
+ elements are equal.
+
+ @retval TRUE A duplicate element was found or one of
+ the input arguments is invalid.
+ @retval FALSE Every element in Array is unique.
+**/
+BOOLEAN
+EFIAPI
+FindDuplicateValue (
+ IN CONST VOID * Array,
+ IN CONST UINTN Count,
+ IN CONST UINTN ElementSize,
+ IN PFN_IS_EQUAL EqualTestFunction
+ )
+{
+ UINTN Index1;
+ UINTN Index2;
+ UINT8 * Element1;
+ UINT8 * Element2;
+
+ if (Array == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: FindDuplicateValues: Array is NULL.\n"));
+ return TRUE;
+ }
+
+ if (ElementSize == 0) {
+ DEBUG ((DEBUG_ERROR, "ERROR: FindDuplicateValues: ElementSize is 0.\n"));
+ return TRUE;
+ }
+
+ if (EqualTestFunction == NULL) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: FindDuplicateValues: EqualTestFunction is NULL.\n"
+ ));
+ return TRUE;
+ }
+
+ if (Count < 2) {
+ return FALSE;
+ }
+
+ for (Index1 = 0; Index1 < Count - 1; Index1++) {
+ for (Index2 = Index1 + 1; Index2 < Count; Index2++) {
+ Element1 = (UINT8*)Array + (Index1 * ElementSize);
+ Element2 = (UINT8*)Array + (Index2 * ElementSize);
+
+ if (EqualTestFunction (Element1, Element2, Index1, Index2)) {
+ return TRUE;
+ }
+ }
+ }
+ 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;
+}
+
+/** Check if a HID is a valid PNP ID.
+
+ @param [in] Hid The Hid to validate.
+
+ @retval TRUE The Hid is a valid PNP ID.
+ @retval FALSE The Hid is not a valid PNP ID.
+**/
+BOOLEAN
+IsValidPnpId (
+ IN CONST CHAR8 * Hid
+ )
+{
+ UINTN Index;
+
+ if (AsciiStrLen (Hid) != 7) {
+ return FALSE;
+ }
+
+ // A valid PNP ID must be of the form "AAA####"
+ // where A is an uppercase letter and # is a hex digit.
+ for (Index = 0; Index < 3; Index++) {
+ if (!IS_UPPER_CHAR (Hid[Index])) {
+ return FALSE;
+ }
+ }
+
+ for (Index = 3; Index < 7; Index++) {
+ if (!IS_UPPER_HEX (Hid[Index])) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/** Check if a HID is a valid ACPI ID.
+
+ @param [in] Hid The Hid to validate.
+
+ @retval TRUE The Hid is a valid ACPI ID.
+ @retval FALSE The Hid is not a valid ACPI ID.
+**/
+BOOLEAN
+IsValidAcpiId (
+ IN CONST CHAR8 * Hid
+ )
+{
+ UINTN Index;
+
+ if (AsciiStrLen (Hid) != 8) {
+ return FALSE;
+ }
+
+ // A valid ACPI ID must be of the form "NNNN####"
+ // where N is an uppercase letter or a digit ('0'-'9')
+ // and # is a hex digit.
+ for (Index = 0; Index < 4; Index++) {
+ if (!(IS_UPPER_CHAR (Hid[Index]) || IS_DIGIT (Hid[Index]))) {
+ return FALSE;
+ }
+ }
+
+ for (Index = 4; Index < 8; Index++) {
+ if (!IS_UPPER_HEX (Hid[Index])) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelperLib.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelperLib.inf
new file mode 100644
index 00000000..41a3b703
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/TableHelperLib/TableHelperLib.inf
@@ -0,0 +1,30 @@
+## @file
+# Table Helper
+#
+# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = DynamicTableHelperLib
+ FILE_GUID = E315C738-3A39-4D0D-A0AF-8EDFA770AB39
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = TableHelperLib
+
+[Sources]
+ TableHelper.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ BaseLib
+
+[Protocols]
+
+[Guids]
+
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Readme.md b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Readme.md
new file mode 100644
index 00000000..e927a210
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Readme.md
@@ -0,0 +1,281 @@
+# Dynamic Tables Framework
+
+Dynamic Tables Framework provides mechanisms to reduce the amount
+of effort required in porting firmware to new platforms. The aim is
+to provide an implementation capable of generating the firmware
+tables from an external source. This is potentially a management
+node, either local or remote, or, where suitable, a file that might
+be generated from the system construction. This initial release
+does not fully implement that - the configuration is held in local
+UEFI modules.
+
+# 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
+firmware for a platform capable of booting both Windows and Linux
+operating systems.
+
+Traditionally the firmware tables are handcrafted using ACPI
+Source Language (ASL), Table Definition Language (TDL) and
+C-code. This approach can be error prone and involves time
+consuming debugging. In addition, it may be desirable to configure
+platform hardware at runtime such as: configuring the number of
+cores available for use by the OS, or turning SoC features ON or
+OFF.
+
+The dynamic tables framework simplifies this by providing a set
+of standard table generators, that are implemented as libraries.
+These generators query a platform specific component, the
+'Configuration Manager', to collate the information required
+for generating the tables at run-time.
+
+The framework also provides the ability to implement custom/OEM
+generators; thereby facilitating support for custom tables. The
+custom generators can also utilize the existing standard generators
+and override any functionality if needed.
+
+The framework currently implements a set of standard ACPI table
+generators for ARM architecture, that can generate Server Base Boot
+Requirement (SBBR) compliant tables. Although, the set of standard
+generators implement the functionality required for ARM architecture;
+the framework is extensible, and support for other architectures can
+be added easily.
+
+The framework currently supports the following table generators for ARM:
+* DBG2 - Debug Port Table 2
+* DSDT - Differentiated system description table. This is essentially
+ a RAW table generator.
+* FADT - Fixed ACPI Description Table
+* GTDT - Generic Timer Description Table
+* IORT - IO Remapping Table
+* MADT - Multiple APIC Description Table
+* MCFG - PCI Express memory mapped configuration space base address
+ Description Table
+* SPCR - Serial Port Console Redirection Table
+* SSDT - Secondary System Description Table. This is essentially
+ a RAW table generator.
+
+## 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
+information file is planned.
+
+Also support for generating SMBIOS tables is planned and will be added
+subsequently.
+
+# Supported Platforms
+
+1. Juno
+2. FVP Models
+
+# Build Instructions
+
+1. Set path for the iASL compiler with support for generating a C header
+ file as output.
+
+2. Set PACKAGES_PATH to point to the locations of the following repositories:
+
+Example:
+
+> set PACKAGES_PATH=%CD%\edk2;%CD%\edk2-platforms;
+
+ or
+
+> export PACKAGES_PATH=$PWD/edk2:$PWD/edk2-platforms
+
+3. To enable Dynamic tables framework the *'DYNAMIC_TABLES_FRAMEWORK'*
+option must be defined. This can be passed as a command line
+parameter to the edk2 build system.
+
+Example:
+
+>build -a AARCH64 -p Platform\ARM\JunoPkg\ArmJuno.dsc
+ -t GCC5 **-D DYNAMIC_TABLES_FRAMEWORK**
+
+or
+
+>build -a AARCH64 -p Platform\ARM\VExpressPkg\ArmVExpress-FVP-AArch64.dsc
+ -t GCC5 **-D DYNAMIC_TABLES_FRAMEWORK**
+
+# 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.
+
+
+#Running CI builds locally
+
+The TianoCore EDKII project has introduced Core CI infrastructure using TianoCore EDKII Tools PIP modules:
+
+ - *[edk2-pytool-library](https://pypi.org/project/edk2-pytool-library)*
+
+ - *[edk2-pytool-extensions](https://pypi.org/project/edk2-pytool-extensions)*
+
+
+The instructions to setup the CI environment are in *'edk2\\.pytool\\Readme.md'*
+
+## Building DynamicTablesPkg with Pytools
+
+1. [Optional] Create a Python Virtual Environment - generally once per workspace
+
+ ```
+ python -m venv <name of virtual environment>
+
+ e.g. python -m venv edk2-ci
+ ```
+
+2. [Optional] Activate Virtual Environment - each time new shell/command window is opened
+
+ ```
+ <name of virtual environment>/Scripts/activate
+
+ e.g. On a windows host PC run:
+ edk2-ci\Scripts\activate.bat
+ ```
+3. Install Pytools - generally once per virtual env or whenever pip-requirements.txt changes
+
+ ```
+ pip install --upgrade -r pip-requirements.txt
+ ```
+
+4. Initialize & Update Submodules - only when submodules updated
+
+ ```
+ stuart_setup -c .pytool/CISettings.py TOOL_CHAIN_TAG=<TOOL_CHAIN_TAG> -a <TARGET_ARCH>
+
+ e.g. stuart_setup -c .pytool/CISettings.py TOOL_CHAIN_TAG=GCC5
+ ```
+
+5. Initialize & Update Dependencies - only as needed when ext_deps change
+
+ ```
+ stuart_update -c .pytool/CISettings.py TOOL_CHAIN_TAG=<TOOL_CHAIN_TAG> -a <TARGET_ARCH>
+
+ e.g. stuart_update -c .pytool/CISettings.py TOOL_CHAIN_TAG=GCC5
+ ```
+
+6. Compile the basetools if necessary - only when basetools C source files change
+
+ ```
+ python BaseTools/Edk2ToolsBuild.py -t <ToolChainTag>
+ ```
+
+7. Compile DynamicTablesPkg
+
+ ```
+ stuart_build-c .pytool/CISettings.py TOOL_CHAIN_TAG=<TOOL_CHAIN_TAG> -a <TARGET_ARCH>
+
+ e.g. stuart_ci_build -c .pytool/CISettings.py TOOL_CHAIN_TAG=GCC5 -p DynamicTablesPkg -a AARCH64 --verbose
+ ```
+
+ - use `stuart_build -c .pytool/CISettings.py -h` option to see help on additional options.
+
+
+# Documentation
+
+Refer to the following presentation from *UEFI Plugfest Seattle 2018*:
+
+[Dynamic Tables Framework: A Step Towards Automatic Generation of Advanced Configuration and Power Interface (ACPI) & System Management BIOS (SMBIOS) Tables](http://www.uefi.org/sites/default/files/resources/Arm_Dynamic%20Tables%20Framework%20A%20Step%20Towards%20Automatic%20Generation%20of%20Advanced%20Configuration%20and%20Power%20Interface%20%28ACPI%29%20%26%20System%20Management%20BIOS%20%28SMBIOS%29%20Tables%20_0.pdf)
+