summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg/Library/Acpi/Common
diff options
context:
space:
mode:
authorPierre Gondois <pierre.gondois@arm.com>2024-06-10 14:00:00 +0200
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2024-07-29 13:44:55 +0000
commite640c04a7bbddc56cb01767a03e508bb012fef19 (patch)
treeef09abe363e3a5568d4f517046d6eb31938e01f3 /DynamicTablesPkg/Library/Acpi/Common
parentfb6a7147f3a9438f83e749dbbd3d1387d9471840 (diff)
downloadedk2-e640c04a7bbddc56cb01767a03e508bb012fef19.tar.gz
edk2-e640c04a7bbddc56cb01767a03e508bb012fef19.tar.bz2
edk2-e640c04a7bbddc56cb01767a03e508bb012fef19.zip
DynamicTablesPkg: Acpi: Move generic libraries to common folder
Some of the ACPI table generators are generic enough to be re-used by other architectures. Move the following generators to a 'Common' folder: - AcpiDbg2Lib - AcpiFadtLib - AcpiMcfgLib - AcpiPcctLib - AcpiPpttLib - AcpiRawLib - AcpiSpcrLib - AcpiSratLib - SsdtSerialPortLib - SsdtCpuTopologyLib - SsdtPcieLib and update DynamicTables.dsc.inc accordingly. Suggested-by: Sunil V L <sunilvl@ventanamicro.com> Signed-off-by: Pierre Gondois <pierre.gondois@arm.com> Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>
Diffstat (limited to 'DynamicTablesPkg/Library/Acpi/Common')
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiDbg2Lib/AcpiDbg2Lib.inf43
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiDbg2Lib/Dbg2Generator.c587
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiFadtLib/AcpiFadtLib.inf36
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiFadtLib/FadtGenerator.c713
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiMcfgLib/AcpiMcfgLib.inf36
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiMcfgLib/McfgGenerator.c369
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/AcpiPcctLib.inf30
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/PcctGenerator.c1188
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/PcctGenerator.h44
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/AcpiPpttLib.inf30
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/PpttGenerator.c1480
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/PpttGenerator.h185
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiRawLib/AcpiRawLib.inf36
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiRawLib/RawGenerator.c146
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSpcrLib/AcpiSpcrLib.inf37
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSpcrLib/SpcrGenerator.c482
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSratLib/AcpiSratLib.inf29
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSratLib/SratGenerator.c842
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyGenerator.c1705
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyGenerator.h147
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyLib.inf33
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieGenerator.c1247
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieGenerator.h55
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieLib.inf35
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtSerialPortLib/SsdtSerialPortGenerator.c373
-rw-r--r--DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtSerialPortLib/SsdtSerialPortLib.inf33
26 files changed, 9941 insertions, 0 deletions
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiDbg2Lib/AcpiDbg2Lib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiDbg2Lib/AcpiDbg2Lib.inf
new file mode 100644
index 0000000000..f7b7c1c025
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiDbg2Lib/AcpiDbg2Lib.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/DynamicTablesPkg/Library/Acpi/Common/AcpiDbg2Lib/Dbg2Generator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiDbg2Lib/Dbg2Generator.c
new file mode 100644
index 0000000000..fbf2ba3733
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiDbg2Lib/Dbg2Generator.c
@@ -0,0 +1,587 @@
+/** @file
+ DBG2 Table Generator
+
+ Copyright (c) 2017 - 2022, 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:
+ - EArchCommonObjSerialDebugPortInfo
+*/
+
+#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"
+
+/** A string representing the full path name of the debug port 0.
+*/
+#define NAMESPACE_STR_DBG_PORT0 "\\_SB_.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 (NAMESPACE_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,
+ NAMESPACE_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 (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjSerialDebugPortInfo,
+ CM_ARCH_COMMON_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_ARCH_COMMON_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_ARCH_COMMON_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 = GetEArchCommonObjSerialDebugPortInfo (
+ 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/DynamicTablesPkg/Library/Acpi/Common/AcpiFadtLib/AcpiFadtLib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiFadtLib/AcpiFadtLib.inf
new file mode 100644
index 0000000000..8fe34013d4
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiFadtLib/AcpiFadtLib.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/DynamicTablesPkg/Library/Acpi/Common/AcpiFadtLib/FadtGenerator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiFadtLib/FadtGenerator.c
new file mode 100644
index 0000000000..470f1acfd1
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiFadtLib/FadtGenerator.c
@@ -0,0 +1,713 @@
+/** @file
+ FADT Table Generator
+
+ Copyright (c) 2017 - 2023, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.5 Specification, Aug 29, 2022
+
+**/
+
+#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:
+ - EArchCommonObjPowerManagementProfileInfo
+ - EArmObjBootArchInfo
+ - EArchCommonObjHypervisorVendorIdentity (OPTIONAL)
+*/
+
+/** This macro defines the FADT flag options for ARM Platforms.
+*/
+#define FADT_FLAGS (EFI_ACPI_6_5_HW_REDUCED_ACPI | \
+ EFI_ACPI_6_5_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_5_WBINVD BIT0
+ EFI_ACPI_6_5_PWR_BUTTON BIT4
+ EFI_ACPI_6_5_SLP_BUTTON BIT5
+ EFI_ACPI_6_5_FIX_RTC BIT6
+ EFI_ACPI_6_5_DCK_CAP BIT9
+ EFI_ACPI_6_5_RESET_REG_SUP BIT10
+ EFI_ACPI_6_5_SEALED_CASE BIT11
+ EFI_ACPI_6_5_HEADLESS BIT12
+ EFI_ACPI_6_5_USE_PLATFORM_CLOCK BIT15
+ EFI_ACPI_6_5_FORCE_APIC_CLUSTER_MODEL BIT18
+ EFI_ACPI_6_5_FORCE_APIC_PHYSICAL_DESTINATION_MODE BIT19
+ EFI_ACPI_6_5_HW_REDUCED_ACPI BIT20
+ EFI_ACPI_6_5_LOW_POWER_S0_IDLE_CAPABLE BIT21
+*/
+#define VALID_HARDWARE_REDUCED_FLAG_MASK ( \
+ EFI_ACPI_6_5_WBINVD | \
+ EFI_ACPI_6_5_PWR_BUTTON | \
+ EFI_ACPI_6_5_SLP_BUTTON | \
+ EFI_ACPI_6_5_FIX_RTC | \
+ EFI_ACPI_6_5_DCK_CAP | \
+ EFI_ACPI_6_5_RESET_REG_SUP | \
+ EFI_ACPI_6_5_SEALED_CASE | \
+ EFI_ACPI_6_5_HEADLESS | \
+ EFI_ACPI_6_5_USE_PLATFORM_CLOCK | \
+ EFI_ACPI_6_5_FORCE_APIC_CLUSTER_MODEL | \
+ EFI_ACPI_6_5_FORCE_APIC_PHYSICAL_DESTINATION_MODE | \
+ EFI_ACPI_6_5_HW_REDUCED_ACPI | \
+ EFI_ACPI_6_5_LOW_POWER_S0_IDLE_CAPABLE)
+
+#pragma pack(1)
+
+/** The AcpiFadt is a template EFI_ACPI_6_5_FIXED_ACPI_DESCRIPTION_TABLE
+ structure used for generating the FADT Table.
+ Note: fields marked with "{Template}" will be updated dynamically.
+*/
+STATIC
+EFI_ACPI_6_5_FIXED_ACPI_DESCRIPTION_TABLE AcpiFadt = {
+ ACPI_HEADER (
+ EFI_ACPI_6_5_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE,
+ EFI_ACPI_6_5_FIXED_ACPI_DESCRIPTION_TABLE,
+ EFI_ACPI_6_5_FIXED_ACPI_DESCRIPTION_TABLE_REVISION
+ ),
+ // UINT32 FirmwareCtrl
+ 0,
+ // UINT32 Dsdt
+ 0,
+ // UINT8 Reserved0
+ EFI_ACPI_RESERVED_BYTE,
+ // UINT8 PreferredPmProfile
+ EFI_ACPI_6_5_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_5_GENERIC_ADDRESS_STRUCTURE ResetReg
+ NULL_GAS,
+ // UINT8 ResetValue
+ 0,
+ // UINT16 ArmBootArch
+ EFI_ACPI_6_5_ARM_PSCI_COMPLIANT, // {Template}: ARM Boot Architecture Flags
+ // UINT8 MinorRevision
+ EFI_ACPI_6_5_FIXED_ACPI_DESCRIPTION_TABLE_MINOR_REVISION, // {Template}
+ // UINT64 XFirmwareCtrl
+ 0,
+ // UINT64 XDsdt
+ 0,
+ // EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE XPm1aEvtBlk
+ NULL_GAS,
+ // EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE XPm1bEvtBlk
+ NULL_GAS,
+ // EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE XPm1aCntBlk
+ NULL_GAS,
+ // EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE XPm1bCntBlk
+ NULL_GAS,
+ // EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE XPm2CntBlk
+ NULL_GAS,
+ // EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE XPmTmrBlk
+ NULL_GAS,
+ // EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE XGpe0Blk
+ NULL_GAS,
+ // EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE XGpe1Blk
+ NULL_GAS,
+ // EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE SleepControlReg
+ NULL_GAS,
+ // EFI_ACPI_6_5_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 (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPowerManagementProfileInfo,
+ CM_ARCH_COMMON_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 (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjHypervisorVendorIdentity,
+ CM_ARCH_COMMON_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 (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjFixedFeatureFlags,
+ CM_ARCH_COMMON_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_ARCH_COMMON_POWER_MANAGEMENT_PROFILE_INFO *PmProfile;
+
+ ASSERT (CfgMgrProtocol != NULL);
+
+ // Get the Power Management Profile from the Platform Configuration Manager
+ Status = GetEArchCommonObjPowerManagementProfileInfo (
+ 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_ARCH_COMMON_HYPERVISOR_VENDOR_ID *HypervisorVendorInfo;
+
+ ASSERT (CfgMgrProtocol != NULL);
+
+ // Get the Hypervisor Vendor ID from the Platform Configuration Manager
+ Status = GetEArchCommonObjHypervisorVendorIdentity (
+ 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: EArchCommonObjHypervisorVendorIdentity = 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_ARCH_COMMON_FIXED_FEATURE_FLAGS *FixedFeatureFlags;
+
+ ASSERT (CfgMgrProtocol != NULL);
+
+ // Get the Fixed feature flags from the Platform Configuration Manager
+ Status = GetEArchCommonObjFixedFeatureFlags (
+ 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: EArchCommonObjFixedFeatureFlags = 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_5_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 the MinorRevision for the FADT table if it has been specified
+ // otherwise default to the latest FADT minor revision supported.
+ // Note:
+ // Bits 0-3 - The low order bits correspond to the minor version of the
+ // specification version.
+ // Bits 4-7 - The high order bits correspond to the version of the ACPI
+ // specification errata.
+ if (AcpiTableInfo->MinorRevision != 0) {
+ if (((AcpiTableInfo->MinorRevision & 0xF) >=
+ EFI_ACPI_6_2_FIXED_ACPI_DESCRIPTION_TABLE_MINOR_REVISION) &&
+ ((AcpiTableInfo->MinorRevision & 0xF) <=
+ EFI_ACPI_6_5_FIXED_ACPI_DESCRIPTION_TABLE_MINOR_REVISION))
+ {
+ AcpiFadt.MinorVersion = AcpiTableInfo->MinorRevision;
+ } else {
+ DEBUG ((
+ DEBUG_WARN,
+ "WARNING: FADT: Unsupported FADT Minor Revision 0x%x specified, " \
+ "defaulting to FADT Minor Revision 0x%x\n",
+ AcpiTableInfo->MinorRevision,
+ EFI_ACPI_6_5_FIXED_ACPI_DESCRIPTION_TABLE_MINOR_REVISION
+ ));
+ }
+ }
+
+ // 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_5_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_5_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/DynamicTablesPkg/Library/Acpi/Common/AcpiMcfgLib/AcpiMcfgLib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiMcfgLib/AcpiMcfgLib.inf
new file mode 100644
index 0000000000..1c7f085274
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiMcfgLib/AcpiMcfgLib.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/DynamicTablesPkg/Library/Acpi/Common/AcpiMcfgLib/McfgGenerator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiMcfgLib/McfgGenerator.c
new file mode 100644
index 0000000000..722f9c17d5
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiMcfgLib/McfgGenerator.c
@@ -0,0 +1,369 @@
+/** @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:
+ - EArchCommonObjPciConfigSpaceInfo
+*/
+
+#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 (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPciConfigSpaceInfo,
+ CM_ARCH_COMMON_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_ARCH_COMMON_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_ARCH_COMMON_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 = GetEArchCommonObjPciConfigSpaceInfo (
+ 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/DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/AcpiPcctLib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/AcpiPcctLib.inf
new file mode 100644
index 0000000000..da54585c2d
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/AcpiPcctLib.inf
@@ -0,0 +1,30 @@
+## @file
+# Pcct Table Generator
+#
+# Copyright (c) 2022, Arm Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = AcpiPcctLibArm
+ FILE_GUID = 38FE945C-D6ED-4CD6-8D20-FCEF3260D15A
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiPcctLibConstructor
+ DESTRUCTOR = AcpiPcctLibDestructor
+
+[Sources]
+ PcctGenerator.c
+ PcctGenerator.h
+
+[Packages]
+ DynamicTablesPkg/DynamicTablesPkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/PcctGenerator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/PcctGenerator.c
new file mode 100644
index 0000000000..205c444057
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/PcctGenerator.c
@@ -0,0 +1,1188 @@
+/** @file
+ PCCT Table Generator
+
+ Copyright (c) 2022, Arm Limited. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.4 Specification - January 2021
+ s14 PLATFORM COMMUNICATIONS CHANNEL (PCC)
+
+**/
+
+#include <Library/AcpiLib.h>
+#include <Library/BaseMemoryLib.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 "PcctGenerator.h"
+
+/** ARM standard PCCT Generator
+
+Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArchCommonObjPccSubspaceType0Info
+ - EArchCommonObjPccSubspaceType1Info
+ - EArchCommonObjPccSubspaceType2Info
+ - EArchCommonObjPccSubspaceType3Info
+ - EArchCommonObjPccSubspaceType4Info
+ - EArchCommonObjPccSubspaceType5Info
+*/
+
+/** This macro expands to a function that retrieves the PCC
+ Subspace of Type 0 Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPccSubspaceType0Info,
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO
+ );
+
+/** This macro expands to a function that retrieves the PCC
+ Subspace of Type 1 Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPccSubspaceType1Info,
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE1_INFO
+ );
+
+/** This macro expands to a function that retrieves the PCC
+ Subspace of Type 2 Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPccSubspaceType2Info,
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE2_INFO
+ );
+
+/** This macro expands to a function that retrieves the PCC
+ Subspace of Type 3 Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPccSubspaceType3Info,
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE3_INFO
+ );
+
+/** This macro expands to a function that retrieves the PCC
+ Subspace of Type 4 Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPccSubspaceType4Info,
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE4_INFO
+ );
+
+/** This macro expands to a function that retrieves the PCC
+ Subspace of Type 5 Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPccSubspaceType5Info,
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE5_INFO
+ );
+
+/** The Platform is capable of generating an interrupt
+ to indicate completion of a command.
+
+ Cf: s14.1.1 Platform Communications Channel Global Flags
+ Platform Interrupt flag
+ and s14.1.6 Extended PCC subspaces (types 3 and 4)
+ If a responder subspace is included in the PCCT,
+ then the global Platform Interrupt flag must be set to 1
+
+ Set this variable and populate the PCCT flag accordingly if either:
+ - One of the PCCT Subspace uses interrupts.
+ - A PCC Subspace of type 4 is used.
+*/
+STATIC BOOLEAN mHasPlatformInterrupt;
+
+/** Initialize the MappingTable.
+
+ @param [in] MappingTable The mapping table structure.
+ @param [in] Count Number of entries to allocate in the
+ MappingTable.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+MappingTableInitialize (
+ IN MAPPING_TABLE *MappingTable,
+ IN UINT32 Count
+ )
+{
+ VOID **Table;
+
+ if ((MappingTable == NULL) ||
+ (Count == 0))
+ {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Table = AllocateZeroPool (sizeof (*Table) * Count);
+ if (Table == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ MappingTable->Table = Table;
+ MappingTable->MaxIndex = Count;
+
+ return EFI_SUCCESS;
+}
+
+/** Free the MappingTable.
+
+ @param [in, out] MappingTable The mapping table structure.
+**/
+STATIC
+VOID
+EFIAPI
+MappingTableFree (
+ IN OUT MAPPING_TABLE *MappingTable
+ )
+{
+ ASSERT (MappingTable != NULL);
+ ASSERT (MappingTable->Table != NULL);
+
+ if (MappingTable->Table != NULL) {
+ FreePool (MappingTable->Table);
+ }
+}
+
+/** Add a new entry for PccSubspace at Index.
+
+ @param [in] MappingTable The mapping table structure.
+ @param [in] PccSubspace A pointer to
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE[X]_INFO.
+ @param [in] Index Index at which PccSubspace must be added.
+ This is the Subspace Id.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_BUFFER_TOO_SMALL Buffer too small.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+MappingTableAdd (
+ IN MAPPING_TABLE *MappingTable,
+ IN VOID *PccSubspace,
+ IN UINT32 Index
+ )
+{
+ if ((MappingTable == NULL) ||
+ (MappingTable->Table == NULL) ||
+ (PccSubspace == NULL))
+ {
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Index >= MappingTable->MaxIndex) ||
+ (MappingTable->Table[Index] != 0))
+ {
+ ASSERT_EFI_ERROR (EFI_BUFFER_TOO_SMALL);
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ // Just map the Pcc Subspace in the Table.
+ MappingTable->Table[Index] = PccSubspace;
+ return EFI_SUCCESS;
+}
+
+/** Parse the CmPccArray objects and add them to the MappingTable.
+
+ @param [in] MappingTable The mapping table structure.
+ @param [in] CmPccArray Pointer to an array of
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE[X]_INFO.
+ @param [in] CmPccCount Count of objects in CmPccArray.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_BUFFER_TOO_SMALL Buffer too small.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+MapPccSubspaceId (
+ IN MAPPING_TABLE *MappingTable,
+ IN VOID *CmPccArray,
+ IN UINT32 CmPccCount
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *PccBuffer;
+ UINT32 Index;
+ UINT32 CmObjSize;
+ PCC_SUBSPACE_GENERIC_INFO *GenericPcc;
+
+ if (CmPccCount == 0) {
+ return EFI_SUCCESS;
+ }
+
+ if ((CmPccArray == NULL) || (MappingTable == NULL)) {
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ GenericPcc = (PCC_SUBSPACE_GENERIC_INFO *)CmPccArray;
+
+ switch (GenericPcc->Type) {
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_GENERIC:
+ CmObjSize = sizeof (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO);
+ break;
+
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_1_HW_REDUCED_COMMUNICATIONS:
+ CmObjSize = sizeof (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE1_INFO);
+ break;
+
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_2_HW_REDUCED_COMMUNICATIONS:
+ CmObjSize = sizeof (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE2_INFO);
+ break;
+
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_3_EXTENDED_PCC:
+ CmObjSize = sizeof (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE3_INFO);
+ break;
+
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_4_EXTENDED_PCC:
+ CmObjSize = sizeof (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE4_INFO);
+ break;
+
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_5_HW_REGISTERS_COMMUNICATIONS:
+ CmObjSize = sizeof (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE5_INFO);
+ break;
+
+ default:
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ PccBuffer = (UINT8 *)CmPccArray;
+
+ // Map the Pcc channel to their Subspace Id.
+ for (Index = 0; Index < CmPccCount; Index++) {
+ GenericPcc = (PCC_SUBSPACE_GENERIC_INFO *)PccBuffer;
+
+ Status = MappingTableAdd (
+ MappingTable,
+ PccBuffer,
+ GenericPcc->SubspaceId
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ PccBuffer += CmObjSize;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Add one PCCT Subspace structure of Type 0 (Generic).
+
+ @param [in] PccCmObj Pointer to a CmObj PCCT Subspace info structure.
+ @param [in] PccAcpi Pointer to the ACPI PCCT Subspace structure to populate.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AddSubspaceStructType0 (
+ IN CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *PccCmObj,
+ IN EFI_ACPI_6_4_PCCT_SUBSPACE_GENERIC *PccAcpi
+ )
+{
+ PCC_MAILBOX_REGISTER_INFO *Doorbell;
+ PCC_SUBSPACE_CHANNEL_TIMING_INFO *ChannelTiming;
+
+ if ((PccCmObj == NULL) ||
+ (PccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_GENERIC) ||
+ (PccAcpi == NULL))
+ {
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Doorbell = &PccCmObj->DoorbellReg;
+ ChannelTiming = &PccCmObj->ChannelTiming;
+
+ PccAcpi->Type = PccCmObj->Type;
+ PccAcpi->Length = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_GENERIC);
+ *(UINT32 *)&PccAcpi->Reserved[0] = EFI_ACPI_RESERVED_DWORD;
+ *(UINT16 *)&PccAcpi->Reserved[4] = EFI_ACPI_RESERVED_WORD;
+ PccAcpi->BaseAddress = PccCmObj->BaseAddress;
+ PccAcpi->AddressLength = PccCmObj->AddressLength;
+
+ CopyMem (
+ &PccAcpi->DoorbellRegister,
+ &Doorbell->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->DoorbellPreserve = Doorbell->PreserveMask;
+ PccAcpi->DoorbellWrite = Doorbell->WriteMask;
+
+ PccAcpi->NominalLatency = ChannelTiming->NominalLatency;
+ PccAcpi->MaximumPeriodicAccessRate = ChannelTiming->MaxPeriodicAccessRate;
+ PccAcpi->MinimumRequestTurnaroundTime = ChannelTiming->MinRequestTurnaroundTime;
+
+ return EFI_SUCCESS;
+}
+
+/** Add one PCCT subspace structure of Type 1 (HW-Reduced).
+
+ @param [in] PccCmObj Pointer to a CmObj PCCT Subspace info structure.
+ @param [in] PccAcpi Pointer to the ACPI PCCT Subspace structure to populate.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AddSubspaceStructType1 (
+ IN CM_ARCH_COMMON_PCC_SUBSPACE_TYPE1_INFO *PccCmObj,
+ IN EFI_ACPI_6_4_PCCT_SUBSPACE_1_HW_REDUCED_COMMUNICATIONS *PccAcpi
+ )
+{
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *GenericPccCmObj;
+ PCC_MAILBOX_REGISTER_INFO *Doorbell;
+ PCC_SUBSPACE_CHANNEL_TIMING_INFO *ChannelTiming;
+
+ GenericPccCmObj = (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *)PccCmObj;
+
+ if ((PccCmObj == NULL) ||
+ (GenericPccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_1_HW_REDUCED_COMMUNICATIONS) ||
+ (PccAcpi == NULL))
+ {
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Doorbell = &GenericPccCmObj->DoorbellReg;
+ ChannelTiming = &GenericPccCmObj->ChannelTiming;
+
+ PccAcpi->Type = GenericPccCmObj->Type;
+ PccAcpi->Length = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_1_HW_REDUCED_COMMUNICATIONS);
+ PccAcpi->PlatformInterrupt = PccCmObj->PlatIrq.Interrupt;
+ PccAcpi->PlatformInterruptFlags = PccCmObj->PlatIrq.Flags;
+ PccAcpi->Reserved = EFI_ACPI_RESERVED_BYTE;
+ PccAcpi->BaseAddress = GenericPccCmObj->BaseAddress;
+ PccAcpi->AddressLength = GenericPccCmObj->AddressLength;
+
+ CopyMem (
+ &PccAcpi->DoorbellRegister,
+ &Doorbell->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->DoorbellPreserve = Doorbell->PreserveMask;
+ PccAcpi->DoorbellWrite = Doorbell->WriteMask;
+
+ PccAcpi->NominalLatency = ChannelTiming->NominalLatency;
+ PccAcpi->MaximumPeriodicAccessRate = ChannelTiming->MaxPeriodicAccessRate;
+ PccAcpi->MinimumRequestTurnaroundTime = ChannelTiming->MinRequestTurnaroundTime;
+
+ if ((PccCmObj->PlatIrq.Interrupt != 0)) {
+ mHasPlatformInterrupt = TRUE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Add one PCCT subspace structure of Type 2 (HW-Reduced).
+
+ @param [in] PccCmObj Pointer to a CmObj PCCT Subspace info structure.
+ @param [in] PccAcpi Pointer to the ACPI PCCT Subspace structure to populate.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AddSubspaceStructType2 (
+ IN CM_ARCH_COMMON_PCC_SUBSPACE_TYPE2_INFO *PccCmObj,
+ IN EFI_ACPI_6_4_PCCT_SUBSPACE_2_HW_REDUCED_COMMUNICATIONS *PccAcpi
+ )
+{
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *GenericPccCmObj;
+ PCC_MAILBOX_REGISTER_INFO *Doorbell;
+ PCC_MAILBOX_REGISTER_INFO *PlatIrqAck;
+ PCC_SUBSPACE_CHANNEL_TIMING_INFO *ChannelTiming;
+
+ GenericPccCmObj = (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *)PccCmObj;
+
+ if ((PccCmObj == NULL) ||
+ (GenericPccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_2_HW_REDUCED_COMMUNICATIONS) ||
+ (PccAcpi == NULL))
+ {
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Doorbell = &GenericPccCmObj->DoorbellReg;
+ PlatIrqAck = &PccCmObj->PlatIrqAckReg;
+ ChannelTiming = &GenericPccCmObj->ChannelTiming;
+
+ PccAcpi->Type = GenericPccCmObj->Type;
+ PccAcpi->Length = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_2_HW_REDUCED_COMMUNICATIONS);
+ PccAcpi->PlatformInterrupt = PccCmObj->PlatIrq.Interrupt;
+ PccAcpi->PlatformInterruptFlags = PccCmObj->PlatIrq.Flags;
+ PccAcpi->BaseAddress = GenericPccCmObj->BaseAddress;
+ PccAcpi->Reserved = EFI_ACPI_RESERVED_BYTE;
+ PccAcpi->BaseAddress = GenericPccCmObj->BaseAddress;
+ PccAcpi->AddressLength = GenericPccCmObj->AddressLength;
+
+ CopyMem (
+ &PccAcpi->DoorbellRegister,
+ &Doorbell->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->DoorbellPreserve = Doorbell->PreserveMask;
+ PccAcpi->DoorbellWrite = Doorbell->WriteMask;
+
+ PccAcpi->NominalLatency = ChannelTiming->NominalLatency;
+ PccAcpi->MaximumPeriodicAccessRate = ChannelTiming->MaxPeriodicAccessRate;
+ PccAcpi->MinimumRequestTurnaroundTime = ChannelTiming->MinRequestTurnaroundTime;
+
+ CopyMem (
+ &PccAcpi->PlatformInterruptAckRegister,
+ &PlatIrqAck->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->PlatformInterruptAckPreserve = PlatIrqAck->PreserveMask;
+ PccAcpi->PlatformInterruptAckWrite = PlatIrqAck->WriteMask;
+
+ if ((PccCmObj->PlatIrq.Interrupt != 0)) {
+ mHasPlatformInterrupt = TRUE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Add one PCCT subspace structure of Type 3 or 4 (Extended).
+
+ @param [in] PccCmObj Pointer to a CmObj PCCT Subspace info structure.
+ @param [in] PccAcpi Pointer to the ACPI PCCT Subspace structure to populate.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AddSubspaceStructType34 (
+ IN CM_ARCH_COMMON_PCC_SUBSPACE_TYPE3_INFO *PccCmObj,
+ IN EFI_ACPI_6_4_PCCT_SUBSPACE_3_EXTENDED_PCC *PccAcpi
+ )
+{
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *GenericPccCmObj;
+ PCC_MAILBOX_REGISTER_INFO *Doorbell;
+ PCC_MAILBOX_REGISTER_INFO *PlatIrqAck;
+ PCC_MAILBOX_REGISTER_INFO *CmdCompleteCheck;
+ PCC_MAILBOX_REGISTER_INFO *CmdCompleteUpdate;
+ PCC_MAILBOX_REGISTER_INFO *ErrorStatus;
+ PCC_SUBSPACE_CHANNEL_TIMING_INFO *ChannelTiming;
+
+ GenericPccCmObj = (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *)PccCmObj;
+
+ if ((PccCmObj == NULL) ||
+ ((GenericPccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_3_EXTENDED_PCC) &&
+ (GenericPccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_4_EXTENDED_PCC)) ||
+ (PccAcpi == NULL))
+ {
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Doorbell = &GenericPccCmObj->DoorbellReg;
+ PlatIrqAck = &PccCmObj->PlatIrqAckReg;
+ CmdCompleteCheck = &PccCmObj->CmdCompleteCheckReg;
+ CmdCompleteUpdate = &PccCmObj->CmdCompleteUpdateReg;
+ ErrorStatus = &PccCmObj->ErrorStatusReg;
+ ChannelTiming = &GenericPccCmObj->ChannelTiming;
+
+ PccAcpi->Type = GenericPccCmObj->Type;
+ PccAcpi->Length = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_3_EXTENDED_PCC);
+ PccAcpi->PlatformInterrupt = PccCmObj->PlatIrq.Interrupt;
+ PccAcpi->PlatformInterruptFlags = PccCmObj->PlatIrq.Flags;
+ PccAcpi->Reserved = EFI_ACPI_RESERVED_BYTE;
+ PccAcpi->BaseAddress = GenericPccCmObj->BaseAddress;
+ PccAcpi->AddressLength = GenericPccCmObj->AddressLength;
+
+ CopyMem (
+ &PccAcpi->DoorbellRegister,
+ &Doorbell->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->DoorbellPreserve = Doorbell->PreserveMask;
+ PccAcpi->DoorbellWrite = Doorbell->WriteMask;
+
+ PccAcpi->NominalLatency = ChannelTiming->NominalLatency;
+ PccAcpi->MaximumPeriodicAccessRate = ChannelTiming->MaxPeriodicAccessRate;
+ PccAcpi->MinimumRequestTurnaroundTime = ChannelTiming->MinRequestTurnaroundTime;
+
+ CopyMem (
+ &PccAcpi->PlatformInterruptAckRegister,
+ &PlatIrqAck->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->PlatformInterruptAckPreserve = PlatIrqAck->PreserveMask;
+ PccAcpi->PlatformInterruptAckSet = PlatIrqAck->WriteMask;
+
+ PccAcpi->Reserved1[0] = EFI_ACPI_RESERVED_BYTE;
+ PccAcpi->Reserved1[1] = EFI_ACPI_RESERVED_BYTE;
+ PccAcpi->Reserved1[1] = EFI_ACPI_RESERVED_BYTE;
+ PccAcpi->Reserved1[3] = EFI_ACPI_RESERVED_BYTE;
+ PccAcpi->Reserved1[4] = EFI_ACPI_RESERVED_BYTE;
+ PccAcpi->Reserved1[5] = EFI_ACPI_RESERVED_BYTE;
+ PccAcpi->Reserved1[6] = EFI_ACPI_RESERVED_BYTE;
+ PccAcpi->Reserved1[7] = EFI_ACPI_RESERVED_BYTE;
+
+ CopyMem (
+ &PccAcpi->CommandCompleteCheckRegister,
+ &CmdCompleteCheck->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->CommandCompleteCheckMask = CmdCompleteCheck->PreserveMask;
+ // No Write mask.
+
+ CopyMem (
+ &PccAcpi->CommandCompleteUpdateRegister,
+ &CmdCompleteUpdate->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->CommandCompleteUpdatePreserve = CmdCompleteUpdate->PreserveMask;
+ PccAcpi->CommandCompleteUpdateSet = CmdCompleteUpdate->WriteMask;
+
+ CopyMem (
+ &PccAcpi->ErrorStatusRegister,
+ &ErrorStatus->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->ErrorStatusMask = ErrorStatus->PreserveMask;
+ // No Write mask.
+
+ if (GenericPccCmObj->Type == EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_4_EXTENDED_PCC) {
+ mHasPlatformInterrupt = TRUE;
+ } else if ((PccCmObj->PlatIrq.Interrupt != 0)) {
+ mHasPlatformInterrupt = TRUE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Add one PCCT subspace structure of Type 5 (HW-Registers).
+
+ @param [in] PccCmObj Pointer to a CmObj PCCT Subspace info structure.
+ @param [in] PccAcpi Pointer to the ACPI PCCT Subspace structure to populate.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AddSubspaceStructType5 (
+ IN CM_ARCH_COMMON_PCC_SUBSPACE_TYPE5_INFO *PccCmObj,
+ IN EFI_ACPI_6_4_PCCT_SUBSPACE_5_HW_REGISTERS_COMMUNICATIONS *PccAcpi
+ )
+{
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *GenericPccCmObj;
+ PCC_MAILBOX_REGISTER_INFO *Doorbell;
+ PCC_MAILBOX_REGISTER_INFO *CmdCompleteCheck;
+ PCC_MAILBOX_REGISTER_INFO *ErrorStatus;
+ PCC_SUBSPACE_CHANNEL_TIMING_INFO *ChannelTiming;
+
+ GenericPccCmObj = (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *)PccCmObj;
+
+ if ((PccCmObj == NULL) ||
+ (GenericPccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_5_HW_REGISTERS_COMMUNICATIONS) ||
+ (PccAcpi == NULL))
+ {
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Doorbell = &GenericPccCmObj->DoorbellReg;
+ CmdCompleteCheck = &PccCmObj->CmdCompleteCheckReg;
+ ErrorStatus = &PccCmObj->ErrorStatusReg;
+ ChannelTiming = &GenericPccCmObj->ChannelTiming;
+
+ PccAcpi->Type = GenericPccCmObj->Type;
+ PccAcpi->Length = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_5_HW_REGISTERS_COMMUNICATIONS);
+ PccAcpi->Version = PccCmObj->Version;
+ PccAcpi->BaseAddress = GenericPccCmObj->BaseAddress;
+ PccAcpi->SharedMemoryRangeLength = GenericPccCmObj->AddressLength;
+
+ CopyMem (
+ &PccAcpi->DoorbellRegister,
+ &Doorbell->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->DoorbellPreserve = Doorbell->PreserveMask;
+ PccAcpi->DoorbellWrite = Doorbell->WriteMask;
+
+ CopyMem (
+ &PccAcpi->CommandCompleteCheckRegister,
+ &CmdCompleteCheck->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->CommandCompleteCheckMask = CmdCompleteCheck->PreserveMask;
+ // No Write mask.
+
+ CopyMem (
+ &PccAcpi->ErrorStatusRegister,
+ &ErrorStatus->Register,
+ sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
+ );
+ PccAcpi->ErrorStatusMask = ErrorStatus->PreserveMask;
+ // No Write mask.
+
+ PccAcpi->NominalLatency = ChannelTiming->NominalLatency;
+ // No MaximumPeriodicAccessRate.
+ PccAcpi->MinimumRequestTurnaroundTime = ChannelTiming->MinRequestTurnaroundTime;
+
+ return EFI_SUCCESS;
+}
+
+/** Populate the PCCT table using the MappingTable.
+
+ @param [in] MappingTable The mapping table structure.
+ @param [in] Pcc Pointer to an array of Pcc Subpace structures.
+ @param [in] Size Size of the Pcc array.
+
+ @retval EFI_SUCCESS Table generated successfully.
+ @retval EFI_BUFFER_TOO_SMALL Buffer too small.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+PopulatePcctTable (
+ IN MAPPING_TABLE *MappingTable,
+ IN VOID *Pcc,
+ IN UINT32 Size
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *PccBuffer;
+ UINT32 CmObjSize;
+ UINT32 Index;
+ UINT32 MaxIndex;
+ VOID **Table;
+ VOID *CurrentPccSubspace;
+
+ ASSERT (MappingTable != NULL);
+ ASSERT (MappingTable->Table != NULL);
+
+ PccBuffer = Pcc;
+ MaxIndex = MappingTable->MaxIndex;
+ Table = MappingTable->Table;
+
+ for (Index = 0; Index < MaxIndex; Index++) {
+ CurrentPccSubspace = Table[Index];
+ if (CurrentPccSubspace == NULL) {
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (((PCC_SUBSPACE_GENERIC_INFO *)CurrentPccSubspace)->Type) {
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_GENERIC:
+ Status = AddSubspaceStructType0 (
+ (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *)CurrentPccSubspace,
+ (EFI_ACPI_6_4_PCCT_SUBSPACE_GENERIC *)PccBuffer
+ );
+
+ CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_GENERIC);
+ break;
+
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_1_HW_REDUCED_COMMUNICATIONS:
+ Status = AddSubspaceStructType1 (
+ (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE1_INFO *)CurrentPccSubspace,
+ (EFI_ACPI_6_4_PCCT_SUBSPACE_1_HW_REDUCED_COMMUNICATIONS *)PccBuffer
+ );
+
+ CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_1_HW_REDUCED_COMMUNICATIONS);
+ break;
+
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_2_HW_REDUCED_COMMUNICATIONS:
+ Status = AddSubspaceStructType2 (
+ (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE2_INFO *)CurrentPccSubspace,
+ (EFI_ACPI_6_4_PCCT_SUBSPACE_2_HW_REDUCED_COMMUNICATIONS *)PccBuffer
+ );
+
+ CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_2_HW_REDUCED_COMMUNICATIONS);
+ break;
+
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_3_EXTENDED_PCC:
+ Status = AddSubspaceStructType34 (
+ (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE3_INFO *)CurrentPccSubspace,
+ (EFI_ACPI_6_4_PCCT_SUBSPACE_3_EXTENDED_PCC *)PccBuffer
+ );
+
+ CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_3_EXTENDED_PCC);
+ break;
+
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_4_EXTENDED_PCC:
+ Status = AddSubspaceStructType34 (
+ (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE4_INFO *)CurrentPccSubspace,
+ (EFI_ACPI_6_4_PCCT_SUBSPACE_4_EXTENDED_PCC *)PccBuffer
+ );
+
+ CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_4_EXTENDED_PCC);
+ break;
+
+ case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_5_HW_REGISTERS_COMMUNICATIONS:
+ Status = AddSubspaceStructType5 (
+ (CM_ARCH_COMMON_PCC_SUBSPACE_TYPE5_INFO *)CurrentPccSubspace,
+ (EFI_ACPI_6_4_PCCT_SUBSPACE_5_HW_REGISTERS_COMMUNICATIONS *)PccBuffer
+ );
+
+ CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_5_HW_REGISTERS_COMMUNICATIONS);
+ break;
+
+ default:
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ } // switch
+
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ if (Size < CmObjSize) {
+ ASSERT_EFI_ERROR (EFI_BUFFER_TOO_SMALL);
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ PccBuffer += CmObjSize;
+ Size -= CmObjSize;
+ } // for
+
+ return EFI_SUCCESS;
+}
+
+/** Construct the PCCT 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_BUFFER_TOO_SMALL Buffer too small.
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BuildPcctTable (
+ 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;
+ ACPI_PCCT_GENERATOR *Generator;
+ UINT32 TableSize;
+ EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER *Pcct;
+ UINT8 *Buffer;
+
+ MAPPING_TABLE *MappingTable;
+ UINT32 MappingTableCount;
+
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE0_INFO *PccType0;
+ UINT32 PccType0Count;
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE1_INFO *PccType1;
+ UINT32 PccType1Count;
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE2_INFO *PccType2;
+ UINT32 PccType2Count;
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE3_INFO *PccType3;
+ UINT32 PccType3Count;
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE4_INFO *PccType4;
+ UINT32 PccType4Count;
+ CM_ARCH_COMMON_PCC_SUBSPACE_TYPE5_INFO *PccType5;
+ UINT32 PccType5Count;
+
+ 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: PCCT: Requested table revision = %d, is not supported."
+ "Supported table revision: Minimum = %d, Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Generator = (ACPI_PCCT_GENERATOR *)This;
+ MappingTable = &Generator->MappingTable;
+ *Table = NULL;
+
+ // First get all the Pcc Subpace CmObj of type X.
+
+ Status = GetEArchCommonObjPccSubspaceType0Info (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &PccType0,
+ &PccType0Count
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = GetEArchCommonObjPccSubspaceType1Info (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &PccType1,
+ &PccType1Count
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = GetEArchCommonObjPccSubspaceType2Info (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &PccType2,
+ &PccType2Count
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = GetEArchCommonObjPccSubspaceType3Info (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &PccType3,
+ &PccType3Count
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = GetEArchCommonObjPccSubspaceType4Info (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &PccType4,
+ &PccType4Count
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = GetEArchCommonObjPccSubspaceType5Info (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &PccType5,
+ &PccType5Count
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ // Count the number of Pcc Subspaces.
+ MappingTableCount = PccType0Count;
+ MappingTableCount += PccType1Count;
+ MappingTableCount += PccType2Count;
+ MappingTableCount += PccType3Count;
+ MappingTableCount += PccType4Count;
+ MappingTableCount += PccType5Count;
+
+ Status = MappingTableInitialize (MappingTable, MappingTableCount);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ // Map the Subspace Ids for all types.
+
+ Status = MapPccSubspaceId (MappingTable, PccType0, PccType0Count);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = MapPccSubspaceId (MappingTable, PccType1, PccType1Count);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = MapPccSubspaceId (MappingTable, PccType2, PccType2Count);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = MapPccSubspaceId (MappingTable, PccType3, PccType3Count);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = MapPccSubspaceId (MappingTable, PccType4, PccType4Count);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = MapPccSubspaceId (MappingTable, PccType5, PccType5Count);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ // Compute the size of the PCCT table.
+ TableSize = sizeof (EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER);
+ TableSize += PccType0Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_GENERIC);
+ TableSize += PccType1Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_1_HW_REDUCED_COMMUNICATIONS);
+ TableSize += PccType2Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_2_HW_REDUCED_COMMUNICATIONS);
+ TableSize += PccType3Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_3_EXTENDED_PCC);
+ TableSize += PccType4Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_4_EXTENDED_PCC);
+ TableSize += PccType5Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_5_HW_REGISTERS_COMMUNICATIONS);
+
+ // Allocate a Buffer for the PCCT table.
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER *)AllocateZeroPool (TableSize);
+ if (*Table == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Pcct = (EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER *)*Table;
+
+ Status = AddAcpiHeader (
+ CfgMgrProtocol,
+ This,
+ &Pcct->Header,
+ AcpiTableInfo,
+ TableSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PCCT: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Buffer = (UINT8 *)Pcct;
+ Buffer += sizeof (EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER);
+ TableSize -= sizeof (EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER);
+
+ // Populate the PCCT table by following the Subspace Id mapping.
+ Status = PopulatePcctTable (MappingTable, Buffer, TableSize);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ // Setup the Reserved fields once mHasPlatformInterrupt hase been populated.
+ Pcct->Flags = mHasPlatformInterrupt;
+ Pcct->Reserved = EFI_ACPI_RESERVED_QWORD;
+
+ MappingTableFree (MappingTable);
+
+ return Status;
+
+error_handler:
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PCCT: Failed to install table. Status = %r\n",
+ Status
+ ));
+
+ if (*Table != NULL) {
+ FreePool (*Table);
+ *Table = NULL;
+ }
+
+ MappingTableFree (MappingTable);
+
+ return Status;
+}
+
+/** Free any resources allocated for constructing the PCCT.
+
+ @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
+FreePcctTableResources (
+ 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: PCCT: Invalid Table Pointer\n"));
+ ASSERT ((Table != NULL) && (*Table != NULL));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (*Table);
+ *Table = NULL;
+ return EFI_SUCCESS;
+}
+
+/** This macro defines the PCCT Table Generator revision.
+*/
+#define PCCT_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the PCCT Table Generator.
+*/
+STATIC
+ACPI_PCCT_GENERATOR PcctGenerator = {
+ // ACPI table generator header
+ {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdPcct),
+ // Generator Description
+ L"ACPI.STD.PCCT.GENERATOR",
+ // ACPI Table Signature
+ EFI_ACPI_6_4_PLATFORM_COMMUNICATIONS_CHANNEL_TABLE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_REVISION,
+ // Minimum ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ PCCT_GENERATOR_REVISION,
+ // Build Table function
+ BuildPcctTable,
+ // Free Resource function
+ FreePcctTableResources,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+ },
+
+ // Private fields are defined from here.
+
+ // Mapping Table
+ {
+ // Table
+ NULL,
+ // MaxIndex
+ 0,
+ },
+};
+
+/** 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
+AcpiPcctLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = RegisterAcpiTableGenerator (&PcctGenerator.Header);
+ DEBUG ((DEBUG_INFO, "PCCT: 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
+AcpiPcctLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = DeregisterAcpiTableGenerator (&PcctGenerator.Header);
+ DEBUG ((DEBUG_INFO, "PCCT: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/PcctGenerator.h b/DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/PcctGenerator.h
new file mode 100644
index 0000000000..b99bf91b41
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiPcctLib/PcctGenerator.h
@@ -0,0 +1,44 @@
+/** @file
+ PCCT Table Generator
+
+ Copyright (c) 2022, Arm Limited. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.4 Specification - January 2021
+ s14 PLATFORM COMMUNICATIONS CHANNEL (PCC)
+
+**/
+
+#ifndef PCCT_GENERATOR_H_
+#define PCCT_GENERATOR_H_
+
+#pragma pack(1)
+
+/** Structure used to map a Pcc Subspace to an index.
+*/
+typedef struct MappingTable {
+ /// Mapping table for Subspace Ids.
+ /// Subspace ID/Index <-> CM_ARCH_COMMON_PCC_SUBSPACE_TYPE[X]_INFO pointer
+ VOID **Table;
+
+ /// Number of entries in the Table.
+ UINT32 MaxIndex;
+} MAPPING_TABLE;
+
+/** A structure holding the Pcct generator and additional private data.
+*/
+typedef struct AcpiPcctGenerator {
+ /// ACPI Table generator header
+ ACPI_TABLE_GENERATOR Header;
+
+ // Private fields are defined from here.
+
+ /// Table to map:
+ /// Subspace ID/Index <-> CM_ARCH_COMMON_PCC_SUBSPACE_TYPE[X]_INFO pointer
+ MAPPING_TABLE MappingTable;
+} ACPI_PCCT_GENERATOR;
+
+#pragma pack()
+
+#endif // PCCT_GENERATOR_H_
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/AcpiPpttLib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/AcpiPpttLib.inf
new file mode 100644
index 0000000000..2c7d19513d
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/AcpiPpttLib.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/DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/PpttGenerator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/PpttGenerator.c
new file mode 100644
index 0000000000..2b8088a07f
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/PpttGenerator.c
@@ -0,0 +1,1480 @@
+/** @file
+ PPTT Table Generator
+
+ Copyright (c) 2021, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.4 Specification, January 2021
+
+ @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:
+ - EArchCommonObjProcHierarchyInfo (REQUIRED)
+ - EArchCommonObjCacheInfo
+ - EArchCommonObjCmRef
+ - EArmObjGicCInfo (REQUIRED)
+*/
+
+/**
+ This macro expands to a function that retrieves the Processor Hierarchy
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjProcHierarchyInfo,
+ CM_ARCH_COMMON_PROC_HIERARCHY_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the cache information
+ from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjCacheInfo,
+ CM_ARCH_COMMON_CACHE_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the cross-CM-object-
+ reference information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjCmRef,
+ CM_ARCH_COMMON_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_ARCH_COMMON_PROC_HIERARCHY_INFO *Node
+ )
+{
+ ASSERT (Node != NULL);
+
+ // <size of Processor Hierarchy Node> + <size of Private Resources array>
+ return sizeof (EFI_ACPI_6_4_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_ARCH_COMMON_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_4_PPTT_STRUCTURE_CACHE),
+ CM_ARCH_COMMON_CACHE_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_ARCH_COMMON_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_ARCH_COMMON_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 = GetEArchCommonObjCmRef (
+ 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. 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
+ AcpiIdObjectToken.
+ @retval FALSE Object1 and Object2 have different
+ AcpiIdObjectTokens.
+**/
+BOOLEAN
+EFIAPI
+IsAcpiIdObjectTokenEqual (
+ IN CONST VOID *Object1,
+ IN CONST VOID *Object2,
+ IN UINTN Index1,
+ IN UINTN Index2
+ )
+{
+ PPTT_NODE_INDEXER *IndexedObject1;
+ PPTT_NODE_INDEXER *IndexedObject2;
+ CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcNode1;
+ CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcNode2;
+
+ ASSERT (
+ (Object1 != NULL) &&
+ (Object2 != NULL)
+ );
+
+ IndexedObject1 = (PPTT_NODE_INDEXER *)Object1;
+ IndexedObject2 = (PPTT_NODE_INDEXER *)Object2;
+ ProcNode1 = (CM_ARCH_COMMON_PROC_HIERARCHY_INFO *)IndexedObject1->Object;
+ ProcNode2 = (CM_ARCH_COMMON_PROC_HIERARCHY_INFO *)IndexedObject2->Object;
+
+ if (IS_ACPI_PROC_ID_VALID (ProcNode1) &&
+ IS_ACPI_PROC_ID_VALID (ProcNode2) &&
+ (ProcNode1->AcpiIdObjectToken != CM_NULL_TOKEN) &&
+ (ProcNode2->AcpiIdObjectToken != CM_NULL_TOKEN) &&
+ (ProcNode1->AcpiIdObjectToken == ProcNode2->AcpiIdObjectToken))
+ {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Two Processor Hierarchy Info objects (%d and %d) map to " \
+ "the same ACPI ID reference object. ACPI Processor IDs are not unique. " \
+ "AcpiIdObjectToken = %p.\n",
+ Index1,
+ Index2,
+ ProcNode1->AcpiIdObjectToken
+ ));
+ 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_4_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER *Pptt,
+ IN CONST UINT32 NodesStartOffset
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_4_PPTT_STRUCTURE_PROCESSOR *ProcStruct;
+ UINT32 *PrivateResources;
+ BOOLEAN IsAcpiIdObjectTokenDuplicated;
+
+ CM_ARM_GICC_INFO *GicCInfoList;
+ UINT32 GicCInfoCount;
+ UINT32 UniqueGicCRefCount;
+
+ PPTT_NODE_INDEXER *PpttNodeFound;
+ CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcInfoNode;
+
+ PPTT_NODE_INDEXER *ProcNodeIterator;
+ UINT32 NodeCount;
+ UINT32 Length;
+
+ ASSERT (
+ (Generator != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Pptt != NULL)
+ );
+
+ ProcStruct = (EFI_ACPI_6_4_PPTT_STRUCTURE_PROCESSOR *)((UINT8 *)Pptt +
+ NodesStartOffset);
+
+ ProcNodeIterator = Generator->ProcHierarchyNodeIndexedList;
+ NodeCount = Generator->ProcHierarchyNodeCount;
+
+ // Check if every GICC Object is referenced by onlu one Proc Node
+ IsAcpiIdObjectTokenDuplicated = FindDuplicateValue (
+ ProcNodeIterator,
+ NodeCount,
+ sizeof (PPTT_NODE_INDEXER),
+ IsAcpiIdObjectTokenEqual
+ );
+ // Duplicate GIC CPU Interface Token was found so two PPTT Processor Hierarchy
+ // Nodes map to the same MADT GICC structure
+ if (IsAcpiIdObjectTokenDuplicated) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ UniqueGicCRefCount = 0;
+
+ while (NodeCount-- != 0) {
+ ProcInfoNode = (CM_ARCH_COMMON_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_4_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. ParentToken = %p. ChildToken = %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_ARCH_COMMON_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->AcpiIdObjectToken == CM_NULL_TOKEN) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The 'ACPI Processor ID valid' flag is set but no " \
+ "ACPI ID Reference object token was provided. " \
+ "AcpiIdObjectToken = %p. RequestorToken = %p. Status = %r\n",
+ ProcInfoNode->AcpiIdObjectToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ } else {
+ Status = GetEArmObjGicCInfo (
+ CfgMgrProtocol,
+ ProcInfoNode->AcpiIdObjectToken,
+ &GicCInfoList,
+ &GicCInfoCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get ACPI ID Reference object token. " \
+ "ACPI Processor ID can't be populated. " \
+ "AcpiIdObjectToken = %p. RequestorToken = %p. Status = %r\n",
+ ProcInfoNode->AcpiIdObjectToken,
+ 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. AcpiIdObjectToken = %p. RequestorToken = %p " \
+ "Status = %r\n",
+ GicCInfoCount,
+ ProcInfoNode->AcpiIdObjectToken,
+ 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_4_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_4_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;
+}
+
+/**
+ Test whether CacheId is unique among the CacheIdList.
+
+ @param [in] CacheId Cache ID to check.
+ @param [in] CacheIdList List of already existing cache IDs.
+ @param [in] CacheIdListSize Size of CacheIdList.
+
+ @retval TRUE CacheId does not exist in CacheIdList.
+ @retval FALSE CacheId already exists in CacheIdList.
+**/
+STATIC
+BOOLEAN
+IsCacheIdUnique (
+ IN CONST UINT32 CacheId,
+ IN CONST UINT32 *CacheIdList,
+ IN CONST UINT32 CacheIdListSize
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < CacheIdListSize; Index++) {
+ if (CacheIdList[Index] == CacheId) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ 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.
+ @param [in] Revision Revision of the PPTT table being requested.
+
+ @retval EFI_SUCCESS Structures updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND A required object was not found.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+**/
+STATIC
+EFI_STATUS
+AddCacheTypeStructures (
+ IN CONST ACPI_PPTT_GENERATOR *CONST Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_4_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER *Pptt,
+ IN CONST UINT32 NodesStartOffset,
+ IN CONST UINT32 Revision
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_4_PPTT_STRUCTURE_CACHE *CacheStruct;
+ PPTT_NODE_INDEXER *PpttNodeFound;
+ CM_ARCH_COMMON_CACHE_INFO *CacheInfoNode;
+ PPTT_NODE_INDEXER *CacheNodeIterator;
+ UINT32 NodeCount;
+ BOOLEAN CacheIdUnique;
+ UINT32 NodeIndex;
+ UINT32 *FoundCacheIds;
+
+ ASSERT (
+ (Generator != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Pptt != NULL)
+ );
+
+ CacheStruct = (EFI_ACPI_6_4_PPTT_STRUCTURE_CACHE *)((UINT8 *)Pptt +
+ NodesStartOffset);
+
+ CacheNodeIterator = Generator->CacheStructIndexedList;
+ NodeCount = Generator->CacheStructCount;
+
+ FoundCacheIds = AllocateZeroPool (NodeCount * sizeof (*FoundCacheIds));
+ if (FoundCacheIds == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: PPTT: Failed to allocate resources.\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (NodeIndex = 0; NodeIndex < NodeCount; NodeIndex++) {
+ CacheInfoNode = (CM_ARCH_COMMON_CACHE_INFO *)CacheNodeIterator->Object;
+
+ // Populate the node header
+ CacheStruct->Type = EFI_ACPI_6_4_PPTT_TYPE_CACHE;
+ CacheStruct->Length = sizeof (EFI_ACPI_6_4_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.4, 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.CacheIdValid = 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
+ ));
+ goto cleanup;
+ }
+
+ // 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
+ ));
+ goto cleanup;
+ }
+
+ 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
+ ));
+ goto cleanup;
+ }
+
+ // 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
+ ));
+ goto cleanup;
+ }
+
+ 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
+ ));
+ goto cleanup;
+ }
+
+ 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
+ ));
+ goto cleanup;
+ }
+
+ CacheStruct->LineSize = CacheInfoNode->LineSize;
+
+ if (Revision >= 3) {
+ // Validate and populate cache id
+ if (CacheInfoNode->CacheId == 0) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The cache id cannot be zero. Status = %r\n",
+ Status
+ ));
+ goto cleanup;
+ }
+
+ CacheIdUnique = IsCacheIdUnique (
+ CacheInfoNode->CacheId,
+ FoundCacheIds,
+ NodeIndex
+ );
+ if (!CacheIdUnique) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The cache id is not unique. " \
+ "CacheId = %d. Status = %r\n",
+ CacheInfoNode->CacheId,
+ Status
+ ));
+ goto cleanup;
+ }
+
+ // Store the cache id so we can check future cache ids for uniqueness
+ FoundCacheIds[NodeIndex] = CacheInfoNode->CacheId;
+
+ CacheStruct->CacheId = CacheInfoNode->CacheId;
+ }
+
+ // Next Cache Type Structure
+ CacheStruct = (EFI_ACPI_6_4_PPTT_STRUCTURE_CACHE *)((UINT8 *)CacheStruct +
+ CacheStruct->Length);
+ CacheNodeIterator++;
+ } // for Cache Type Structure
+
+ Status = EFI_SUCCESS;
+
+cleanup:
+ FreePool (FoundCacheIds);
+
+ return Status;
+}
+
+/**
+ 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 ProcHierarchyNodeOffset;
+ UINT32 CacheStructOffset;
+
+ CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcHierarchyNodeList;
+ CM_ARCH_COMMON_CACHE_INFO *CacheStructList;
+
+ ACPI_PPTT_GENERATOR *Generator;
+
+ // Pointer to the Node Indexer array
+ PPTT_NODE_INDEXER *NodeIndexer;
+
+ EFI_ACPI_6_4_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 = GetEArchCommonObjProcHierarchyInfo (
+ 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 = GetEArchCommonObjCacheInfo (
+ 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;
+
+ // 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_4_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
+ ));
+ }
+
+ 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_4_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,
+ AcpiTableInfo->AcpiTableRevision
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to add Cache 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_4_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_STRUCTURE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_4_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,
+ // 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/DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/PpttGenerator.h b/DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/PpttGenerator.h
new file mode 100644
index 0000000000..15b0a9871c
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiPpttLib/PpttGenerator.h
@@ -0,0 +1,185 @@
+/** @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;
+ /// 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;
+} ACPI_PPTT_GENERATOR;
+
+#pragma pack()
+
+#endif // PPTT_GENERATOR_H_
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiRawLib/AcpiRawLib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiRawLib/AcpiRawLib.inf
new file mode 100644
index 0000000000..f2ab1b7111
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiRawLib/AcpiRawLib.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/DynamicTablesPkg/Library/Acpi/Common/AcpiRawLib/RawGenerator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiRawLib/RawGenerator.c
new file mode 100644
index 0000000000..a8323ad4ea
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiRawLib/RawGenerator.c
@@ -0,0 +1,146 @@
+/** @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/DynamicTablesPkg/Library/Acpi/Common/AcpiSpcrLib/AcpiSpcrLib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiSpcrLib/AcpiSpcrLib.inf
new file mode 100644
index 0000000000..e11f878ec8
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSpcrLib/AcpiSpcrLib.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/DynamicTablesPkg/Library/Acpi/Common/AcpiSpcrLib/SpcrGenerator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiSpcrLib/SpcrGenerator.c
new file mode 100644
index 0000000000..6f027f3bf9
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSpcrLib/SpcrGenerator.c
@@ -0,0 +1,482 @@
+/** @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:
+ - EArchCommonObjConsolePortInfo
+
+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 (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjConsolePortInfo,
+ CM_ARCH_COMMON_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_ARCH_COMMON_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 = GetEArchCommonObjConsolePortInfo (
+ 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/DynamicTablesPkg/Library/Acpi/Common/AcpiSratLib/AcpiSratLib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiSratLib/AcpiSratLib.inf
new file mode 100644
index 0000000000..5891dc4d1c
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSratLib/AcpiSratLib.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/DynamicTablesPkg/Library/Acpi/Common/AcpiSratLib/SratGenerator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiSratLib/SratGenerator.c
new file mode 100644
index 0000000000..48c9970a71
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSratLib/SratGenerator.c
@@ -0,0 +1,842 @@
+/** @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)
+ - EArchCommonObjMemoryAffinityInfo (OPTIONAL)
+ - EArchCommonObjGenericInitiatorAffinityInfo (OPTIONAL)
+ - EArchCommonObjDeviceHandleAcpi (OPTIONAL)
+ - EArchCommonObjDeviceHandlePci (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 (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjMemoryAffinityInfo,
+ CM_ARCH_COMMON_MEMORY_AFFINITY_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the Generic Initiator Affinity
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjGenericInitiatorAffinityInfo,
+ CM_ARCH_COMMON_GENERIC_INITIATOR_AFFINITY_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the ACPI Device Handle
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjDeviceHandleAcpi,
+ CM_ARCH_COMMON_DEVICE_HANDLE_ACPI
+ );
+
+/**
+ This macro expands to a function that retrieves the PCI Device Handle
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjDeviceHandlePci,
+ CM_ARCH_COMMON_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_ARCH_COMMON_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_ARCH_COMMON_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_ARCH_COMMON_GENERIC_INITIATOR_AFFINITY_INFO *GenInitAffInfo,
+ IN UINT32 GenInitAffCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY_STRUCTURE *GenInitAff;
+ CM_ARCH_COMMON_DEVICE_HANDLE_ACPI *DeviceHandleAcpi;
+ CM_ARCH_COMMON_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 = GetEArchCommonObjDeviceHandleAcpi (
+ 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 = GetEArchCommonObjDeviceHandlePci (
+ 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_ARCH_COMMON_MEMORY_AFFINITY_INFO *MemAffInfo;
+ CM_ARCH_COMMON_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 = GetEArchCommonObjMemoryAffinityInfo (
+ 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 = GetEArchCommonObjGenericInitiatorAffinityInfo (
+ 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/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyGenerator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyGenerator.c
new file mode 100644
index 0000000000..2deaa4640c
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyGenerator.c
@@ -0,0 +1,1705 @@
+/** @file
+ SSDT Cpu Topology Table Generator.
+
+ Copyright (c) 2021 - 2023, Arm Limited. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.3 Specification - January 2019 - s8.4 Declaring Processors
+ - ACPI for CoreSight version 1.2 Platform Design Document
+ (https://developer.arm.com/documentation/den0067/a/?lang=en)
+
+ @par Glossary:
+ - ETE - Embedded Trace Extension.
+ - ETM - Embedded Trace Macrocell.
+**/
+
+#include <Library/AcpiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.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/AcpiHelperLib.h>
+#include <Library/TableHelperLib.h>
+#include <Library/AmlLib/AmlLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+#include "SsdtCpuTopologyGenerator.h"
+
+/** ARM standard SSDT Cpu Topology Table Generator.
+
+Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArmObjGicCInfo
+ - EArchCommonObjProcHierarchyInfo (OPTIONAL) along with
+ - EArchCommonObjCmRef (OPTIONAL)
+ - EArchCommonObjLpiInfo (OPTIONAL)
+ - GetEArmObjEtInfo (OPTIONAL)
+ - EArchCommonObjPsdInfo (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 Processor Hierarchy
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjProcHierarchyInfo,
+ CM_ARCH_COMMON_PROC_HIERARCHY_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the cross-CM-object-
+ reference information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjCmRef,
+ CM_ARCH_COMMON_OBJ_REF
+ );
+
+/**
+ This macro expands to a function that retrieves the Lpi
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjLpiInfo,
+ CM_ARCH_COMMON_LPI_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the CPC
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjCpcInfo,
+ CM_ARCH_COMMON_CPC_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the ET device
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjEtInfo,
+ CM_ARM_ET_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the PSD
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPsdInfo,
+ CM_ARCH_COMMON_PSD_INFO
+ );
+
+/** Initialize the TokenTable.
+
+ One entry should be allocated for each CM_ARCH_COMMON_PROC_HIERARCHY_INFO
+ structure of the platform. The TokenTable allows to have a mapping:
+ Index <-> CM_OBJECT_TOKEN (to CM_ARCH_COMMON_LPI_INFO structures).
+
+ There will always be less sets of Lpi states (CM_ARCH_COMMON_OBJ_REF)
+ than the number of cpus/clusters (CM_ARCH_COMMON_PROC_HIERARCHY_INFO).
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] Count Number of entries to allocate in the TokenTable.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+TokenTableInitialize (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN UINT32 Count
+ )
+{
+ CM_OBJECT_TOKEN *Table;
+
+ if ((Generator == NULL) ||
+ (Count == 0) ||
+ (Count >= MAX_NODE_COUNT))
+ {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Table = AllocateZeroPool (sizeof (CM_OBJECT_TOKEN) * Count);
+ if (Table == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Generator->TokenTable.Table = Table;
+
+ return EFI_SUCCESS;
+}
+
+/** Free the TokenTable.
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+**/
+STATIC
+VOID
+EFIAPI
+TokenTableFree (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator
+ )
+{
+ ASSERT (Generator != NULL);
+ ASSERT (Generator->TokenTable.Table != NULL);
+
+ if (Generator->TokenTable.Table != NULL) {
+ FreePool (Generator->TokenTable.Table);
+ }
+}
+
+/** Add a new entry to the TokenTable and return its index.
+
+ If an entry with Token is already available in the table,
+ return its index without adding a new entry.
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] Token New Token entry to add.
+
+ @retval The index of the token entry in the TokenTable.
+**/
+STATIC
+UINT32
+EFIAPI
+TokenTableAdd (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CM_OBJECT_TOKEN Token
+ )
+{
+ CM_OBJECT_TOKEN *Table;
+ UINT32 Index;
+ UINT32 LastIndex;
+
+ ASSERT (Generator != NULL);
+ ASSERT (Generator->TokenTable.Table != NULL);
+
+ Table = Generator->TokenTable.Table;
+ LastIndex = Generator->TokenTable.LastIndex;
+
+ // Search if there is already an entry with this Token.
+ for (Index = 0; Index < LastIndex; Index++) {
+ if (Table[Index] == Token) {
+ return Index;
+ }
+ }
+
+ ASSERT (LastIndex < MAX_NODE_COUNT);
+ ASSERT (LastIndex < Generator->ProcNodeCount);
+
+ // If no, create a new entry.
+ Table[LastIndex] = Token;
+
+ return Generator->TokenTable.LastIndex++;
+}
+
+/** Write a string 'Xxxx\0' in AslName (5 bytes long),
+ with 'X' being the leading char of the name, and
+ with 'xxx' being Value in hexadecimal.
+
+ As 'xxx' in hexadecimal represents a number on 12 bits,
+ we have Value < (1 << 12).
+
+ @param [in] LeadChar Leading char of the name.
+ @param [in] Value Hex value of the name.
+ Must be lower than (2 << 12).
+ @param [in, out] AslName Pointer to write the 'Xxxx' string to.
+ Must be at least 5 bytes long.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+WriteAslName (
+ IN CHAR8 LeadChar,
+ IN UINT32 Value,
+ IN OUT CHAR8 *AslName
+ )
+{
+ UINT8 Index;
+
+ if ((Value >= MAX_NODE_COUNT) ||
+ (AslName == NULL))
+ {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ AslName[0] = LeadChar;
+ AslName[AML_NAME_SEG_SIZE] = '\0';
+
+ for (Index = 0; Index < AML_NAME_SEG_SIZE - 1; Index++) {
+ AslName[AML_NAME_SEG_SIZE - Index - 1] =
+ AsciiFromHex (((Value >> (4 * Index)) & 0xF));
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Create and add an _PSD Node to Cpu Node.
+
+ For instance, transform an AML node from:
+ Device (C002)
+ {
+ Name (_UID, 2)
+ Name (_HID, "ACPI0007")
+ }
+
+ To:
+ Device (C002)
+ {
+ Name (_UID, 2)
+ Name (_HID, "ACPI0007")
+ Name (_PSD, Package()
+ {
+ NumEntries, // Integer
+ Revision, // Integer
+ Domain, // Integer
+ CoordType, // Integer
+ NumProcessors, // Integer
+ })
+ }
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] GicCInfo Pointer to the CM_ARM_GICC_INFO object
+ describing the Cpu.
+ @param [in] Node CPU Node to which the _CPC node is
+ attached.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlPsdNode (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN CM_ARM_GICC_INFO *GicCInfo,
+ IN AML_OBJECT_NODE_HANDLE *Node
+ )
+{
+ EFI_STATUS Status;
+ CM_ARCH_COMMON_PSD_INFO *PsdInfo;
+
+ Status = GetEArchCommonObjPsdInfo (
+ CfgMgrProtocol,
+ GicCInfo->PsdToken,
+ &PsdInfo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ Status = AmlCreatePsdNode (
+ PsdInfo,
+ Node,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
+
+/** Create and add an _CPC Node to Cpu Node.
+
+ For instance, transform an AML node from:
+ Device (C002)
+ {
+ Name (_UID, 2)
+ Name (_HID, "ACPI0007")
+ }
+
+ To:
+ Device (C002)
+ {
+ Name (_UID, 2)
+ Name (_HID, "ACPI0007")
+ Name(_CPC, Package()
+ {
+ NumEntries, // Integer
+ Revision, // Integer
+ HighestPerformance, // Integer or Buffer (Resource Descriptor)
+ NominalPerformance, // Integer or Buffer (Resource Descriptor)
+ LowestNonlinearPerformance, // Integer or Buffer (Resource Descriptor)
+ LowestPerformance, // Integer or Buffer (Resource Descriptor)
+ GuaranteedPerformanceRegister, // Buffer (Resource Descriptor)
+ DesiredPerformanceRegister , // Buffer (Resource Descriptor)
+ MinimumPerformanceRegister , // Buffer (Resource Descriptor)
+ MaximumPerformanceRegister , // Buffer (Resource Descriptor)
+ PerformanceReductionToleranceRegister, // Buffer (Resource Descriptor)
+ TimeWindowRegister, // Buffer (Resource Descriptor)
+ CounterWraparoundTime, // Integer or Buffer (Resource Descriptor)
+ ReferencePerformanceCounterRegister, // Buffer (Resource Descriptor)
+ DeliveredPerformanceCounterRegister, // Buffer (Resource Descriptor)
+ PerformanceLimitedRegister, // Buffer (Resource Descriptor)
+ CPPCEnableRegister // Buffer (Resource Descriptor)
+ AutonomousSelectionEnable, // Integer or Buffer (Resource Descriptor)
+ AutonomousActivityWindowRegister, // Buffer (Resource Descriptor)
+ EnergyPerformancePreferenceRegister, // Buffer (Resource Descriptor)
+ ReferencePerformance // Integer or Buffer (Resource Descriptor)
+ LowestFrequency, // Integer or Buffer (Resource Descriptor)
+ NominalFrequency // Integer or Buffer (Resource Descriptor)
+ })
+ }
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] GicCInfo Pointer to the CM_ARM_GICC_INFO object
+ describing the Cpu.
+ @param [in] Node CPU Node to which the _CPC node is
+ attached.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlCpcNode (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN CM_ARM_GICC_INFO *GicCInfo,
+ IN AML_OBJECT_NODE_HANDLE *Node
+ )
+{
+ EFI_STATUS Status;
+ CM_ARCH_COMMON_CPC_INFO *CpcInfo;
+
+ Status = GetEArchCommonObjCpcInfo (
+ CfgMgrProtocol,
+ GicCInfo->CpcToken,
+ &CpcInfo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCreateCpcNode (
+ CpcInfo,
+ Node,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
+
+/** Create an embedded trace device and add it to the Cpu Node in the
+ AML namespace.
+
+ This generates the following ASL code:
+ Device (E002)
+ {
+ Name (_UID, 2)
+ Name (_HID, "ARMHC500")
+ }
+
+ Note: Currently we only support generating ETE nodes. Unlike ETM,
+ ETE has a system register interface and therefore does not need
+ the MMIO range to be described.
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] ParentNode Parent node to attach the Cpu node to.
+ @param [in] GicCInfo CM_ARM_GICC_INFO object used to create the node.
+ @param [in] CpuName Value used to generate the node name.
+ @param [out] EtNodePtr If not NULL, return the created Cpu node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlEtd (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN AML_NODE_HANDLE ParentNode,
+ IN CM_ARM_GICC_INFO *GicCInfo,
+ IN UINT32 CpuName,
+ OUT AML_OBJECT_NODE_HANDLE *EtNodePtr OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE_HANDLE EtNode;
+ CHAR8 AslName[AML_NAME_SEG_SIZE + 1];
+
+ ASSERT (Generator != NULL);
+ ASSERT (ParentNode != NULL);
+
+ Status = WriteAslName ('E', CpuName, AslName);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenDevice (AslName, ParentNode, &EtNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenNameInteger (
+ "_UID",
+ GicCInfo->AcpiProcessorUid,
+ EtNode,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenNameString (
+ "_HID",
+ ACPI_HID_ET_DEVICE,
+ EtNode,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // If requested, return the handle to the EtNode.
+ if (EtNodePtr != NULL) {
+ *EtNodePtr = EtNode;
+ }
+
+ return Status;
+}
+
+/** Create and add an Embedded trace device to the Cpu Node.
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] GicCInfo Pointer to the CM_ARM_GICC_INFO object
+ describing the Cpu.
+ @param [in] CpuName Value used to generate the CPU node name.
+ @param [in] Node CPU Node to which the ET device node is
+ attached.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_UNSUPPORTED Feature Unsupported.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlEtNode (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN CM_ARM_GICC_INFO *GicCInfo,
+ IN UINT32 CpuName,
+ IN AML_OBJECT_NODE_HANDLE *Node
+ )
+{
+ EFI_STATUS Status;
+ CM_ARM_ET_INFO *EtInfo;
+
+ Status = GetEArmObjEtInfo (
+ CfgMgrProtocol,
+ GicCInfo->EtToken,
+ &EtInfo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Currently we only support creation of a ETE Node.
+ if (EtInfo->EtType != ArmEtTypeEte) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = CreateAmlEtd (
+ Generator,
+ Node,
+ GicCInfo,
+ CpuName,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
+
+/** Create and add an _LPI method to Cpu/Cluster Node.
+
+ For instance, transform an AML node from:
+ Device (C002)
+ {
+ Name (_UID, 2)
+ Name (_HID, "ACPI0007")
+ }
+
+ To:
+ Device (C002)
+ {
+ Name (_UID, 2)
+ Name (_HID, "ACPI0007")
+ Method (_LPI, 0, NotSerialized)
+ {
+ Return (\_SB.L003)
+ }
+ }
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] ProcHierarchyNodeInfo CM_ARCH_COMMON_PROC_HIERARCHY_INFO
+ describing the Cpu.
+ @param [in] Node Node to which the _LPI method is
+ attached. Can represent a Cpu or a
+ Cluster.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlLpiMethod (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcHierarchyNodeInfo,
+ IN AML_OBJECT_NODE_HANDLE *Node
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TokenIndex;
+ CHAR8 AslName[SB_SCOPE_PREFIX_SIZE + AML_NAME_SEG_SIZE];
+
+ ASSERT (Generator != NULL);
+ ASSERT (ProcHierarchyNodeInfo != NULL);
+ ASSERT (ProcHierarchyNodeInfo->LpiToken != CM_NULL_TOKEN);
+ ASSERT (Node != NULL);
+
+ TokenIndex = TokenTableAdd (Generator, ProcHierarchyNodeInfo->LpiToken);
+
+ CopyMem (AslName, SB_SCOPE_PREFIX, SB_SCOPE_PREFIX_SIZE);
+
+ Status = WriteAslName (
+ 'L',
+ TokenIndex,
+ AslName + SB_SCOPE_PREFIX_SIZE - 1
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // ASL:
+ // Method (_LPI, 0) {
+ // Return ([AslName])
+ // }
+ Status = AmlCodeGenMethodRetNameString (
+ "_LPI",
+ AslName,
+ 0,
+ FALSE,
+ 0,
+ Node,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ }
+
+ return Status;
+}
+
+/** Generate all the Lpi states under the '_SB' scope.
+
+ This function generates the following ASL code:
+ Scope (\_SB) {
+ Name (L000, Package() {
+ 0, // Version
+ 0, // Level Index
+ X, // Count
+ Package() {
+ [An Lpi state]
+ },
+ Package() {
+ [Another Lpi state]
+ },
+ } // Name L000
+
+ Name (L001, Package() {
+ ...
+ } // Name L001
+
+ ...
+ } // Scope /_SB
+
+ The Lpi states are fetched from the Configuration Manager.
+ The names of the Lpi states are generated from the TokenTable.
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] ScopeNode Scope node handle ('\_SB' scope).
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenerateLpiStates (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN AML_OBJECT_NODE_HANDLE ScopeNode
+ )
+{
+ EFI_STATUS Status;
+
+ UINT32 Index;
+ UINT32 LastIndex;
+
+ AML_OBJECT_NODE_HANDLE LpiNode;
+ CM_ARCH_COMMON_OBJ_REF *LpiRefInfo;
+ UINT32 LpiRefInfoCount;
+ UINT32 LpiRefIndex;
+ CM_ARCH_COMMON_LPI_INFO *LpiInfo;
+ CHAR8 AslName[AML_NAME_SEG_SIZE + 1];
+
+ ASSERT (Generator != NULL);
+ ASSERT (Generator->TokenTable.Table != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (ScopeNode != NULL);
+
+ LastIndex = Generator->TokenTable.LastIndex;
+
+ // For each entry in the TokenTable, create a name in the AML namespace
+ // under SB_SCOPE, to store the Lpi states associated with the LpiToken.
+ for (Index = 0; Index < LastIndex; Index++) {
+ Status = WriteAslName ('L', Index, AslName);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // We do not support the LevelId field for now, let it to 0.
+ Status = AmlCreateLpiNode (AslName, 0, 0, ScopeNode, &LpiNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Fetch the LPI objects referenced by the token.
+ Status = GetEArchCommonObjCmRef (
+ CfgMgrProtocol,
+ Generator->TokenTable.Table[Index],
+ &LpiRefInfo,
+ &LpiRefInfoCount
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ for (LpiRefIndex = 0; LpiRefIndex < LpiRefInfoCount; LpiRefIndex++) {
+ // For each CM_ARCH_COMMON_LPI_INFO referenced by the token,
+ // add an Lpi state.
+ Status = GetEArchCommonObjLpiInfo (
+ CfgMgrProtocol,
+ LpiRefInfo[LpiRefIndex].ReferenceToken,
+ &LpiInfo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlAddLpiState (
+ LpiInfo->MinResidency,
+ LpiInfo->WorstCaseWakeLatency,
+ LpiInfo->Flags,
+ LpiInfo->ArchFlags,
+ LpiInfo->ResCntFreq,
+ LpiInfo->EnableParentState,
+ LpiInfo->IsInteger ?
+ NULL :
+ &LpiInfo->RegisterEntryMethod,
+ LpiInfo->IsInteger ?
+ LpiInfo->IntegerEntryMethod :
+ 0,
+ &LpiInfo->ResidencyCounterRegister,
+ &LpiInfo->UsageCounterRegister,
+ LpiInfo->StateName,
+ LpiNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ } // for LpiRefIndex
+ } // for Index
+
+ return EFI_SUCCESS;
+}
+
+/** Create a Cpu in the AML namespace.
+
+ This generates the following ASL code:
+ Device (C002)
+ {
+ Name (_UID, 2)
+ Name (_HID, "ACPI0007")
+ }
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] ParentNode Parent node to attach the Cpu node to.
+ @param [in] GicCInfo CM_ARM_GICC_INFO object used to create the node.
+ @param [in] CpuName Value used to generate the node name.
+ @param [out] CpuNodePtr If not NULL, return the created Cpu node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlCpu (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN AML_NODE_HANDLE ParentNode,
+ IN CM_ARM_GICC_INFO *GicCInfo,
+ IN UINT32 CpuName,
+ OUT AML_OBJECT_NODE_HANDLE *CpuNodePtr OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE_HANDLE CpuNode;
+ CHAR8 AslName[AML_NAME_SEG_SIZE + 1];
+
+ ASSERT (Generator != NULL);
+ ASSERT (ParentNode != NULL);
+ ASSERT (GicCInfo != NULL);
+
+ Status = WriteAslName ('C', CpuName, AslName);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenDevice (AslName, ParentNode, &CpuNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenNameInteger (
+ "_UID",
+ GicCInfo->AcpiProcessorUid,
+ CpuNode,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenNameString (
+ "_HID",
+ ACPI_HID_PROCESSOR_DEVICE,
+ CpuNode,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // If requested, return the handle to the CpuNode.
+ if (CpuNodePtr != NULL) {
+ *CpuNodePtr = CpuNode;
+ }
+
+ return Status;
+}
+
+/** Create a Cpu in the AML namespace from a CM_ARCH_COMMON_PROC_HIERARCHY_INFO
+ CM object.
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] ParentNode Parent node to attach the Cpu node to.
+ @param [in] CpuName Value used to generate the node name.
+ @param [in] ProcHierarchyNodeInfo CM_ARCH_COMMON_PROC_HIERARCHY_INFO
+ describing the Cpu.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlCpuFromProcHierarchy (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN AML_NODE_HANDLE ParentNode,
+ IN UINT32 CpuName,
+ IN CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcHierarchyNodeInfo
+ )
+{
+ EFI_STATUS Status;
+ CM_ARM_GICC_INFO *GicCInfo;
+ AML_OBJECT_NODE_HANDLE CpuNode;
+
+ ASSERT (Generator != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (ParentNode != NULL);
+ ASSERT (ProcHierarchyNodeInfo != NULL);
+ ASSERT (ProcHierarchyNodeInfo->AcpiIdObjectToken != CM_NULL_TOKEN);
+
+ Status = GetEArmObjGicCInfo (
+ CfgMgrProtocol,
+ ProcHierarchyNodeInfo->AcpiIdObjectToken,
+ &GicCInfo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = CreateAmlCpu (Generator, ParentNode, GicCInfo, CpuName, &CpuNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // If a set of Lpi states is associated with the
+ // CM_ARCH_COMMON_PROC_HIERARCHY_INFO, create an _LPI method returning them.
+ if (ProcHierarchyNodeInfo->LpiToken != CM_NULL_TOKEN) {
+ Status = CreateAmlLpiMethod (Generator, ProcHierarchyNodeInfo, CpuNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+
+ if (GicCInfo->PsdToken != CM_NULL_TOKEN) {
+ Status = CreateAmlPsdNode (Generator, CfgMgrProtocol, GicCInfo, CpuNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+
+ // If a CPC info is associated with the
+ // GicCinfo, create an _CPC method returning them.
+ if (GicCInfo->CpcToken != CM_NULL_TOKEN) {
+ Status = CreateAmlCpcNode (Generator, CfgMgrProtocol, GicCInfo, CpuNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+
+ // Add an Embedded Trace node if present.
+ if (GicCInfo->EtToken != CM_NULL_TOKEN) {
+ Status = CreateAmlEtNode (
+ Generator,
+ CfgMgrProtocol,
+ GicCInfo,
+ CpuName,
+ CpuNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+
+ return Status;
+}
+
+/** Create a Processor Container in the AML namespace.
+
+ Any CM_ARCH_COMMON_PROC_HIERARCHY_INFO object with the following flags is
+ assumed to be a processor container:
+ - EFI_ACPI_6_3_PPTT_PACKAGE_NOT_PHYSICAL
+ - EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID
+ - EFI_ACPI_6_3_PPTT_NODE_IS_NOT_LEAF
+
+ This generates the following ASL code:
+ Device (C002)
+ {
+ Name (_UID, 2)
+ Name (_HID, "ACPI0010")
+ }
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] ParentNode Parent node to attach the processor
+ container node to.
+ @param [in] ProcHierarchyNodeInfo CM_ARCH_COMMON_PROC_HIERARCHY_INFO object
+ used to create the node.
+ @param [in] ProcContainerName Name of the processor container.
+ @param [in] ProcContainerUid Uid of the processor container.
+ @param [out] ProcContainerNodePtr If success, contains the created processor
+ container node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlProcessorContainer (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN AML_NODE_HANDLE ParentNode,
+ IN CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcHierarchyNodeInfo,
+ IN UINT16 ProcContainerName,
+ IN UINT32 ProcContainerUid,
+ OUT AML_OBJECT_NODE_HANDLE *ProcContainerNodePtr
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE_HANDLE ProcContainerNode;
+ CHAR8 AslNameProcContainer[AML_NAME_SEG_SIZE + 1];
+
+ ASSERT (Generator != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (ParentNode != NULL);
+ ASSERT (ProcHierarchyNodeInfo != NULL);
+ ASSERT (ProcContainerNodePtr != NULL);
+
+ Status = WriteAslName ('C', ProcContainerName, AslNameProcContainer);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenDevice (AslNameProcContainer, ParentNode, &ProcContainerNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Use the ProcContainerIndex for the _UID value as there is no AcpiProcessorUid
+ // and EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID is set for non-Cpus.
+ Status = AmlCodeGenNameInteger (
+ "_UID",
+ ProcContainerUid,
+ ProcContainerNode,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenNameString (
+ "_HID",
+ ACPI_HID_PROCESSOR_CONTAINER_DEVICE,
+ ProcContainerNode,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // If a set of Lpi states are associated with the
+ // CM_ARCH_COMMON_PROC_HIERARCHY_INFO, create an _LPI method returning them.
+ if (ProcHierarchyNodeInfo->LpiToken != CM_NULL_TOKEN) {
+ Status = CreateAmlLpiMethod (
+ Generator,
+ ProcHierarchyNodeInfo,
+ ProcContainerNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ }
+
+ *ProcContainerNodePtr = ProcContainerNode;
+
+ return Status;
+}
+
+/** Check flags and topology of a ProcNode.
+
+ @param [in] NodeFlags Flags of the ProcNode to check.
+ @param [in] IsLeaf The ProcNode is a leaf.
+ @param [in] NodeToken NodeToken of the ProcNode.
+ @param [in] ParentNodeToken Parent NodeToken of the ProcNode.
+ @param [in] PackageNodeSeen A parent of the ProcNode has the physical package flag set.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CheckProcNode (
+ UINT32 NodeFlags,
+ BOOLEAN IsLeaf,
+ CM_OBJECT_TOKEN NodeToken,
+ CM_OBJECT_TOKEN ParentNodeToken,
+ BOOLEAN PackageNodeSeen
+ )
+{
+ BOOLEAN InvalidFlags;
+ BOOLEAN HasPhysicalPackageBit;
+
+ HasPhysicalPackageBit = (NodeFlags & EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL) ==
+ EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL;
+
+ // Only one Physical Package flag is allowed in the hierarchy
+ InvalidFlags = HasPhysicalPackageBit && PackageNodeSeen;
+
+ // Check Leaf specific flags.
+ if (IsLeaf) {
+ InvalidFlags |= ((NodeFlags & PPTT_LEAF_MASK) != PPTT_LEAF_MASK);
+ // Must have Physical Package flag somewhere in the hierarchy
+ InvalidFlags |= !(HasPhysicalPackageBit || PackageNodeSeen);
+ } else {
+ InvalidFlags |= ((NodeFlags & PPTT_LEAF_MASK) != 0);
+ }
+
+ if (InvalidFlags) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CPU-TOPOLOGY: Invalid flags for ProcNode: 0x%p.\n",
+ (VOID *)NodeToken
+ ));
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Create an AML representation of the Cpu topology.
+
+ A processor container is by extension any non-leave device in the cpu topology.
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] NodeToken Token of the CM_ARCH_COMMON_PROC_HIERARCHY_INFO currently handled.
+ @param [in] ParentNode Parent node to attach the created
+ node to.
+ @param [in,out] ProcContainerIndex Pointer to the current processor container
+ index to be used as UID.
+ @param [in] PackageNodeSeen A parent of the ProcNode has the physical package flag set.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlCpuTopologyTree (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN CM_OBJECT_TOKEN NodeToken,
+ IN AML_NODE_HANDLE ParentNode,
+ IN OUT UINT32 *ProcContainerIndex,
+ IN BOOLEAN PackageNodeSeen
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINT32 CpuIndex;
+ UINT32 ProcContainerName;
+ AML_OBJECT_NODE_HANDLE ProcContainerNode;
+ UINT32 Uid;
+ UINT16 Name;
+ BOOLEAN HasPhysicalPackageBit;
+
+ ASSERT (Generator != NULL);
+ ASSERT (Generator->ProcNodeList != NULL);
+ ASSERT (Generator->ProcNodeCount != 0);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (ParentNode != NULL);
+ ASSERT (ProcContainerIndex != NULL);
+
+ CpuIndex = 0;
+ ProcContainerName = 0;
+
+ for (Index = 0; Index < Generator->ProcNodeCount; Index++) {
+ // Find the children of the CM_ARCH_COMMON_PROC_HIERARCHY_INFO
+ // currently being handled (i.e. ParentToken == NodeToken).
+ if (Generator->ProcNodeList[Index].ParentToken == NodeToken) {
+ // Only Cpus (leaf nodes in this tree) have a AcpiIdObjectToken.
+ // Create a Cpu node.
+ if (Generator->ProcNodeList[Index].AcpiIdObjectToken != CM_NULL_TOKEN) {
+ Status = CheckProcNode (
+ Generator->ProcNodeList[Index].Flags,
+ TRUE,
+ Generator->ProcNodeList[Index].Token,
+ NodeToken,
+ PackageNodeSeen
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ if (Generator->ProcNodeList[Index].OverrideNameUidEnabled) {
+ Name = Generator->ProcNodeList[Index].OverrideName;
+ } else {
+ Name = CpuIndex;
+ }
+
+ Status = CreateAmlCpuFromProcHierarchy (
+ Generator,
+ CfgMgrProtocol,
+ ParentNode,
+ Name,
+ &Generator->ProcNodeList[Index]
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ CpuIndex++;
+ } else {
+ // If this is not a Cpu, then this is a processor container.
+
+ Status = CheckProcNode (
+ Generator->ProcNodeList[Index].Flags,
+ FALSE,
+ Generator->ProcNodeList[Index].Token,
+ NodeToken,
+ PackageNodeSeen
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ if (Generator->ProcNodeList[Index].OverrideNameUidEnabled) {
+ Name = Generator->ProcNodeList[Index].OverrideName;
+ Uid = Generator->ProcNodeList[Index].OverrideUid;
+ } else {
+ Name = ProcContainerName;
+ Uid = *ProcContainerIndex;
+ }
+
+ Status = CreateAmlProcessorContainer (
+ Generator,
+ CfgMgrProtocol,
+ ParentNode,
+ &Generator->ProcNodeList[Index],
+ Name,
+ Uid,
+ &ProcContainerNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Nodes must have a unique name in the ASL namespace.
+ // Reset the Cpu index whenever we create a new processor container.
+ (*ProcContainerIndex)++;
+ CpuIndex = 0;
+
+ // And reset the cluster name whenever there is a package.
+ if (NodeToken == CM_NULL_TOKEN) {
+ ProcContainerName = 0;
+ } else {
+ ProcContainerName++;
+ }
+
+ HasPhysicalPackageBit = (Generator->ProcNodeList[Index].Flags & EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL) ==
+ EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL;
+
+ // Recursively continue creating an AML tree.
+ Status = CreateAmlCpuTopologyTree (
+ Generator,
+ CfgMgrProtocol,
+ Generator->ProcNodeList[Index].Token,
+ ProcContainerNode,
+ ProcContainerIndex,
+ (PackageNodeSeen || HasPhysicalPackageBit)
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ }
+ } // if ParentToken == NodeToken
+ } // for
+
+ return EFI_SUCCESS;
+}
+
+/** Create the processor hierarchy AML tree from
+ CM_ARCH_COMMON_PROC_HIERARCHY_INFO CM objects.
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] ScopeNode Scope node handle ('\_SB' scope).
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateTopologyFromProcHierarchy (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN AML_OBJECT_NODE_HANDLE ScopeNode
+ )
+{
+ EFI_STATUS Status;
+ UINT32 ProcContainerIndex;
+
+ ASSERT (Generator != NULL);
+ ASSERT (Generator->ProcNodeCount != 0);
+ ASSERT (Generator->ProcNodeList != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (ScopeNode != NULL);
+
+ ProcContainerIndex = 0;
+
+ Status = TokenTableInitialize (Generator, Generator->ProcNodeCount);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = CreateAmlCpuTopologyTree (
+ Generator,
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ ScopeNode,
+ &ProcContainerIndex,
+ FALSE
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+ Status = GenerateLpiStates (Generator, CfgMgrProtocol, ScopeNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+exit_handler:
+ TokenTableFree (Generator);
+ return Status;
+}
+
+/** Create the processor hierarchy AML tree from CM_ARM_GICC_INFO
+ CM objects.
+
+ A processor container is by extension any non-leave device in the cpu topology.
+
+ @param [in] Generator The SSDT Cpu Topology generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] ScopeNode Scope node handle ('\_SB' scope).
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateTopologyFromGicC (
+ IN ACPI_CPU_TOPOLOGY_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN AML_OBJECT_NODE_HANDLE ScopeNode
+ )
+{
+ EFI_STATUS Status;
+ CM_ARM_GICC_INFO *GicCInfo;
+ UINT32 GicCInfoCount;
+ UINT32 Index;
+ AML_OBJECT_NODE_HANDLE CpuNode;
+
+ ASSERT (Generator != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (ScopeNode != NULL);
+
+ Status = GetEArmObjGicCInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GicCInfo,
+ &GicCInfoCount
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // For each CM_ARM_GICC_INFO object, create an AML node.
+ for (Index = 0; Index < GicCInfoCount; Index++) {
+ Status = CreateAmlCpu (
+ Generator,
+ ScopeNode,
+ &GicCInfo[Index],
+ Index,
+ &CpuNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ break;
+ }
+
+ if (GicCInfo->PsdToken != CM_NULL_TOKEN) {
+ Status = CreateAmlPsdNode (Generator, CfgMgrProtocol, GicCInfo, CpuNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+
+ // If a CPC info is associated with the
+ // GicCinfo, create an _CPC method returning them.
+ if (GicCInfo[Index].CpcToken != CM_NULL_TOKEN) {
+ Status = CreateAmlCpcNode (Generator, CfgMgrProtocol, &GicCInfo[Index], CpuNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ break;
+ }
+ }
+
+ if (GicCInfo[Index].EtToken != CM_NULL_TOKEN) {
+ Status = CreateAmlEtNode (
+ Generator,
+ CfgMgrProtocol,
+ &GicCInfo[Index],
+ Index,
+ CpuNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+ } // for
+
+ return Status;
+}
+
+/** Construct the SSDT Cpu Topology 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
+BuildSsdtCpuTopologyTable (
+ 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;
+ AML_ROOT_NODE_HANDLE RootNode;
+ AML_OBJECT_NODE_HANDLE ScopeNode;
+ CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcHierarchyNodeList;
+ UINT32 ProcHierarchyNodeCount;
+ ACPI_CPU_TOPOLOGY_GENERATOR *Generator;
+
+ ASSERT (This != NULL);
+ ASSERT (AcpiTableInfo != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (Table != NULL);
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+ Generator = (ACPI_CPU_TOPOLOGY_GENERATOR *)This;
+
+ Status = AddSsdtAcpiHeader (
+ CfgMgrProtocol,
+ This,
+ AcpiTableInfo,
+ &RootNode
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = AmlCodeGenScope (SB_SCOPE, RootNode, &ScopeNode);
+ if (EFI_ERROR (Status)) {
+ goto exit_handler;
+ }
+
+ // Get the processor hierarchy info and update the processor topology
+ // structure count with Processor Hierarchy Nodes (Type 0)
+ Status = GetEArchCommonObjProcHierarchyInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &ProcHierarchyNodeList,
+ &ProcHierarchyNodeCount
+ );
+ if (EFI_ERROR (Status) &&
+ (Status != EFI_NOT_FOUND))
+ {
+ goto exit_handler;
+ }
+
+ if (Status == EFI_NOT_FOUND) {
+ // If hierarchy information is not found generate a flat topology
+ // using CM_ARM_GICC_INFO objects.
+ Status = CreateTopologyFromGicC (
+ Generator,
+ CfgMgrProtocol,
+ ScopeNode
+ );
+ if (EFI_ERROR (Status)) {
+ goto exit_handler;
+ }
+ } else {
+ // Generate the topology from CM_ARCH_COMMON_PROC_HIERARCHY_INFO objects.
+ Generator->ProcNodeList = ProcHierarchyNodeList;
+ Generator->ProcNodeCount = ProcHierarchyNodeCount;
+
+ Status = CreateTopologyFromProcHierarchy (
+ Generator,
+ CfgMgrProtocol,
+ ScopeNode
+ );
+ if (EFI_ERROR (Status)) {
+ goto exit_handler;
+ }
+ }
+
+ Status = AmlSerializeDefinitionBlock (
+ RootNode,
+ Table
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-CPU-TOPOLOGY: Failed to Serialize SSDT Table Data."
+ " Status = %r\n",
+ Status
+ ));
+ goto exit_handler;
+ }
+
+exit_handler:
+ // Delete the RootNode and its attached children.
+ return AmlDeleteTree (RootNode);
+}
+
+/** Free any resources allocated for constructing the
+ SSDT Cpu Topology ACPI table.
+
+ @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
+FreeSsdtCpuTopologyTableResources (
+ 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: SSDT-CPU-TOPOLOGY: Invalid Table Pointer\n"));
+ ASSERT ((Table != NULL) && (*Table != NULL));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (*Table);
+ *Table = NULL;
+ return EFI_SUCCESS;
+}
+
+/** This macro defines the SSDT Cpu Topology Table Generator revision.
+*/
+#define SSDT_CPU_TOPOLOGY_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the SSDT Cpu Topology Table Generator.
+*/
+STATIC
+ACPI_CPU_TOPOLOGY_GENERATOR SsdtCpuTopologyGenerator = {
+ // ACPI table generator header
+ {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtCpuTopology),
+ // Generator Description
+ L"ACPI.STD.SSDT.CPU.TOPOLOGY.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_CPU_TOPOLOGY_GENERATOR_REVISION,
+ // Build Table function
+ BuildSsdtCpuTopologyTable,
+ // Free Resource function
+ FreeSsdtCpuTopologyTableResources,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+ },
+
+ // Private fields are defined from here.
+
+ // TokenTable
+ {
+ // Table
+ NULL,
+ // LastIndex
+ 0
+ },
+ // ProcNodeList
+ NULL,
+ // ProcNodeCount
+ 0
+};
+
+/** 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
+AcpiSsdtCpuTopologyLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = RegisterAcpiTableGenerator (&SsdtCpuTopologyGenerator.Header);
+ DEBUG ((
+ DEBUG_INFO,
+ "SSDT-CPU-TOPOLOGY: 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
+AcpiSsdtCpuTopologyLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = DeregisterAcpiTableGenerator (&SsdtCpuTopologyGenerator.Header);
+ DEBUG ((
+ DEBUG_INFO,
+ "SSDT-CPU-TOPOLOGY: Deregister Generator. Status = %r\n",
+ Status
+ ));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyGenerator.h b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyGenerator.h
new file mode 100644
index 0000000000..6fb44c7e58
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyGenerator.h
@@ -0,0 +1,147 @@
+/** @file
+ SSDT Cpu Topology Table Generator.
+
+ Copyright (c) 2021 - 2023, Arm Limited. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.3 Specification - January 2019 - s8.4 Declaring Processors
+ - ACPI for CoreSight version 1.2 Platform Design Document
+ (https://developer.arm.com/documentation/den0067/a/?lang=en)
+
+ @par Glossary:
+ - ETE - Embedded Trace Extension.
+ - ETM - Embedded Trace Macrocell.
+**/
+
+#ifndef SSDT_CPU_TOPOLOGY_GENERATOR_H_
+#define SSDT_CPU_TOPOLOGY_GENERATOR_H_
+
+#pragma pack(1)
+
+// Mask for the flags that need to be checked.
+#define PPTT_PROCESSOR_MASK ( \
+ (EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL) | \
+ (EFI_ACPI_6_3_PPTT_PROCESSOR_ID_VALID << 1) | \
+ (EFI_ACPI_6_3_PPTT_NODE_IS_LEAF << 3))
+
+// Mask for the cpu flags.
+#define PPTT_CPU_PROCESSOR_MASK ( \
+ (EFI_ACPI_6_3_PPTT_PACKAGE_NOT_PHYSICAL) | \
+ (EFI_ACPI_6_3_PPTT_PROCESSOR_ID_VALID << 1) | \
+ (EFI_ACPI_6_3_PPTT_NODE_IS_LEAF << 3))
+
+// Mask for the cluster flags.
+// Even though a _UID is generated for clusters, it is simpler to use
+// EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID and to not match the cluster id of
+// the PPTT table (not sure the PPTT table is generated).
+#define PPTT_CLUSTER_PROCESSOR_MASK ( \
+ (EFI_ACPI_6_3_PPTT_PACKAGE_NOT_PHYSICAL) | \
+ (EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID << 1) | \
+ (EFI_ACPI_6_3_PPTT_NODE_IS_NOT_LEAF << 3))
+
+// Leaf nodes specific mask.
+#define PPTT_LEAF_MASK ((EFI_ACPI_6_3_PPTT_PROCESSOR_ID_VALID << 1) | \
+ (EFI_ACPI_6_3_PPTT_NODE_IS_LEAF << 3))
+
+/** LPI states are stored in the ASL namespace at '\_SB_.Lxxx',
+ with xxx being the node index of the LPI state.
+*/
+#define SB_SCOPE "\\_SB_"
+#define SB_SCOPE_PREFIX SB_SCOPE "."
+/// Size of the SB_SCOPE_PREFIX string.
+#define SB_SCOPE_PREFIX_SIZE sizeof (SB_SCOPE_PREFIX)
+
+/// HID for a processor device.
+#define ACPI_HID_PROCESSOR_DEVICE "ACPI0007"
+
+/// HID for a ETM/ETE device.
+#define ACPI_HID_ET_DEVICE "ARMHC500"
+
+/// HID for a processor container device.
+#define ACPI_HID_PROCESSOR_CONTAINER_DEVICE "ACPI0010"
+
+/** Node names of Cpus and Clusters are 'Cxxx', and 'Lxxx' for LPI states.
+ The 'xxx' is an index on 12 bits is given to node name,
+ thus the limitation in the number of nodes.
+*/
+#define MAX_NODE_COUNT (1 << 12)
+
+/** A structure used to handle the Lpi structures referencing.
+
+ A CM_ARCH_COMMON_PROC_HIERARCHY_INFO structure references a CM_ARCH_COMMON_OBJ_REF.
+ This CM_ARCH_COMMON_OBJ_REF references CM_ARCH_COMMON_LPI_INFO structures.
+
+ Example:
+ (Cpu0) (Cpu1)
+ CM_ARCH_COMMON_PROC_HIERARCHY_INFO CM_ARCH_COMMON_PROC_HIERARCHY_INFO
+ | |
+ +----------------------------------------
+ |
+ v
+ (List of references to Lpi states)
+ CM_ARCH_COMMON_OBJ_REF
+ |
+ +----------------------------------------
+ | |
+ v v
+ (A first Lpi state) (A second Lpi state)
+ CM_ARCH_COMMON_LPI_INFO[0] CM_ARCH_COMMON_LPI_INFO[1]
+
+ Here, Cpu0 and Cpu1 have the same Lpi states. Both CM_ARCH_COMMON_PROC_HIERARCHY_INFO
+ structures reference the same CM_ARCH_COMMON_OBJ_REF. An entry is created in the
+ TokenTable such as:
+ 0 <-> CM_ARCH_COMMON_OBJ_REF
+
+ This will lead to the creation of this pseudo-ASL code where Cpu0 and Cpu1
+ return the same object at \_SB.L000:
+ Scope (\_SB) {
+ Device (C000) {
+ [...]
+ Method (_LPI) {
+ Return (\_SB.L000)
+ }
+ } // C000
+
+ Device (C001) {
+ [...]
+ Method (_LPI) {
+ Return (\_SB.L000)
+ }
+ } // C001
+
+ // Lpi states
+ Name (L000, Package (0x05) {
+ [...]
+ }
+ }
+*/
+typedef struct TokenTable {
+ /// TokenTable, a table allowing to map:
+ /// Index <-> CM_OBJECT_TOKEN (to CM_ARCH_COMMON_LPI_INFO structures).
+ CM_OBJECT_TOKEN *Table;
+
+ /// Last used index of the TokenTable.
+ /// LastIndex is bound by ProcNodeCount.
+ UINT32 LastIndex;
+} TOKEN_TABLE;
+
+/** A structure holding the Cpu topology generator and additional private data.
+*/
+typedef struct AcpiCpuTopologyGenerator {
+ /// ACPI Table generator header
+ ACPI_TABLE_GENERATOR Header;
+
+ // Private fields are defined from here.
+
+ /// Private object used to handle token referencing.
+ TOKEN_TABLE TokenTable;
+ /// List of CM_ARCH_COMMON_PROC_HIERARCHY_INFO CM objects.
+ CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcNodeList;
+ /// Count of CM_ARCH_COMMON_PROC_HIERARCHY_INFO CM objects.
+ UINT32 ProcNodeCount;
+} ACPI_CPU_TOPOLOGY_GENERATOR;
+
+#pragma pack()
+
+#endif // SSDT_CPU_TOPOLOGY_GENERATOR_H_
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyLib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyLib.inf
new file mode 100644
index 0000000000..3e2d154749
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtCpuTopologyLib/SsdtCpuTopologyLib.inf
@@ -0,0 +1,33 @@
+## @file
+# Ssdt Cpu Topology Table Generator
+#
+# Copyright (c) 2021, Arm Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = SsdtCpuTopologyLibArm
+ FILE_GUID = F2835EB6-4B05-48D4-A475-147DA0F3755C
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiSsdtCpuTopologyLibConstructor
+ DESTRUCTOR = AcpiSsdtCpuTopologyLibDestructor
+
+[Sources]
+ SsdtCpuTopologyGenerator.c
+ SsdtCpuTopologyGenerator.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ ArmPlatformPkg/ArmPlatformPkg.dec
+ DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+ AcpiHelperLib
+ AmlLib
+ BaseLib
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieGenerator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieGenerator.c
new file mode 100644
index 0000000000..2b488016e5
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieGenerator.c
@@ -0,0 +1,1247 @@
+/** @file
+ SSDT Pcie Table Generator.
+
+ Copyright (c) 2021 - 2022, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - PCI Firmware Specification - Revision 3.0
+ - ACPI 6.4 specification:
+ - s6.2.13 "_PRT (PCI Routing Table)"
+ - s6.1.1 "_ADR (Address)"
+ - linux kernel code
+ - Arm Base Boot Requirements v1.0
+ - Arm Base System Architecture v1.0
+**/
+
+#include <Library/AcpiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.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/AcpiHelperLib.h>
+#include <Library/TableHelperLib.h>
+#include <Library/AmlLib/AmlLib.h>
+#include <Library/SsdtPcieSupportLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+#include "SsdtPcieGenerator.h"
+
+#define PCI_MAX_DEVICE_COUNT_PER_BUS 32
+#define PCI_MAX_FUNCTION_COUNT_PER_DEVICE 8
+
+/** ARM standard SSDT Pcie Table Generator.
+
+Requirements:
+ The following Configuration Manager Object(s) are required by
+ this Generator:
+ - EArchCommonObjCmRef
+ - EArchCommonObjPciConfigSpaceInfo
+ - EArchCommonObjPciAddressMapInfo
+ - EArchCommonObjPciInterruptMapInfo
+*/
+
+/** This macro expands to a function that retrieves the cross-CM-object-
+ reference information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjCmRef,
+ CM_ARCH_COMMON_OBJ_REF
+ );
+
+/** This macro expands to a function that retrieves the Pci
+ Configuration Space Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPciConfigSpaceInfo,
+ CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO
+ );
+
+/** This macro expands to a function that retrieves the Pci
+ Address Mapping Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPciAddressMapInfo,
+ CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO
+ );
+
+/** This macro expands to a function that retrieves the Pci
+ Interrupt Mapping Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjPciInterruptMapInfo,
+ CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO
+ );
+
+/** Initialize the MappingTable.
+
+ @param [in] MappingTable The mapping table structure.
+ @param [in] Count Number of entries to allocate in the
+ MappingTable.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+MappingTableInitialize (
+ IN MAPPING_TABLE *MappingTable,
+ IN UINT32 Count
+ )
+{
+ UINT32 *Table;
+
+ if ((MappingTable == NULL) ||
+ (Count == 0))
+ {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Table = AllocateZeroPool (sizeof (*Table) * Count);
+ if (Table == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ MappingTable->Table = Table;
+ MappingTable->LastIndex = 0;
+ MappingTable->MaxIndex = Count;
+
+ return EFI_SUCCESS;
+}
+
+/** Free the MappingTable.
+
+ @param [in, out] MappingTable The mapping table structure.
+**/
+STATIC
+VOID
+EFIAPI
+MappingTableFree (
+ IN OUT MAPPING_TABLE *MappingTable
+ )
+{
+ ASSERT (MappingTable != NULL);
+ ASSERT (MappingTable->Table != NULL);
+
+ if (MappingTable->Table != NULL) {
+ FreePool (MappingTable->Table);
+ }
+}
+
+/** Add a new entry to the MappingTable and return its index.
+
+ If an entry with [Integer] is already available in the table,
+ return its index without adding a new entry.
+
+ @param [in] MappingTable The mapping table structure.
+ @param [in] Integer New Integer entry to add.
+
+ @retval The index of the Integer entry in the MappingTable.
+**/
+STATIC
+UINT32
+EFIAPI
+MappingTableAdd (
+ IN MAPPING_TABLE *MappingTable,
+ IN UINT32 Integer
+ )
+{
+ UINT32 *Table;
+ UINT32 Index;
+ UINT32 LastIndex;
+
+ ASSERT (MappingTable != NULL);
+ ASSERT (MappingTable->Table != NULL);
+
+ Table = MappingTable->Table;
+ LastIndex = MappingTable->LastIndex;
+
+ // Search if there is already an entry with this Integer.
+ for (Index = 0; Index < LastIndex; Index++) {
+ if (Table[Index] == Integer) {
+ return Index;
+ }
+ }
+
+ ASSERT (LastIndex < MappingTable->MaxIndex);
+
+ // If no, create a new entry.
+ Table[LastIndex] = Integer;
+
+ return MappingTable->LastIndex++;
+}
+
+/** Generate required Pci device information.
+
+ ASL code:
+ Name (_UID, <Uid>) // Uid of the Pci device
+ Name (_HID, EISAID ("PNP0A08")) // PCI Express Root Bridge
+ Name (_CID, EISAID ("PNP0A03")) // Compatible PCI Root Bridge
+ Name (_SEG, <Pci segment group>) // PCI Segment Group number
+ Name (_BBN, <Bus number>) // PCI Base Bus Number
+ Name (_CCA, 1) // Initially mark the PCI coherent
+
+ @param [in] PciInfo Pci device information.
+ @param [in] Uid Unique Id of the Pci device.
+ @param [in, out] PciNode Pci node to amend.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GeneratePciDeviceInfo (
+ IN CONST CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO *PciInfo,
+ IN UINT32 Uid,
+ IN OUT AML_OBJECT_NODE_HANDLE PciNode
+ )
+{
+ EFI_STATUS Status;
+ UINT32 EisaId;
+
+ ASSERT (PciInfo != NULL);
+ ASSERT (PciNode != NULL);
+
+ // ASL: Name (_UID, <Uid>)
+ Status = AmlCodeGenNameInteger ("_UID", Uid, PciNode, NULL);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // ASL: Name (_HID, EISAID ("PNP0A08"))
+ Status = AmlGetEisaIdFromString ("PNP0A08", &EisaId);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenNameInteger ("_HID", EisaId, PciNode, NULL);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // ASL: Name (_CID, EISAID ("PNP0A03"))
+ Status = AmlGetEisaIdFromString ("PNP0A03", &EisaId);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenNameInteger ("_CID", EisaId, PciNode, NULL);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // ASL: Name (_SEG, <Pci segment group>)
+ Status = AmlCodeGenNameInteger (
+ "_SEG",
+ PciInfo->PciSegmentGroupNumber,
+ PciNode,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // ASL: Name (_BBN, <Bus number>)
+ Status = AmlCodeGenNameInteger (
+ "_BBN",
+ PciInfo->StartBusNumber,
+ PciNode,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // ASL: Name (_CCA, 1)
+ // Must be aligned with the IORT CCA property in
+ // "Table 14 Memory access properties"
+ Status = AmlCodeGenNameInteger ("_CCA", 1, PciNode, NULL);
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
+
+/** Generate a _PRT object (Pci Routing Table) for the Pci device.
+
+ Cf. ACPI 6.4 specification, s6.2.13 "_PRT (PCI Routing Table)"
+
+ @param [in] Generator The SSDT Pci generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol interface.
+ @param [in] PciInfo Pci device information.
+ @param [in] Uid Unique Id of the Pci device.
+ @param [in, out] PciNode Pci node to amend.
+
+ @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
+GeneratePrt (
+ IN ACPI_PCI_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN CONST CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO *PciInfo,
+ IN UINT32 Uid,
+ IN OUT AML_OBJECT_NODE_HANDLE PciNode
+ )
+{
+ EFI_STATUS Status;
+ INT32 Index;
+ AML_OBJECT_NODE_HANDLE PrtNode;
+ CM_ARCH_COMMON_OBJ_REF *RefInfo;
+ UINT32 RefCount;
+ CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO *IrqMapInfo;
+
+ ASSERT (Generator != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (PciInfo != NULL);
+ ASSERT (PciNode != NULL);
+
+ PrtNode = NULL;
+
+ // Get the array of CM_ARCH_COMMON_OBJ_REF referencing the
+ // CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO objects.
+ Status = GetEArchCommonObjCmRef (
+ CfgMgrProtocol,
+ PciInfo->InterruptMapToken,
+ &RefInfo,
+ &RefCount
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Initialized DeviceTable.
+ Status = MappingTableInitialize (&Generator->DeviceTable, RefCount);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler0;
+ }
+
+ // ASL: Name (_PRT, Package () {})
+ Status = AmlCodeGenNamePackage ("_PRT", NULL, &PrtNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+ for (Index = 0; Index < RefCount; Index++) {
+ // Get CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO structures one by one.
+ Status = GetEArchCommonObjPciInterruptMapInfo (
+ CfgMgrProtocol,
+ RefInfo[Index].ReferenceToken,
+ &IrqMapInfo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+ // Check that the interrupts flags are SPIs, level high.
+ // Cf. Arm BSA v1.0, sE.6 "Legacy interrupts"
+ if ((Index > 0) &&
+ (IrqMapInfo->IntcInterrupt.Interrupt >= 32) &&
+ (IrqMapInfo->IntcInterrupt.Interrupt < 1020) &&
+ ((IrqMapInfo->IntcInterrupt.Flags & 0xB) != 0))
+ {
+ Status = EFI_INVALID_PARAMETER;
+ ASSERT_EFI_ERROR (Status);
+ goto exit_handler;
+ }
+
+ // Add the device to the DeviceTable.
+ MappingTableAdd (&Generator->DeviceTable, IrqMapInfo->PciDevice);
+
+ /* Add a _PRT entry.
+ ASL
+ Name (_PRT, Package () {
+ <OldPrtEntries>,
+ <NewPrtEntry>
+ })
+
+ Address is set as:
+ ACPI 6.4 specification, Table 6.2: "ADR Object Address Encodings"
+ High word-Device #, Low word-Function #. (for example, device 3,
+ function 2 is 0x00030002). To refer to all the functions on a device #,
+ use a function number of FFFF).
+
+ Use the second model for _PRT object and describe a hardwired interrupt.
+ */
+ Status = AmlAddPrtEntry (
+ (IrqMapInfo->PciDevice << 16) | 0xFFFF,
+ IrqMapInfo->PciInterrupt,
+ NULL,
+ IrqMapInfo->IntcInterrupt.Interrupt,
+ PrtNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+ } // for
+
+ // Attach the _PRT entry.
+ Status = AmlAttachNode (PciNode, PrtNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto exit_handler;
+ }
+
+ PrtNode = NULL;
+
+ // Generate the Pci slots once all the device have been added.
+ Status = GeneratePciSlots (PciInfo, &Generator->DeviceTable, Uid, PciNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+exit_handler:
+ MappingTableFree (&Generator->DeviceTable);
+exit_handler0:
+ if (PrtNode != NULL) {
+ AmlDeleteTree (PrtNode);
+ }
+
+ return Status;
+}
+
+/** Generate a _CRS method for the Pci device.
+
+ @param [in] Generator The SSDT Pci generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol interface.
+ @param [in] PciInfo Pci device information.
+ @param [in, out] PciNode Pci node to amend.
+
+ @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
+GeneratePciCrs (
+ IN ACPI_PCI_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN CONST CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO *PciInfo,
+ IN OUT AML_OBJECT_NODE_HANDLE PciNode
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN Translation;
+ UINT32 Index;
+ CM_ARCH_COMMON_OBJ_REF *RefInfo;
+ UINT32 RefCount;
+ CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO *AddrMapInfo;
+ AML_OBJECT_NODE_HANDLE CrsNode;
+ BOOLEAN IsPosDecode;
+
+ ASSERT (Generator != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (PciInfo != NULL);
+ ASSERT (PciNode != NULL);
+
+ // ASL: Name (_CRS, ResourceTemplate () {})
+ Status = AmlCodeGenNameResourceTemplate ("_CRS", PciNode, &CrsNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // ASL:
+ // WordBusNumber ( // Bus numbers assigned to this root
+ // ResourceProducer, MinFixed, MaxFixed, PosDecode,
+ // 0, // AddressGranularity
+ // <Start>, // AddressMinimum - Minimum Bus Number
+ // <End>, // AddressMaximum - Maximum Bus Number
+ // 0, // AddressTranslation - Set to 0
+ // <End> - <Start> + 1 // RangeLength - Number of Busses
+ // )
+ Status = AmlCodeGenRdWordBusNumber (
+ FALSE,
+ TRUE,
+ TRUE,
+ TRUE,
+ 0,
+ PciInfo->StartBusNumber,
+ PciInfo->EndBusNumber,
+ 0,
+ PciInfo->EndBusNumber - PciInfo->StartBusNumber + 1,
+ 0,
+ NULL,
+ CrsNode,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Get the array of CM_ARCH_COMMON_OBJ_REF referencing the
+ // CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO objects.
+ Status = GetEArchCommonObjCmRef (
+ CfgMgrProtocol,
+ PciInfo->AddressMapToken,
+ &RefInfo,
+ &RefCount
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ for (Index = 0; Index < RefCount; Index++) {
+ // Get CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO structures one by one.
+ Status = GetEArchCommonObjPciAddressMapInfo (
+ CfgMgrProtocol,
+ RefInfo[Index].ReferenceToken,
+ &AddrMapInfo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Translation = (AddrMapInfo->CpuAddress != AddrMapInfo->PciAddress);
+ if (AddrMapInfo->CpuAddress >= AddrMapInfo->PciAddress) {
+ IsPosDecode = TRUE;
+ } else {
+ IsPosDecode = FALSE;
+ }
+
+ switch (AddrMapInfo->SpaceCode) {
+ case PCI_SS_IO:
+ Status = AmlCodeGenRdQWordIo (
+ FALSE,
+ TRUE,
+ TRUE,
+ IsPosDecode,
+ 3,
+ 0,
+ AddrMapInfo->PciAddress,
+ AddrMapInfo->PciAddress + AddrMapInfo->AddressSize - 1,
+ Translation ? AddrMapInfo->CpuAddress - AddrMapInfo->PciAddress : 0,
+ AddrMapInfo->AddressSize,
+ 0,
+ NULL,
+ TRUE,
+ FALSE,
+ CrsNode,
+ NULL
+ );
+ break;
+
+ case PCI_SS_M32:
+ Status = AmlCodeGenRdDWordMemory (
+ FALSE,
+ IsPosDecode,
+ TRUE,
+ TRUE,
+ AmlMemoryCacheable,
+ TRUE,
+ 0,
+ AddrMapInfo->PciAddress,
+ AddrMapInfo->PciAddress + AddrMapInfo->AddressSize - 1,
+ Translation ? AddrMapInfo->CpuAddress - AddrMapInfo->PciAddress : 0,
+ AddrMapInfo->AddressSize,
+ 0,
+ NULL,
+ AmlAddressRangeMemory,
+ TRUE,
+ CrsNode,
+ NULL
+ );
+ break;
+
+ case PCI_SS_M64:
+ Status = AmlCodeGenRdQWordMemory (
+ FALSE,
+ IsPosDecode,
+ TRUE,
+ TRUE,
+ AmlMemoryCacheable,
+ TRUE,
+ 0,
+ AddrMapInfo->PciAddress,
+ AddrMapInfo->PciAddress + AddrMapInfo->AddressSize - 1,
+ Translation ? AddrMapInfo->CpuAddress - AddrMapInfo->PciAddress : 0,
+ AddrMapInfo->AddressSize,
+ 0,
+ NULL,
+ AmlAddressRangeMemory,
+ TRUE,
+ CrsNode,
+ NULL
+ );
+ break;
+
+ default:
+ Status = EFI_INVALID_PARAMETER;
+ } // switch
+
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ } // for
+
+ return Status;
+}
+
+/** Generate a RES0 device node to reserve PNP motherboard resources
+ for a given PCI node.
+
+ @param [in] PciNode Parent PCI node handle of the generated
+ resource object.
+ @param [out] CrsNode CRS node of the AML tree to populate.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid input parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenerateMotherboardDevice (
+ IN AML_OBJECT_NODE_HANDLE PciNode,
+ OUT AML_OBJECT_NODE_HANDLE *CrsNode
+ )
+{
+ EFI_STATUS Status;
+ UINT32 EisaId;
+ AML_OBJECT_NODE_HANDLE ResNode;
+
+ if (CrsNode == NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // ASL: Device (RES0) {}
+ Status = AmlCodeGenDevice ("RES0", PciNode, &ResNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // ASL: Name (_HID, EISAID ("PNP0C02"))
+ Status = AmlGetEisaIdFromString ("PNP0C02", &EisaId); /* PNP Motherboard Resources */
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenNameInteger ("_HID", EisaId, ResNode, NULL);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // ASL: Name (_CRS, ResourceTemplate () {})
+ Status = AmlCodeGenNameResourceTemplate ("_CRS", ResNode, CrsNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ return Status;
+}
+
+/** Reserves ECAM space for PCI config space
+
+ @param [in] Generator The SSDT Pci generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol interface.
+ @param [in] PciInfo Pci device information.
+ @param [in, out] PciNode RootNode of the AML tree to populate.
+
+ @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
+ReserveEcamSpace (
+ IN ACPI_PCI_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN CONST CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO *PciInfo,
+ IN OUT AML_OBJECT_NODE_HANDLE PciNode
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE_HANDLE CrsNode;
+ UINT64 AddressMinimum;
+ UINT64 AddressMaximum;
+
+ Status = GenerateMotherboardDevice (PciNode, &CrsNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ AddressMinimum = PciInfo->BaseAddress + (PciInfo->StartBusNumber *
+ PCI_MAX_DEVICE_COUNT_PER_BUS * PCI_MAX_FUNCTION_COUNT_PER_DEVICE * SIZE_4KB);
+ AddressMaximum = PciInfo->BaseAddress + ((PciInfo->EndBusNumber + 1) *
+ PCI_MAX_DEVICE_COUNT_PER_BUS * PCI_MAX_FUNCTION_COUNT_PER_DEVICE * SIZE_4KB) - 1;
+
+ Status = AmlCodeGenRdQWordMemory (
+ FALSE,
+ TRUE,
+ TRUE,
+ TRUE,
+ AmlMemoryNonCacheable,
+ TRUE,
+ 0,
+ AddressMinimum,
+ AddressMaximum,
+ 0, // no translation
+ AddressMaximum - AddressMinimum + 1,
+ 0,
+ NULL,
+ AmlAddressRangeMemory,
+ TRUE,
+ CrsNode,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ return Status;
+}
+
+/** Generate a Pci device.
+
+ @param [in] Generator The SSDT Pci generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol interface.
+ @param [in] PciInfo Pci device information.
+ @param [in] Uid Unique Id of the Pci device.
+ @param [in, out] RootNode RootNode of the AML tree to populate.
+
+ @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
+GeneratePciDevice (
+ IN ACPI_PCI_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN CONST CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO *PciInfo,
+ IN UINT32 Uid,
+ IN OUT AML_ROOT_NODE_HANDLE *RootNode
+ )
+{
+ EFI_STATUS Status;
+
+ CHAR8 AslName[AML_NAME_SEG_SIZE + 1];
+ AML_OBJECT_NODE_HANDLE ScopeNode;
+ AML_OBJECT_NODE_HANDLE PciNode;
+
+ ASSERT (Generator != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (PciInfo != NULL);
+ ASSERT (RootNode != NULL);
+
+ PciNode = NULL;
+
+ // ASL: Scope (\_SB) {}
+ Status = AmlCodeGenScope (SB_SCOPE, RootNode, &ScopeNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Write the name of the PCI device.
+ CopyMem (AslName, "PCIx", AML_NAME_SEG_SIZE + 1);
+ AslName[AML_NAME_SEG_SIZE - 1] = AsciiFromHex (Uid & 0xF);
+ if (Uid > 0xF) {
+ AslName[AML_NAME_SEG_SIZE - 2] = AsciiFromHex ((Uid >> 4) & 0xF);
+ }
+
+ // ASL: Device (PCIx) {}
+ Status = AmlCodeGenDevice (AslName, ScopeNode, &PciNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Populate the PCIx node with some Id values.
+ Status = GeneratePciDeviceInfo (PciInfo, Uid, PciNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Generate the Pci Routing Table (_PRT).
+ if (PciInfo->InterruptMapToken != CM_NULL_TOKEN) {
+ Status = GeneratePrt (
+ Generator,
+ CfgMgrProtocol,
+ PciInfo,
+ Uid,
+ PciNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ }
+
+ // Generate the _CRS method.
+ Status = GeneratePciCrs (Generator, CfgMgrProtocol, PciInfo, PciNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Add the PNP Motherboard Resources Device to reserve ECAM space
+ Status = ReserveEcamSpace (Generator, CfgMgrProtocol, PciInfo, PciNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Add the template _OSC method.
+ Status = AddOscMethod (PciInfo, PciNode);
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+/** Build an Ssdt table describing a Pci device.
+
+ @param [in] Generator The SSDT Pci generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol interface.
+ @param [in] AcpiTableInfo Pointer to the ACPI table information.
+ @param [in] PciInfo Pci device information.
+ @param [in] Uid Unique Id of the Pci device.
+ @param [out] Table If success, contains the created SSDT table.
+
+ @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
+BuildSsdtPciTable (
+ IN ACPI_PCI_GENERATOR *Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO *CONST AcpiTableInfo,
+ IN CONST CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO *PciInfo,
+ IN UINT32 Uid,
+ OUT EFI_ACPI_DESCRIPTION_HEADER **Table
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS Status1;
+ AML_ROOT_NODE_HANDLE RootNode;
+
+ ASSERT (Generator != NULL);
+ ASSERT (CfgMgrProtocol != NULL);
+ ASSERT (PciInfo != NULL);
+ ASSERT (Table != NULL);
+
+ // Create a new Ssdt table.
+ Status = AddSsdtAcpiHeader (
+ CfgMgrProtocol,
+ &Generator->Header,
+ AcpiTableInfo,
+ &RootNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = GeneratePciDevice (
+ Generator,
+ CfgMgrProtocol,
+ PciInfo,
+ Uid,
+ RootNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+ // Serialize the tree.
+ Status = AmlSerializeDefinitionBlock (
+ RootNode,
+ Table
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-PCI: Failed to Serialize SSDT Table Data."
+ " Status = %r\n",
+ Status
+ ));
+ }
+
+exit_handler:
+ // Cleanup
+ Status1 = AmlDeleteTree (RootNode);
+ if (EFI_ERROR (Status1)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-PCI: 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;
+}
+
+/** Construct SSDT tables describing Pci root complexes.
+
+ 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
+BuildSsdtPciTableEx (
+ 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_ARCH_COMMON_PCI_CONFIG_SPACE_INFO *PciInfo;
+ UINT32 PciCount;
+ UINTN Index;
+ EFI_ACPI_DESCRIPTION_HEADER **TableList;
+ ACPI_PCI_GENERATOR *Generator;
+ UINT32 Uid;
+
+ 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);
+
+ *TableCount = 0;
+ Generator = (ACPI_PCI_GENERATOR *)This;
+
+ Status = GetEArchCommonObjPciConfigSpaceInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &PciInfo,
+ &PciCount
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ if (PciCount > MAX_PCI_ROOT_COMPLEXES_SUPPORTED) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-PCI: Too many Pci root complexes: %d."
+ " Maximum Pci root complexes supported = %d.\n",
+ PciCount,
+ MAX_PCI_ROOT_COMPLEXES_SUPPORTED
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Allocate a table to store pointers to the SSDT tables.
+ TableList = (EFI_ACPI_DESCRIPTION_HEADER **)
+ AllocateZeroPool (
+ (sizeof (EFI_ACPI_DESCRIPTION_HEADER *) * PciCount)
+ );
+ if (TableList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-PCI: Failed to allocate memory for Table List."
+ " Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // Setup the table list early so that appropriate cleanup
+ // can be done in case of failure.
+ *Table = TableList;
+
+ for (Index = 0; Index < PciCount; Index++) {
+ if (PcdGetBool (PcdPciUseSegmentAsUid)) {
+ Uid = PciInfo[Index].PciSegmentGroupNumber;
+ if (Uid > MAX_PCI_ROOT_COMPLEXES_SUPPORTED) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-PCI: Pci root complexes segment number: %d."
+ " Greater than maximum number of Pci root complexes supported = %d.\n",
+ Uid,
+ MAX_PCI_ROOT_COMPLEXES_SUPPORTED
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ Uid = Index;
+ }
+
+ // Build a SSDT table describing the Pci devices.
+ Status = BuildSsdtPciTable (
+ Generator,
+ CfgMgrProtocol,
+ AcpiTableInfo,
+ &PciInfo[Index],
+ Uid,
+ &TableList[Index]
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: SSDT-PCI: Failed to build associated SSDT table."
+ " Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ *TableCount += 1;
+ } // for
+
+error_handler:
+ // Note: Table list and Table count have been setup. The
+ // error handler does nothing here as the framework will invoke
+ // FreeSsdtPciTableEx () even on failure.
+ 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
+FreeSsdtPciTableEx (
+ 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-PCI: 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-PCI: Could not free SSDT table at index %d.",
+ Index
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+ } // for
+
+ // Free the table list.
+ FreePool (*Table);
+
+ return EFI_SUCCESS;
+}
+
+/** This macro defines the SSDT Pci Table Generator revision.
+*/
+#define SSDT_PCI_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the SSDT Pci Table Generator.
+*/
+STATIC
+ACPI_PCI_GENERATOR SsdtPcieGenerator = {
+ // ACPI table generator header
+ {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtPciExpress),
+ // Generator Description
+ L"ACPI.STD.SSDT.PCI.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_PCI_GENERATOR_REVISION,
+ // Build table function. Use the extended version instead.
+ NULL,
+ // Free table function. Use the extended version instead.
+ NULL,
+ // Extended Build table function.
+ BuildSsdtPciTableEx,
+ // Extended free function.
+ FreeSsdtPciTableEx
+ },
+
+ // Private fields are defined from here.
+
+ // DeviceTable
+ {
+ // Table
+ NULL,
+ // LastIndex
+ 0,
+ // MaxIndex
+ 0
+ },
+};
+
+/** 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
+AcpiSsdtPcieLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = RegisterAcpiTableGenerator (&SsdtPcieGenerator.Header);
+ DEBUG ((
+ DEBUG_INFO,
+ "SSDT-PCI: 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
+AcpiSsdtPcieLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = DeregisterAcpiTableGenerator (&SsdtPcieGenerator.Header);
+ DEBUG ((
+ DEBUG_INFO,
+ "SSDT-PCI: Deregister Generator. Status = %r\n",
+ Status
+ ));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieGenerator.h b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieGenerator.h
new file mode 100644
index 0000000000..7410f9ffd4
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieGenerator.h
@@ -0,0 +1,55 @@
+/** @file
+ SSDT Pcie Table Generator.
+
+ Copyright (c) 2021, Arm Limited. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - PCI Firmware Specification - Revision 3.0
+ - ACPI 6.4 specification:
+ - s6.2.13 "_PRT (PCI Routing Table)"
+ - s6.1.1 "_ADR (Address)"
+ - linux kernel code
+ - Arm Base Boot Requirements v1.0
+**/
+
+#ifndef SSDT_PCIE_GENERATOR_H_
+#define SSDT_PCIE_GENERATOR_H_
+
+/** Pci address attributes.
+
+ This can also be denoted as space code, address space or ss.
+*/
+#define PCI_SS_CONFIG 0
+#define PCI_SS_IO 1
+#define PCI_SS_M32 2
+#define PCI_SS_M64 3
+
+/** Maximum Pci root complexes supported by this generator.
+
+ 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 Pci root complexes.
+*/
+#define MAX_PCI_ROOT_COMPLEXES_SUPPORTED 256
+
+// _SB scope of the AML namespace.
+#define SB_SCOPE "\\_SB_"
+
+#pragma pack(1)
+
+/** A structure holding the Pcie generator and additional private data.
+*/
+typedef struct AcpiPcieGenerator {
+ /// ACPI Table generator header
+ ACPI_TABLE_GENERATOR Header;
+
+ // Private fields are defined from here.
+
+ /// Table to map: Index <-> Pci device
+ MAPPING_TABLE DeviceTable;
+} ACPI_PCI_GENERATOR;
+
+#pragma pack()
+
+#endif // SSDT_PCIE_GENERATOR_H_
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieLib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieLib.inf
new file mode 100644
index 0000000000..c2a1acb570
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtPcieLib/SsdtPcieLib.inf
@@ -0,0 +1,35 @@
+## @file
+# Ssdt Serial Port Table Generator
+#
+# Copyright (c) 2021, Arm Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = SsdtPcieLibArm
+ FILE_GUID = E431D7FD-26BF-4E3D-9064-5B13B0439057
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = NULL|DXE_DRIVER
+ CONSTRUCTOR = AcpiSsdtPcieLibConstructor
+ DESTRUCTOR = AcpiSsdtPcieLibDestructor
+
+[Sources]
+ SsdtPcieGenerator.c
+ SsdtPcieGenerator.h
+
+[Packages]
+ DynamicTablesPkg/DynamicTablesPkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ AcpiHelperLib
+ AmlLib
+ BaseLib
+ SsdtPcieSupportLib
+
+[Pcd]
+ gEdkiiDynamicTablesPkgTokenSpaceGuid.PcdPciUseSegmentAsUid
diff --git a/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtSerialPortLib/SsdtSerialPortGenerator.c b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtSerialPortLib/SsdtSerialPortGenerator.c
new file mode 100644
index 0000000000..671ba05740
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtSerialPortLib/SsdtSerialPortGenerator.c
@@ -0,0 +1,373 @@
+/** @file
+ SSDT Serial Port Table Generator.
+
+ Copyright (c) 2020 - 2021, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/AcpiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.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/AcpiHelperLib.h>
+#include <Library/SsdtSerialPortFixupLib.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:
+ - EArchCommonObjSerialPortInfo
+*/
+
+/** This macro expands to a function that retrieves the Serial-port
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArchCommon,
+ EArchCommonObjSerialPortInfo,
+ CM_ARCH_COMMON_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_ARCH_COMMON_SERIAL_PORT_INFO *SerialPortInfo;
+ UINT32 SerialPortCount;
+ UINTN Index;
+ CHAR8 NewName[AML_NAME_SEG_SIZE + 1];
+ 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 = GetEArchCommonObjSerialPortInfo (
+ 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 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/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtSerialPortLib/SsdtSerialPortLib.inf b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtSerialPortLib/SsdtSerialPortLib.inf
new file mode 100644
index 0000000000..36e61ea9b1
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Common/AcpiSsdtSerialPortLib/SsdtSerialPortLib.inf
@@ -0,0 +1,33 @@
+## @file
+# Ssdt Serial Port Table Generator
+#
+# Copyright (c) 2019 - 2021, 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]
+ AcpiHelperLib
+ AmlLib
+ BaseLib
+ SsdtSerialPortFixupLib