diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/DynamicTablesPkg | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/DynamicTablesPkg')
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, + >BlockTimerFrameList, + >BlockTimerFrameCount + ); + 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, + >BlockInfo, + &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) + |