diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi')
28 files changed, 10326 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf new file mode 100644 index 00000000..d0c863e6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/AcpiDbg2LibArm.inf @@ -0,0 +1,43 @@ +## @file +# DBG2 Table Generator +# +# Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = AcpiDbg2LibArm + FILE_GUID = A17BA4F0-3DEB-4FE5-BD27-EC008E541B22 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiDbg2LibConstructor + DESTRUCTOR = AcpiDbg2LibDestructor + +[Sources] + Dbg2Generator.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + BaseLib + PL011UartLib + SsdtSerialPortFixupLib + +[FixedPcd] + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits + +[Protocols] + +[Guids] + diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c new file mode 100644 index 00000000..7b4e9f4a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c @@ -0,0 +1,577 @@ +/** @file + DBG2 Table Generator + + Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - Microsoft Debug Port Table 2 (DBG2) Specification - December 10, 2015. + +**/ + +#include <IndustryStandard/DebugPort2Table.h> +#include <Library/AcpiLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/PL011UartLib.h> +#include <Protocol/AcpiTable.h> +#include <Protocol/SerialIo.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/SsdtSerialPortFixupLib.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +/** ARM standard DBG2 Table Generator + + Constructs the DBG2 table for PL011 or SBSA UART peripherals. + +Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjSerialDebugPortInfo +*/ + +#pragma pack(1) + +/** The number of debug ports represented by the Table. +*/ +#define DBG2_NUM_DEBUG_PORTS 1 + +/** The number of Generic Address Registers + presented in the debug device information. +*/ +#define DBG2_NUMBER_OF_GENERIC_ADDRESS_REGISTERS 1 + +/** The index for the debug port 0 in the Debug port information list. +*/ +#define INDEX_DBG_PORT0 0 + +/** A string representing the name of the debug port 0. +*/ +#define NAME_STR_DBG_PORT0 "COM0" + +/** An UID representing the debug port 0. +*/ +#define UID_DBG_PORT0 0 + +/** The length of the namespace string. +*/ +#define DBG2_NAMESPACESTRING_FIELD_SIZE sizeof (NAME_STR_DBG_PORT0) + +/** The PL011 UART address range length. +*/ +#define PL011_UART_LENGTH 0x1000 + +/** A structure that provides the OS with the required information + for initializing a debugger connection. +*/ +typedef struct { + /// The debug device information for the platform + EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT Dbg2Device; + + /// The base address register for the serial port + EFI_ACPI_6_2_GENERIC_ADDRESS_STRUCTURE BaseAddressRegister; + + /// The address size + UINT32 AddressSize; + + /// The debug port name string + UINT8 NameSpaceString[DBG2_NAMESPACESTRING_FIELD_SIZE]; +} DBG2_DEBUG_DEVICE_INFORMATION; + +/** A structure representing the information about the debug port(s) + available on the platform. +*/ +typedef struct { + /// The DBG2 table header + EFI_ACPI_DEBUG_PORT_2_DESCRIPTION_TABLE Description; + + /// Debug port information list + DBG2_DEBUG_DEVICE_INFORMATION Dbg2DeviceInfo[DBG2_NUM_DEBUG_PORTS]; +} DBG2_TABLE; + +/** A helper macro used for initializing the debug port device + information structure. + + @param [in] SubType The DBG Port SubType. + @param [in] UartBase The UART port base address. + @param [in] UartAddrLen The UART port address range length. + @param [in] UartNameStr The UART port name string. +**/ +#define DBG2_DEBUG_PORT_DDI( \ + SubType, \ + UartBase, \ + UartAddrLen, \ + UartNameStr \ + ) { \ + { \ + /* UINT8 Revision */ \ + EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT_REVISION, \ + /* UINT16 Length */ \ + sizeof (DBG2_DEBUG_DEVICE_INFORMATION), \ + /* UINT8 NumberofGenericAddressRegisters */ \ + DBG2_NUMBER_OF_GENERIC_ADDRESS_REGISTERS, \ + /* UINT16 NameSpaceStringLength */ \ + DBG2_NAMESPACESTRING_FIELD_SIZE, \ + /* UINT16 NameSpaceStringOffset */ \ + OFFSET_OF (DBG2_DEBUG_DEVICE_INFORMATION, NameSpaceString), \ + /* UINT16 OemDataLength */ \ + 0, \ + /* UINT16 OemDataOffset */ \ + 0, \ + /* UINT16 Port Type */ \ + EFI_ACPI_DBG2_PORT_TYPE_SERIAL, \ + /* UINT16 Port Subtype */ \ + SubType, \ + /* UINT8 Reserved[2] */ \ + {EFI_ACPI_RESERVED_BYTE, EFI_ACPI_RESERVED_BYTE}, \ + /* UINT16 BaseAddressRegister Offset */ \ + OFFSET_OF (DBG2_DEBUG_DEVICE_INFORMATION, BaseAddressRegister), \ + /* UINT16 AddressSize Offset */ \ + OFFSET_OF (DBG2_DEBUG_DEVICE_INFORMATION, AddressSize) \ + }, \ + /* EFI_ACPI_6_2_GENERIC_ADDRESS_STRUCTURE BaseAddressRegister */ \ + ARM_GAS32 (UartBase), \ + /* UINT32 AddressSize */ \ + UartAddrLen, \ + /* UINT8 NameSpaceString[MAX_DBG2_NAME_LEN] */ \ + UartNameStr \ + } + +/** The DBG2 Table template definition. + + Note: fields marked with "{Template}" will be set dynamically +*/ +STATIC +DBG2_TABLE AcpiDbg2 = { + { + ACPI_HEADER ( + EFI_ACPI_6_2_DEBUG_PORT_2_TABLE_SIGNATURE, + DBG2_TABLE, + EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT_REVISION + ), + OFFSET_OF (DBG2_TABLE, Dbg2DeviceInfo), + DBG2_NUM_DEBUG_PORTS + }, + { + /* + * Debug port 1 + */ + DBG2_DEBUG_PORT_DDI ( + 0, // {Template}: Serial Port Subtype + 0, // {Template}: Serial Port Base Address + PL011_UART_LENGTH, + NAME_STR_DBG_PORT0 + ) + } +}; + +#pragma pack() + +/** This macro expands to a function that retrieves the Serial + debug port information from the Configuration Manager +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjSerialDebugPortInfo, + CM_ARM_SERIAL_PORT_INFO + ); + +/** Initialize the PL011/SBSA UART with the parameters obtained from + the Configuration Manager. + + @param [in] SerialPortInfo Pointer to the Serial Port Information. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER The parameters for serial port initialization + are invalid. +**/ +STATIC +EFI_STATUS +SetupDebugUart ( + IN CONST CM_ARM_SERIAL_PORT_INFO * CONST SerialPortInfo + ) +{ + EFI_STATUS Status; + UINT64 BaudRate; + UINT32 ReceiveFifoDepth; + EFI_PARITY_TYPE Parity; + UINT8 DataBits; + EFI_STOP_BITS_TYPE StopBits; + + ASSERT (SerialPortInfo != NULL); + + // Initialize the Serial Debug UART + DEBUG ((DEBUG_INFO, "Initializing Serial Debug UART...\n")); + ReceiveFifoDepth = 0; // Use the default value for FIFO depth + Parity = (EFI_PARITY_TYPE)FixedPcdGet8 (PcdUartDefaultParity); + DataBits = FixedPcdGet8 (PcdUartDefaultDataBits); + StopBits = (EFI_STOP_BITS_TYPE)FixedPcdGet8 (PcdUartDefaultStopBits); + + BaudRate = SerialPortInfo->BaudRate; + Status = PL011UartInitializePort ( + (UINTN)SerialPortInfo->BaseAddress, + SerialPortInfo->Clock, + &BaudRate, + &ReceiveFifoDepth, + &Parity, + &DataBits, + &StopBits + ); + + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Free any resources allocated for constructing the tables. + + @param [in] This Pointer to the ACPI table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to an array of pointers + to ACPI Table(s). + @param [in] TableCount Number of ACPI table(s). + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +EFIAPI +FreeDbg2TableEx ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER *** CONST Table, + IN CONST UINTN TableCount + ) +{ + EFI_STATUS Status; + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((Table == NULL) || + (*Table == NULL) || + (TableCount != 2)) { + DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Invalid Table Pointer\n")); + return EFI_INVALID_PARAMETER; + } + + TableList = *Table; + + if ((TableList[1] == NULL) || + (TableList[1]->Signature != + EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE)) { + DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Invalid SSDT table pointer.\n")); + return EFI_INVALID_PARAMETER; + } + + // Only need to free the SSDT table at index 1. The DBG2 table is static. + Status = FreeSsdtSerialPortTable (TableList[1]); + ASSERT_EFI_ERROR (Status); + + // Free the table list. + FreePool (*Table); + + return Status; +} + +/** Construct the DBG2 ACPI table and its associated SSDT table. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResourcesEx function. + + @param [in] This Pointer to the ACPI table generator. + @param [in] AcpiTableInfo Pointer to the ACPI table information. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol interface. + @param [out] Table Pointer to a list of generated ACPI table(s). + @param [out] TableCount Number of generated ACPI table(s). + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for + the requested object. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND Could not find information. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. + @retval EFI_UNSUPPORTED Unsupported configuration. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildDbg2TableEx ( + IN CONST ACPI_TABLE_GENERATOR * This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER *** Table, + OUT UINTN * CONST TableCount + ) +{ + EFI_STATUS Status; + CM_ARM_SERIAL_PORT_INFO * SerialPortInfo; + UINT32 SerialPortCount; + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (TableCount != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) || + (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: DBG2: Requested table revision = %d, is not supported." + "Supported table revision: Minimum = %d, Maximum = %d\n", + AcpiTableInfo->AcpiTableRevision, + This->MinAcpiTableRevision, + This->AcpiTableRevision + )); + return EFI_INVALID_PARAMETER; + } + + *Table = NULL; + + Status = GetEArmObjSerialDebugPortInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &SerialPortInfo, + &SerialPortCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: DBG2: Failed to get serial port information. Status = %r\n", + Status + )); + return Status; + } + + if (SerialPortCount == 0) { + DEBUG (( + DEBUG_ERROR, + "ERROR: DBG2: Serial port information not found. Status = %r\n", + EFI_NOT_FOUND + )); + return EFI_NOT_FOUND; + } + + // Only use the first DBG2 port information. + Status = ValidateSerialPortInfo (SerialPortInfo, 1); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: DBG2: Invalid serial port information. Status = %r\n", + Status + )); + return Status; + } + + // Allocate a table to store pointers to the DBG2 and SSDT tables. + TableList = (EFI_ACPI_DESCRIPTION_HEADER**) + AllocateZeroPool (sizeof (EFI_ACPI_DESCRIPTION_HEADER*) * 2); + if (TableList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: DBG2: Failed to allocate memory for Table List," \ + " Status = %r\n", + Status + )); + return Status; + } + + Status = AddAcpiHeader ( + CfgMgrProtocol, + This, + (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiDbg2, + AcpiTableInfo, + sizeof (DBG2_TABLE) + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: DBG2: Failed to add ACPI header. Status = %r\n", + Status + )); + goto error_handler; + } + + // Update the base address + AcpiDbg2.Dbg2DeviceInfo[INDEX_DBG_PORT0].BaseAddressRegister.Address = + SerialPortInfo->BaseAddress; + + // Set the access size + if (SerialPortInfo->AccessSize >= EFI_ACPI_6_3_QWORD) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: DBG2: Access size must be <= 3 (DWORD). Status = %r\n", + Status + )); + goto error_handler; + } else if (SerialPortInfo->AccessSize == EFI_ACPI_6_3_UNDEFINED) { + // 0 Undefined (legacy reasons) + // Default to DWORD access size as the access + // size field was introduced at a later date + // and some ConfigurationManager implementations + // may not be providing this field data + AcpiDbg2.Dbg2DeviceInfo[INDEX_DBG_PORT0].BaseAddressRegister.AccessSize = + EFI_ACPI_6_3_DWORD; + } else { + AcpiDbg2.Dbg2DeviceInfo[INDEX_DBG_PORT0].BaseAddressRegister.AccessSize = + SerialPortInfo->AccessSize; + } + + // Update the serial port subtype + AcpiDbg2.Dbg2DeviceInfo[INDEX_DBG_PORT0].Dbg2Device.PortSubtype = + SerialPortInfo->PortSubtype; + + if ((SerialPortInfo->PortSubtype == + EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART) || + (SerialPortInfo->PortSubtype == + EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X) || + (SerialPortInfo->PortSubtype == + EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART)) { + // Initialize the serial port + Status = SetupDebugUart (SerialPortInfo); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: DBG2: Failed to configure debug serial port. Status = %r\n", + Status + )); + goto error_handler; + } + } + + TableList[0] = (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiDbg2; + + // Build a SSDT table describing the serial port. + Status = BuildSsdtSerialPortTable ( + AcpiTableInfo, + SerialPortInfo, + NAME_STR_DBG_PORT0, + UID_DBG_PORT0, + &TableList[1] + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: DBG2: Failed to build associated SSDT table. Status = %r\n", + Status + )); + goto error_handler; + } + + *TableCount = 2; + *Table = TableList; + + return Status; + +error_handler: + if (TableList != NULL) { + FreePool (TableList); + } + + return Status; +} + +/** This macro defines the DBG2 Table Generator revision. +*/ +#define DBG2_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the DBG2 Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR Dbg2Generator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdDbg2), + // Generator Description + L"ACPI.STD.DBG2.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_3_DEBUG_PORT_2_TABLE_SIGNATURE, + // ACPI Table Revision supported by this Generator + EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT_REVISION, + // Minimum supported ACPI Table Revision + EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT_REVISION, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + DBG2_GENERATOR_REVISION, + // Build table function. Use the extended version instead. + NULL, + // Free table function. Use the extended version instead. + NULL, + // Extended Build table function. + BuildDbg2TableEx, + // Extended free function. + FreeDbg2TableEx +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiDbg2LibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&Dbg2Generator); + DEBUG ((DEBUG_INFO, "DBG2: Register Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiDbg2LibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&Dbg2Generator); + DEBUG ((DEBUG_INFO, "DBG2: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/AcpiFadtLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/AcpiFadtLibArm.inf new file mode 100644 index 00000000..deaf7d20 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/AcpiFadtLibArm.inf @@ -0,0 +1,36 @@ +## @file +# FADT Table Generator +# +# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = AcpiFadtLibArm + FILE_GUID = 686FE5FE-B944-485F-8B1C-7D60E0056487 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiFadtLibConstructor + DESTRUCTOR = AcpiFadtLibDestructor + +[Sources] + FadtGenerator.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + BaseLib + +[Pcd] + +[Protocols] + +[Guids] + diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/FadtGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/FadtGenerator.c new file mode 100644 index 00000000..27b0257d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiFadtLibArm/FadtGenerator.c @@ -0,0 +1,683 @@ +/** @file + FADT Table Generator + + Copyright (c) 2017 - 2019, ARM Limited. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - ACPI 6.3 Specification, January 2019 + +**/ + +#include <Library/AcpiLib.h> +#include <Library/DebugLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +/** ARM standard FADT Generator + +Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjPowerManagementProfileInfo + - EArmObjBootArchInfo + - EArmObjHypervisorVendorIdentity (OPTIONAL) +*/ + +/** This macro defines the FADT flag options for ARM Platforms. +*/ +#define FADT_FLAGS (EFI_ACPI_6_3_HW_REDUCED_ACPI | \ + EFI_ACPI_6_3_LOW_POWER_S0_IDLE_CAPABLE) + +/** This macro defines the valid mask for the FADT flag option + if HW_REDUCED_ACPI flag in the table is set. + + Invalid bits are: 1, 2, 3,7, 8, 13, 14,16, 17 and + 22-31 (reserved). + + Valid bits are: + EFI_ACPI_6_3_WBINVD BIT0 + EFI_ACPI_6_3_PWR_BUTTON BIT4 + EFI_ACPI_6_3_SLP_BUTTON BIT5 + EFI_ACPI_6_3_FIX_RTC BIT6 + EFI_ACPI_6_3_DCK_CAP BIT9 + EFI_ACPI_6_3_RESET_REG_SUP BIT10 + EFI_ACPI_6_3_SEALED_CASE BIT11 + EFI_ACPI_6_3_HEADLESS BIT12 + EFI_ACPI_6_3_USE_PLATFORM_CLOCK BIT15 + EFI_ACPI_6_3_FORCE_APIC_CLUSTER_MODEL BIT18 + EFI_ACPI_6_3_FORCE_APIC_PHYSICAL_DESTINATION_MODE BIT19 + EFI_ACPI_6_3_HW_REDUCED_ACPI BIT20 + EFI_ACPI_6_3_LOW_POWER_S0_IDLE_CAPABLE BIT21 +*/ +#define VALID_HARDWARE_REDUCED_FLAG_MASK ( \ + EFI_ACPI_6_3_WBINVD | \ + EFI_ACPI_6_3_PWR_BUTTON | \ + EFI_ACPI_6_3_SLP_BUTTON | \ + EFI_ACPI_6_3_FIX_RTC | \ + EFI_ACPI_6_3_DCK_CAP | \ + EFI_ACPI_6_3_RESET_REG_SUP | \ + EFI_ACPI_6_3_SEALED_CASE | \ + EFI_ACPI_6_3_HEADLESS | \ + EFI_ACPI_6_3_USE_PLATFORM_CLOCK | \ + EFI_ACPI_6_3_FORCE_APIC_CLUSTER_MODEL | \ + EFI_ACPI_6_3_FORCE_APIC_PHYSICAL_DESTINATION_MODE | \ + EFI_ACPI_6_3_HW_REDUCED_ACPI | \ + EFI_ACPI_6_3_LOW_POWER_S0_IDLE_CAPABLE) + +#pragma pack(1) + +/** The AcpiFadt is a template EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE + structure used for generating the FADT Table. + Note: fields marked with "{Template}" will be updated dynamically. +*/ +STATIC +EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE AcpiFadt = { + ACPI_HEADER ( + EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE, + EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE, + EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_REVISION + ), + // UINT32 FirmwareCtrl + 0, + // UINT32 Dsdt + 0, + // UINT8 Reserved0 + EFI_ACPI_RESERVED_BYTE, + // UINT8 PreferredPmProfile + EFI_ACPI_6_3_PM_PROFILE_UNSPECIFIED, // {Template}: Power Management Profile + // UINT16 SciInt + 0, + // UINT32 SmiCmd + 0, + // UINT8 AcpiEnable + 0, + // UINT8 AcpiDisable + 0, + // UINT8 S4BiosReq + 0, + // UINT8 PstateCnt + 0, + // UINT32 Pm1aEvtBlk + 0, + // UINT32 Pm1bEvtBlk + 0, + // UINT32 Pm1aCntBlk + 0, + // UINT32 Pm1bCntBlk + 0, + // UINT32 Pm2CntBlk + 0, + // UINT32 PmTmrBlk + 0, + // UINT32 Gpe0Blk + 0, + // UINT32 Gpe1Blk + 0, + // UINT8 Pm1EvtLen + 0, + // UINT8 Pm1CntLen + 0, + // UINT8 Pm2CntLen + 0, + // UINT8 PmTmrLen + 0, + // UINT8 Gpe0BlkLen + 0, + // UINT8 Gpe1BlkLen + 0, + // UINT8 Gpe1Base + 0, + // UINT8 CstCnt + 0, + // UINT16 PLvl2Lat + 0, + // UINT16 PLvl3Lat + 0, + // UINT16 FlushSize + 0, + // UINT16 FlushStride + 0, + // UINT8 DutyOffset + 0, + // UINT8 DutyWidth + 0, + // UINT8 DayAlrm + 0, + // UINT8 MonAlrm + 0, + // UINT8 Century + 0, + // UINT16 IaPcBootArch + 0, + // UINT8 Reserved1 + 0, + // UINT32 Flags + FADT_FLAGS, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE ResetReg + NULL_GAS, + // UINT8 ResetValue + 0, + // UINT16 ArmBootArch + EFI_ACPI_6_3_ARM_PSCI_COMPLIANT, // {Template}: ARM Boot Architecture Flags + // UINT8 MinorRevision + EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_MINOR_REVISION, + // UINT64 XFirmwareCtrl + 0, + // UINT64 XDsdt + 0, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1aEvtBlk + NULL_GAS, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1bEvtBlk + NULL_GAS, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1aCntBlk + NULL_GAS, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1bCntBlk + NULL_GAS, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm2CntBlk + NULL_GAS, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPmTmrBlk + NULL_GAS, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XGpe0Blk + NULL_GAS, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XGpe1Blk + NULL_GAS, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE SleepControlReg + NULL_GAS, + // EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE SleepStatusReg + NULL_GAS, + // UINT64 HypervisorVendorIdentity + EFI_ACPI_RESERVED_QWORD // {Template}: Hypervisor Vendor ID +}; + +#pragma pack() + +/** This macro expands to a function that retrieves the Power + Management Profile Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjPowerManagementProfileInfo, + CM_ARM_POWER_MANAGEMENT_PROFILE_INFO + ); + +/** This macro expands to a function that retrieves the Boot + Architecture Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjBootArchInfo, + CM_ARM_BOOT_ARCH_INFO + ); + +/** This macro expands to a function that retrieves the Hypervisor + Vendor ID from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjHypervisorVendorIdentity, + CM_ARM_HYPERVISOR_VENDOR_ID + ); + +/** This macro expands to a function that retrieves the Fixed + feature flags for the platform from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjFixedFeatureFlags, + CM_ARM_FIXED_FEATURE_FLAGS + ); + +/** Update the Power Management Profile information in the FADT Table. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. +**/ +STATIC +EFI_STATUS +EFIAPI +FadtAddPmProfileInfo ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol +) +{ + EFI_STATUS Status; + CM_ARM_POWER_MANAGEMENT_PROFILE_INFO * PmProfile; + + ASSERT (CfgMgrProtocol != NULL); + + // Get the Power Management Profile from the Platform Configuration Manager + Status = GetEArmObjPowerManagementProfileInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &PmProfile, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: FADT: Failed to get Power Management Profile information." \ + " Status = %r\n", + Status + )); + goto error_handler; + } + + DEBUG (( + DEBUG_INFO, + "FADT: PreferredPmProfile = 0x%x\n", + PmProfile->PowerManagementProfile + )); + + AcpiFadt.PreferredPmProfile = PmProfile->PowerManagementProfile; + +error_handler: + return Status; +} + +/** Updates the Boot Architecture information in the FADT Table. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. +**/ +STATIC +EFI_STATUS +EFIAPI +FadtAddBootArchInfo ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol +) +{ + EFI_STATUS Status; + CM_ARM_BOOT_ARCH_INFO * BootArchInfo; + + ASSERT (CfgMgrProtocol != NULL); + + // Get the Boot Architecture flags from the Platform Configuration Manager + Status = GetEArmObjBootArchInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &BootArchInfo, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: FADT: Failed to get Boot Architecture flags. Status = %r\n", + Status + )); + goto error_handler; + } + + DEBUG (( + DEBUG_INFO, + "FADT BootArchFlag = 0x%x\n", + BootArchInfo->BootArchFlags + )); + + AcpiFadt.ArmBootArch = BootArchInfo->BootArchFlags; + +error_handler: + return Status; +} + +/** Update the Hypervisor Vendor ID in the FADT Table. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. +**/ +STATIC +EFI_STATUS +EFIAPI +FadtAddHypervisorVendorId ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol +) +{ + EFI_STATUS Status; + CM_ARM_HYPERVISOR_VENDOR_ID * HypervisorVendorInfo; + + ASSERT (CfgMgrProtocol != NULL); + + // Get the Hypervisor Vendor ID from the Platform Configuration Manager + Status = GetEArmObjHypervisorVendorIdentity ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &HypervisorVendorInfo, + NULL + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + DEBUG (( + DEBUG_INFO, + "INFO: FADT: Platform does not have a Hypervisor Vendor ID." + "Status = %r\n", + Status + )); + } else { + DEBUG (( + DEBUG_ERROR, + "ERROR: FADT: Failed to get Hypervisor Vendor ID. Status = %r\n", + Status + )); + } + goto error_handler; + } + + DEBUG (( + DEBUG_INFO, + "FADT: EArmObjHypervisorVendorIdentity = 0x%lx\n", + HypervisorVendorInfo->HypervisorVendorId + )); + + AcpiFadt.HypervisorVendorIdentity = HypervisorVendorInfo->HypervisorVendorId; + +error_handler: + return Status; +} + +/** Update the Fixed Feature Flags in the FADT Table. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. +**/ +STATIC +EFI_STATUS +EFIAPI +FadtAddFixedFeatureFlags ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol +) +{ + EFI_STATUS Status; + CM_ARM_FIXED_FEATURE_FLAGS * FixedFeatureFlags; + + ASSERT (CfgMgrProtocol != NULL); + + // Get the Fixed feature flags from the Platform Configuration Manager + Status = GetEArmObjFixedFeatureFlags ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &FixedFeatureFlags, + NULL + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + DEBUG (( + DEBUG_INFO, + "INFO: FADT: Platform does not define additional Fixed feature flags." + "Status = %r\n", + Status + )); + } else { + DEBUG (( + DEBUG_ERROR, + "ERROR: FADT: Failed to get Fixed feature flags. Status = %r\n", + Status + )); + } + goto error_handler; + } + + DEBUG (( + DEBUG_INFO, + "FADT: EArmObjFixedFeatureFlags = 0x%x\n", + FixedFeatureFlags->Flags + )); + + if ((FixedFeatureFlags->Flags & ~(VALID_HARDWARE_REDUCED_FLAG_MASK)) != 0) { + DEBUG (( + DEBUG_WARN, + "FADT: Invalid Fixed feature flags defined by platform," + "Invalid Flags bits are = 0x%x\n", + (FixedFeatureFlags->Flags & ~(VALID_HARDWARE_REDUCED_FLAG_MASK)) + )); + } + + AcpiFadt.Flags |= (FixedFeatureFlags->Flags & + VALID_HARDWARE_REDUCED_FLAG_MASK); + +error_handler: + return Status; +} + +/** Construct the FADT table. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResources function. + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [out] Table Pointer to the constructed ACPI Table. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildFadtTable ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + EFI_STATUS Status; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) || + (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: FADT: Requested table revision = %d, is not supported." + "Supported table revision: Minimum = %d, Maximum = %d\n", + AcpiTableInfo->AcpiTableRevision, + This->MinAcpiTableRevision, + This->AcpiTableRevision + )); + return EFI_INVALID_PARAMETER; + } + + *Table = NULL; + + Status = AddAcpiHeader ( + CfgMgrProtocol, + This, + (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiFadt, + AcpiTableInfo, + sizeof (EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE) + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: FADT: Failed to add ACPI header. Status = %r\n", + Status + )); + goto error_handler; + } + + // Update PmProfile Info + Status = FadtAddPmProfileInfo (CfgMgrProtocol); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Update BootArch Info + Status = FadtAddBootArchInfo (CfgMgrProtocol); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Add the Hypervisor Vendor Id if present + // Note if no hypervisor is present the zero bytes + // will be placed in this field. + Status = FadtAddHypervisorVendorId (CfgMgrProtocol); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + DEBUG (( + DEBUG_INFO, + "INFO: FADT: No Hypervisor Vendor ID found," \ + " assuming no Hypervisor is present in the firmware.\n" + )); + } else { + DEBUG (( + DEBUG_ERROR, + "ERROR: FADT: Error reading Hypervisor Vendor ID, Status = %r", + Status + )); + goto error_handler; + } + } + + Status = FadtAddFixedFeatureFlags (CfgMgrProtocol); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + DEBUG (( + DEBUG_INFO, + "INFO: FADT: No Fixed feature flags found," \ + " assuming no additional flags are defined for the platform.\n" + )); + Status = EFI_SUCCESS; + } else { + DEBUG (( + DEBUG_ERROR, + "ERROR: FADT: Error reading Fixed feature flags, Status = %r", + Status + )); + goto error_handler; + } + } + + *Table = (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiFadt; +error_handler: + return Status; +} + +/** This macro defines the FADT Table Generator revision. +*/ +#define FADT_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the FADT Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR FadtGenerator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdFadt), + // Generator Description + L"ACPI.STD.FADT.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE, + // ACPI Table Revision supported by this Generator + EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_REVISION, + // Minimum supported ACPI Table Revision + EFI_ACPI_6_2_FIXED_ACPI_DESCRIPTION_TABLE_REVISION, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + FADT_GENERATOR_REVISION, + // Build Table function + BuildFadtTable, + // No additional resources are allocated by the generator. + // Hence the Free Resource function is not required. + NULL, + // Extended build function not needed + NULL, + // Extended build function not implemented by the generator. + // Hence extended free resource function is not required. + NULL +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiFadtLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&FadtGenerator); + DEBUG ((DEBUG_INFO, "FADT: Register Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiFadtLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&FadtGenerator); + DEBUG ((DEBUG_INFO, "FADT: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/AcpiGtdtLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/AcpiGtdtLibArm.inf new file mode 100644 index 00000000..ef449603 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/AcpiGtdtLibArm.inf @@ -0,0 +1,36 @@ +## @file +# GTDT Table Generator +# +# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = AcpiGtdtLibArm + FILE_GUID = 26490F7A-7FA2-423C-8939-C6206329BC37 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiGtdtLibConstructor + DESTRUCTOR = AcpiGtdtLibDestructor + +[Sources] + GtdtGenerator.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + BaseLib + +[FixedPcd] + +[Protocols] + +[Guids] + diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/GtdtGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/GtdtGenerator.c new file mode 100644 index 00000000..27dccab5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiGtdtLibArm/GtdtGenerator.c @@ -0,0 +1,780 @@ +/** @file + GTDT Table Generator + + Copyright (c) 2017 - 2019, ARM Limited. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - ACPI 6.3 Specification - January 2019 + +**/ + +#include <Library/AcpiLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +/** ARM standard GTDT Generator + +Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjGenericTimerInfo + - EArmObjPlatformGenericWatchdogInfo (OPTIONAL) + - EArmObjPlatformGTBlockInfo (OPTIONAL) + - EArmObjGTBlockTimerFrameInfo (OPTIONAL) +*/ + +/** This macro expands to a function that retrieves the Generic + Timer Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGenericTimerInfo, + CM_ARM_GENERIC_TIMER_INFO + ); + +/** This macro expands to a function that retrieves the SBSA Generic + Watchdog Timer Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjPlatformGenericWatchdogInfo, + CM_ARM_GENERIC_WATCHDOG_INFO + ); + +/** This macro expands to a function that retrieves the Platform Generic + Timer Block Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjPlatformGTBlockInfo, + CM_ARM_GTBLOCK_INFO + ); + +/** This macro expands to a function that retrieves the Generic + Timer Block Timer Frame Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGTBlockTimerFrameInfo, + CM_ARM_GTBLOCK_TIMER_FRAME_INFO + ); + +/** Add the Generic Timer Information to the GTDT table. + + Also update the Platform Timer offset information if the platform + implements platform timers. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Gtdt Pointer to the GTDT Table. + @param [in] PlatformTimerCount Platform timer count. + @param [in] AcpiTableRevision Acpi Revision targeted by the platform. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. +**/ +STATIC +EFI_STATUS +EFIAPI +AddGenericTimerInfo ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE * CONST Gtdt, + IN CONST UINT32 PlatformTimerCount, + IN CONST UINT32 AcpiTableRevision +) +{ + EFI_STATUS Status; + CM_ARM_GENERIC_TIMER_INFO * GenericTimerInfo; + + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Gtdt != NULL); + + Status = GetEArmObjGenericTimerInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &GenericTimerInfo, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Failed to get GenericTimerInfo. Status = %r\n", + Status + )); + return Status; + } + + Gtdt->CntControlBasePhysicalAddress = + GenericTimerInfo->CounterControlBaseAddress; + Gtdt->Reserved = EFI_ACPI_RESERVED_DWORD; + Gtdt->SecurePL1TimerGSIV = GenericTimerInfo->SecurePL1TimerGSIV; + Gtdt->SecurePL1TimerFlags = GenericTimerInfo->SecurePL1TimerFlags; + Gtdt->NonSecurePL1TimerGSIV = GenericTimerInfo->NonSecurePL1TimerGSIV; + Gtdt->NonSecurePL1TimerFlags = GenericTimerInfo->NonSecurePL1TimerFlags; + Gtdt->VirtualTimerGSIV = GenericTimerInfo->VirtualTimerGSIV; + Gtdt->VirtualTimerFlags = GenericTimerInfo->VirtualTimerFlags; + Gtdt->NonSecurePL2TimerGSIV = GenericTimerInfo->NonSecurePL2TimerGSIV; + Gtdt->NonSecurePL2TimerFlags = GenericTimerInfo->NonSecurePL2TimerFlags; + Gtdt->CntReadBasePhysicalAddress = + GenericTimerInfo->CounterReadBaseAddress; + Gtdt->PlatformTimerCount = PlatformTimerCount; + Gtdt->PlatformTimerOffset = (PlatformTimerCount == 0) ? 0 : + sizeof (EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE); + + if (AcpiTableRevision > EFI_ACPI_6_2_GENERIC_TIMER_DESCRIPTION_TABLE_REVISION) { + Gtdt->VirtualPL2TimerGSIV = GenericTimerInfo->VirtualPL2TimerGSIV; + Gtdt->VirtualPL2TimerFlags = GenericTimerInfo->VirtualPL2TimerFlags; + } + + return Status; +} + +/** Add the SBSA Generic Watchdog Timers to the GTDT table. + + @param [in] Gtdt Pointer to the GTDT Table. + @param [in] WatchdogOffset Offset to the watchdog information in the + GTDT Table. + @param [in] WatchdogInfoList Pointer to the watchdog information list. + @param [in] WatchdogCount Platform timer count. +**/ +STATIC +VOID +AddGenericWatchdogList ( + IN EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE * CONST Gtdt, + IN CONST UINT32 WatchdogOffset, + IN CONST CM_ARM_GENERIC_WATCHDOG_INFO * WatchdogInfoList, + IN UINT32 WatchdogCount + ) +{ + EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_STRUCTURE * Watchdog; + + ASSERT (Gtdt != NULL); + ASSERT (WatchdogInfoList != NULL); + + Watchdog = (EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_STRUCTURE *) + ((UINT8*)Gtdt + WatchdogOffset); + + while (WatchdogCount-- != 0) { + // Add watchdog entry + DEBUG ((DEBUG_INFO, "GTDT: Watchdog = 0x%p\n", Watchdog)); + Watchdog->Type = EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG; + Watchdog->Length = + sizeof (EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_STRUCTURE); + Watchdog->Reserved = EFI_ACPI_RESERVED_BYTE; + Watchdog->RefreshFramePhysicalAddress = + WatchdogInfoList->RefreshFrameAddress; + Watchdog->WatchdogControlFramePhysicalAddress = + WatchdogInfoList->ControlFrameAddress; + Watchdog->WatchdogTimerGSIV = WatchdogInfoList->TimerGSIV; + Watchdog->WatchdogTimerFlags = WatchdogInfoList->Flags; + Watchdog++; + WatchdogInfoList++; + } // for +} + +/** + Function to test if two Generic Timer Block Frame Info structures have the + same frame number. + + @param [in] Frame1 Pointer to the first GT Block Frame Info + structure. + @param [in] Frame2 Pointer to the second GT Block Frame Info + structure. + @param [in] Index1 Index of Frame1 in the shared GT Block Frame + Information List. + @param [in] Index2 Index of Frame2 in the shared GT Block Frame + Information List. + + @retval TRUE Frame1 and Frame2 have the same frame number. + @return FALSE Frame1 and Frame2 have different frame numbers. + +**/ +BOOLEAN +EFIAPI +IsGtFrameNumberEqual ( + IN CONST VOID * Frame1, + IN CONST VOID * Frame2, + IN UINTN Index1, + IN UINTN Index2 + ) +{ + UINT8 FrameNumber1; + UINT8 FrameNumber2; + + ASSERT ((Frame1 != NULL) && (Frame2 != NULL)); + + FrameNumber1 = ((CM_ARM_GTBLOCK_TIMER_FRAME_INFO*)Frame1)->FrameNumber; + FrameNumber2 = ((CM_ARM_GTBLOCK_TIMER_FRAME_INFO*)Frame2)->FrameNumber; + + if (FrameNumber1 == FrameNumber2) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: GT Block Frame Info Structures %d and %d have the same " \ + "frame number: 0x%x.\n", + Index1, + Index2, + FrameNumber1 + )); + return TRUE; + } + + return FALSE; +} + +/** Update the GT Block Timer Frame lists in the GTDT Table. + + @param [in] GtBlockFrame Pointer to the GT Block Frames + list to be updated. + @param [in] GTBlockTimerFrameList Pointer to the GT Block Frame + Information List. + @param [in] GTBlockFrameCount Number of GT Block Frames. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. +**/ +STATIC +EFI_STATUS +AddGTBlockTimerFrames ( + IN EFI_ACPI_6_3_GTDT_GT_BLOCK_TIMER_STRUCTURE * GtBlockFrame, + IN CONST CM_ARM_GTBLOCK_TIMER_FRAME_INFO * GTBlockTimerFrameList, + IN UINT32 GTBlockFrameCount +) +{ + BOOLEAN IsFrameNumberDuplicated; + + ASSERT (GtBlockFrame != NULL); + ASSERT (GTBlockTimerFrameList != NULL); + + IsFrameNumberDuplicated = FindDuplicateValue ( + GTBlockTimerFrameList, + GTBlockFrameCount, + sizeof (CM_ARM_GTBLOCK_TIMER_FRAME_INFO), + IsGtFrameNumberEqual + ); + // Duplicate entry was found so timer frame numbers provided are invalid + if (IsFrameNumberDuplicated) { + return EFI_INVALID_PARAMETER; + } + + while (GTBlockFrameCount-- != 0) { + DEBUG (( + DEBUG_INFO, + "GTDT: GtBlockFrame = 0x%p\n", + GtBlockFrame + )); + + if (GTBlockTimerFrameList->FrameNumber >= 8) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Frame number %d is not in the range 0-7\n", + GTBlockTimerFrameList->FrameNumber + )); + return EFI_INVALID_PARAMETER; + } + + GtBlockFrame->GTFrameNumber = GTBlockTimerFrameList->FrameNumber; + GtBlockFrame->Reserved[0] = EFI_ACPI_RESERVED_BYTE; + GtBlockFrame->Reserved[1] = EFI_ACPI_RESERVED_BYTE; + GtBlockFrame->Reserved[2] = EFI_ACPI_RESERVED_BYTE; + + GtBlockFrame->CntBaseX = GTBlockTimerFrameList->PhysicalAddressCntBase; + GtBlockFrame->CntEL0BaseX = + GTBlockTimerFrameList->PhysicalAddressCntEL0Base; + + GtBlockFrame->GTxPhysicalTimerGSIV = + GTBlockTimerFrameList->PhysicalTimerGSIV; + GtBlockFrame->GTxPhysicalTimerFlags = + GTBlockTimerFrameList->PhysicalTimerFlags; + + GtBlockFrame->GTxVirtualTimerGSIV = GTBlockTimerFrameList->VirtualTimerGSIV; + GtBlockFrame->GTxVirtualTimerFlags = + GTBlockTimerFrameList->VirtualTimerFlags; + + GtBlockFrame->GTxCommonFlags = GTBlockTimerFrameList->CommonFlags; + GtBlockFrame++; + GTBlockTimerFrameList++; + } // for + return EFI_SUCCESS; +} + +/** Add the GT Block Timers in the GTDT Table. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Gtdt Pointer to the GTDT Table. + @param [in] GTBlockOffset Offset of the GT Block + information in the GTDT Table. + @param [in] GTBlockInfo Pointer to the GT Block + Information List. + @param [in] BlockTimerCount Number of GT Block Timers. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. +**/ +STATIC +EFI_STATUS +AddGTBlockList ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE * CONST Gtdt, + IN CONST UINT32 GTBlockOffset, + IN CONST CM_ARM_GTBLOCK_INFO * GTBlockInfo, + IN UINT32 BlockTimerCount +) +{ + EFI_STATUS Status; + EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE * GTBlock; + EFI_ACPI_6_3_GTDT_GT_BLOCK_TIMER_STRUCTURE * GtBlockFrame; + CM_ARM_GTBLOCK_TIMER_FRAME_INFO * GTBlockTimerFrameList; + UINT32 GTBlockTimerFrameCount; + UINTN Length; + + ASSERT (Gtdt != NULL); + ASSERT (GTBlockInfo != NULL); + + GTBlock = (EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE *)((UINT8*)Gtdt + + GTBlockOffset); + + while (BlockTimerCount-- != 0) { + DEBUG ((DEBUG_INFO, "GTDT: GTBlock = 0x%p\n", GTBlock)); + + Status = GetEArmObjGTBlockTimerFrameInfo ( + CfgMgrProtocol, + GTBlockInfo->GTBlockTimerFrameToken, + >BlockTimerFrameList, + >BlockTimerFrameCount + ); + if (EFI_ERROR (Status) || + (GTBlockTimerFrameCount != GTBlockInfo->GTBlockTimerFrameCount)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Failed to get Generic Timer Frames. Status = %r\n", + Status + )); + return Status; + } + + Length = sizeof (EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE) + + (sizeof (EFI_ACPI_6_3_GTDT_GT_BLOCK_TIMER_STRUCTURE) * + GTBlockInfo->GTBlockTimerFrameCount); + + // Check that the length of the GT block does not + // exceed MAX_UINT16 + if (Length > MAX_UINT16) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Too many GT Frames. Count = %d. " \ + "Maximum supported GT Block size exceeded. " \ + "Status = %r\n", + GTBlockInfo->GTBlockTimerFrameCount, + Status + )); + return Status; + } + + GTBlock->Type = EFI_ACPI_6_3_GTDT_GT_BLOCK; + GTBlock->Length = (UINT16)Length; + GTBlock->Reserved = EFI_ACPI_RESERVED_BYTE; + GTBlock->CntCtlBase = GTBlockInfo->GTBlockPhysicalAddress; + GTBlock->GTBlockTimerCount = GTBlockInfo->GTBlockTimerFrameCount; + GTBlock->GTBlockTimerOffset = + sizeof (EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE); + + GtBlockFrame = (EFI_ACPI_6_3_GTDT_GT_BLOCK_TIMER_STRUCTURE*) + ((UINT8*)GTBlock + GTBlock->GTBlockTimerOffset); + + // Add GT Block Timer frames + Status = AddGTBlockTimerFrames ( + GtBlockFrame, + GTBlockTimerFrameList, + GTBlockTimerFrameCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Failed to add Generic Timer Frames. Status = %r\n", + Status + )); + return Status; + } + + // Next GTBlock + GTBlock = (EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE *)((UINT8*)GTBlock + + GTBlock->Length); + GTBlockInfo++; + }// for + return EFI_SUCCESS; +} + +/** Construct the GTDT ACPI table. + + Called by the Dynamic Table Manager, this function invokes the + Configuration Manager protocol interface to get the required hardware + information for generating the ACPI table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResources function. + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [out] Table Pointer to the constructed ACPI Table. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildGtdtTable ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + EFI_STATUS Status; + UINT32 TableSize; + UINT32 PlatformTimerCount; + UINT32 WatchdogCount; + UINT32 BlockTimerCount; + CM_ARM_GENERIC_WATCHDOG_INFO * WatchdogInfoList; + CM_ARM_GTBLOCK_INFO * GTBlockInfo; + EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE * Gtdt; + UINT32 Idx; + UINT32 GTBlockOffset; + UINT32 WatchdogOffset; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) || + (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Requested table revision = %d, is not supported." + "Supported table revision: Minimum = %d, Maximum = %d\n", + AcpiTableInfo->AcpiTableRevision, + This->MinAcpiTableRevision, + This->AcpiTableRevision + )); + return EFI_INVALID_PARAMETER; + } + + *Table = NULL; + Status = GetEArmObjPlatformGTBlockInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + >BlockInfo, + &BlockTimerCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Failed to Get Platform GT Block Information." \ + " Status = %r\n", + Status + )); + goto error_handler; + } + + Status = GetEArmObjPlatformGenericWatchdogInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &WatchdogInfoList, + &WatchdogCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Failed to Get Platform Generic Watchdog Information." \ + " Status = %r\n", + Status + )); + goto error_handler; + } + + DEBUG (( + DEBUG_INFO, + "GTDT: BlockTimerCount = %d, WatchdogCount = %d\n", + BlockTimerCount, + WatchdogCount + )); + + // Calculate the GTDT Table Size + PlatformTimerCount = 0; + TableSize = sizeof (EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE); + if (BlockTimerCount != 0) { + GTBlockOffset = TableSize; + PlatformTimerCount += BlockTimerCount; + TableSize += (sizeof (EFI_ACPI_6_3_GTDT_GT_BLOCK_STRUCTURE) * + BlockTimerCount); + + for (Idx = 0; Idx < BlockTimerCount; Idx++) { + if (GTBlockInfo[Idx].GTBlockTimerFrameCount > 8) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "GTDT: GTBockFrameCount cannot be more than 8." \ + " GTBockFrameCount = %d, Status = %r\n", + GTBlockInfo[Idx].GTBlockTimerFrameCount, + Status + )); + goto error_handler; + } + TableSize += (sizeof (EFI_ACPI_6_3_GTDT_GT_BLOCK_TIMER_STRUCTURE) * + GTBlockInfo[Idx].GTBlockTimerFrameCount); + } + + DEBUG (( + DEBUG_INFO, + "GTDT: GTBockOffset = 0x%x, PLATFORM_TIMER_COUNT = %d\n", + GTBlockOffset, + PlatformTimerCount + )); + } + + WatchdogOffset = 0; + if (WatchdogCount != 0) { + WatchdogOffset = TableSize; + PlatformTimerCount += WatchdogCount; + TableSize += (sizeof (EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_STRUCTURE) * + WatchdogCount); + DEBUG (( + DEBUG_INFO, + "GTDT: WatchdogOffset = 0x%x, PLATFORM_TIMER_COUNT = %d\n", + WatchdogOffset, + PlatformTimerCount + )); + } + + *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize); + if (*Table == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Failed to allocate memory for GTDT Table, Size = %d," \ + " Status = %r\n", + TableSize, + Status + )); + goto error_handler; + } + + Gtdt = (EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE*)*Table; + DEBUG (( + DEBUG_INFO, + "GTDT: Gtdt = 0x%p TableSize = 0x%x\n", + Gtdt, + TableSize + )); + + Status = AddAcpiHeader ( + CfgMgrProtocol, + This, + &Gtdt->Header, + AcpiTableInfo, + TableSize + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Failed to add ACPI header. Status = %r\n", + Status + )); + goto error_handler; + } + + Status = AddGenericTimerInfo ( + CfgMgrProtocol, + Gtdt, + PlatformTimerCount, + AcpiTableInfo->AcpiTableRevision + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Failed to add Generic Timer Info. Status = %r\n", + Status + )); + goto error_handler; + } + + if (BlockTimerCount != 0) { + Status = AddGTBlockList ( + CfgMgrProtocol, + Gtdt, + GTBlockOffset, + GTBlockInfo, + BlockTimerCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: GTDT: Failed to add GT Block timers. Status = %r\n", + Status + )); + goto error_handler; + } + } + + if (WatchdogCount != 0) { + AddGenericWatchdogList ( + Gtdt, + WatchdogOffset, + WatchdogInfoList, + WatchdogCount + ); + } + + return Status; + +error_handler: + if (*Table != NULL) { + FreePool (*Table); + *Table = NULL; + } + return Status; +} + +/** Free any resources allocated for constructing the GTDT. + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to the ACPI Table. + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +FreeGtdtTableResources ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table +) +{ + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((Table == NULL) || (*Table == NULL)) { + DEBUG ((DEBUG_ERROR, "ERROR: GTDT: Invalid Table Pointer\n")); + ASSERT ((Table != NULL) && (*Table != NULL)); + return EFI_INVALID_PARAMETER; + } + + FreePool (*Table); + *Table = NULL; + return EFI_SUCCESS; +} + +/** This macro defines the GTDT Table Generator revision. +*/ +#define GTDT_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the GTDT Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR GtdtGenerator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdGtdt), + // Generator Description + L"ACPI.STD.GTDT.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE_SIGNATURE, + // ACPI Table Revision supported by this Generator + EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE_REVISION, + // Minimum ACPI Table Revision supported by this Generator + EFI_ACPI_6_2_GENERIC_TIMER_DESCRIPTION_TABLE_REVISION, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + GTDT_GENERATOR_REVISION, + // Build Table function + BuildGtdtTable, + // Free Resource function + FreeGtdtTableResources, + // Extended build function not needed + NULL, + // Extended build function not implemented by the generator. + // Hence extended free resource function is not required. + NULL +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiGtdtLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&GtdtGenerator); + DEBUG ((DEBUG_INFO, "GTDT: Register Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiGtdtLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&GtdtGenerator); + DEBUG ((DEBUG_INFO, "GTDT: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/AcpiIortLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/AcpiIortLibArm.inf new file mode 100644 index 00000000..f7fa6e2d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/AcpiIortLibArm.inf @@ -0,0 +1,37 @@ +## @file +# IORT Table Generator +# +# Copyright (c) 2017 - 2019, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = AcpiIortLibArm + FILE_GUID = 25682BA8-B41D-4403-B034-253769E0DAD5 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiIortLibConstructor + DESTRUCTOR = AcpiIortLibDestructor + +[Sources] + IortGenerator.c + IortGenerator.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + BaseLib + +[Pcd] + +[Protocols] + +[Guids] + diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c new file mode 100644 index 00000000..3dac52c0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c @@ -0,0 +1,2229 @@ +/** @file + IORT Table Generator + + Copyright (c) 2017 - 2020, ARM Limited. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - IO Remapping Table, Platform Design Document, + Document number: ARM DEN 0049D, Issue D, March 2018 + +**/ + +#include <IndustryStandard/IoRemappingTable.h> +#include <Library/AcpiLib.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +#include "IortGenerator.h" + +/** ARM standard IORT Generator + +Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjItsGroup + - EArmObjNamedComponent + - EArmObjRootComplex + - EArmObjSmmuV1SmmuV2 + - EArmObjSmmuV3 + - EArmObjPmcg + - EArmObjGicItsIdentifierArray + - EArmObjIdMappingArray + - EArmObjGicItsIdentifierArray +*/ + +/** This macro expands to a function that retrieves the ITS + Group node information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjItsGroup, + CM_ARM_ITS_GROUP_NODE + ); + +/** This macro expands to a function that retrieves the + Named Component node information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjNamedComponent, + CM_ARM_NAMED_COMPONENT_NODE + ); + +/** This macro expands to a function that retrieves the + Root Complex node information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjRootComplex, + CM_ARM_ROOT_COMPLEX_NODE + ); + +/** This macro expands to a function that retrieves the + SMMU v1/v2 node information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjSmmuV1SmmuV2, + CM_ARM_SMMUV1_SMMUV2_NODE + ); + +/** This macro expands to a function that retrieves the + SMMU v3 node information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjSmmuV3, + CM_ARM_SMMUV3_NODE + ); + +/** This macro expands to a function that retrieves the + PMCG node information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjPmcg, + CM_ARM_PMCG_NODE + ); + +/** This macro expands to a function that retrieves the + ITS Identifier Array information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGicItsIdentifierArray, + CM_ARM_ITS_IDENTIFIER + ); + +/** This macro expands to a function that retrieves the + Id Mapping Array information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjIdMappingArray, + CM_ARM_ID_MAPPING + ); + +/** This macro expands to a function that retrieves the + SMMU Interrupt Array information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjSmmuInterruptArray, + CM_ARM_SMMU_INTERRUPT + ); + +/** Returns the size of the ITS Group node. + + @param [in] Node Pointer to ITS Group node. + + @retval Size of the ITS Group Node. +**/ +STATIC +UINT32 +GetItsGroupNodeSize ( + IN CONST CM_ARM_ITS_GROUP_NODE * Node + ) +{ + ASSERT (Node != NULL); + + /* Size of ITS Group Node + + Size of ITS Identifier array + */ + return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE) + + (Node->ItsIdCount * sizeof (UINT32))); +} + +/** Returns the total size required for the ITS Group nodes and + updates the Node Indexer. + + This function calculates the size required for the node group + and also populates the Node Indexer array with offsets for the + individual nodes. + + @param [in] NodeStartOffset Offset from the start of the + IORT where this node group starts. + @param [in] NodeList Pointer to ITS Group node list. + @param [in] NodeCount Count of the ITS Group nodes. + @param [in, out] NodeIndexer Pointer to the next Node Indexer. + + @retval Total size of the ITS Group Nodes. +**/ +STATIC +UINT64 +GetSizeofItsGroupNodes ( + IN CONST UINT32 NodeStartOffset, + IN CONST CM_ARM_ITS_GROUP_NODE * NodeList, + IN UINT32 NodeCount, + IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer + ) +{ + UINT64 Size; + + ASSERT (NodeList != NULL); + + Size = 0; + while (NodeCount-- != 0) { + (*NodeIndexer)->Token = NodeList->Token; + (*NodeIndexer)->Object = (VOID*)NodeList; + (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset); + DEBUG (( + DEBUG_INFO, + "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n", + *NodeIndexer, + (*NodeIndexer)->Token, + (*NodeIndexer)->Object, + (*NodeIndexer)->Offset + )); + + Size += GetItsGroupNodeSize (NodeList); + (*NodeIndexer)++; + NodeList++; + } + return Size; +} + +/** Returns the size of the Named Component node. + + @param [in] Node Pointer to Named Component node. + + @retval Size of the Named Component node. +**/ +STATIC +UINT32 +GetNamedComponentNodeSize ( + IN CONST CM_ARM_NAMED_COMPONENT_NODE * Node + ) +{ + ASSERT (Node != NULL); + + /* Size of Named Component node + + Size of ID mapping array + + Size of ASCII string + 'padding to 32-bit word aligned'. + */ + return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE) + + (Node->IdMappingCount * + sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)) + + ALIGN_VALUE (AsciiStrSize (Node->ObjectName), 4)); +} + +/** Returns the total size required for the Named Component nodes and + updates the Node Indexer. + + This function calculates the size required for the node group + and also populates the Node Indexer array with offsets for the + individual nodes. + + @param [in] NodeStartOffset Offset from the start of the + IORT where this node group starts. + @param [in] NodeList Pointer to Named Component node list. + @param [in] NodeCount Count of the Named Component nodes. + @param [in, out] NodeIndexer Pointer to the next Node Indexer. + + @retval Total size of the Named Component nodes. +**/ +STATIC +UINT64 +GetSizeofNamedComponentNodes ( + IN CONST UINT32 NodeStartOffset, + IN CONST CM_ARM_NAMED_COMPONENT_NODE * NodeList, + IN UINT32 NodeCount, + IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer + ) +{ + UINT64 Size; + + ASSERT (NodeList != NULL); + + Size = 0; + while (NodeCount-- != 0) { + (*NodeIndexer)->Token = NodeList->Token; + (*NodeIndexer)->Object = (VOID*)NodeList; + (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset); + DEBUG (( + DEBUG_INFO, + "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n", + *NodeIndexer, + (*NodeIndexer)->Token, + (*NodeIndexer)->Object, + (*NodeIndexer)->Offset + )); + + Size += GetNamedComponentNodeSize (NodeList); + (*NodeIndexer)++; + NodeList++; + } + + return Size; +} + +/** Returns the size of the Root Complex node. + + @param [in] Node Pointer to Root Complex node. + + @retval Size of the Root Complex node. +**/ +STATIC +UINT32 +GetRootComplexNodeSize ( + IN CONST CM_ARM_ROOT_COMPLEX_NODE * Node + ) +{ + ASSERT (Node != NULL); + + /* Size of Root Complex node + + Size of ID mapping array + */ + return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE) + + (Node->IdMappingCount * + sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE))); +} + +/** Returns the total size required for the Root Complex nodes and + updates the Node Indexer. + + This function calculates the size required for the node group + and also populates the Node Indexer array with offsets for the + individual nodes. + + @param [in] NodeStartOffset Offset from the start of the + IORT where this node group starts. + @param [in] NodeList Pointer to Root Complex node list. + @param [in] NodeCount Count of the Root Complex nodes. + @param [in, out] NodeIndexer Pointer to the next Node Indexer. + + @retval Total size of the Root Complex nodes. +**/ +STATIC +UINT64 +GetSizeofRootComplexNodes ( + IN CONST UINT32 NodeStartOffset, + IN CONST CM_ARM_ROOT_COMPLEX_NODE * NodeList, + IN UINT32 NodeCount, + IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer + ) +{ + UINT64 Size; + + ASSERT (NodeList != NULL); + + Size = 0; + while (NodeCount-- != 0) { + (*NodeIndexer)->Token = NodeList->Token; + (*NodeIndexer)->Object = (VOID*)NodeList; + (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset); + DEBUG (( + DEBUG_INFO, + "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n", + *NodeIndexer, + (*NodeIndexer)->Token, + (*NodeIndexer)->Object, + (*NodeIndexer)->Offset + )); + + Size += GetRootComplexNodeSize (NodeList); + (*NodeIndexer)++; + NodeList++; + } + + return Size; +} + +/** Returns the size of the SMMUv1/SMMUv2 node. + + @param [in] Node Pointer to SMMUv1/SMMUv2 node list. + + @retval Size of the SMMUv1/SMMUv2 node. +**/ +STATIC +UINT32 +GetSmmuV1V2NodeSize ( + IN CONST CM_ARM_SMMUV1_SMMUV2_NODE * Node + ) +{ + ASSERT (Node != NULL); + + /* Size of SMMU v1/SMMU v2 node + + Size of ID mapping array + + Size of context interrupt array + + Size of PMU interrupt array + */ + return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE) + + (Node->IdMappingCount * + sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)) + + (Node->ContextInterruptCount * + sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT)) + + (Node->PmuInterruptCount * + sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT))); +} + +/** Returns the total size required for the SMMUv1/SMMUv2 nodes and + updates the Node Indexer. + + This function calculates the size required for the node group + and also populates the Node Indexer array with offsets for the + individual nodes. + + @param [in] NodeStartOffset Offset from the start of the + IORT where this node group starts. + @param [in] NodeList Pointer to SMMUv1/SMMUv2 node list. + @param [in] NodeCount Count of the SMMUv1/SMMUv2 nodes. + @param [in, out] NodeIndexer Pointer to the next Node Indexer. + + @retval Total size of the SMMUv1/SMMUv2 nodes. +**/ +STATIC +UINT64 +GetSizeofSmmuV1V2Nodes ( + IN CONST UINT32 NodeStartOffset, + IN CONST CM_ARM_SMMUV1_SMMUV2_NODE * NodeList, + IN UINT32 NodeCount, + IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer + ) +{ + UINT64 Size; + + ASSERT (NodeList != NULL); + + Size = 0; + while (NodeCount-- != 0) { + (*NodeIndexer)->Token = NodeList->Token; + (*NodeIndexer)->Object = (VOID*)NodeList; + (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset); + DEBUG (( + DEBUG_INFO, + "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n", + *NodeIndexer, + (*NodeIndexer)->Token, + (*NodeIndexer)->Object, + (*NodeIndexer)->Offset + )); + + Size += GetSmmuV1V2NodeSize (NodeList); + (*NodeIndexer)++; + NodeList++; + } + return Size; +} + +/** Returns the size of the SMMUv3 node. + + @param [in] Node Pointer to SMMUv3 node list. + + @retval Total size of the SMMUv3 nodes. +**/ +STATIC +UINT32 +GetSmmuV3NodeSize ( + IN CONST CM_ARM_SMMUV3_NODE * Node + ) +{ + ASSERT (Node != NULL); + + /* Size of SMMU v1/SMMU v2 node + + Size of ID mapping array + */ + return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE) + + (Node->IdMappingCount * + sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE))); +} + +/** Returns the total size required for the SMMUv3 nodes and + updates the Node Indexer. + + This function calculates the size required for the node group + and also populates the Node Indexer array with offsets for the + individual nodes. + + @param [in] NodeStartOffset Offset from the start of the + IORT where this node group starts. + @param [in] NodeList Pointer to SMMUv3 node list. + @param [in] NodeCount Count of the SMMUv3 nodes. + @param [in, out] NodeIndexer Pointer to the next Node Indexer. + + @retval Total size of the SMMUv3 nodes. +**/ +STATIC +UINT64 +GetSizeofSmmuV3Nodes ( + IN CONST UINT32 NodeStartOffset, + IN CONST CM_ARM_SMMUV3_NODE * NodeList, + IN UINT32 NodeCount, + IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer + ) +{ + UINT64 Size; + + ASSERT (NodeList != NULL); + + Size = 0; + while (NodeCount-- != 0) { + (*NodeIndexer)->Token = NodeList->Token; + (*NodeIndexer)->Object = (VOID*)NodeList; + (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset); + DEBUG (( + DEBUG_INFO, + "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n", + *NodeIndexer, + (*NodeIndexer)->Token, + (*NodeIndexer)->Object, + (*NodeIndexer)->Offset + )); + + Size += GetSmmuV3NodeSize (NodeList); + (*NodeIndexer)++; + NodeList++; + } + return Size; +} + +/** Returns the size of the PMCG node. + + @param [in] Node Pointer to PMCG node. + + @retval Size of the PMCG node. +**/ +STATIC +UINT32 +GetPmcgNodeSize ( + IN CONST CM_ARM_PMCG_NODE * Node + ) +{ + ASSERT (Node != NULL); + + /* Size of PMCG node + + Size of ID mapping array + */ + return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE) + + (Node->IdMappingCount * + sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE))); +} + +/** Returns the total size required for the PMCG nodes and + updates the Node Indexer. + + This function calculates the size required for the node group + and also populates the Node Indexer array with offsets for the + individual nodes. + + @param [in] NodeStartOffset Offset from the start of the + IORT where this node group starts. + @param [in] NodeList Pointer to PMCG node list. + @param [in] NodeCount Count of the PMCG nodes. + @param [in, out] NodeIndexer Pointer to the next Node Indexer. + + @retval Total size of the PMCG nodes. +**/ +STATIC +UINT64 +GetSizeofPmcgNodes ( + IN CONST UINT32 NodeStartOffset, + IN CONST CM_ARM_PMCG_NODE * NodeList, + IN UINT32 NodeCount, + IN OUT IORT_NODE_INDEXER ** CONST NodeIndexer + ) +{ + UINT64 Size; + + ASSERT (NodeList != NULL); + + Size = 0; + while (NodeCount-- != 0) { + (*NodeIndexer)->Token = NodeList->Token; + (*NodeIndexer)->Object = (VOID*)NodeList; + (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset); + DEBUG (( + DEBUG_INFO, + "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n", + *NodeIndexer, + (*NodeIndexer)->Token, + (*NodeIndexer)->Object, + (*NodeIndexer)->Offset + )); + + Size += GetPmcgNodeSize (NodeList); + (*NodeIndexer)++; + NodeList++; + } + return Size; +} + +/** Returns the offset of the Node referenced by the Token. + + @param [in] NodeIndexer Pointer to node indexer array. + @param [in] NodeCount Count of the nodes. + @param [in] Token Reference token for the node. + @param [out] NodeOffset Offset of the node from the + start of the IORT table. + + @retval EFI_SUCCESS Success. + @retval EFI_NOT_FOUND No matching token reference + found in node indexer array. +**/ +STATIC +EFI_STATUS +GetNodeOffsetReferencedByToken ( + IN IORT_NODE_INDEXER * NodeIndexer, + IN UINT32 NodeCount, + IN CM_OBJECT_TOKEN Token, + OUT UINT32 * NodeOffset + ) +{ + DEBUG (( + DEBUG_INFO, + "IORT: Node Indexer: Search Token = %p\n", + Token + )); + while (NodeCount-- != 0) { + DEBUG (( + DEBUG_INFO, + "IORT: Node Indexer: NodeIndexer->Token = %p, Offset = %d\n", + NodeIndexer->Token, + NodeIndexer->Offset + )); + if (NodeIndexer->Token == Token) { + *NodeOffset = NodeIndexer->Offset; + DEBUG (( + DEBUG_INFO, + "IORT: Node Indexer: Token = %p, Found\n", + Token + )); + return EFI_SUCCESS; + } + NodeIndexer++; + } + DEBUG (( + DEBUG_INFO, + "IORT: Node Indexer: Token = %p, Not Found\n", + Token + )); + return EFI_NOT_FOUND; +} + +/** Update the Id Mapping Array. + + This function retrieves the Id Mapping Array object referenced by the + IdMappingToken and updates the IdMapArray. + + @param [in] This Pointer to the table Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] IdMapArray Pointer to an array of Id Mappings. + @param [in] IdCount Number of Id Mappings. + @param [in] IdMappingToken Reference Token for retrieving the + Id Mapping Array object. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. +**/ +STATIC +EFI_STATUS +AddIdMappingArray ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray, + IN UINT32 IdCount, + IN CONST CM_OBJECT_TOKEN IdMappingToken + ) +{ + EFI_STATUS Status; + CM_ARM_ID_MAPPING * IdMappings; + UINT32 IdMappingCount; + ACPI_IORT_GENERATOR * Generator; + + ASSERT (IdMapArray != NULL); + + Generator = (ACPI_IORT_GENERATOR*)This; + + // Get the Id Mapping Array + Status = GetEArmObjIdMappingArray ( + CfgMgrProtocol, + IdMappingToken, + &IdMappings, + &IdMappingCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get Id Mapping array. Status = %r\n", + Status + )); + return Status; + } + + if (IdMappingCount < IdCount) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get the required number of Id Mappings.\n" + )); + return EFI_NOT_FOUND; + } + + // Populate the Id Mapping array + while (IdCount-- != 0) { + Status = GetNodeOffsetReferencedByToken ( + Generator->NodeIndexer, + Generator->IortNodeCount, + IdMappings->OutputReferenceToken, + &IdMapArray->OutputReference + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get Output Reference for ITS Identifier array." + "Reference Token = %p" + " Status = %r\n", + IdMappings->OutputReferenceToken, + Status + )); + return Status; + } + + IdMapArray->InputBase = IdMappings->InputBase; + IdMapArray->NumIds = IdMappings->NumIds; + IdMapArray->OutputBase = IdMappings->OutputBase; + IdMapArray->Flags = IdMappings->Flags; + + IdMapArray++; + IdMappings++; + } // Id Mapping array + + return EFI_SUCCESS; +} + +/** Update the ITS Group Node Information. + + @param [in] This Pointer to the table Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Iort Pointer to IORT table structure. + @param [in] NodesStartOffset Offset for the start of the ITS Group + Nodes. + @param [in] NodeList Pointer to an array of ITS Group Node + Objects. + @param [in] NodeCount Number of ITS Group Node Objects. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. +**/ +STATIC +EFI_STATUS +AddItsGroupNodes ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort, + IN CONST UINT32 NodesStartOffset, + IN CONST CM_ARM_ITS_GROUP_NODE * NodeList, + IN UINT32 NodeCount + ) +{ + EFI_STATUS Status; + EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE * ItsGroupNode; + UINT32 * ItsIds; + CM_ARM_ITS_IDENTIFIER * ItsIdentifier; + UINT32 ItsIdentifierCount; + UINT32 IdIndex; + UINT64 NodeLength; + + ASSERT (Iort != NULL); + + ItsGroupNode = (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE*)((UINT8*)Iort + + NodesStartOffset); + + while (NodeCount-- != 0) { + NodeLength = GetItsGroupNodeSize (NodeList); + if (NodeLength > MAX_UINT16) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: ITS Id Array Node length 0x%lx > MAX_UINT16." + " Status = %r\n", + NodeLength, + Status + )); + return Status; + } + + // Populate the node header + ItsGroupNode->Node.Type = EFI_ACPI_IORT_TYPE_ITS_GROUP; + ItsGroupNode->Node.Length = (UINT16)NodeLength; + ItsGroupNode->Node.Revision = 0; + ItsGroupNode->Node.Reserved = EFI_ACPI_RESERVED_DWORD; + ItsGroupNode->Node.NumIdMappings = 0; + ItsGroupNode->Node.IdReference = 0; + + // IORT specific data + ItsGroupNode->NumItsIdentifiers = NodeList->ItsIdCount; + ItsIds = (UINT32*)((UINT8*)ItsGroupNode + + sizeof (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE)); + + Status = GetEArmObjGicItsIdentifierArray ( + CfgMgrProtocol, + NodeList->ItsIdToken, + &ItsIdentifier, + &ItsIdentifierCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get ITS Identifier array. Status = %r\n", + Status + )); + return Status; + } + + if (ItsIdentifierCount < ItsGroupNode->NumItsIdentifiers) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get the required number of ITS Identifiers.\n" + )); + return EFI_NOT_FOUND; + } + + // Populate the ITS identifier array + for (IdIndex = 0; IdIndex < ItsGroupNode->NumItsIdentifiers; IdIndex++) { + ItsIds[IdIndex] = ItsIdentifier[IdIndex].ItsId; + } // ITS identifier array + + // Next IORT Group Node + ItsGroupNode = (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE*)((UINT8*)ItsGroupNode + + ItsGroupNode->Node.Length); + NodeList++; + } // IORT Group Node + + return EFI_SUCCESS; +} + +/** Update the Named Component Node Information. + + This function updates the Named Component node information in the IORT + table. + + @param [in] This Pointer to the table Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Iort Pointer to IORT table structure. + @param [in] NodesStartOffset Offset for the start of the Named + Component Nodes. + @param [in] NodeList Pointer to an array of Named Component + Node Objects. + @param [in] NodeCount Number of Named Component Node Objects. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. +**/ +STATIC +EFI_STATUS +AddNamedComponentNodes ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort, + IN CONST UINT32 NodesStartOffset, + IN CONST CM_ARM_NAMED_COMPONENT_NODE * NodeList, + IN UINT32 NodeCount + ) +{ + EFI_STATUS Status; + EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE * NcNode; + EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray; + CHAR8 * ObjectName; + UINTN ObjectNameLength; + UINT64 NodeLength; + + ASSERT (Iort != NULL); + + NcNode = (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE*)((UINT8*)Iort + + NodesStartOffset); + + while (NodeCount-- != 0) { + NodeLength = GetNamedComponentNodeSize (NodeList); + if (NodeLength > MAX_UINT16) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Named Component Node length 0x%lx > MAX_UINT16." + " Status = %r\n", + NodeLength, + Status + )); + return Status; + } + + // Populate the node header + NcNode->Node.Type = EFI_ACPI_IORT_TYPE_NAMED_COMP; + NcNode->Node.Length = (UINT16)NodeLength; + NcNode->Node.Revision = 2; + NcNode->Node.Reserved = EFI_ACPI_RESERVED_DWORD; + NcNode->Node.NumIdMappings = NodeList->IdMappingCount; + + ObjectNameLength = AsciiStrLen (NodeList->ObjectName) + 1; + NcNode->Node.IdReference = + (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE) + + (ALIGN_VALUE (ObjectNameLength, 4))); + + // Named Component specific data + NcNode->Flags = NodeList->Flags; + NcNode->CacheCoherent = NodeList->CacheCoherent; + NcNode->AllocationHints = NodeList->AllocationHints; + NcNode->Reserved = EFI_ACPI_RESERVED_WORD; + NcNode->MemoryAccessFlags = NodeList->MemoryAccessFlags; + NcNode->AddressSizeLimit = NodeList->AddressSizeLimit; + + // Copy the object name + ObjectName = (CHAR8*)((UINT8*)NcNode + + sizeof (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE)); + Status = AsciiStrCpyS ( + ObjectName, + ObjectNameLength, + NodeList->ObjectName + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to copy Object Name. Status = %r\n", + Status + )); + return Status; + } + + if ((NodeList->IdMappingCount > 0) && + (NodeList->IdMappingToken != CM_NULL_TOKEN)) { + // Ids for Named Component + IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE*)((UINT8*)NcNode + + NcNode->Node.IdReference); + + Status = AddIdMappingArray ( + This, + CfgMgrProtocol, + IdMapArray, + NodeList->IdMappingCount, + NodeList->IdMappingToken + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n", + Status + )); + return Status; + } + } + + // Next Named Component Node + NcNode = (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE*)((UINT8*)NcNode + + NcNode->Node.Length); + NodeList++; + } // Named Component Node + + return EFI_SUCCESS; +} + +/** Update the Root Complex Node Information. + + This function updates the Root Complex node information in the IORT table. + + @param [in] This Pointer to the table Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Iort Pointer to IORT table structure. + @param [in] NodesStartOffset Offset for the start of the Root Complex + Nodes. + @param [in] NodeList Pointer to an array of Root Complex Node + Objects. + @param [in] NodeCount Number of Root Complex Node Objects. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. +**/ +STATIC +EFI_STATUS +AddRootComplexNodes ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort, + IN CONST UINT32 NodesStartOffset, + IN CONST CM_ARM_ROOT_COMPLEX_NODE * NodeList, + IN UINT32 NodeCount + ) +{ + EFI_STATUS Status; + EFI_ACPI_6_0_IO_REMAPPING_RC_NODE * RcNode; + EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray; + UINT64 NodeLength; + + ASSERT (Iort != NULL); + + RcNode = (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE*)((UINT8*)Iort + + NodesStartOffset); + + while (NodeCount-- != 0) { + NodeLength = GetRootComplexNodeSize (NodeList); + if (NodeLength > MAX_UINT16) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Root Complex Node length 0x%lx > MAX_UINT16." + " Status = %r\n", + NodeLength, + Status + )); + return Status; + } + + // Populate the node header + RcNode->Node.Type = EFI_ACPI_IORT_TYPE_ROOT_COMPLEX; + RcNode->Node.Length = (UINT16)NodeLength; + RcNode->Node.Revision = 1; + RcNode->Node.Reserved = EFI_ACPI_RESERVED_DWORD; + RcNode->Node.NumIdMappings = NodeList->IdMappingCount; + RcNode->Node.IdReference = sizeof (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE); + + // Root Complex specific data + RcNode->CacheCoherent = NodeList->CacheCoherent; + RcNode->AllocationHints = NodeList->AllocationHints; + RcNode->Reserved = EFI_ACPI_RESERVED_WORD; + RcNode->MemoryAccessFlags = NodeList->MemoryAccessFlags; + RcNode->AtsAttribute = NodeList->AtsAttribute; + RcNode->PciSegmentNumber = NodeList->PciSegmentNumber; + RcNode->MemoryAddressSize = NodeList->MemoryAddressSize; + RcNode->Reserved1[0] = EFI_ACPI_RESERVED_BYTE; + RcNode->Reserved1[1] = EFI_ACPI_RESERVED_BYTE; + RcNode->Reserved1[2] = EFI_ACPI_RESERVED_BYTE; + + if ((NodeList->IdMappingCount > 0) && + (NodeList->IdMappingToken != CM_NULL_TOKEN)) { + // Ids for Root Complex + IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE*)((UINT8*)RcNode + + RcNode->Node.IdReference); + Status = AddIdMappingArray ( + This, + CfgMgrProtocol, + IdMapArray, + NodeList->IdMappingCount, + NodeList->IdMappingToken + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n", + Status + )); + return Status; + } + } + + // Next Root Complex Node + RcNode = (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE*)((UINT8*)RcNode + + RcNode->Node.Length); + NodeList++; + } // Root Complex Node + + return EFI_SUCCESS; +} + +/** Update the SMMU Interrupt Array. + + This function retrieves the InterruptArray object referenced by the + InterruptToken and updates the SMMU InterruptArray. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] InterruptArray Pointer to an array of Interrupts. + @param [in] InterruptCount Number of entries in the InterruptArray. + @param [in] InterruptToken Reference Token for retrieving the SMMU + InterruptArray object. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. +**/ +STATIC +EFI_STATUS +AddSmmuInterruptArray ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT * InterruptArray, + IN UINT32 InterruptCount, + IN CONST CM_OBJECT_TOKEN InterruptToken + ) +{ + EFI_STATUS Status; + CM_ARM_SMMU_INTERRUPT * SmmuInterrupt; + UINT32 SmmuInterruptCount; + + ASSERT (InterruptArray != NULL); + + // Get the SMMU Interrupt Array + Status = GetEArmObjSmmuInterruptArray ( + CfgMgrProtocol, + InterruptToken, + &SmmuInterrupt, + &SmmuInterruptCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get SMMU Interrupt array. Status = %r\n", + Status + )); + return Status; + } + + if (SmmuInterruptCount < InterruptCount) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get the required number of SMMU Interrupts.\n" + )); + return EFI_NOT_FOUND; + } + + // Populate the Id Mapping array + while (InterruptCount-- != 0) { + InterruptArray->Interrupt = SmmuInterrupt->Interrupt; + InterruptArray->InterruptFlags = SmmuInterrupt->Flags; + InterruptArray++; + SmmuInterrupt++; + } // Id Mapping array + + return EFI_SUCCESS; +} + +/** Update the SMMU v1/v2 Node Information. + + @param [in] This Pointer to the table Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Iort Pointer to IORT table structure. + @param [in] NodesStartOffset Offset for the start of the SMMU v1/v2 + Nodes. + @param [in] NodeList Pointer to an array of SMMU v1/v2 Node + Objects. + @param [in] NodeCount Number of SMMU v1/v2 Node Objects. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. +**/ +STATIC +EFI_STATUS +AddSmmuV1V2Nodes ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort, + IN CONST UINT32 NodesStartOffset, + IN CONST CM_ARM_SMMUV1_SMMUV2_NODE * NodeList, + IN UINT32 NodeCount + ) +{ + EFI_STATUS Status; + EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE * SmmuNode; + EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray; + + EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT * ContextInterruptArray; + EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT * PmuInterruptArray; + UINT64 NodeLength; + + ASSERT (Iort != NULL); + + SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE*)((UINT8*)Iort + + NodesStartOffset); + + while (NodeCount-- != 0) { + NodeLength = GetSmmuV1V2NodeSize (NodeList); + if (NodeLength > MAX_UINT16) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: SMMU V1/V2 Node length 0x%lx > MAX_UINT16. Status = %r\n", + NodeLength, + Status + )); + return Status; + } + + // Populate the node header + SmmuNode->Node.Type = EFI_ACPI_IORT_TYPE_SMMUv1v2; + SmmuNode->Node.Length = (UINT16)NodeLength; + SmmuNode->Node.Revision = 0; + SmmuNode->Node.Reserved = EFI_ACPI_RESERVED_DWORD; + SmmuNode->Node.NumIdMappings = NodeList->IdMappingCount; + SmmuNode->Node.IdReference = sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE) + + (NodeList->ContextInterruptCount * + sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT)) + + (NodeList->PmuInterruptCount * + sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT)); + + // SMMU v1/v2 specific data + SmmuNode->Base = NodeList->BaseAddress; + SmmuNode->Span = NodeList->Span; + SmmuNode->Model = NodeList->Model; + SmmuNode->Flags = NodeList->Flags; + + // Reference to Global Interrupt Array + SmmuNode->GlobalInterruptArrayRef = + OFFSET_OF (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE, SMMU_NSgIrpt); + + // Context Interrupt + SmmuNode->NumContextInterrupts = NodeList->ContextInterruptCount; + SmmuNode->ContextInterruptArrayRef = + sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE); + ContextInterruptArray = + (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT*)((UINT8*)SmmuNode + + sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE)); + + // PMU Interrupt + SmmuNode->NumPmuInterrupts = NodeList->PmuInterruptCount; + SmmuNode->PmuInterruptArrayRef = SmmuNode->ContextInterruptArrayRef + + (NodeList->ContextInterruptCount * + sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT)); + PmuInterruptArray = + (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT*)((UINT8*)SmmuNode + + SmmuNode->PmuInterruptArrayRef); + + SmmuNode->SMMU_NSgIrpt = NodeList->SMMU_NSgIrpt; + SmmuNode->SMMU_NSgIrptFlags = NodeList->SMMU_NSgIrptFlags; + SmmuNode->SMMU_NSgCfgIrpt = NodeList->SMMU_NSgCfgIrpt; + SmmuNode->SMMU_NSgCfgIrptFlags = NodeList->SMMU_NSgCfgIrptFlags; + + // Add Context Interrupt Array + Status = AddSmmuInterruptArray ( + CfgMgrProtocol, + ContextInterruptArray, + SmmuNode->NumContextInterrupts, + NodeList->ContextInterruptToken + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to Context Interrupt Array. Status = %r\n", + Status + )); + return Status; + } + + // Add PMU Interrupt Array + if ((SmmuNode->NumPmuInterrupts > 0) && + (NodeList->PmuInterruptToken != CM_NULL_TOKEN)) { + Status = AddSmmuInterruptArray ( + CfgMgrProtocol, + PmuInterruptArray, + SmmuNode->NumPmuInterrupts, + NodeList->PmuInterruptToken + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to PMU Interrupt Array. Status = %r\n", + Status + )); + return Status; + } + } + + if ((NodeList->IdMappingCount > 0) && + (NodeList->IdMappingToken != CM_NULL_TOKEN)) { + // Ids for SMMU v1/v2 Node + IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE*)((UINT8*)SmmuNode + + SmmuNode->Node.IdReference); + Status = AddIdMappingArray ( + This, + CfgMgrProtocol, + IdMapArray, + NodeList->IdMappingCount, + NodeList->IdMappingToken + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n", + Status + )); + return Status; + } + } + // Next SMMU v1/v2 Node + SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE*)((UINT8*)SmmuNode + + SmmuNode->Node.Length); + NodeList++; + } // SMMU v1/v2 Node + + return EFI_SUCCESS; +} + +/** Update the SMMUv3 Node Information. + + This function updates the SMMUv3 node information in the IORT table. + + @param [in] This Pointer to the table Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Iort Pointer to IORT table structure. + @param [in] NodesStartOffset Offset for the start of the SMMUv3 Nodes. + @param [in] NodeList Pointer to an array of SMMUv3 Node Objects. + @param [in] NodeCount Number of SMMUv3 Node Objects. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. +**/ +STATIC +EFI_STATUS +AddSmmuV3Nodes ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort, + IN CONST UINT32 NodesStartOffset, + IN CONST CM_ARM_SMMUV3_NODE * NodeList, + IN UINT32 NodeCount + ) +{ + EFI_STATUS Status; + EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE * SmmuV3Node; + EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray; + UINT64 NodeLength; + + ASSERT (Iort != NULL); + + SmmuV3Node = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE*)((UINT8*)Iort + + NodesStartOffset); + + while (NodeCount-- != 0) { + NodeLength = GetSmmuV3NodeSize (NodeList); + if (NodeLength > MAX_UINT16) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: SMMU V3 Node length 0x%lx > MAX_UINT16. Status = %r\n", + NodeLength, + Status + )); + return Status; + } + + // Populate the node header + SmmuV3Node->Node.Type = EFI_ACPI_IORT_TYPE_SMMUv3; + SmmuV3Node->Node.Length = (UINT16)NodeLength; + SmmuV3Node->Node.Revision = 2; + SmmuV3Node->Node.Reserved = EFI_ACPI_RESERVED_DWORD; + SmmuV3Node->Node.NumIdMappings = NodeList->IdMappingCount; + SmmuV3Node->Node.IdReference = + sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE); + + // SMMUv3 specific data + SmmuV3Node->Base = NodeList->BaseAddress; + SmmuV3Node->Flags = NodeList->Flags; + SmmuV3Node->Reserved = EFI_ACPI_RESERVED_WORD; + SmmuV3Node->VatosAddress = NodeList->VatosAddress; + SmmuV3Node->Model = NodeList->Model; + SmmuV3Node->Event = NodeList->EventInterrupt; + SmmuV3Node->Pri = NodeList->PriInterrupt; + SmmuV3Node->Gerr = NodeList->GerrInterrupt; + SmmuV3Node->Sync = NodeList->SyncInterrupt; + + if ((SmmuV3Node->Flags & EFI_ACPI_IORT_SMMUv3_FLAG_PROXIMITY_DOMAIN) != 0) { + // The Proximity Domain Valid flag is set to 1 + SmmuV3Node->ProximityDomain = NodeList->ProximityDomain; + } else { + SmmuV3Node->ProximityDomain = 0; + } + + if ((SmmuV3Node->Event != 0) && (SmmuV3Node->Pri != 0) && + (SmmuV3Node->Gerr != 0) && (SmmuV3Node->Sync != 0)) { + // If all the SMMU control interrupts are GSIV based, + // the DeviceID mapping index field is ignored. + SmmuV3Node->DeviceIdMappingIndex = 0; + } else { + SmmuV3Node->DeviceIdMappingIndex = NodeList->DeviceIdMappingIndex; + } + + if ((NodeList->IdMappingCount > 0) && + (NodeList->IdMappingToken != CM_NULL_TOKEN)) { + // Ids for SMMUv3 node + IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE*)((UINT8*)SmmuV3Node + + SmmuV3Node->Node.IdReference); + Status = AddIdMappingArray ( + This, + CfgMgrProtocol, + IdMapArray, + NodeList->IdMappingCount, + NodeList->IdMappingToken + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n", + Status + )); + return Status; + } + } + + // Next SMMUv3 Node + SmmuV3Node = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE*)((UINT8*)SmmuV3Node + + SmmuV3Node->Node.Length); + NodeList++; + } // SMMUv3 Node + + return EFI_SUCCESS; +} + +/** Update the PMCG Node Information. + + This function updates the PMCG node information in the IORT table. + + @param [in] This Pointer to the table Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Iort Pointer to IORT table structure. + @param [in] NodesStartOffset Offset for the start of the PMCG Nodes. + @param [in] NodeList Pointer to an array of PMCG Node Objects. + @param [in] NodeCount Number of PMCG Node Objects. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. +**/ +STATIC +EFI_STATUS +AddPmcgNodes ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort, + IN CONST UINT32 NodesStartOffset, + IN CONST CM_ARM_PMCG_NODE * NodeList, + IN UINT32 NodeCount + ) +{ + EFI_STATUS Status; + EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE * PmcgNode; + EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE * IdMapArray; + ACPI_IORT_GENERATOR * Generator; + UINT64 NodeLength; + + ASSERT (Iort != NULL); + + Generator = (ACPI_IORT_GENERATOR*)This; + PmcgNode = (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE*)((UINT8*)Iort + + NodesStartOffset); + + while (NodeCount-- != 0) { + NodeLength = GetPmcgNodeSize (NodeList); + if (NodeLength > MAX_UINT16) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: PMCG Node length 0x%lx > MAX_UINT16. Status = %r\n", + NodeLength, + Status + )); + return Status; + } + + // Populate the node header + PmcgNode->Node.Type = EFI_ACPI_IORT_TYPE_PMCG; + PmcgNode->Node.Length = (UINT16)NodeLength; + PmcgNode->Node.Revision = 1; + PmcgNode->Node.Reserved = EFI_ACPI_RESERVED_DWORD; + PmcgNode->Node.NumIdMappings = NodeList->IdMappingCount; + PmcgNode->Node.IdReference = sizeof (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE); + + // PMCG specific data + PmcgNode->Base = NodeList->BaseAddress; + PmcgNode->OverflowInterruptGsiv = NodeList->OverflowInterrupt; + PmcgNode->Page1Base = NodeList->Page1BaseAddress; + + Status = GetNodeOffsetReferencedByToken ( + Generator->NodeIndexer, + Generator->IortNodeCount, + NodeList->ReferenceToken, + &PmcgNode->NodeReference + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get Output Reference for PMCG Node." + "Reference Token = %p" + " Status = %r\n", + NodeList->ReferenceToken, + Status + )); + return Status; + } + + if ((NodeList->IdMappingCount > 0) && + (NodeList->IdMappingToken != CM_NULL_TOKEN)) { + // Ids for PMCG node + IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE*)((UINT8*)PmcgNode + + PmcgNode->Node.IdReference); + + Status = AddIdMappingArray ( + This, + CfgMgrProtocol, + IdMapArray, + NodeList->IdMappingCount, + NodeList->IdMappingToken + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n", + Status + )); + return Status; + } + } + + // Next PMCG Node + PmcgNode = (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE*)((UINT8*)PmcgNode + + PmcgNode->Node.Length); + NodeList++; + } // PMCG Node + + return EFI_SUCCESS; +} + +/** Construct the IORT ACPI table. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResources function. + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [out] Table Pointer to the constructed ACPI Table. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildIortTable ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + EFI_STATUS Status; + + UINT64 TableSize; + UINT64 NodeSize; + + UINT32 IortNodeCount; + UINT32 ItsGroupNodeCount; + UINT32 NamedComponentNodeCount; + UINT32 RootComplexNodeCount; + UINT32 SmmuV1V2NodeCount; + UINT32 SmmuV3NodeCount; + UINT32 PmcgNodeCount; + + UINT32 ItsGroupOffset; + UINT32 NamedComponentOffset; + UINT32 RootComplexOffset; + UINT32 SmmuV1V2Offset; + UINT32 SmmuV3Offset; + UINT32 PmcgOffset; + + CM_ARM_ITS_GROUP_NODE * ItsGroupNodeList; + CM_ARM_NAMED_COMPONENT_NODE * NamedComponentNodeList; + CM_ARM_ROOT_COMPLEX_NODE * RootComplexNodeList; + CM_ARM_SMMUV1_SMMUV2_NODE * SmmuV1V2NodeList; + CM_ARM_SMMUV3_NODE * SmmuV3NodeList; + CM_ARM_PMCG_NODE * PmcgNodeList; + + EFI_ACPI_6_0_IO_REMAPPING_TABLE * Iort; + IORT_NODE_INDEXER * NodeIndexer; + ACPI_IORT_GENERATOR * Generator; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) || + (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Requested table revision = %d, is not supported." + "Supported table revision: Minimum = %d, Maximum = %d\n", + AcpiTableInfo->AcpiTableRevision, + This->MinAcpiTableRevision, + This->AcpiTableRevision + )); + return EFI_INVALID_PARAMETER; + } + + Generator = (ACPI_IORT_GENERATOR*)This; + *Table = NULL; + + // Get the ITS group node info + Status = GetEArmObjItsGroup ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &ItsGroupNodeList, + &ItsGroupNodeCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get ITS Group Node Info. Status = %r\n", + Status + )); + goto error_handler; + } + + // Add the ITS group node count + IortNodeCount = ItsGroupNodeCount; + + // Get the Named component node info + Status = GetEArmObjNamedComponent ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &NamedComponentNodeList, + &NamedComponentNodeCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get Named Component Node Info. Status = %r\n", + Status + )); + goto error_handler; + } + + // Add the Named Component group count + IortNodeCount += NamedComponentNodeCount; + + // Get the Root complex node info + Status = GetEArmObjRootComplex ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &RootComplexNodeList, + &RootComplexNodeCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get Root Complex Node Info. Status = %r\n", + Status + )); + goto error_handler; + } + + // Add the Root Complex node count + IortNodeCount += RootComplexNodeCount; + + // Get the SMMU v1/v2 node info + Status = GetEArmObjSmmuV1SmmuV2 ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &SmmuV1V2NodeList, + &SmmuV1V2NodeCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get SMMUv1/SMMUv2 Node Info. Status = %r\n", + Status + )); + goto error_handler; + } + + // Add the SMMU v1/v2 node count + IortNodeCount += SmmuV1V2NodeCount; + + // Get the SMMUv3 node info + Status = GetEArmObjSmmuV3 ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &SmmuV3NodeList, + &SmmuV3NodeCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get SMMUv3 Node Info. Status = %r\n", + Status + )); + goto error_handler; + } + + // Add the SMMUv3 node count + IortNodeCount += SmmuV3NodeCount; + + // Get the PMCG node info + Status = GetEArmObjPmcg ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &PmcgNodeList, + &PmcgNodeCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to get PMCG Node Info. Status = %r\n", + Status + )); + goto error_handler; + } + + // Add the PMCG node count + IortNodeCount += PmcgNodeCount; + + // Allocate Node Indexer array + NodeIndexer = (IORT_NODE_INDEXER*)AllocateZeroPool ( + (sizeof (IORT_NODE_INDEXER) * + IortNodeCount) + ); + if (NodeIndexer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to allocate memory for Node Indexer" \ + " Status = %r\n", + Status + )); + goto error_handler; + } + + DEBUG ((DEBUG_INFO, "INFO: NodeIndexer = %p\n", NodeIndexer)); + Generator->IortNodeCount = IortNodeCount; + Generator->NodeIndexer = NodeIndexer; + + // Calculate the size of the IORT table + TableSize = sizeof (EFI_ACPI_6_0_IO_REMAPPING_TABLE); + + // ITS Group Nodes + if (ItsGroupNodeCount > 0) { + ItsGroupOffset = (UINT32)TableSize; + // Size of ITS Group node list. + NodeSize = GetSizeofItsGroupNodes ( + ItsGroupOffset, + ItsGroupNodeList, + ItsGroupNodeCount, + &NodeIndexer + ); + if (NodeSize > MAX_UINT32) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Invalid Size of Group Nodes. Status = %r\n", + Status + )); + goto error_handler; + } + TableSize += NodeSize; + + DEBUG (( + DEBUG_INFO, + " ItsGroupNodeCount = %d\n" \ + " ItsGroupOffset = %d\n", + ItsGroupNodeCount, + ItsGroupOffset + )); + } + + // Named Component Nodes + if (NamedComponentNodeCount > 0) { + NamedComponentOffset = (UINT32)TableSize; + // Size of Named Component node list. + NodeSize = GetSizeofNamedComponentNodes ( + NamedComponentOffset, + NamedComponentNodeList, + NamedComponentNodeCount, + &NodeIndexer + ); + if (NodeSize > MAX_UINT32) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Invalid Size of Named Component Nodes. Status = %r\n", + Status + )); + goto error_handler; + } + TableSize += NodeSize; + + DEBUG (( + DEBUG_INFO, + " NamedComponentNodeCount = %d\n" \ + " NamedComponentOffset = %d\n", + NamedComponentNodeCount, + NamedComponentOffset + )); + } + + // Root Complex Nodes + if (RootComplexNodeCount > 0) { + RootComplexOffset = (UINT32)TableSize; + // Size of Root Complex node list. + NodeSize = GetSizeofRootComplexNodes ( + RootComplexOffset, + RootComplexNodeList, + RootComplexNodeCount, + &NodeIndexer + ); + if (NodeSize > MAX_UINT32) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Invalid Size of Root Complex Nodes. Status = %r\n", + Status + )); + goto error_handler; + } + TableSize += NodeSize; + + DEBUG (( + DEBUG_INFO, + " RootComplexNodeCount = %d\n" \ + " RootComplexOffset = %d\n", + RootComplexNodeCount, + RootComplexOffset + )); + } + + // SMMUv1/SMMUv2 Nodes + if (SmmuV1V2NodeCount > 0) { + SmmuV1V2Offset = (UINT32)TableSize; + // Size of SMMUv1/SMMUv2 node list. + NodeSize = GetSizeofSmmuV1V2Nodes ( + SmmuV1V2Offset, + SmmuV1V2NodeList, + SmmuV1V2NodeCount, + &NodeIndexer + ); + if (NodeSize > MAX_UINT32) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Invalid Size of SMMUv1/v2 Nodes. Status = %r\n", + Status + )); + goto error_handler; + } + TableSize += NodeSize; + + DEBUG (( + DEBUG_INFO, + " SmmuV1V2NodeCount = %d\n" \ + " SmmuV1V2Offset = %d\n", + SmmuV1V2NodeCount, + SmmuV1V2Offset + )); + } + + // SMMUv3 Nodes + if (SmmuV3NodeCount > 0) { + SmmuV3Offset = (UINT32)TableSize; + // Size of SMMUv3 node list. + NodeSize = GetSizeofSmmuV3Nodes ( + SmmuV3Offset, + SmmuV3NodeList, + SmmuV3NodeCount, + &NodeIndexer + ); + if (NodeSize > MAX_UINT32) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Invalid Size of SMMUv3 Nodes. Status = %r\n", + Status + )); + goto error_handler; + } + TableSize += NodeSize; + + DEBUG (( + DEBUG_INFO, + " SmmuV3NodeCount = %d\n" \ + " SmmuV3Offset = %d\n", + SmmuV3NodeCount, + SmmuV3Offset + )); + } + + // PMCG Nodes + if (PmcgNodeCount > 0) { + PmcgOffset = (UINT32)TableSize; + // Size of PMCG node list. + NodeSize = GetSizeofPmcgNodes ( + PmcgOffset, + PmcgNodeList, + PmcgNodeCount, + &NodeIndexer + ); + if (NodeSize > MAX_UINT32) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Invalid Size of PMCG Nodes. Status = %r\n", + Status + )); + goto error_handler; + } + TableSize += NodeSize; + + DEBUG (( + DEBUG_INFO, + " PmcgNodeCount = %d\n" \ + " PmcgOffset = %d\n", + PmcgNodeCount, + PmcgOffset + )); + } + + DEBUG (( + DEBUG_INFO, + "INFO: IORT:\n" \ + " IortNodeCount = %d\n" \ + " TableSize = 0x%lx\n", + IortNodeCount, + TableSize + )); + + if (TableSize > MAX_UINT32) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: IORT Table Size 0x%lx > MAX_UINT32," \ + " Status = %r\n", + TableSize, + Status + )); + goto error_handler; + } + + // Allocate the Buffer for IORT table + *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize); + if (*Table == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to allocate memory for IORT Table, Size = %d," \ + " Status = %r\n", + TableSize, + Status + )); + goto error_handler; + } + + Iort = (EFI_ACPI_6_0_IO_REMAPPING_TABLE*)*Table; + + DEBUG (( + DEBUG_INFO, + "IORT: Iort = 0x%p TableSize = 0x%lx\n", + Iort, + TableSize + )); + + Status = AddAcpiHeader ( + CfgMgrProtocol, + This, + &Iort->Header, + AcpiTableInfo, + (UINT32)TableSize + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add ACPI header. Status = %r\n", + Status + )); + goto error_handler; + } + + // Update IORT table + Iort->NumNodes = IortNodeCount; + Iort->NodeOffset = sizeof (EFI_ACPI_6_0_IO_REMAPPING_TABLE); + Iort->Reserved = EFI_ACPI_RESERVED_DWORD; + + if (ItsGroupNodeCount > 0) { + Status = AddItsGroupNodes ( + This, + CfgMgrProtocol, + Iort, + ItsGroupOffset, + ItsGroupNodeList, + ItsGroupNodeCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add ITS Group Node. Status = %r\n", + Status + )); + goto error_handler; + } + } + + if (NamedComponentNodeCount > 0) { + Status = AddNamedComponentNodes ( + This, + CfgMgrProtocol, + Iort, + NamedComponentOffset, + NamedComponentNodeList, + NamedComponentNodeCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add Named Component Node. Status = %r\n", + Status + )); + goto error_handler; + } + } + + if (RootComplexNodeCount > 0) { + Status = AddRootComplexNodes ( + This, + CfgMgrProtocol, + Iort, + RootComplexOffset, + RootComplexNodeList, + RootComplexNodeCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add Root Complex Node. Status = %r\n", + Status + )); + goto error_handler; + } + } + + if (SmmuV1V2NodeCount > 0) { + Status = AddSmmuV1V2Nodes ( + This, + CfgMgrProtocol, + Iort, + SmmuV1V2Offset, + SmmuV1V2NodeList, + SmmuV1V2NodeCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add SMMU v1/v2 Node. Status = %r\n", + Status + )); + goto error_handler; + } + } + + if (SmmuV3NodeCount > 0) { + Status = AddSmmuV3Nodes ( + This, + CfgMgrProtocol, + Iort, + SmmuV3Offset, + SmmuV3NodeList, + SmmuV3NodeCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add SMMUv3 Node. Status = %r\n", + Status + )); + goto error_handler; + } + } + + if (PmcgNodeCount > 0) { + Status = AddPmcgNodes ( + This, + CfgMgrProtocol, + Iort, + PmcgOffset, + PmcgNodeList, + PmcgNodeCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: IORT: Failed to add SMMUv3 Node. Status = %r\n", + Status + )); + goto error_handler; + } + } + + return EFI_SUCCESS; + +error_handler: + if (Generator->NodeIndexer != NULL) { + FreePool (Generator->NodeIndexer); + Generator->NodeIndexer = NULL; + } + + if (*Table != NULL) { + FreePool (*Table); + *Table = NULL; + } + return Status; +} + +/** Free any resources allocated for constructing the IORT + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to the ACPI Table. + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +FreeIortTableResources ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + ACPI_IORT_GENERATOR * Generator; + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + Generator = (ACPI_IORT_GENERATOR*)This; + + // Free any memory allocated by the generator + if (Generator->NodeIndexer != NULL) { + FreePool (Generator->NodeIndexer); + Generator->NodeIndexer = NULL; + } + + if ((Table == NULL) || (*Table == NULL)) { + DEBUG ((DEBUG_ERROR, "ERROR: IORT: Invalid Table Pointer\n")); + ASSERT ((Table != NULL) && (*Table != NULL)); + return EFI_INVALID_PARAMETER; + } + + FreePool (*Table); + *Table = NULL; + return EFI_SUCCESS; +} + +/** The IORT Table Generator revision. +*/ +#define IORT_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the MADT Table Generator. +*/ +STATIC +ACPI_IORT_GENERATOR IortGenerator = { + // ACPI table generator header + { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdIort), + // Generator Description + L"ACPI.STD.IORT.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_2_IO_REMAPPING_TABLE_SIGNATURE, + // ACPI Table Revision supported by this Generator + EFI_ACPI_IO_REMAPPING_TABLE_REVISION, + // Minimum supported ACPI Table Revision + EFI_ACPI_IO_REMAPPING_TABLE_REVISION, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + IORT_GENERATOR_REVISION, + // Build Table function + BuildIortTable, + // Free Resource function + FreeIortTableResources, + // Extended build function not needed + NULL, + // Extended build function not implemented by the generator. + // Hence extended free resource function is not required. + NULL + }, + + // IORT Generator private data + + // Iort Node count + 0, + // Pointer to Iort node indexer + NULL +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiIortLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&IortGenerator.Header); + DEBUG ((DEBUG_INFO, "IORT: Register Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiIortLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&IortGenerator.Header); + DEBUG ((DEBUG_INFO, "Iort: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.h new file mode 100644 index 00000000..7b61f0b8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.h @@ -0,0 +1,44 @@ +/** @file + + Copyright (c) 2018, ARM Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Glossary: + - Cm or CM - Configuration Manager + - Obj or OBJ - Object + - Std or STD - Standard +**/ + +#ifndef IORT_GENERATOR_H_ +#define IORT_GENERATOR_H_ + +#pragma pack(1) + +/** A structure that describes the Node indexer + used for indexing the IORT nodes. +*/ +typedef struct IortNodeIndexer { + /// Index token for the Node + CM_OBJECT_TOKEN Token; + /// Pointer to the node + VOID * Object; + /// Node offset from the start of the IORT table + UINT32 Offset; +} IORT_NODE_INDEXER; + +typedef struct AcpiIortGenerator { + /// ACPI Table generator header + ACPI_TABLE_GENERATOR Header; + + // IORT Generator private data + + /// IORT node count + UINT32 IortNodeCount; + /// Pointer to the node indexer array + IORT_NODE_INDEXER * NodeIndexer; +} ACPI_IORT_GENERATOR; + +#pragma pack() + +#endif // IORT_GENERATOR_H_ diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/AcpiMadtLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/AcpiMadtLibArm.inf new file mode 100644 index 00000000..850c3f6b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/AcpiMadtLibArm.inf @@ -0,0 +1,36 @@ +## @file +# MADT Table Generator +# +# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = AcpiMadtLibArm + FILE_GUID = AF76C93B-41B5-454D-83CD-D2A80A1C1E38 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiMadtLibConstructor + DESTRUCTOR = AcpiMadtLibDestructor + +[Sources] + MadtGenerator.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + BaseLib + +[Pcd] + +[Protocols] + +[Guids] + diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/MadtGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/MadtGenerator.c new file mode 100644 index 00000000..fa7654a4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/MadtGenerator.c @@ -0,0 +1,830 @@ +/** @file + MADT Table Generator + + Copyright (c) 2017 - 2020, ARM Limited. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - ACPI 6.3 Specification - January 2019 + +**/ + +#include <Library/AcpiLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +/** ARM standard MADT Generator + +Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjGicCInfo + - EArmObjGicDInfo + - EArmObjGicMsiFrameInfo (OPTIONAL) + - EArmObjGicRedistributorInfo (OPTIONAL) + - EArmObjGicItsInfo (OPTIONAL) +*/ + +/** This macro expands to a function that retrieves the GIC + CPU interface Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGicCInfo, + CM_ARM_GICC_INFO + ); + +/** This macro expands to a function that retrieves the GIC + Distributor Information from the Configuration Manager. +*/ + +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGicDInfo, + CM_ARM_GICD_INFO + ); + +/** This macro expands to a function that retrieves the GIC + MSI Frame Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGicMsiFrameInfo, + CM_ARM_GIC_MSI_FRAME_INFO + ); + +/** This macro expands to a function that retrieves the GIC + Redistributor Information from the Configuration Manager. +*/ + +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGicRedistributorInfo, + CM_ARM_GIC_REDIST_INFO + ); + +/** This macro expands to a function that retrieves the GIC + Interrupt Translation Service Information from the + Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGicItsInfo, + CM_ARM_GIC_ITS_INFO + ); + +/** This function updates the GIC CPU Interface Information in the + EFI_ACPI_6_3_GIC_STRUCTURE structure. + + @param [in] Gicc Pointer to GIC CPU Interface structure. + @param [in] GicCInfo Pointer to the GIC CPU Interface Information. + @param [in] MadtRev MADT table revision. +**/ +STATIC +VOID +AddGICC ( + IN EFI_ACPI_6_3_GIC_STRUCTURE * CONST Gicc, + IN CONST CM_ARM_GICC_INFO * CONST GicCInfo, + IN CONST UINT8 MadtRev + ) +{ + ASSERT (Gicc != NULL); + ASSERT (GicCInfo != NULL); + + // UINT8 Type + Gicc->Type = EFI_ACPI_6_3_GIC; + // UINT8 Length + Gicc->Length = sizeof (EFI_ACPI_6_3_GIC_STRUCTURE); + // UINT16 Reserved + Gicc->Reserved = EFI_ACPI_RESERVED_WORD; + + // UINT32 CPUInterfaceNumber + Gicc->CPUInterfaceNumber = GicCInfo->CPUInterfaceNumber; + // UINT32 AcpiProcessorUid + Gicc->AcpiProcessorUid = GicCInfo->AcpiProcessorUid; + // UINT32 Flags + Gicc->Flags = GicCInfo->Flags; + // UINT32 ParkingProtocolVersion + Gicc->ParkingProtocolVersion = GicCInfo->ParkingProtocolVersion; + // UINT32 PerformanceInterruptGsiv + Gicc->PerformanceInterruptGsiv = GicCInfo->PerformanceInterruptGsiv; + // UINT64 ParkedAddress + Gicc->ParkedAddress = GicCInfo->ParkedAddress; + + // UINT64 PhysicalBaseAddress + Gicc->PhysicalBaseAddress = GicCInfo->PhysicalBaseAddress; + // UINT64 GICV + Gicc->GICV = GicCInfo->GICV; + // UINT64 GICH + Gicc->GICH = GicCInfo->GICH; + + // UINT32 VGICMaintenanceInterrupt + Gicc->VGICMaintenanceInterrupt = GicCInfo->VGICMaintenanceInterrupt; + // UINT64 GICRBaseAddress + Gicc->GICRBaseAddress = GicCInfo->GICRBaseAddress; + + // UINT64 MPIDR + Gicc->MPIDR = GicCInfo->MPIDR; + // UINT8 ProcessorPowerEfficiencyClass + Gicc->ProcessorPowerEfficiencyClass = + GicCInfo->ProcessorPowerEfficiencyClass; + // UINT8 Reserved2 + Gicc->Reserved2 = EFI_ACPI_RESERVED_BYTE; + + // UINT16 SpeOverflowInterrupt + if (MadtRev > EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION) { + Gicc->SpeOverflowInterrupt = GicCInfo->SpeOverflowInterrupt; + } else { + // Setting SpeOverflowInterrupt to 0 ensures backward compatibility with + // ACPI 6.2 by also clearing the Reserved2[1] and Reserved2[2] fields + // in EFI_ACPI_6_2_GIC_STRUCTURE. + Gicc->SpeOverflowInterrupt = 0; + } +} + +/** + Function to test if two GIC CPU Interface information structures have the + same ACPI Processor UID. + + @param [in] GicCInfo1 Pointer to the first GICC info structure. + @param [in] GicCInfo2 Pointer to the second GICC info structure. + @param [in] Index1 Index of GicCInfo1 in the shared list of GIC + CPU Interface Info structures. + @param [in] Index2 Index of GicCInfo2 in the shared list of GIC + CPU Interface Info structures. + + @retval TRUE GicCInfo1 and GicCInfo2 have the same UID. + @retval FALSE GicCInfo1 and GicCInfo2 have different UIDs. +**/ +BOOLEAN +EFIAPI +IsAcpiUidEqual ( + IN CONST VOID * GicCInfo1, + IN CONST VOID * GicCInfo2, + IN UINTN Index1, + IN UINTN Index2 + ) +{ + UINT32 Uid1; + UINT32 Uid2; + + ASSERT ((GicCInfo1 != NULL) && (GicCInfo2 != NULL)); + + Uid1 = ((CM_ARM_GICC_INFO*)GicCInfo1)->AcpiProcessorUid; + Uid2 = ((CM_ARM_GICC_INFO*)GicCInfo2)->AcpiProcessorUid; + + if (Uid1 == Uid2) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: GICC Info Structures %d and %d have the same ACPI " \ + "Processor UID: 0x%x.\n", + Index1, + Index2, + Uid1 + )); + return TRUE; + } + + return FALSE; +} + +/** Add the GIC CPU Interface Information to the MADT Table. + + This function also checks for duplicate ACPI Processor UIDs. + + @param [in] Gicc Pointer to GIC CPU Interface structure list. + @param [in] GicCInfo Pointer to the GIC CPU Information list. + @param [in] GicCCount Count of GIC CPU Interfaces. + @param [in] MadtRev MADT table revision. + + @retval EFI_SUCCESS GIC CPU Interface Information was added + successfully. + @retval EFI_INVALID_PARAMETER One or more invalid GIC CPU Info values were + provided and the generator failed to add the + information to the table. +**/ +STATIC +EFI_STATUS +AddGICCList ( + IN EFI_ACPI_6_3_GIC_STRUCTURE * Gicc, + IN CONST CM_ARM_GICC_INFO * GicCInfo, + IN UINT32 GicCCount, + IN CONST UINT8 MadtRev + ) +{ + BOOLEAN IsAcpiProcUidDuplicated; + + ASSERT (Gicc != NULL); + ASSERT (GicCInfo != NULL); + + IsAcpiProcUidDuplicated = FindDuplicateValue ( + GicCInfo, + GicCCount, + sizeof (CM_ARM_GICC_INFO), + IsAcpiUidEqual + ); + // Duplicate ACPI Processor UID was found so the GICC info provided + // is invalid + if (IsAcpiProcUidDuplicated) { + return EFI_INVALID_PARAMETER; + } + + while (GicCCount-- != 0) { + AddGICC (Gicc++, GicCInfo++, MadtRev); + } + + return EFI_SUCCESS; +} + +/** Update the GIC Distributor Information in the MADT Table. + + @param [in] Gicd Pointer to GIC Distributor structure. + @param [in] GicDInfo Pointer to the GIC Distributor Information. +**/ +STATIC +VOID +AddGICD ( + EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE * CONST Gicd, + CONST CM_ARM_GICD_INFO * CONST GicDInfo +) +{ + ASSERT (Gicd != NULL); + ASSERT (GicDInfo != NULL); + + // UINT8 Type + Gicd->Type = EFI_ACPI_6_3_GICD; + // UINT8 Length + Gicd->Length = sizeof (EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE); + // UINT16 Reserved + Gicd->Reserved1 = EFI_ACPI_RESERVED_WORD; + // UINT32 Identifier + // One, and only one, GIC distributor structure must be present + // in the MADT for an ARM based system + Gicd->GicId = 0; + // UINT64 PhysicalBaseAddress + Gicd->PhysicalBaseAddress = GicDInfo->PhysicalBaseAddress; + // UINT32 VectorBase + Gicd->SystemVectorBase = EFI_ACPI_RESERVED_DWORD; + // UINT8 GicVersion + Gicd->GicVersion = GicDInfo->GicVersion; + // UINT8 Reserved2[3] + Gicd->Reserved2[0] = EFI_ACPI_RESERVED_BYTE; + Gicd->Reserved2[1] = EFI_ACPI_RESERVED_BYTE; + Gicd->Reserved2[2] = EFI_ACPI_RESERVED_BYTE; +} + +/** Update the GIC MSI Frame Information. + + @param [in] GicMsiFrame Pointer to GIC MSI Frame structure. + @param [in] GicMsiFrameInfo Pointer to the GIC MSI Frame Information. +**/ +STATIC +VOID +AddGICMsiFrame ( + IN EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE * CONST GicMsiFrame, + IN CONST CM_ARM_GIC_MSI_FRAME_INFO * CONST GicMsiFrameInfo +) +{ + ASSERT (GicMsiFrame != NULL); + ASSERT (GicMsiFrameInfo != NULL); + + GicMsiFrame->Type = EFI_ACPI_6_3_GIC_MSI_FRAME; + GicMsiFrame->Length = sizeof (EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE); + GicMsiFrame->Reserved1 = EFI_ACPI_RESERVED_WORD; + GicMsiFrame->GicMsiFrameId = GicMsiFrameInfo->GicMsiFrameId; + GicMsiFrame->PhysicalBaseAddress = GicMsiFrameInfo->PhysicalBaseAddress; + + GicMsiFrame->Flags = GicMsiFrameInfo->Flags; + GicMsiFrame->SPICount = GicMsiFrameInfo->SPICount; + GicMsiFrame->SPIBase = GicMsiFrameInfo->SPIBase; +} + +/** Add the GIC MSI Frame Information to the MADT Table. + + @param [in] GicMsiFrame Pointer to GIC MSI Frame structure list. + @param [in] GicMsiFrameInfo Pointer to the GIC MSI Frame info list. + @param [in] GicMsiFrameCount Count of GIC MSI Frames. +**/ +STATIC +VOID +AddGICMsiFrameInfoList ( + IN EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE * GicMsiFrame, + IN CONST CM_ARM_GIC_MSI_FRAME_INFO * GicMsiFrameInfo, + IN UINT32 GicMsiFrameCount +) +{ + ASSERT (GicMsiFrame != NULL); + ASSERT (GicMsiFrameInfo != NULL); + + while (GicMsiFrameCount-- != 0) { + AddGICMsiFrame (GicMsiFrame++, GicMsiFrameInfo++); + } +} + +/** Update the GIC Redistributor Information. + + @param [in] Gicr Pointer to GIC Redistributor structure. + @param [in] GicRedistributorInfo Pointer to the GIC Redistributor Info. +**/ +STATIC +VOID +AddGICRedistributor ( + IN EFI_ACPI_6_3_GICR_STRUCTURE * CONST Gicr, + IN CONST CM_ARM_GIC_REDIST_INFO * CONST GicRedistributorInfo + ) +{ + ASSERT (Gicr != NULL); + ASSERT (GicRedistributorInfo != NULL); + + Gicr->Type = EFI_ACPI_6_3_GICR; + Gicr->Length = sizeof (EFI_ACPI_6_3_GICR_STRUCTURE); + Gicr->Reserved = EFI_ACPI_RESERVED_WORD; + Gicr->DiscoveryRangeBaseAddress = + GicRedistributorInfo->DiscoveryRangeBaseAddress; + Gicr->DiscoveryRangeLength = GicRedistributorInfo->DiscoveryRangeLength; +} + +/** Add the GIC Redistributor Information to the MADT Table. + + @param [in] Gicr Pointer to GIC Redistributor structure list. + @param [in] GicRInfo Pointer to the GIC Distributor info list. + @param [in] GicRCount Count of GIC Distributors. +**/ +STATIC +VOID +AddGICRedistributorList ( + IN EFI_ACPI_6_3_GICR_STRUCTURE * Gicr, + IN CONST CM_ARM_GIC_REDIST_INFO * GicRInfo, + IN UINT32 GicRCount +) +{ + ASSERT (Gicr != NULL); + ASSERT (GicRInfo != NULL); + + while (GicRCount-- != 0) { + AddGICRedistributor (Gicr++, GicRInfo++); + } +} + +/** Update the GIC Interrupt Translation Service Information + + @param [in] GicIts Pointer to GIC ITS structure. + @param [in] GicItsInfo Pointer to the GIC ITS Information. +**/ +STATIC +VOID +AddGICInterruptTranslationService ( + IN EFI_ACPI_6_3_GIC_ITS_STRUCTURE * CONST GicIts, + IN CONST CM_ARM_GIC_ITS_INFO * CONST GicItsInfo +) +{ + ASSERT (GicIts != NULL); + ASSERT (GicItsInfo != NULL); + + GicIts->Type = EFI_ACPI_6_3_GIC_ITS; + GicIts->Length = sizeof (EFI_ACPI_6_3_GIC_ITS_STRUCTURE); + GicIts->Reserved = EFI_ACPI_RESERVED_WORD; + GicIts->GicItsId = GicItsInfo->GicItsId; + GicIts->PhysicalBaseAddress = GicItsInfo->PhysicalBaseAddress; + GicIts->Reserved2 = EFI_ACPI_RESERVED_DWORD; +} + +/** Add the GIC Interrupt Translation Service Information + to the MADT Table. + + @param [in] GicIts Pointer to GIC ITS structure list. + @param [in] GicItsInfo Pointer to the GIC ITS list. + @param [in] GicItsCount Count of GIC ITS. +**/ +STATIC +VOID +AddGICItsList ( + IN EFI_ACPI_6_3_GIC_ITS_STRUCTURE * GicIts, + IN CONST CM_ARM_GIC_ITS_INFO * GicItsInfo, + IN UINT32 GicItsCount +) +{ + ASSERT (GicIts != NULL); + ASSERT (GicItsInfo != NULL); + + while (GicItsCount-- != 0) { + AddGICInterruptTranslationService (GicIts++, GicItsInfo++); + } +} + +/** Construct the MADT ACPI table. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResources function. + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [out] Table Pointer to the constructed ACPI Table. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildMadtTable ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + EFI_STATUS Status; + UINT32 TableSize; + UINT32 GicCCount; + UINT32 GicDCount; + UINT32 GicMSICount; + UINT32 GicRedistCount; + UINT32 GicItsCount; + CM_ARM_GICC_INFO * GicCInfo; + CM_ARM_GICD_INFO * GicDInfo; + CM_ARM_GIC_MSI_FRAME_INFO * GicMSIInfo; + CM_ARM_GIC_REDIST_INFO * GicRedistInfo; + CM_ARM_GIC_ITS_INFO * GicItsInfo; + UINT32 GicCOffset; + UINT32 GicDOffset; + UINT32 GicMSIOffset; + UINT32 GicRedistOffset; + UINT32 GicItsOffset; + + EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER * Madt; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) || + (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: Requested table revision = %d, is not supported." + "Supported table revision: Minimum = %d, Maximum = %d\n", + AcpiTableInfo->AcpiTableRevision, + This->MinAcpiTableRevision, + This->AcpiTableRevision + )); + return EFI_INVALID_PARAMETER; + } + + *Table = NULL; + + Status = GetEArmObjGicCInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &GicCInfo, + &GicCCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: Failed to get GICC Info. Status = %r\n", + Status + )); + goto error_handler; + } + + if (GicCCount == 0) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: GIC CPU Interface information not provided.\n" + )); + ASSERT (GicCCount != 0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + Status = GetEArmObjGicDInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &GicDInfo, + &GicDCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: Failed to get GICD Info. Status = %r\n", + Status + )); + goto error_handler; + } + + if (GicDCount == 0) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: GIC Distributor information not provided.\n" + )); + ASSERT (GicDCount != 0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + if (GicDCount > 1) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: One, and only one, GIC distributor must be present." + "GicDCount = %d\n", + GicDCount + )); + ASSERT (GicDCount <= 1); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + Status = GetEArmObjGicMsiFrameInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &GicMSIInfo, + &GicMSICount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: Failed to get GIC MSI Info. Status = %r\n", + Status + )); + goto error_handler; + } + + Status = GetEArmObjGicRedistributorInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &GicRedistInfo, + &GicRedistCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: Failed to get GIC Redistributor Info. Status = %r\n", + Status + )); + goto error_handler; + } + + Status = GetEArmObjGicItsInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &GicItsInfo, + &GicItsCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: Failed to get GIC ITS Info. Status = %r\n", + Status + )); + goto error_handler; + } + + TableSize = sizeof (EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER); + + GicCOffset = TableSize; + TableSize += (sizeof (EFI_ACPI_6_3_GIC_STRUCTURE) * GicCCount); + + GicDOffset = TableSize; + TableSize += (sizeof (EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE) * GicDCount); + + GicMSIOffset = TableSize; + TableSize += (sizeof (EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE) * GicMSICount); + + GicRedistOffset = TableSize; + TableSize += (sizeof (EFI_ACPI_6_3_GICR_STRUCTURE) * GicRedistCount); + + GicItsOffset = TableSize; + TableSize += (sizeof (EFI_ACPI_6_3_GIC_ITS_STRUCTURE) * GicItsCount); + + // Allocate the Buffer for MADT table + *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize); + if (*Table == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: Failed to allocate memory for MADT Table, Size = %d," \ + " Status = %r\n", + TableSize, + Status + )); + goto error_handler; + } + + Madt = (EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER*)*Table; + + DEBUG (( + DEBUG_INFO, + "MADT: Madt = 0x%p TableSize = 0x%x\n", + Madt, + TableSize + )); + + Status = AddAcpiHeader ( + CfgMgrProtocol, + This, + &Madt->Header, + AcpiTableInfo, + TableSize + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: Failed to add ACPI header. Status = %r\n", + Status + )); + goto error_handler; + } + + Status = AddGICCList ( + (EFI_ACPI_6_3_GIC_STRUCTURE*)((UINT8*)Madt + GicCOffset), + GicCInfo, + GicCCount, + Madt->Header.Revision + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MADT: Failed to add GICC structures. Status = %r\n", + Status + )); + goto error_handler; + } + + AddGICD ( + (EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE*)((UINT8*)Madt + GicDOffset), + GicDInfo + ); + + if (GicMSICount != 0) { + AddGICMsiFrameInfoList ( + (EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE*)((UINT8*)Madt + GicMSIOffset), + GicMSIInfo, + GicMSICount + ); + } + + if (GicRedistCount != 0) { + AddGICRedistributorList ( + (EFI_ACPI_6_3_GICR_STRUCTURE*)((UINT8*)Madt + GicRedistOffset), + GicRedistInfo, + GicRedistCount + ); + } + + if (GicItsCount != 0) { + AddGICItsList ( + (EFI_ACPI_6_3_GIC_ITS_STRUCTURE*)((UINT8*)Madt + GicItsOffset), + GicItsInfo, + GicItsCount + ); + } + + return EFI_SUCCESS; + +error_handler: + if (*Table != NULL) { + FreePool (*Table); + *Table = NULL; + } + return Status; +} + +/** Free any resources allocated for constructing the MADT + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to the ACPI Table. + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +FreeMadtTableResources ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((Table == NULL) || (*Table == NULL)) { + DEBUG ((DEBUG_ERROR, "ERROR: MADT: Invalid Table Pointer\n")); + ASSERT ((Table != NULL) && (*Table != NULL)); + return EFI_INVALID_PARAMETER; + } + + FreePool (*Table); + *Table = NULL; + return EFI_SUCCESS; +} + +/** The MADT Table Generator revision. +*/ +#define MADT_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the MADT Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR MadtGenerator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdMadt), + // Generator Description + L"ACPI.STD.MADT.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE, + // ACPI Table Revision supported by this Generator + EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION, + // Minimum supported ACPI Table Revision + EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + MADT_GENERATOR_REVISION, + // Build Table function + BuildMadtTable, + // Free Resource function + FreeMadtTableResources, + // Extended build function not needed + NULL, + // Extended build function not implemented by the generator. + // Hence extended free resource function is not required. + NULL +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiMadtLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&MadtGenerator); + DEBUG ((DEBUG_INFO, "MADT: Register Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiMadtLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&MadtGenerator); + DEBUG ((DEBUG_INFO, "MADT: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/AcpiMcfgLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/AcpiMcfgLibArm.inf new file mode 100644 index 00000000..60cf8f3d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/AcpiMcfgLibArm.inf @@ -0,0 +1,36 @@ +## @file +# MCFG Table Generator +# +# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = AcpiMcfgLibArm + FILE_GUID = 8C9BDCB2-72D4-4F30-A12D-1145C3807FF7 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiMcfgLibConstructor + DESTRUCTOR = AcpiMcfgLibDestructor + +[Sources] + McfgGenerator.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + BaseLib + +[Pcd] + +[Protocols] + +[Guids] + diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/McfgGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/McfgGenerator.c new file mode 100644 index 00000000..9412b423 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/McfgGenerator.c @@ -0,0 +1,364 @@ +/** @file + MCFG Table Generator + + Copyright (c) 2017 - 2019, ARM Limited. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - PCI Firmware Specification - Revision 3.2, January 26, 2015. + +**/ + +#include <IndustryStandard/MemoryMappedConfigurationSpaceAccessTable.h> +#include <Library/AcpiLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +/** ARM standard MCFG Generator + +Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjPciConfigSpaceInfo +*/ + +#pragma pack(1) + +/** This typedef is used to shorten the name of the MCFG Table + header structure. +*/ +typedef + EFI_ACPI_MEMORY_MAPPED_CONFIGURATION_BASE_ADDRESS_TABLE_HEADER + MCFG_TABLE; + +/** This typedef is used to shorten the name of the Enhanced + Configuration Space address structure. +*/ +typedef + EFI_ACPI_MEMORY_MAPPED_ENHANCED_CONFIGURATION_SPACE_BASE_ADDRESS_ALLOCATION_STRUCTURE + MCFG_CFG_SPACE_ADDR; + +#pragma pack() + +/** Retrieve the PCI Configuration Space Information. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjPciConfigSpaceInfo, + CM_ARM_PCI_CONFIG_SPACE_INFO + ); + +/** Add the PCI Enhanced Configuration Space Information to the MCFG Table. + + @param [in] Mcfg Pointer to MCFG Table. + @param [in] PciCfgSpaceOffset Offset for the PCI Configuration Space + Info structure in the MCFG Table. + @param [in] PciCfgSpaceInfoList Pointer to the PCI Configuration Space + Info List. + @param [in] PciCfgSpaceCount Count of PCI Configuration Space Info. +**/ +STATIC +VOID +AddPciConfigurationSpaceList ( + IN MCFG_TABLE * CONST Mcfg, + IN CONST UINT32 PciCfgSpaceOffset, + IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO * PciCfgSpaceInfoList, + IN UINT32 PciCfgSpaceCount +) +{ + MCFG_CFG_SPACE_ADDR * PciCfgSpace; + + ASSERT (Mcfg != NULL); + ASSERT (PciCfgSpaceInfoList != NULL); + + PciCfgSpace = (MCFG_CFG_SPACE_ADDR *)((UINT8*)Mcfg + PciCfgSpaceOffset); + + while (PciCfgSpaceCount-- != 0) { + // Add PCI Configuration Space entry + PciCfgSpace->BaseAddress = PciCfgSpaceInfoList->BaseAddress; + PciCfgSpace->PciSegmentGroupNumber = + PciCfgSpaceInfoList->PciSegmentGroupNumber; + PciCfgSpace->StartBusNumber = PciCfgSpaceInfoList->StartBusNumber; + PciCfgSpace->EndBusNumber = PciCfgSpaceInfoList->EndBusNumber; + PciCfgSpace->Reserved = EFI_ACPI_RESERVED_DWORD; + PciCfgSpace++; + PciCfgSpaceInfoList++; + } +} + +/** Construct the MCFG ACPI table. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResources function. + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [out] Table Pointer to the constructed ACPI Table. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildMcfgTable ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + EFI_STATUS Status; + UINT32 TableSize; + UINT32 ConfigurationSpaceCount; + CM_ARM_PCI_CONFIG_SPACE_INFO * PciConfigSpaceInfoList; + MCFG_TABLE * Mcfg; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) || + (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MCFG: Requested table revision = %d, is not supported." + "Supported table revision: Minimum = %d, Maximum = %d\n", + AcpiTableInfo->AcpiTableRevision, + This->MinAcpiTableRevision, + This->AcpiTableRevision + )); + return EFI_INVALID_PARAMETER; + } + + *Table = NULL; + Status = GetEArmObjPciConfigSpaceInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &PciConfigSpaceInfoList, + &ConfigurationSpaceCount + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, + "ERROR: MCFG: Failed to get PCI Configuration Space Information." \ + " Status = %r\n", + Status + )); + goto error_handler; + } + + if (ConfigurationSpaceCount == 0) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MCFG: Configuration Space Count = %d\n", + ConfigurationSpaceCount + )); + Status = EFI_INVALID_PARAMETER; + ASSERT (ConfigurationSpaceCount != 0); + goto error_handler; + } + + DEBUG (( + DEBUG_INFO, + "MCFG: Configuration Space Count = %d\n", + ConfigurationSpaceCount + )); + + // Calculate the MCFG Table Size + TableSize = sizeof (MCFG_TABLE) + + ((sizeof (MCFG_CFG_SPACE_ADDR) * ConfigurationSpaceCount)); + + *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize); + if (*Table == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: MCFG: Failed to allocate memory for MCFG Table, Size = %d," \ + " Status = %r\n", + TableSize, + Status + )); + goto error_handler; + } + + Mcfg = (MCFG_TABLE*)*Table; + DEBUG (( + DEBUG_INFO, + "MCFG: Mcfg = 0x%p TableSize = 0x%x\n", + Mcfg, + TableSize + )); + + Status = AddAcpiHeader ( + CfgMgrProtocol, + This, + &Mcfg->Header, + AcpiTableInfo, + TableSize + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: MCFG: Failed to add ACPI header. Status = %r\n", + Status + )); + goto error_handler; + } + + Mcfg->Reserved = EFI_ACPI_RESERVED_QWORD; + + AddPciConfigurationSpaceList ( + Mcfg, + sizeof (MCFG_TABLE), + PciConfigSpaceInfoList, + ConfigurationSpaceCount + ); + + return EFI_SUCCESS; + +error_handler: + if (*Table != NULL) { + FreePool (*Table); + *Table = NULL; + } + return Status; +} + +/** Free any resources allocated for constructing the MCFG + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to the ACPI Table. + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +FreeMcfgTableResources ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((Table == NULL) || (*Table == NULL)) { + DEBUG ((DEBUG_ERROR, "ERROR: MCFG: Invalid Table Pointer\n")); + ASSERT ((Table != NULL) && (*Table != NULL)); + return EFI_INVALID_PARAMETER; + } + + FreePool (*Table); + *Table = NULL; + return EFI_SUCCESS; +} + +/** This macro defines the MCFG Table Generator revision. +*/ +#define MCFG_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the MCFG Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR McfgGenerator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdMcfg), + // Generator Description + L"ACPI.STD.MCFG.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_2_PCI_EXPRESS_MEMORY_MAPPED_CONFIGURATION_SPACE_BASE_ADDRESS_DESCRIPTION_TABLE_SIGNATURE, + // ACPI Table Revision supported by this Generator + EFI_ACPI_MEMORY_MAPPED_CONFIGURATION_SPACE_ACCESS_TABLE_REVISION, + // Minimum supported ACPI Table Revision + EFI_ACPI_MEMORY_MAPPED_CONFIGURATION_SPACE_ACCESS_TABLE_REVISION, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + MCFG_GENERATOR_REVISION, + // Build Table function + BuildMcfgTable, + // Free Resource function + FreeMcfgTableResources, + // Extended build function not needed + NULL, + // Extended build function not implemented by the generator. + // Hence extended free resource function is not required. + NULL +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiMcfgLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&McfgGenerator); + DEBUG ((DEBUG_INFO, "MCFG: Register Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiMcfgLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&McfgGenerator); + DEBUG ((DEBUG_INFO, "MCFG: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf new file mode 100644 index 00000000..8c56efde --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf @@ -0,0 +1,30 @@ +## @file +# PPTT Table Generator +# +# Copyright (c) 2019, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = AcpiPpttLibArm + FILE_GUID = FA102D52-5A92-4F95-A097-1D53F9CF5959 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiPpttLibConstructor + DESTRUCTOR = AcpiPpttLibDestructor + +[Sources] + PpttGenerator.c + PpttGenerator.h + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c new file mode 100644 index 00000000..13c932dd --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c @@ -0,0 +1,1540 @@ +/** @file + PPTT Table Generator + + Copyright (c) 2019, ARM Limited. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - ACPI 6.3 Specification, January 2019 + + @par Glossary: + - Cm or CM - Configuration Manager + - Obj or OBJ - Object +**/ + +#include <Library/AcpiLib.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +#include "PpttGenerator.h" + +/** + ARM standard PPTT Generator + + Requirements: + The following Configuration Manager Object(s) are used by this Generator: + - EArmObjProcHierarchyInfo (REQUIRED) + - EArmObjCacheInfo + - EArmObjProcNodeIdInfo + - EArmObjCmRef + - EArmObjGicCInfo (REQUIRED) +*/ + +/** + This macro expands to a function that retrieves the Processor Hierarchy + information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjProcHierarchyInfo, + CM_ARM_PROC_HIERARCHY_INFO + ); + +/** + This macro expands to a function that retrieves the cache information + from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjCacheInfo, + CM_ARM_CACHE_INFO + ); + +/** + This macro expands to a function that retrieves the ID information for + Processor Hierarchy Nodes from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjProcNodeIdInfo, + CM_ARM_PROC_NODE_ID_INFO + ); + +/** + This macro expands to a function that retrieves the cross-CM-object- + reference information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjCmRef, + CM_ARM_OBJ_REF + ); + +/** + This macro expands to a function that retrieves the GIC CPU interface + information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGicCInfo, + CM_ARM_GICC_INFO + ); + +/** + Returns the size of the PPTT Processor Hierarchy Node (Type 0) given a + Processor Hierarchy Info CM object. + + @param [in] Node Pointer to Processor Hierarchy Info CM object which + represents the Processor Hierarchy Node to be generated. + + @retval Size of the Processor Hierarchy Node in bytes. +**/ +STATIC +UINT32 +GetProcHierarchyNodeSize ( + IN CONST CM_ARM_PROC_HIERARCHY_INFO * Node + ) +{ + ASSERT (Node != NULL); + + // <size of Processor Hierarchy Node> + <size of Private Resources array> + return sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR) + + (Node->NoOfPrivateResources * sizeof (UINT32)); +} + +/** + This macro expands to a function that retrieves the amount of memory required + to store the Processor Hierarchy Nodes (Type 0) and updates the Node Indexer. +*/ +GET_SIZE_OF_PPTT_STRUCTS ( + ProcHierarchyNodes, + GetProcHierarchyNodeSize (NodesToIndex), + CM_ARM_PROC_HIERARCHY_INFO + ); + +/** + This macro expands to a function that retrieves the amount of memory required + to store the Cache Type Structures (Type 1) and updates the Node Indexer. +*/ +GET_SIZE_OF_PPTT_STRUCTS ( + CacheTypeStructs, + sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE), + CM_ARM_CACHE_INFO + ); + +/** This macro expands to a function that retrieves the amount of memory + required to store the ID Structures (Type 2) and updates the Node Indexer. +*/ +GET_SIZE_OF_PPTT_STRUCTS ( + IdStructs, + sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_ID), + CM_ARM_PROC_NODE_ID_INFO + ); + +/** + Search the Node Indexer and return the indexed PPTT node with the given + Token. + + @param [in] NodeIndexer Pointer to the Node Indexer array. + @param [in] NodeCount Number of elements in Node Indexer. + @param [in] SearchToken Token used for Node Indexer lookup. + @param [out] IndexedNodeFound Pointer to the Node Indexer array element + with the given Token. + + @retval EFI_SUCCESS Success. + @retval EFI_NOT_FOUND No element with a matching token was + found in the Node Indexer array. +**/ +STATIC +EFI_STATUS +GetPpttNodeReferencedByToken ( + IN PPTT_NODE_INDEXER * NodeIndexer, + IN UINT32 NodeCount, + IN CONST CM_OBJECT_TOKEN SearchToken, + OUT PPTT_NODE_INDEXER ** IndexedNodeFound + ) +{ + EFI_STATUS Status; + + ASSERT (NodeIndexer != NULL); + + DEBUG (( + DEBUG_INFO, + "PPTT: Node Indexer: SearchToken = %p\n", + SearchToken + )); + + while (NodeCount-- != 0) { + DEBUG (( + DEBUG_INFO, + "PPTT: Node Indexer: NodeIndexer->Token = %p. Offset = %d\n", + NodeIndexer->Token, + NodeIndexer->Offset + )); + + if (NodeIndexer->Token == SearchToken) { + *IndexedNodeFound = NodeIndexer; + Status = EFI_SUCCESS; + DEBUG (( + DEBUG_INFO, + "PPTT: Node Indexer: Token = %p. Found, Status = %r\n", + SearchToken, + Status + )); + return Status; + } + NodeIndexer++; + } + + Status = EFI_NOT_FOUND; + DEBUG (( + DEBUG_ERROR, + "PPTT: Node Indexer: SearchToken = %p. Status = %r\n", + SearchToken, + Status + )); + + return Status; +} + +/** + Detect cycles in the processor and cache topology graph represented in + the PPTT table. + + @param [in] Generator Pointer to the PPTT Generator. + + @retval EFI_SUCCESS There are no cyclic references in the graph. + @retval EFI_INVALID_PARAMETER Processor or cache references form a cycle. +**/ +STATIC +EFI_STATUS +DetectCyclesInTopology ( + IN CONST ACPI_PPTT_GENERATOR * CONST Generator + ) +{ + EFI_STATUS Status; + PPTT_NODE_INDEXER * Iterator; + PPTT_NODE_INDEXER * CycleDetector; + UINT32 NodesRemaining; + + ASSERT (Generator != NULL); + + Iterator = Generator->NodeIndexer; + NodesRemaining = Generator->ProcTopologyStructCount; + + while (NodesRemaining != 0) { + DEBUG (( + DEBUG_INFO, + "INFO: PPTT: Cycle detection for element with index %d\n", + Generator->ProcTopologyStructCount - NodesRemaining + )); + + CycleDetector = Iterator; + + // Walk the topology tree + while (CycleDetector->TopologyParent != NULL) { + DEBUG (( + DEBUG_INFO, + "INFO: PPTT: %p -> %p\n", + CycleDetector->Token, + CycleDetector->TopologyParent->Token + )); + + // Check if we have already visited this node + if (CycleDetector->CycleDetectionStamp == NodesRemaining) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Cycle in processor and cache topology detected for " \ + "a chain of references originating from a node with: Token = %p " \ + "Status = %r\n", + Iterator->Token, + Status + )); + return Status; + } + + // Stamp the visited node + CycleDetector->CycleDetectionStamp = NodesRemaining; + CycleDetector = CycleDetector->TopologyParent; + } // Continue topology tree walk + + Iterator++; + NodesRemaining--; + } // Next Node Indexer + + return EFI_SUCCESS; +} + +/** + Update the array of private resources for a given Processor Hierarchy Node. + + @param [in] Generator Pointer to the PPTT Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] PrivResArray Pointer to the array of private resources. + @param [in] PrivResCount Number of private resources. + @param [in] PrivResArrayToken Reference Token for the CM_ARM_OBJ_REF + array describing node's private resources. + + @retval EFI_SUCCESS Array updated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND A private resource was not found. +**/ +STATIC +EFI_STATUS +AddPrivateResources ( + IN CONST ACPI_PPTT_GENERATOR * CONST Generator, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN UINT32 * PrivResArray, + IN UINT32 PrivResCount, + IN CONST CM_OBJECT_TOKEN PrivResArrayToken + ) +{ + EFI_STATUS Status; + CM_ARM_OBJ_REF * CmObjRefs; + UINT32 CmObjRefCount; + PPTT_NODE_INDEXER * PpttNodeFound; + + ASSERT ( + (Generator != NULL) && + (CfgMgrProtocol != NULL) && + (PrivResArray != NULL) && + (PrivResCount != 0) + ); + + // Validate input arguments + if (PrivResArrayToken == CM_NULL_TOKEN) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: The number of private resources is %d while " \ + "PrivResToken = CM_NULL_TOKEN. Status = %r\n", + PrivResCount, + Status + )); + return Status; + } + + CmObjRefCount = 0; + // Get the CM Object References + Status = GetEArmObjCmRef ( + CfgMgrProtocol, + PrivResArrayToken, + &CmObjRefs, + &CmObjRefCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to get CM Object References. " \ + "PrivResToken = %p. Status = %r\n", + PrivResArrayToken, + Status + )); + return Status; + } + + if (CmObjRefCount != PrivResCount) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: The number of CM Object References retrieved and the " \ + "number of private resources don't match. CmObjRefCount = %d. " \ + "PrivResourceCount = %d. PrivResToken = %p. Status = %r\n", + CmObjRefCount, + PrivResCount, + PrivResArrayToken, + Status + )); + return Status; + } + + while (PrivResCount-- != 0) { + if (CmObjRefs->ReferenceToken == CM_NULL_TOKEN) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: CM_NULL_TOKEN provided as reference token for a " \ + "private resource. Status = %r\n", + Status + )); + return Status; + } + + // The Node indexer has the Processor hierarchy nodes at the begining + // followed by the cache structs and Id structs. Therefore we can + // skip the Processor hierarchy nodes in the node indexer search. + Status = GetPpttNodeReferencedByToken ( + Generator->CacheStructIndexedList, + (Generator->ProcTopologyStructCount - + Generator->ProcHierarchyNodeCount), + CmObjRefs->ReferenceToken, + &PpttNodeFound + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to get a private resource with Token = %p from " \ + "Node Indexer. Status = %r\n", + CmObjRefs->ReferenceToken, + Status + )); + return Status; + } + + // Update the offset of the private resources in the Processor + // Hierarchy Node structure + *(PrivResArray++) = PpttNodeFound->Offset; + CmObjRefs++; + } + + return EFI_SUCCESS; +} + +/** + Function to test if two indexed Processor Hierarchy Info objects map to the + same GIC CPU Interface Info object. + + This is a callback function that can be invoked by FindDuplicateValue (). + + @param [in] Object1 Pointer to the first indexed Processor Hierarchy + Info object. + @param [in] Object2 Pointer to the second indexed Processor Hierarchy + Info object. + @param [in] Index1 Index of Object1 to be displayed for debugging + purposes. + @param [in] Index2 Index of Object2 to be displayed for debugging + purposes. + + @retval TRUE Object1 and Object2 have the same GicCToken. + @retval FALSE Object1 and Object2 have different GicCTokens. +**/ +BOOLEAN +EFIAPI +IsGicCTokenEqual ( + IN CONST VOID * Object1, + IN CONST VOID * Object2, + IN UINTN Index1, + IN UINTN Index2 + ) +{ + PPTT_NODE_INDEXER * IndexedObject1; + PPTT_NODE_INDEXER * IndexedObject2; + CM_ARM_PROC_HIERARCHY_INFO * ProcNode1; + CM_ARM_PROC_HIERARCHY_INFO * ProcNode2; + + ASSERT ( + (Object1 != NULL) && + (Object2 != NULL) + ); + + IndexedObject1 = (PPTT_NODE_INDEXER*)Object1; + IndexedObject2 = (PPTT_NODE_INDEXER*)Object2; + ProcNode1 = (CM_ARM_PROC_HIERARCHY_INFO*)IndexedObject1->Object; + ProcNode2 = (CM_ARM_PROC_HIERARCHY_INFO*)IndexedObject2->Object; + + if (IS_ACPI_PROC_ID_VALID (ProcNode1) && + IS_ACPI_PROC_ID_VALID (ProcNode2) && + (ProcNode1->GicCToken != CM_NULL_TOKEN) && + (ProcNode2->GicCToken != CM_NULL_TOKEN) && + (ProcNode1->GicCToken == ProcNode2->GicCToken)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Two Processor Hierarchy Info objects (%d and %d) map to " \ + "the same GICC Info object. ACPI Processor IDs are not unique. " \ + "GicCToken = %p.\n", + Index1, + IndexedObject1->Token, + Index2, + ProcNode1->GicCToken + )); + return TRUE; + } + + return FALSE; +} + +/** + Update the Processor Hierarchy Node (Type 0) information. + + This function populates the Processor Hierarchy Nodes with information from + the Configuration Manager and adds this information to the PPTT table. + + @param [in] Generator Pointer to the PPTT Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Pptt Pointer to PPTT table structure. + @param [in] NodesStartOffset Offset from the start of PPTT table to the + start of Processor Hierarchy Nodes. + + @retval EFI_SUCCESS Node updated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. +**/ +STATIC +EFI_STATUS +AddProcHierarchyNodes ( + IN CONST ACPI_PPTT_GENERATOR * CONST Generator, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt, + IN CONST UINT32 NodesStartOffset + ) +{ + EFI_STATUS Status; + EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR * ProcStruct; + UINT32 * PrivateResources; + BOOLEAN IsGicCTokenDuplicated; + + CM_ARM_GICC_INFO * GicCInfoList; + UINT32 GicCInfoCount; + UINT32 UniqueGicCRefCount; + + PPTT_NODE_INDEXER * PpttNodeFound; + CM_ARM_PROC_HIERARCHY_INFO * ProcInfoNode; + + PPTT_NODE_INDEXER * ProcNodeIterator; + UINT32 NodeCount; + UINT32 Length; + + ASSERT ( + (Generator != NULL) && + (CfgMgrProtocol != NULL) && + (Pptt != NULL) + ); + + ProcStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR*)((UINT8*)Pptt + + NodesStartOffset); + + ProcNodeIterator = Generator->ProcHierarchyNodeIndexedList; + NodeCount = Generator->ProcHierarchyNodeCount; + + // Check if every GICC Object is referenced by onlu one Proc Node + IsGicCTokenDuplicated = FindDuplicateValue ( + ProcNodeIterator, + NodeCount, + sizeof (PPTT_NODE_INDEXER), + IsGicCTokenEqual + ); + // Duplicate GIC CPU Interface Token was found so two PPTT Processor Hierarchy + // Nodes map to the same MADT GICC structure + if (IsGicCTokenDuplicated) { + return EFI_INVALID_PARAMETER; + } + + UniqueGicCRefCount = 0; + + while (NodeCount-- != 0) { + ProcInfoNode = (CM_ARM_PROC_HIERARCHY_INFO*)ProcNodeIterator->Object; + + // Check if the private resource count is within the size limit + // imposed on the Processor Hierarchy node by the specification. + // Note: The length field is 8 bit wide while the number of private + // resource field is 32 bit wide. + Length = GetProcHierarchyNodeSize (ProcInfoNode); + if (Length > MAX_UINT8) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Too many private resources. Count = %d. " \ + "Maximum supported Processor Node size exceeded. " \ + "Token = %p. Status = %r\n", + ProcInfoNode->NoOfPrivateResources, + ProcInfoNode->ParentToken, + Status + )); + return Status; + } + + // Populate the node header + ProcStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_PROCESSOR; + ProcStruct->Length = (UINT8)Length; + ProcStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE; + ProcStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE; + + // Populate the flags + ProcStruct->Flags.PhysicalPackage = ProcInfoNode->Flags & BIT0; + ProcStruct->Flags.AcpiProcessorIdValid = (ProcInfoNode->Flags & BIT1) >> 1; + ProcStruct->Flags.ProcessorIsAThread = (ProcInfoNode->Flags & BIT2) >> 2; + ProcStruct->Flags.NodeIsALeaf = (ProcInfoNode->Flags & BIT3) >> 3; + ProcStruct->Flags.IdenticalImplementation = + (ProcInfoNode->Flags & BIT4) >> 4; + ProcStruct->Flags.Reserved = 0; + + // Populate the parent reference + if (ProcInfoNode->ParentToken == CM_NULL_TOKEN) { + ProcStruct->Parent = 0; + } else { + Status = GetPpttNodeReferencedByToken ( + Generator->ProcHierarchyNodeIndexedList, + Generator->ProcHierarchyNodeCount, + ProcInfoNode->ParentToken, + &PpttNodeFound + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to get parent processor hierarchy node " \ + "reference. Token = %p, Status = %r\n", + ProcInfoNode->ParentToken, + ProcInfoNode->Token, + Status + )); + return Status; + } + + // Test if the reference is to a 'leaf' node + if (IS_PROC_NODE_LEAF ( + ((CM_ARM_PROC_HIERARCHY_INFO*)PpttNodeFound->Object))) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Reference to a leaf Processor Hierarchy Node. " \ + "ParentToken = %p. ChildToken = %p. Status = %r\n", + ProcInfoNode->ParentToken, + ProcInfoNode->Token, + Status + )); + return Status; + } + + // Update Proc Structure with the offset of the parent node + ProcStruct->Parent = PpttNodeFound->Offset; + + // Store the reference for the parent node in the Node Indexer + // so that this can be used later for cycle detection + ProcNodeIterator->TopologyParent = PpttNodeFound; + } + + // Populate ACPI Processor ID + if (!IS_ACPI_PROC_ID_VALID (ProcInfoNode)) { + // Default invalid ACPI Processor ID to 0 + ProcStruct->AcpiProcessorId = 0; + } else if (ProcInfoNode->GicCToken == CM_NULL_TOKEN) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: The 'ACPI Processor ID valid' flag is set but no GICC " \ + "structure token was provided. GicCToken = %p. RequestorToken = %p. " \ + "Status = %r\n", + ProcInfoNode->GicCToken, + ProcInfoNode->Token, + Status + )); + return Status; + } else { + Status = GetEArmObjGicCInfo ( + CfgMgrProtocol, + ProcInfoNode->GicCToken, + &GicCInfoList, + &GicCInfoCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to get GICC structure. ACPI Processor ID " \ + "can't be populated. GicCToken = %p. RequestorToken = %p. " \ + "Status = %r\n", + ProcInfoNode->GicCToken, + ProcInfoNode->Token, + Status + )); + return Status; + } + + if (GicCInfoCount != 1) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to find a unique GICC structure. " \ + "ACPI Processor ID can't be populated. " \ + "GICC Structure Count = %d. GicCToken = %p. RequestorToken = %p " \ + "Status = %r\n", + GicCInfoCount, + ProcInfoNode->GicCToken, + ProcInfoNode->Token, + Status + )); + return Status; + } + + // Update the ACPI Processor Id + ProcStruct->AcpiProcessorId = GicCInfoList->AcpiProcessorUid; + + // Increment the reference count for the number of + // Unique GICC objects that were retrieved. + UniqueGicCRefCount++; + } + + ProcStruct->NumberOfPrivateResources = ProcInfoNode->NoOfPrivateResources; + PrivateResources = (UINT32*)((UINT8*)ProcStruct + + sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR)); + + if (ProcStruct->NumberOfPrivateResources != 0) { + // Populate the private resources array + Status = AddPrivateResources ( + Generator, + CfgMgrProtocol, + PrivateResources, + ProcStruct->NumberOfPrivateResources, + ProcInfoNode->PrivateResourcesArrayToken + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to populate the private resources array. " \ + "Status = %r\n", + Status + )); + return Status; + } + } + + // Next Processor Hierarchy Node + ProcStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR*)((UINT8*)ProcStruct + + ProcStruct->Length); + ProcNodeIterator++; + } // Processor Hierarchy Node + + // Knowing the total number of GICC references made and that all GICC Token + // references are unique, we can test if no GICC instances have been left out. + Status = GetEArmObjGicCInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &GicCInfoList, + &GicCInfoCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to get GICC Info. Status = %r\n", + Status + )); + return Status; + } + + // MADT - PPTT cross validation + // This checks that one and only one GICC structure is referenced by a + // Processor Hierarchy Node in the PPTT. + // Since we have already checked that the GICC objects referenced by the + // Proc Nodes are unique, the UniqueGicCRefCount cannot be greater than + // the total number of GICC objects in the platform. + if (GicCInfoCount > UniqueGicCRefCount) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: %d GICC structure(s) exposed by MADT don't have " \ + "a corresponding Processor Hierarchy Node. Status = %r\n", + GicCInfoCount - UniqueGicCRefCount, + Status + )); + } + + return Status; +} + +/** + Update the Cache Type Structure (Type 1) information. + + This function populates the Cache Type Structures with information from + the Configuration Manager and adds this information to the PPTT table. + + @param [in] Generator Pointer to the PPTT Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Pptt Pointer to PPTT table structure. + @param [in] NodesStartOffset Offset from the start of PPTT table to the + start of Cache Type Structures. + + @retval EFI_SUCCESS Structures updated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND A required object was not found. +**/ +STATIC +EFI_STATUS +AddCacheTypeStructures ( + IN CONST ACPI_PPTT_GENERATOR * CONST Generator, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt, + IN CONST UINT32 NodesStartOffset + ) +{ + EFI_STATUS Status; + EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE * CacheStruct; + PPTT_NODE_INDEXER * PpttNodeFound; + CM_ARM_CACHE_INFO * CacheInfoNode; + PPTT_NODE_INDEXER * CacheNodeIterator; + UINT32 NodeCount; + + ASSERT ( + (Generator != NULL) && + (CfgMgrProtocol != NULL) && + (Pptt != NULL) + ); + + CacheStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE*)((UINT8*)Pptt + + NodesStartOffset); + + CacheNodeIterator = Generator->CacheStructIndexedList; + NodeCount = Generator->CacheStructCount; + + while (NodeCount-- != 0) { + CacheInfoNode = (CM_ARM_CACHE_INFO*)CacheNodeIterator->Object; + + // Populate the node header + CacheStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_CACHE; + CacheStruct->Length = sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE); + CacheStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE; + CacheStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE; + + // "On Arm-based systems, all cache properties must be provided in the + // table." (ACPI 6.3, Section 5.2.29.2) + CacheStruct->Flags.SizePropertyValid = 1; + CacheStruct->Flags.NumberOfSetsValid = 1; + CacheStruct->Flags.AssociativityValid = 1; + CacheStruct->Flags.AllocationTypeValid = 1; + CacheStruct->Flags.CacheTypeValid = 1; + CacheStruct->Flags.WritePolicyValid = 1; + CacheStruct->Flags.LineSizeValid = 1; + CacheStruct->Flags.Reserved = 0; + + // Populate the reference to the next level of cache + if (CacheInfoNode->NextLevelOfCacheToken == CM_NULL_TOKEN) { + CacheStruct->NextLevelOfCache = 0; + } else { + Status = GetPpttNodeReferencedByToken ( + Generator->CacheStructIndexedList, + Generator->CacheStructCount, + CacheInfoNode->NextLevelOfCacheToken, + &PpttNodeFound + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to get the reference to the Next Level of " \ + "Cache. NextLevelOfCacheToken = %p. RequestorToken = %p. " \ + "Status = %r\n", + CacheInfoNode->NextLevelOfCacheToken, + CacheInfoNode->Token, + Status + )); + return Status; + } + + // Update Cache Structure with the offset for the next level of cache + CacheStruct->NextLevelOfCache = PpttNodeFound->Offset; + + // Store the next level of cache information in the Node Indexer + // so that this can be used later for cycle detection + CacheNodeIterator->TopologyParent = PpttNodeFound; + } + + CacheStruct->Size = CacheInfoNode->Size; + + // Validate and populate the 'Number of sets' field + if (CacheInfoNode->NumberOfSets > PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: When ARMv8.3-CCIDX is implemented the maximum number " \ + "of sets can be %d. NumberOfSets = %d. Status = %r\n", + PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX, + CacheInfoNode->NumberOfSets, + Status + )); + return Status; + } + + if (CacheInfoNode->NumberOfSets > PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX) { + DEBUG (( + DEBUG_INFO, + "INFO: PPTT: When ARMv8.3-CCIDX is not implemented the maximum " \ + "number of sets can be %d. NumberOfSets = %d\n", + PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX, + CacheInfoNode->NumberOfSets + )); + } + + CacheStruct->NumberOfSets = CacheInfoNode->NumberOfSets; + + // Validate Associativity field based on maximum associativity + // supported by ACPI Cache type structure. + if (CacheInfoNode->Associativity > MAX_UINT8) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: The maximum associativity supported by ACPI " \ + "Cache type structure is %d. Associativity = %d, Status = %r\n", + MAX_UINT8, + CacheInfoNode->Associativity, + Status + )); + return Status; + } + + // Validate the Associativity field based on the architecture specification + // The architecture supports much larger associativity values than the + // current ACPI specification. + // These checks will be needed in the future when the ACPI specification + // is extended. Disabling this code for now. +#if 0 + if (CacheInfoNode->Associativity > PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: When ARMv8.3-CCIDX is implemented the maximum cache " \ + "associativity can be %d. Associativity = %d. Status = %r\n", + PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX, + CacheInfoNode->Associativity, + Status + )); + return Status; + } + + if (CacheInfoNode->Associativity > PPTT_ARM_CACHE_ASSOCIATIVITY_MAX) { + DEBUG (( + DEBUG_INFO, + "INFO: PPTT: When ARMv8.3-CCIDX is not implemented the maximum " \ + "cache associativity can be %d. Associativity = %d\n", + PPTT_ARM_CACHE_ASSOCIATIVITY_MAX, + CacheInfoNode->Associativity + )); + } +#endif + + // Note a typecast is needed as the maximum associativity + // supported by ACPI Cache type structure is MAX_UINT8. + CacheStruct->Associativity = (UINT8)CacheInfoNode->Associativity; + + // Populate cache attributes + CacheStruct->Attributes.AllocationType = + CacheInfoNode->Attributes & (BIT0 | BIT1); + CacheStruct->Attributes.CacheType = + (CacheInfoNode->Attributes & (BIT2 | BIT3)) >> 2; + CacheStruct->Attributes.WritePolicy = + (CacheInfoNode->Attributes & BIT4) >> 4; + CacheStruct->Attributes.Reserved = 0; + + // Validate and populate cache line size + if ((CacheInfoNode->LineSize < PPTT_ARM_CACHE_LINE_SIZE_MIN) || + (CacheInfoNode->LineSize > PPTT_ARM_CACHE_LINE_SIZE_MAX)) { + + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: The cache line size must be between %d and %d bytes " \ + "on ARM Platforms. LineSize = %d. Status = %r\n" , + PPTT_ARM_CACHE_LINE_SIZE_MIN, + PPTT_ARM_CACHE_LINE_SIZE_MAX, + CacheInfoNode->LineSize, + Status + )); + return Status; + } + + if ((CacheInfoNode->LineSize & (CacheInfoNode->LineSize - 1)) != 0) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: The cache line size is not a power of 2. " \ + "LineSize = %d. Status = %r\n" , + CacheInfoNode->LineSize, + Status + )); + return Status; + } + + CacheStruct->LineSize = CacheInfoNode->LineSize; + + // Next Cache Type Structure + CacheStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE*)((UINT8*)CacheStruct + + CacheStruct->Length); + CacheNodeIterator++; + } // Cache Type Structure + + return EFI_SUCCESS; +} + +/** + Update the ID Type Structure (Type 2) information. + + This function populates the ID Type Structures with information from + the Configuration Manager and and adds this information to the PPTT table. + + @param [in] Generator Pointer to the PPTT Generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Pptt Pointer to PPTT table structure. + @param [in] NodesStartOffset Offset from the start of PPTT table to the + start of ID Type Structures. + + @retval EFI_SUCCESS Structures updated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND A required object was not found. +**/ +STATIC +EFI_STATUS +AddIdTypeStructures ( + IN CONST ACPI_PPTT_GENERATOR * CONST Generator, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt, + IN CONST UINT32 NodesStartOffset + ) +{ + EFI_ACPI_6_3_PPTT_STRUCTURE_ID * IdStruct; + CM_ARM_PROC_NODE_ID_INFO * ProcIdInfoNode; + PPTT_NODE_INDEXER * IdStructIterator; + UINT32 NodeCount; + + + ASSERT ( + (Generator != NULL) && + (CfgMgrProtocol != NULL) && + (Pptt != NULL) + ); + + IdStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_ID*)((UINT8*)Pptt + NodesStartOffset); + + IdStructIterator = Generator->IdStructIndexedList; + NodeCount = Generator->IdStructCount; + while (NodeCount-- != 0) { + ProcIdInfoNode = (CM_ARM_PROC_NODE_ID_INFO*)IdStructIterator->Object; + + // Populate the node + IdStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_ID; + IdStruct->Length = sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_ID); + IdStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE; + IdStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE; + IdStruct->VendorId = ProcIdInfoNode->VendorId; + IdStruct->Level1Id = ProcIdInfoNode->Level1Id; + IdStruct->Level2Id = ProcIdInfoNode->Level2Id; + IdStruct->MajorRev = ProcIdInfoNode->MajorRev; + IdStruct->MinorRev = ProcIdInfoNode->MinorRev; + IdStruct->SpinRev = ProcIdInfoNode->SpinRev; + + // Next ID Type Structure + IdStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_ID*)((UINT8*)IdStruct + + IdStruct->Length); + IdStructIterator++; + } // ID Type Structure + + return EFI_SUCCESS; +} + +/** + Construct the PPTT ACPI table. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResources function. + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI table generator to be used. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [out] Table Pointer to the constructed ACPI Table. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for + the requested object. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildPpttTable ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + EFI_STATUS Status; + UINT32 TableSize; + UINT32 ProcTopologyStructCount; + UINT32 ProcHierarchyNodeCount; + UINT32 CacheStructCount; + UINT32 IdStructCount; + + UINT32 ProcHierarchyNodeOffset; + UINT32 CacheStructOffset; + UINT32 IdStructOffset; + + CM_ARM_PROC_HIERARCHY_INFO * ProcHierarchyNodeList; + CM_ARM_CACHE_INFO * CacheStructList; + CM_ARM_PROC_NODE_ID_INFO * IdStructList; + + ACPI_PPTT_GENERATOR * Generator; + + // Pointer to the Node Indexer array + PPTT_NODE_INDEXER * NodeIndexer; + + EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt; + + ASSERT ( + (This != NULL) && + (AcpiTableInfo != NULL) && + (CfgMgrProtocol != NULL) && + (Table != NULL) && + (AcpiTableInfo->TableGeneratorId == This->GeneratorID) && + (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature) + ); + + if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) || + (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Requested table revision = %d is not supported. " + "Supported table revisions: Minimum = %d. Maximum = %d\n", + AcpiTableInfo->AcpiTableRevision, + This->MinAcpiTableRevision, + This->AcpiTableRevision + )); + return EFI_INVALID_PARAMETER; + } + + Generator = (ACPI_PPTT_GENERATOR*)This; + *Table = NULL; + + // Get the processor hierarchy info and update the processor topology + // structure count with Processor Hierarchy Nodes (Type 0) + Status = GetEArmObjProcHierarchyInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &ProcHierarchyNodeList, + &ProcHierarchyNodeCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to get processor hierarchy info. Status = %r\n", + Status + )); + goto error_handler; + } + + ProcTopologyStructCount = ProcHierarchyNodeCount; + Generator->ProcHierarchyNodeCount = ProcHierarchyNodeCount; + + // Get the cache info and update the processor topology structure count with + // Cache Type Structures (Type 1) + Status = GetEArmObjCacheInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &CacheStructList, + &CacheStructCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to get cache info. Status = %r\n", + Status + )); + goto error_handler; + } + + ProcTopologyStructCount += CacheStructCount; + Generator->CacheStructCount = CacheStructCount; + + // Get the processor hierarchy node ID info and update the processor topology + // structure count with ID Structures (Type 2) + Status = GetEArmObjProcNodeIdInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &IdStructList, + &IdStructCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to get processor hierarchy node ID info. " \ + "Status = %r\n", + Status + )); + goto error_handler; + } + + ProcTopologyStructCount += IdStructCount; + Generator->IdStructCount = IdStructCount; + + // Allocate Node Indexer array + NodeIndexer = (PPTT_NODE_INDEXER*)AllocateZeroPool ( + sizeof (PPTT_NODE_INDEXER) * + ProcTopologyStructCount + ); + if (NodeIndexer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to allocate memory for Node Indexer. Status = %r\n ", + Status + )); + goto error_handler; + } + + DEBUG ((DEBUG_INFO, "INFO: NodeIndexer = %p\n", NodeIndexer)); + Generator->ProcTopologyStructCount = ProcTopologyStructCount; + Generator->NodeIndexer = NodeIndexer; + + // Calculate the size of the PPTT table + TableSize = sizeof (EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER); + + // Include the size of Processor Hierarchy Nodes and index them + if (Generator->ProcHierarchyNodeCount != 0) { + ProcHierarchyNodeOffset = TableSize; + Generator->ProcHierarchyNodeIndexedList = NodeIndexer; + TableSize += GetSizeofProcHierarchyNodes ( + ProcHierarchyNodeOffset, + ProcHierarchyNodeList, + Generator->ProcHierarchyNodeCount, + &NodeIndexer + ); + + DEBUG (( + DEBUG_INFO, + " ProcHierarchyNodeCount = %d\n" \ + " ProcHierarchyNodeOffset = 0x%x\n" \ + " ProcHierarchyNodeIndexedList = 0x%p\n", + Generator->ProcHierarchyNodeCount, + ProcHierarchyNodeOffset, + Generator->ProcHierarchyNodeIndexedList + )); + + } + + // Include the size of Cache Type Structures and index them + if (Generator->CacheStructCount != 0) { + CacheStructOffset = TableSize; + Generator->CacheStructIndexedList = NodeIndexer; + TableSize += GetSizeofCacheTypeStructs ( + CacheStructOffset, + CacheStructList, + Generator->CacheStructCount, + &NodeIndexer + ); + DEBUG (( + DEBUG_INFO, + " CacheStructCount = %d\n" \ + " CacheStructOffset = 0x%x\n" \ + " CacheStructIndexedList = 0x%p\n", + Generator->CacheStructCount, + CacheStructOffset, + Generator->CacheStructIndexedList + )); + } + + // Include the size of ID Type Structures and index them + if (Generator->IdStructCount != 0) { + IdStructOffset = TableSize; + Generator->IdStructIndexedList = NodeIndexer; + TableSize += GetSizeofIdStructs ( + IdStructOffset, + IdStructList, + Generator->IdStructCount, + &NodeIndexer + ); + DEBUG (( + DEBUG_INFO, + " IdStructCount = %d\n" \ + " IdStructOffset = 0x%x\n" \ + " IdStructIndexedList = 0x%p\n", + Generator->IdStructCount, + IdStructOffset, + Generator->IdStructIndexedList + )); + } + + DEBUG (( + DEBUG_INFO, + "INFO: PPTT:\n" \ + " ProcTopologyStructCount = %d\n" \ + " TableSize = %d\n", + ProcTopologyStructCount, + TableSize + )); + + // Allocate the Buffer for the PPTT table + *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize); + if (*Table == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to allocate memory for PPTT Table. " \ + "Size = %d. Status = %r\n", + TableSize, + Status + )); + goto error_handler; + } + + Pptt = (EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER*)*Table; + + DEBUG (( + DEBUG_INFO, + "PPTT: Pptt = 0x%p. TableSize = 0x%x\n", + Pptt, + TableSize + )); + + // Add ACPI header + Status = AddAcpiHeader ( + CfgMgrProtocol, + This, + &Pptt->Header, + AcpiTableInfo, + TableSize + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to add ACPI header. Status = %r\n", + Status + )); + goto error_handler; + } + + // Add Processor Hierarchy Nodes (Type 0) to the generated table + if (Generator->ProcHierarchyNodeCount != 0) { + Status = AddProcHierarchyNodes ( + Generator, + CfgMgrProtocol, + Pptt, + ProcHierarchyNodeOffset + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to add Processor Hierarchy Nodes. Status = %r\n", + Status + )); + goto error_handler; + } + } + + // Add Cache Type Structures (Type 1) to the generated table + if (Generator->CacheStructCount != 0) { + Status = AddCacheTypeStructures ( + Generator, + CfgMgrProtocol, + Pptt, + CacheStructOffset + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to add Cache Type Structures. Status = %r\n", + Status + )); + goto error_handler; + } + } + + // Add ID Type Structures (Type 2) to the generated table + if (Generator->IdStructCount != 0) { + Status = AddIdTypeStructures ( + Generator, + CfgMgrProtocol, + Pptt, + IdStructOffset + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to add ID Type Structures. Status = %r\n", + Status + )); + goto error_handler; + } + } + + // Validate CM object cross-references in PPTT + Status = DetectCyclesInTopology (Generator); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Invalid processor and cache topology. Status = %r\n", + Status + )); + goto error_handler; + } + + return Status; + +error_handler: + if (Generator->NodeIndexer != NULL) { + FreePool (Generator->NodeIndexer); + Generator->NodeIndexer = NULL; + } + + if (*Table != NULL) { + FreePool (*Table); + *Table = NULL; + } + + return Status; +} + +/** + Free any resources allocated for constructing the PPTT + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to the ACPI Table. + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +FreePpttTableResources ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + ACPI_PPTT_GENERATOR * Generator; + + ASSERT ( + (This != NULL) && + (AcpiTableInfo != NULL) && + (CfgMgrProtocol != NULL) && + (AcpiTableInfo->TableGeneratorId == This->GeneratorID) && + (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature) + ); + + Generator = (ACPI_PPTT_GENERATOR*)This; + + // Free any memory allocated by the generator + if (Generator->NodeIndexer != NULL) { + FreePool (Generator->NodeIndexer); + Generator->NodeIndexer = NULL; + } + + if ((Table == NULL) || (*Table == NULL)) { + DEBUG ((DEBUG_ERROR, "ERROR: PPTT: Invalid Table Pointer\n")); + ASSERT ( + (Table != NULL) && + (*Table != NULL) + ); + return EFI_INVALID_PARAMETER; + } + + FreePool (*Table); + *Table = NULL; + return EFI_SUCCESS; +} + +/** The PPTT Table Generator revision. +*/ +#define PPTT_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the PPTT Table Generator. +*/ +STATIC +ACPI_PPTT_GENERATOR PpttGenerator = { + // ACPI table generator header + { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdPptt), + // Generator Description + L"ACPI.STD.PPTT.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_STRUCTURE_SIGNATURE, + // ACPI Table Revision supported by this Generator + EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_REVISION, + // Minimum supported ACPI Table Revision + EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_REVISION, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + PPTT_GENERATOR_REVISION, + // Build Table function + BuildPpttTable, + // Free Resource function + FreePpttTableResources, + // Extended build function not needed + NULL, + // Extended build function not implemented by the generator. + // Hence extended free resource function is not required. + NULL + }, + + // PPTT Generator private data + + // Processor topology node count + 0, + // Count of Processor Hierarchy Nodes + 0, + // Count of Cache Structures + 0, + // Count of Id Structures + 0, + // Pointer to PPTT Node Indexer + NULL +}; + +/** + Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiPpttLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&PpttGenerator.Header); + DEBUG ((DEBUG_INFO, "PPTT: Register Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiPpttLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&PpttGenerator.Header); + DEBUG ((DEBUG_INFO, "PPTT: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.h new file mode 100644 index 00000000..1e9cf543 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.h @@ -0,0 +1,189 @@ +/** @file + Header file for the dynamic PPTT generator + + Copyright (c) 2019, ARM Limited. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - ACPI 6.3 Specification, January 2019 + - ARM Architecture Reference Manual ARMv8 (D.a) + + @par Glossary: + - Cm or CM - Configuration Manager + - Obj or OBJ - Object +**/ + +#ifndef PPTT_GENERATOR_H_ +#define PPTT_GENERATOR_H_ + +#pragma pack(1) + +/// Cache parameters allowed by the architecture with +/// ARMv8.3-CCIDX (Cache extended number of sets) +/// Derived from CCSIDR_EL1 when ID_AA64MMFR2_EL1.CCIDX==0001 +#define PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX (1 << 24) +#define PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX (1 << 21) + +/// Cache parameters allowed by the architecture without +/// ARMv8.3-CCIDX (Cache extended number of sets) +/// Derived from CCSIDR_EL1 when ID_AA64MMFR2_EL1.CCIDX==0000 +#define PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX (1 << 15) +#define PPTT_ARM_CACHE_ASSOCIATIVITY_MAX (1 << 10) + +/// Common cache parameters +/// Derived from CCSIDR_EL1 +/// The LineSize is represented by bits 2:0 +/// (Log2(Number of bytes in cache line)) - 4 is used to represent +/// the LineSize bits. +#define PPTT_ARM_CACHE_LINE_SIZE_MAX (1 << 11) +#define PPTT_ARM_CACHE_LINE_SIZE_MIN (1 << 4) + +/// Test if the given Processor Hierarchy Info object has the 'Node is a Leaf' +/// flag set +#define IS_PROC_NODE_LEAF(Node) ((Node->Flags & BIT3) != 0) + +/// Test if the given Processor Hierarchy Info object has the 'ACPI Processor +/// ID valid' flag set +#define IS_ACPI_PROC_ID_VALID(Node) ((Node->Flags & BIT1) != 0) + +/** + The GET_SIZE_OF_PPTT_STRUCTS macro expands to a function that is used to + calculate the total memory requirement for the PPTT structures represented + by the given list of Configuration Manager Objects of the same type. This + function also indexes the input CM objects so that various other CM objects + (possibly of different type) can reference them. + + The size of memory needed for the specified type of PPTT structures is based + on the number and type of CM objects provided. The macro assumes that the + ACPI object PpttObjName has fixed size. + + The macro expands to a function which has the following prototype: + + STATIC + UINT32 + EFIAPI + GetSizeof<PpttObjName> ( + IN CONST UINT32 StartOffset, + IN CONST CmObjectType * Nodes, + IN UINT32 NodeCount, + IN OUT PPTT_NODE_INDEXER ** CONST NodeIndexer + ) + + Generated function parameters: + @param [in] StartOffset Offset from the start of PPTT to where + the PPTT structures will be placed. + @param [in] NodesToIndex Pointer to the list of CM objects to be + indexed and size-estimated. + @param [out] NodeCount Number of CM objects in NodesToIndex. + @param [in, out] NodeIndexer Pointer to the list of Node Indexer + elements to populate. + @retval Size Total memory requirement for the PPTT + structures described in NodesToIndex. + + Macro Parameters: + @param [in] PpttObjName Name for the type of PPTT structures which + size is estimated. + @param [in] PpttObjSize Expression to use to calculate the size of + of a single instance of the PPTT structure + which corresponds to the CM object being + indexed. + @param [in] CmObjectType Data type of the CM nodes in NodesToIndex. +**/ +#define GET_SIZE_OF_PPTT_STRUCTS( \ + PpttObjName, \ + PpttObjSize, \ + CmObjectType \ +) \ +STATIC \ +UINT32 \ +GetSizeof##PpttObjName ( \ + IN CONST UINT32 StartOffset, \ + IN CONST CmObjectType * NodesToIndex, \ + IN UINT32 NodeCount, \ + IN OUT PPTT_NODE_INDEXER ** CONST NodeIndexer \ + ) \ +{ \ + UINT32 Size; \ + \ + ASSERT ( \ + (NodesToIndex != NULL) && \ + (NodeIndexer != NULL) \ + ); \ + \ + Size = 0; \ + while (NodeCount-- != 0) { \ + (*NodeIndexer)->Token = NodesToIndex->Token; \ + (*NodeIndexer)->Object = (VOID*)NodesToIndex; \ + (*NodeIndexer)->Offset = Size + StartOffset; \ + (*NodeIndexer)->CycleDetectionStamp = 0; \ + (*NodeIndexer)->TopologyParent = NULL; \ + DEBUG (( \ + DEBUG_INFO, \ + "PPTT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n", \ + *NodeIndexer, \ + (*NodeIndexer)->Token, \ + (*NodeIndexer)->Object, \ + (*NodeIndexer)->Offset \ + )); \ + \ + Size += PpttObjSize; \ + (*NodeIndexer)++; \ + NodesToIndex++; \ + } \ + return Size; \ +} + +/** + A structure for indexing CM objects (nodes) used in PPTT generation. + + PPTT_NODE_INDEXER is a wrapper around CM objects which augments these objects + with additional information that enables generating PPTT structures with + correct cross-references. + + PPTT_NODE_INDEXER keeps track of each structure's offset from the base + address of the generated table. It also caches certain information and makes + PPTT cyclic reference detection possible. +*/ +typedef struct PpttNodeIndexer { + /// Unique identifier for the node + CM_OBJECT_TOKEN Token; + /// Pointer to the CM object being indexed + VOID * Object; + /// Offset from the start of the PPTT table to the PPTT structure which is + /// represented by Object + UINT32 Offset; + /// Field used to mark nodes as 'visited' when detecting cycles in processor + /// and cache topology + UINT32 CycleDetectionStamp; + /// Reference to a Node Indexer element which is the parent of this Node + /// Indexer element in the processor and cache topology + /// e.g For a hardware thread the TopologyParent would point to a CPU node + /// For a L1 cache the TopologyParent would point to a L2 cache + struct PpttNodeIndexer * TopologyParent; +} PPTT_NODE_INDEXER; + +typedef struct AcpiPpttGenerator { + /// ACPI Table generator header + ACPI_TABLE_GENERATOR Header; + /// PPTT structure count + UINT32 ProcTopologyStructCount; + /// Count of Processor Hierarchy Nodes + UINT32 ProcHierarchyNodeCount; + /// Count of Cache Structures + UINT32 CacheStructCount; + /// Count of Id Structures + UINT32 IdStructCount; + /// List of indexed CM objects for PPTT generation + PPTT_NODE_INDEXER * NodeIndexer; + /// Pointer to the start of Processor Hierarchy nodes in + /// the Node Indexer array + PPTT_NODE_INDEXER * ProcHierarchyNodeIndexedList; + /// Pointer to the start of Cache Structures in the Node Indexer array + PPTT_NODE_INDEXER * CacheStructIndexedList; + /// Pointer to the start of Id Structures in the Node Indexer array + PPTT_NODE_INDEXER * IdStructIndexedList; +} ACPI_PPTT_GENERATOR; + +#pragma pack() + +#endif // PPTT_GENERATOR_H_ diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf new file mode 100644 index 00000000..53d2b1ac --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf @@ -0,0 +1,36 @@ +## @file +# Raw Table Generator +# +# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = AcpiRawLibArm + FILE_GUID = 20F31568-D687-49BA-B326-CCD9D38EDE16 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiRawLibConstructor + DESTRUCTOR = AcpiRawLibDestructor + +[Sources] + RawGenerator.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + BaseLib + +[Pcd] + +[Protocols] + +[Guids] + diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/RawGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/RawGenerator.c new file mode 100644 index 00000000..39c684ef --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/RawGenerator.c @@ -0,0 +1,144 @@ +/** @file + MCFG Table Generator + + Copyright (c) 2017 - 2019, ARM Limited. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include <Library/AcpiLib.h> +#include <Library/DebugLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +/** Construct the ACPI table using the ACPI table data provided. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResources function. + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [out] Table Pointer to the constructed ACPI Table. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildRawTable ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableData != NULL); + + if (AcpiTableInfo->AcpiTableData == NULL) { + *Table = NULL; + return EFI_INVALID_PARAMETER; + } + + *Table = AcpiTableInfo->AcpiTableData; + + return EFI_SUCCESS; +} + +/** This macro defines the Raw Generator revision. +*/ +#define RAW_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the Raw Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR RawGenerator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdRaw), + // Generator Description + L"ACPI.STD.RAW.GENERATOR", + // ACPI Table Signature - Unused + 0, + // ACPI Table Revision - Unused + 0, + // Minimum ACPI Table Revision - Unused + 0, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + RAW_GENERATOR_REVISION, + // Build Table function + BuildRawTable, + // No additional resources are allocated by the generator. + // Hence the Free Resource function is not required. + NULL, + // Extended build function not needed + NULL, + // Extended build function not implemented by the generator. + // Hence extended free resource function is not required. + NULL +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiRawLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&RawGenerator); + DEBUG ((DEBUG_INFO, "RAW: Register Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiRawLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&RawGenerator); + DEBUG ((DEBUG_INFO, "RAW: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf new file mode 100644 index 00000000..bc3b0d3e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf @@ -0,0 +1,37 @@ +## @file +# SPCR Table Generator +# +# Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = AcpiSpcrLibArm + FILE_GUID = 55088136-7B78-4974-B1EE-F630150D0DE7 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiSpcrLibConstructor + DESTRUCTOR = AcpiSpcrLibDestructor + +[Sources] + SpcrGenerator.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + BaseLib + SsdtSerialPortFixupLib + +[Pcd] + +[Protocols] + +[Guids] + diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c new file mode 100644 index 00000000..6c07bfdc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/SpcrGenerator.c @@ -0,0 +1,477 @@ +/** @file + SPCR Table Generator + + Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - Microsoft Serial Port Console Redirection Table + Specification - Version 1.03 - August 10, 2015. + +**/ + +#include <IndustryStandard/DebugPort2Table.h> +#include <IndustryStandard/SerialPortConsoleRedirectionTable.h> +#include <Library/AcpiLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/SsdtSerialPortFixupLib.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +/** ARM standard SPCR Table Generator + + Constructs the SPCR table for PL011 or SBSA UART peripherals. + +Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjSerialConsolePortInfo + +NOTE: This implementation ignores the possibility that the Serial settings may + be modified from the UEFI Shell. A more complex handler would be needed + to (e.g.) recover serial port settings from the UART, or non-volatile + storage. +*/ + +#pragma pack(1) + +/** A string representing the name of the SPCR port. +*/ +#define NAME_STR_SPCR_PORT "COM1" + +/** An UID representing the SPCR port. +*/ +#define UID_SPCR_PORT 1 + +/** This macro defines the no flow control option. +*/ +#define SPCR_FLOW_CONTROL_NONE 0 + +/**A template for generating the SPCR Table. + + Note: fields marked "{Template}" will be updated dynamically. +*/ +STATIC +EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE AcpiSpcr = { + ACPI_HEADER ( + EFI_ACPI_6_2_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE, + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE, + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_REVISION + ), + 0, // {Template}: Serial Port Subtype + { + EFI_ACPI_RESERVED_BYTE, + EFI_ACPI_RESERVED_BYTE, + EFI_ACPI_RESERVED_BYTE + }, + ARM_GAS32 (0), // {Template}: Serial Port Base Address + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERRUPT_TYPE_GIC, + 0, // Not used on ARM + 0, // {Template}: Serial Port Interrupt + 0, // {Template}: Serial Port Baudrate + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_PARITY_NO_PARITY, + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_STOP_BITS_1, + SPCR_FLOW_CONTROL_NONE, + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_TERMINAL_TYPE_ANSI, + EFI_ACPI_RESERVED_BYTE, + 0xFFFF, + 0xFFFF, + 0x00, + 0x00, + 0x00, + 0x00000000, + 0x00, + EFI_ACPI_RESERVED_DWORD +}; + +#pragma pack() + +/** This macro expands to a function that retrieves the Serial + Port Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjSerialConsolePortInfo, + CM_ARM_SERIAL_PORT_INFO + ) + +/** Free any resources allocated for constructing the tables. + + @param [in] This Pointer to the ACPI table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to an array of pointers + to ACPI Table(s). + @param [in] TableCount Number of ACPI table(s). + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +EFIAPI +FreeSpcrTableEx ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER *** CONST Table, + IN CONST UINTN TableCount + ) +{ + EFI_STATUS Status; + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((Table == NULL) || + (*Table == NULL) || + (TableCount != 2)) { + DEBUG ((DEBUG_ERROR, "ERROR: SPCR: Invalid Table Pointer\n")); + return EFI_INVALID_PARAMETER; + } + + TableList = *Table; + + if ((TableList[1] == NULL) || + (TableList[1]->Signature != + EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE)) { + DEBUG ((DEBUG_ERROR, "ERROR: SPCR: Invalid SSDT table pointer.\n")); + return EFI_INVALID_PARAMETER; + } + + // Only need to free the SSDT table at index 1. The SPCR table is static. + Status = FreeSsdtSerialPortTable (TableList[1]); + ASSERT_EFI_ERROR (Status); + + // Free the table list. + FreePool (*Table); + + return Status; +} + +/** Construct the SPCR ACPI table and its associated SSDT table. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResourcesEx function. + + @param [in] This Pointer to the ACPI table generator. + @param [in] AcpiTableInfo Pointer to the ACPI table information. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol interface. + @param [out] Table Pointer to a list of generated ACPI table(s). + @param [out] TableCount Number of generated ACPI table(s). + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for + the requested object. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND Could not find information. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. + @retval EFI_UNSUPPORTED Unsupported configuration. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildSpcrTableEx ( + IN CONST ACPI_TABLE_GENERATOR * This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER *** Table, + OUT UINTN * CONST TableCount + ) +{ + EFI_STATUS Status; + CM_ARM_SERIAL_PORT_INFO * SerialPortInfo; + UINT32 SerialPortCount; + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (TableCount != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) || + (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SPCR: Requested table revision = %d, is not supported." + "Supported table revision: Minimum = %d, Maximum = %d\n", + AcpiTableInfo->AcpiTableRevision, + This->MinAcpiTableRevision, + This->AcpiTableRevision + )); + return EFI_INVALID_PARAMETER; + } + + *Table = NULL; + + Status = GetEArmObjSerialConsolePortInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &SerialPortInfo, + &SerialPortCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SPCR: Failed to get serial port information. Status = %r\n", + Status + )); + return Status; + } + + if (SerialPortCount == 0) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SPCR: Serial port information not found. Status = %r\n", + EFI_NOT_FOUND + )); + return EFI_NOT_FOUND; + } + + // Validate the SerialPort info. Only one SPCR port can be described. + // If platform provides description for multiple SPCR ports, use the + // first SPCR port information. + Status = ValidateSerialPortInfo (SerialPortInfo, 1); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SPCR: Invalid serial port information. Status = %r\n", + Status + )); + return Status; + } + + // Allocate a table to store pointers to the SPCR and SSDT tables. + TableList = (EFI_ACPI_DESCRIPTION_HEADER**) + AllocateZeroPool (sizeof (EFI_ACPI_DESCRIPTION_HEADER*) * 2); + if (TableList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: SPCR: Failed to allocate memory for Table List," \ + " Status = %r\n", + Status + )); + return Status; + } + + // Build SPCR table. + Status = AddAcpiHeader ( + CfgMgrProtocol, + This, + (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiSpcr, + AcpiTableInfo, + sizeof (EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE) + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SPCR: Failed to add ACPI header. Status = %r\n", + Status + )); + goto error_handler; + } + + // The SPCR InterfaceType uses the same encoding as that of the + // DBG2 table Port Subtype field. However InterfaceType is 8-bit + // while the Port Subtype field in the DBG2 table is 16-bit. + if ((SerialPortInfo->PortSubtype & 0xFF00) != 0) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: SPCR: Invalid Port subtype (must be < 256). Status = %r\n", + Status + )); + goto error_handler; + } + + // Update the serial port subtype + AcpiSpcr.InterfaceType = (UINT8)SerialPortInfo->PortSubtype; + + // Update the base address + AcpiSpcr.BaseAddress.Address = SerialPortInfo->BaseAddress; + + // Set the access size + if (SerialPortInfo->AccessSize >= EFI_ACPI_6_3_QWORD) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "ERROR: SPCR: Access size must be <= 3 (DWORD). Status = %r\n", + Status + )); + goto error_handler; + } else if (SerialPortInfo->AccessSize == EFI_ACPI_6_3_UNDEFINED) { + // 0 Undefined (legacy reasons) + // Default to DWORD access size as the access + // size field was introduced at a later date + // and some ConfigurationManager implementations + // may not be providing this field data + AcpiSpcr.BaseAddress.AccessSize = EFI_ACPI_6_3_DWORD; + } else { + AcpiSpcr.BaseAddress.AccessSize = SerialPortInfo->AccessSize; + } + + // Update the UART interrupt + AcpiSpcr.GlobalSystemInterrupt = SerialPortInfo->Interrupt; + + switch (SerialPortInfo->BaudRate) { + case 9600: + AcpiSpcr.BaudRate = + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_9600; + break; + case 19200: + AcpiSpcr.BaudRate = + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_19200; + break; + case 57600: + AcpiSpcr.BaudRate = + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_57600; + break; + case 115200: + AcpiSpcr.BaudRate = + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_115200; + break; + default: + Status = EFI_UNSUPPORTED; + DEBUG (( + DEBUG_ERROR, + "ERROR: SPCR: Invalid Baud Rate %ld, Status = %r\n", + SerialPortInfo->BaudRate, + Status + )); + goto error_handler; + } // switch + + TableList[0] = (EFI_ACPI_DESCRIPTION_HEADER*)&AcpiSpcr; + + // Build a SSDT table describing the serial port. + Status = BuildSsdtSerialPortTable ( + AcpiTableInfo, + SerialPortInfo, + NAME_STR_SPCR_PORT, + UID_SPCR_PORT, + &TableList[1] + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SPCR: Failed to build associated SSDT table. Status = %r\n", + Status + )); + goto error_handler; + } + + *TableCount = 2; + *Table = TableList; + + return Status; + +error_handler: + if (TableList != NULL) { + FreePool (TableList); + } + + return Status; +} + +/** This macro defines the SPCR Table Generator revision. +*/ +#define SPCR_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the SPCR Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR SpcrGenerator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSpcr), + // Generator Description + L"ACPI.STD.SPCR.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_3_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE, + // ACPI Table Revision supported by this Generator + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_REVISION, + // Minimum supported ACPI Table Revision + EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_REVISION, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + SPCR_GENERATOR_REVISION, + // Build table function. Use the extended version instead. + NULL, + // Free table function. Use the extended version instead. + NULL, + // Extended Build table function. + BuildSpcrTableEx, + // Extended free function. + FreeSpcrTableEx +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiSpcrLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&SpcrGenerator); + DEBUG ((DEBUG_INFO, "SPCR: Register Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiSpcrLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&SpcrGenerator); + DEBUG ((DEBUG_INFO, "SPCR: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf new file mode 100644 index 00000000..ac7e0ffa --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf @@ -0,0 +1,29 @@ +## @file +# SRAT Table Generator +# +# Copyright (c) 2019, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = AcpiSratLibArm + FILE_GUID = 2CE21E0A-A39C-4B26-BC0E-526178036ACD + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiSratLibConstructor + DESTRUCTOR = AcpiSratLibDestructor + +[Sources] + SratGenerator.c + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/SratGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/SratGenerator.c new file mode 100644 index 00000000..1d723939 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/SratGenerator.c @@ -0,0 +1,835 @@ +/** @file + SRAT Table Generator + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - ACPI 6.3 Specification, January 2019 + + @par Glossary: + - Cm or CM - Configuration Manager + - Obj or OBJ - Object +**/ + +#include <Library/AcpiLib.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +/** + ARM standard SRAT Generator + + Requirements: + The following Configuration Manager Object(s) are used by this Generator: + - EArmObjGicCInfo (REQUIRED) + - EArmObjGicItsInfo (OPTIONAL) + - EArmObjMemoryAffinityInfo (OPTIONAL) + - EArmObjGenericInitiatorAffinityInfo (OPTIONAL) + - EArmObjDeviceHandleAcpi (OPTIONAL) + - EArmObjDeviceHandlePci (OPTIONAL) +*/ + +/** This macro expands to a function that retrieves the GIC + CPU interface Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGicCInfo, + CM_ARM_GICC_INFO + ); + +/** This macro expands to a function that retrieves the GIC + Interrupt Translation Service Information from the + Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGicItsInfo, + CM_ARM_GIC_ITS_INFO + ); + +/** + This macro expands to a function that retrieves the Memory Affinity + information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjMemoryAffinityInfo, + CM_ARM_MEMORY_AFFINITY_INFO + ); + +/** + This macro expands to a function that retrieves the Generic Initiator Affinity + information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjGenericInitiatorAffinityInfo, + CM_ARM_GENERIC_INITIATOR_AFFINITY_INFO + ); + +/** + This macro expands to a function that retrieves the ACPI Device Handle + information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjDeviceHandleAcpi, + CM_ARM_DEVICE_HANDLE_ACPI + ); + +/** + This macro expands to a function that retrieves the PCI Device Handle + information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjDeviceHandlePci, + CM_ARM_DEVICE_HANDLE_PCI + ); + + +/** Return the PCI Device information in BDF format + + PCI Bus Number - Max 256 busses (Bits 15:8 of BDF) + PCI Device Number - Max 32 devices (Bits 7:3 of BDF) + PCI Function Number - Max 8 functions (Bits 2:0 of BDF) + + @param [in] DeviceHandlePci Pointer to the PCI Device Handle. + + @retval BDF value corresponding to the PCI Device Handle. +**/ +STATIC +UINT16 +GetBdf ( + IN CONST CM_ARM_DEVICE_HANDLE_PCI * DeviceHandlePci + ) +{ + UINT16 Bdf; + Bdf = (UINT16)DeviceHandlePci->BusNumber << 8; + Bdf |= (DeviceHandlePci->DeviceNumber & 0x1F) << 3; + Bdf |= DeviceHandlePci->FunctionNumber & 0x7; + return Bdf; +} + +/** Add the GICC Affinity Structures in the SRAT Table. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Srat Pointer to the SRAT Table. + @param [in] GicCAffOffset Offset of the GICC Affinity + information in the SRAT Table. + @param [in] GicCInfo Pointer to the GIC CPU Information list. + @param [in] GicCCount Count of GIC CPU Interfaces. + + @retval EFI_SUCCESS Table generated successfully. +**/ +STATIC +EFI_STATUS +AddGICCAffinity ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER * CONST Srat, + IN CONST UINT32 GicCAffOffset, + IN CONST CM_ARM_GICC_INFO * GicCInfo, + IN UINT32 GicCCount + ) +{ + EFI_ACPI_6_3_GICC_AFFINITY_STRUCTURE * GicCAff; + + ASSERT (Srat != NULL); + ASSERT (GicCInfo != NULL); + + GicCAff = (EFI_ACPI_6_3_GICC_AFFINITY_STRUCTURE *)((UINT8*)Srat + + GicCAffOffset); + + while (GicCCount-- != 0) { + DEBUG ((DEBUG_INFO, "SRAT: GicCAff = 0x%p\n", GicCAff)); + + GicCAff->Type = EFI_ACPI_6_3_GICC_AFFINITY; + GicCAff->Length = sizeof (EFI_ACPI_6_3_GICC_AFFINITY_STRUCTURE); + GicCAff->ProximityDomain = GicCInfo->ProximityDomain; + GicCAff->AcpiProcessorUid = GicCInfo->AcpiProcessorUid; + GicCAff->Flags = GicCInfo->AffinityFlags; + GicCAff->ClockDomain = GicCInfo->ClockDomain; + + // Next + GicCAff++; + GicCInfo++; + }// while + return EFI_SUCCESS; +} + +/** Add the GIC ITS Affinity Structures in the SRAT Table. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Srat Pointer to the SRAT Table. + @param [in] GicItsAffOffset Offset of the GIC ITS Affinity + information in the SRAT Table. + @param [in] GicItsInfo Pointer to the GIC ITS Information list. + @param [in] GicItsCount Count of GIC ITS. + + @retval EFI_SUCCESS Table generated successfully. +**/ +STATIC +EFI_STATUS +AddGICItsAffinity ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER * CONST Srat, + IN CONST UINT32 GicItsAffOffset, + IN CONST CM_ARM_GIC_ITS_INFO * GicItsInfo, + IN UINT32 GicItsCount + ) +{ + EFI_ACPI_6_3_GIC_ITS_AFFINITY_STRUCTURE * GicItsAff; + + ASSERT (Srat != NULL); + ASSERT (GicItsInfo != NULL); + + GicItsAff = (EFI_ACPI_6_3_GIC_ITS_AFFINITY_STRUCTURE *)((UINT8*)Srat + + GicItsAffOffset); + + while (GicItsCount-- != 0) { + DEBUG ((DEBUG_INFO, "SRAT: GicItsAff = 0x%p\n", GicItsAff)); + + GicItsAff->Type = EFI_ACPI_6_3_GIC_ITS_AFFINITY; + GicItsAff->Length = sizeof (EFI_ACPI_6_3_GIC_ITS_AFFINITY_STRUCTURE); + GicItsAff->ProximityDomain = GicItsInfo->ProximityDomain; + GicItsAff->Reserved[0] = EFI_ACPI_RESERVED_BYTE; + GicItsAff->Reserved[1] = EFI_ACPI_RESERVED_BYTE; + GicItsAff->ItsId = GicItsInfo->GicItsId; + + // Next + GicItsAff++; + GicItsInfo++; + }// while + return EFI_SUCCESS; +} + +/** Add the Memory Affinity Structures in the SRAT Table. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Srat Pointer to the SRAT Table. + @param [in] MemAffOffset Offset of the Memory Affinity + information in the SRAT Table. + @param [in] MemAffInfo Pointer to the Memory Affinity Information list. + @param [in] MemAffCount Count of Memory Affinity objects. + + @retval EFI_SUCCESS Table generated successfully. +**/ +STATIC +EFI_STATUS +AddMemoryAffinity ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER * CONST Srat, + IN CONST UINT32 MemAffOffset, + IN CONST CM_ARM_MEMORY_AFFINITY_INFO * MemAffInfo, + IN UINT32 MemAffCount + ) +{ + EFI_ACPI_6_3_MEMORY_AFFINITY_STRUCTURE * MemAff; + + ASSERT (Srat != NULL); + ASSERT (MemAffInfo != NULL); + + MemAff = (EFI_ACPI_6_3_MEMORY_AFFINITY_STRUCTURE *)((UINT8*)Srat + + MemAffOffset); + + while (MemAffCount-- != 0) { + DEBUG ((DEBUG_INFO, "SRAT: MemAff = 0x%p\n", MemAff)); + + MemAff->Type = EFI_ACPI_6_3_MEMORY_AFFINITY; + MemAff->Length = sizeof (EFI_ACPI_6_3_MEMORY_AFFINITY_STRUCTURE); + MemAff->ProximityDomain = MemAffInfo->ProximityDomain; + MemAff->Reserved1 = EFI_ACPI_RESERVED_WORD; + MemAff->AddressBaseLow = (UINT32)(MemAffInfo->BaseAddress & MAX_UINT32); + MemAff->AddressBaseHigh = (UINT32)(MemAffInfo->BaseAddress >> 32); + MemAff->LengthLow = (UINT32)(MemAffInfo->Length & MAX_UINT32); + MemAff->LengthHigh = (UINT32)(MemAffInfo->Length >> 32); + MemAff->Reserved2 = EFI_ACPI_RESERVED_DWORD; + MemAff->Flags = MemAffInfo->Flags; + MemAff->Reserved3 = EFI_ACPI_RESERVED_QWORD; + + // Next + MemAff++; + MemAffInfo++; + }// while + return EFI_SUCCESS; +} + + +/** Add the Generic Initiator Affinity Structures in the SRAT Table. + + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in] Srat Pointer to the SRAT Table. + @param [in] GenInitAffOff Offset of the Generic Initiator Affinity + information in the SRAT Table. + @param [in] GenInitAffInfo Pointer to the Generic Initiator Affinity + Information list. + @param [in] GenInitAffCount Count of Generic Initiator Affinity + objects. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object information is not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. +**/ +STATIC +EFI_STATUS +AddGenericInitiatorAffinity ( + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER * CONST Srat, + IN CONST UINT32 GenInitAffOff, + IN CONST CM_ARM_GENERIC_INITIATOR_AFFINITY_INFO * GenInitAffInfo, + IN UINT32 GenInitAffCount + ) +{ + EFI_STATUS Status; + EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY_STRUCTURE * GenInitAff; + CM_ARM_DEVICE_HANDLE_ACPI * DeviceHandleAcpi; + CM_ARM_DEVICE_HANDLE_PCI * DeviceHandlePci; + UINT32 DeviceHandleCount; + + ASSERT (Srat != NULL); + ASSERT (GenInitAffInfo != NULL); + + GenInitAff = (EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY_STRUCTURE *)( + (UINT8*)Srat + GenInitAffOff); + + while (GenInitAffCount-- != 0) { + DEBUG ((DEBUG_INFO, "SRAT: GenInitAff = 0x%p\n", GenInitAff)); + + GenInitAff->Type = EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY; + GenInitAff->Length = + sizeof (EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY_STRUCTURE); + GenInitAff->Reserved1 = EFI_ACPI_RESERVED_WORD; + GenInitAff->DeviceHandleType = GenInitAffInfo->DeviceHandleType; + GenInitAff->ProximityDomain = GenInitAffInfo->ProximityDomain; + + if (GenInitAffInfo->DeviceHandleToken == CM_NULL_TOKEN) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Invalid Device Handle Token.\n" + )); + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (GenInitAffInfo->DeviceHandleType == EFI_ACPI_6_3_ACPI_DEVICE_HANDLE) { + Status = GetEArmObjDeviceHandleAcpi ( + CfgMgrProtocol, + GenInitAffInfo->DeviceHandleToken, + &DeviceHandleAcpi, + &DeviceHandleCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to get ACPI Device Handle Inf." + " DeviceHandleToken = %p." + " Status = %r\n", + GenInitAffInfo->DeviceHandleToken, + Status + )); + return Status; + } + + // We are expecting only one device handle. + ASSERT (DeviceHandleCount == 1); + + // Populate the ACPI device handle information. + GenInitAff->DeviceHandle.Acpi.AcpiHid = DeviceHandleAcpi->Hid; + GenInitAff->DeviceHandle.Acpi.AcpiUid = DeviceHandleAcpi->Uid; + GenInitAff->DeviceHandle.Acpi.Reserved[0] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Acpi.Reserved[1] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Acpi.Reserved[2] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Acpi.Reserved[3] = EFI_ACPI_RESERVED_BYTE; + } else if (GenInitAffInfo->DeviceHandleType == + EFI_ACPI_6_3_PCI_DEVICE_HANDLE) { + Status = GetEArmObjDeviceHandlePci ( + CfgMgrProtocol, + GenInitAffInfo->DeviceHandleToken, + &DeviceHandlePci, + &DeviceHandleCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to get ACPI Device Handle Inf." + " DeviceHandleToken = %p." + " Status = %r\n", + GenInitAffInfo->DeviceHandleToken, + Status + )); + return Status; + } + + // We are expecting only one device handle + ASSERT (DeviceHandleCount == 1); + + // Populate the ACPI device handle information. + GenInitAff->DeviceHandle.Pci.PciSegment = DeviceHandlePci->SegmentNumber; + GenInitAff->DeviceHandle.Pci.PciBdfNumber = GetBdf (DeviceHandlePci); + + GenInitAff->DeviceHandle.Pci.Reserved[0] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[1] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[2] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[3] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[4] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[5] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[6] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[7] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[8] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[9] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[10] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->DeviceHandle.Pci.Reserved[11] = EFI_ACPI_RESERVED_BYTE; + } else { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Invalid Device Handle Type.\n" + )); + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + GenInitAff->Flags = GenInitAffInfo->Flags; + GenInitAff->Reserved2[0] = EFI_ACPI_RESERVED_BYTE; + GenInitAff->Reserved2[1] = EFI_ACPI_RESERVED_BYTE; + + // Next + GenInitAff++; + GenInitAffInfo++; + }// while + return EFI_SUCCESS; +} + +/** Construct the SRAT ACPI table. + + Called by the Dynamic Table Manager, this function invokes the + Configuration Manager protocol interface to get the required hardware + information for generating the ACPI table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResources function. + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [out] Table Pointer to the constructed ACPI Table. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The required object was not found. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for the + requested object. + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildSratTable ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + EFI_STATUS Status; + UINT32 TableSize; + UINT32 GicCCount; + UINT32 GicItsCount; + UINT32 MemAffCount; + UINT32 GenInitiatorAffCount; + + UINT32 GicCAffOffset; + UINT32 GicItsAffOffset; + UINT32 MemAffOffset; + UINT32 GenInitiatorAffOffset; + + CM_ARM_GICC_INFO * GicCInfo; + CM_ARM_GIC_ITS_INFO * GicItsInfo; + CM_ARM_MEMORY_AFFINITY_INFO * MemAffInfo; + CM_ARM_GENERIC_INITIATOR_AFFINITY_INFO * GenInitiatorAffInfo; + + EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER * Srat; + + ASSERT ( + (This != NULL) && + (AcpiTableInfo != NULL) && + (CfgMgrProtocol != NULL) && + (Table != NULL) && + (AcpiTableInfo->TableGeneratorId == This->GeneratorID) && + (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature) + ); + + if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) || + (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Requested table revision = %d is not supported. " + "Supported table revisions: Minimum = %d. Maximum = %d\n", + AcpiTableInfo->AcpiTableRevision, + This->MinAcpiTableRevision, + This->AcpiTableRevision + )); + return EFI_INVALID_PARAMETER; + } + + *Table = NULL; + + Status = GetEArmObjGicCInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &GicCInfo, + &GicCCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to get GICC Info. Status = %r\n", + Status + )); + goto error_handler; + } + + if (GicCCount == 0) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: GIC CPU Interface information not provided.\n" + )); + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + Status = GetEArmObjGicItsInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &GicItsInfo, + &GicItsCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to get GIC ITS Info. Status = %r\n", + Status + )); + goto error_handler; + } + + Status = GetEArmObjMemoryAffinityInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &MemAffInfo, + &MemAffCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to get Memory Affinity Info. Status = %r\n", + Status + )); + goto error_handler; + } + + Status = GetEArmObjGenericInitiatorAffinityInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &GenInitiatorAffInfo, + &GenInitiatorAffCount + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to get Generic Initiator Affinity Info." + " Status = %r\n", + Status + )); + goto error_handler; + } + + // Calculate the size of the SRAT table + TableSize = sizeof (EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER); + + GicCAffOffset = TableSize; + TableSize += (sizeof (EFI_ACPI_6_3_GICC_AFFINITY_STRUCTURE) * GicCCount); + + if (GicItsCount != 0) { + GicItsAffOffset = TableSize; + TableSize += (sizeof (EFI_ACPI_6_3_GIC_ITS_AFFINITY_STRUCTURE) * + GicItsCount); + } + + if (MemAffCount != 0) { + MemAffOffset = TableSize; + TableSize += (sizeof (EFI_ACPI_6_3_MEMORY_AFFINITY_STRUCTURE) * + MemAffCount); + } + + if (GenInitiatorAffCount != 0) { + GenInitiatorAffOffset = TableSize; + TableSize += (sizeof (EFI_ACPI_6_3_GENERIC_INITIATOR_AFFINITY_STRUCTURE) * + GenInitiatorAffCount); + } + + // Allocate the Buffer for SRAT table + *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize); + if (*Table == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to allocate memory for SRAT Table, Size = %d," \ + " Status = %r\n", + TableSize, + Status + )); + goto error_handler; + } + + Srat = (EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_HEADER*)*Table; + + DEBUG (( + DEBUG_INFO, + "SRAT: Srat = 0x%p TableSize = 0x%x\n", + Srat, + TableSize + )); + + Status = AddAcpiHeader ( + CfgMgrProtocol, + This, + &Srat->Header, + AcpiTableInfo, + TableSize + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to add ACPI header. Status = %r\n", + Status + )); + goto error_handler; + } + + // Setup the Reserved fields + // Reserved1 must be set to 1 for backward compatibility + Srat->Reserved1 = 1; + Srat->Reserved2 = EFI_ACPI_RESERVED_QWORD; + + Status = AddGICCAffinity ( + CfgMgrProtocol, + Srat, + GicCAffOffset, + GicCInfo, + GicCCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to add GICC Affinity structures. Status = %r\n", + Status + )); + goto error_handler; + } + + if (GicItsCount != 0) { + Status = AddGICItsAffinity ( + CfgMgrProtocol, + Srat, + GicItsAffOffset, + GicItsInfo, + GicItsCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to add GIC ITS Affinity structures. Status = %r\n", + Status + )); + goto error_handler; + } + } + + if (MemAffCount != 0) { + Status = AddMemoryAffinity ( + CfgMgrProtocol, + Srat, + MemAffOffset, + MemAffInfo, + MemAffCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to add Memory Affinity structures. Status = %r\n", + Status + )); + goto error_handler; + } + } + + if (GenInitiatorAffCount != 0) { + Status = AddGenericInitiatorAffinity ( + CfgMgrProtocol, + Srat, + GenInitiatorAffOffset, + GenInitiatorAffInfo, + GenInitiatorAffCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SRAT: Failed to add Generic Initiator Affinity structures." + " Status = %r\n", + Status + )); + goto error_handler; + } + } + + return Status; + +error_handler: + + if (*Table != NULL) { + FreePool (*Table); + *Table = NULL; + } + + return Status; +} + +/** Free any resources allocated for constructing the SRAT. + + @param [in] This Pointer to the table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to the ACPI Table. + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +FreeSratTableResources ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table + ) +{ + ASSERT ( + (This != NULL) && + (AcpiTableInfo != NULL) && + (CfgMgrProtocol != NULL) && + (AcpiTableInfo->TableGeneratorId == This->GeneratorID) && + (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature) + ); + + if ((Table == NULL) || (*Table == NULL)) { + DEBUG ((DEBUG_ERROR, "ERROR: SRAT: Invalid Table Pointer\n")); + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + FreePool (*Table); + *Table = NULL; + return EFI_SUCCESS; +} + +/** The SRAT Table Generator revision. +*/ +#define SRAT_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the SRAT Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR SratGenerator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSrat), + // Generator Description + L"ACPI.STD.SRAT.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_SIGNATURE, + // ACPI Table Revision supported by this Generator + EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_REVISION, + // Minimum supported ACPI Table Revision + EFI_ACPI_6_3_SYSTEM_RESOURCE_AFFINITY_TABLE_REVISION, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + SRAT_GENERATOR_REVISION, + // Build Table function + BuildSratTable, + // Free Resource function + FreeSratTableResources, + // Extended build function not needed + NULL, + // Extended build function not implemented by the generator. + // Hence extended free resource function is not required. + NULL +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiSratLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&SratGenerator); + DEBUG ((DEBUG_INFO, "SRAT: Register Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiSratLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&SratGenerator); + DEBUG ((DEBUG_INFO, "SRAT: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c new file mode 100644 index 00000000..a0c4a7fa --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c @@ -0,0 +1,708 @@ +/** @file + SSDT CMN-600 AML Table Generator. + + Copyright (c) 2020, Arm Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - Arm CoreLink CMN-600 Coherent Mesh Network Technical Reference Manual r3p0 + - Generic ACPI for Arm Components 1.0 Platform Design Document +**/ + +#include <IndustryStandard/DebugPort2Table.h> +#include <Library/AcpiLib.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/AmlLib/AmlLib.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> +#include "SsdtCmn600Generator.h" + +/** C array containing the compiled AML template. + This symbol is defined in the auto generated C file + containing the AML bytecode array. +*/ +extern CHAR8 ssdtcmn600template_aml_code[]; + +/** SSDT CMN-600 Table Generator. + + Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjCmn600Info +*/ + +/** This macro expands to a function that retrieves the CMN-600 + Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjCmn600Info, + CM_ARM_CMN_600_INFO + ); + +/** Check the CMN-600 Information. + + @param [in] Cmn600InfoList Array of CMN-600 information structure. + @param [in] Cmn600Count Count of CMN-600 information structure. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +ValidateCmn600Info ( + IN CONST CM_ARM_CMN_600_INFO * Cmn600InfoList, + IN CONST UINT32 Cmn600Count + ) +{ + UINT32 Index; + UINT32 DtcIndex; + CONST CM_ARM_CMN_600_INFO * Cmn600Info; + CONST CM_ARM_GENERIC_INTERRUPT * DtcInterrupt; + + if ((Cmn600InfoList == NULL) || + (Cmn600Count == 0)) { + return EFI_INVALID_PARAMETER; + } + + // Validate each Cmn600Info structure. + for (Index = 0; Index < Cmn600Count; Index++) { + Cmn600Info = &Cmn600InfoList[Index]; + + // At least one DTC is required. + if ((Cmn600Info->DtcCount == 0) || + (Cmn600Info->DtcCount > MAX_DTC_COUNT)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Invalid DTC configuration:\n" + )); + goto error_handler; + } + + // Check PERIPHBASE and ROOTNODEBASE address spaces are initialized. + if ((Cmn600Info->PeriphBaseAddress == 0) || + (Cmn600Info->RootNodeBaseAddress == 0)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Invalid PERIPHBASE or ROOTNODEBASE.\n" + )); + goto error_handler; + } + + // The PERIPHBASE address must be 64MB aligned for a (X < 4) && (Y < 4) + // dimension mesh, and 256MB aligned otherwise. + // Check it is a least 64MB aligned. + if ((Cmn600Info->PeriphBaseAddress & + (PERIPHBASE_MIN_ADDRESS_LENGTH - 1)) != 0) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: PERIPHBASE address must be 64MB aligned.\n" + )); + goto error_handler; + } + + // The PERIPHBASE address is at most 64MB for a (X < 4) && (Y < 4) + // dimension mesh, and 256MB otherwise. Check it is not more than 256MB. + if (Cmn600Info->PeriphBaseAddressLength > PERIPHBASE_MAX_ADDRESS_LENGTH) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: PERIPHBASE address range must be < 256MB.\n" + )); + goto error_handler; + } + + // Check the 16 KB alignment of the ROOTNODEBASE address. + if ((Cmn600Info->PeriphBaseAddress & + (ROOTNODEBASE_ADDRESS_LENGTH - 1)) != 0) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Root base address must be 16KB aligned.\n" + )); + goto error_handler; + } + + // The ROOTNODEBASE address space should be included in the PERIPHBASE + // address space. + if ((Cmn600Info->PeriphBaseAddress > Cmn600Info->RootNodeBaseAddress) || + ((Cmn600Info->PeriphBaseAddress + Cmn600Info->PeriphBaseAddressLength) < + (Cmn600Info->RootNodeBaseAddress + ROOTNODEBASE_ADDRESS_LENGTH))) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600:" + " ROOTNODEBASE address space not in PERIPHBASE address space.\n" + )); + goto error_handler; + } + + for (DtcIndex = 0; DtcIndex < Cmn600Info->DtcCount; DtcIndex++) { + DtcInterrupt = &Cmn600Info->DtcInterrupt[DtcIndex]; + if (((DtcInterrupt->Flags & + EFI_ACPI_EXTENDED_INTERRUPT_FLAG_PRODUCER_CONSUMER_MASK) == 0)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: DTC Interrupt must be consumer.\n" + )); + goto error_handler; + } + } // for DTC Interrupt + + } //for Cmn600InfoList + + return EFI_SUCCESS; + +error_handler: + + DEBUG (( + DEBUG_ERROR, + "PeriphBaseAddress = 0x%llx\n" + "PeriphBaseAddressLength = 0x%llx\n" + "RootNodeBaseAddress = 0x%llx\n" + "DtcCount = %u\n", + Cmn600Info->PeriphBaseAddress, + Cmn600Info->PeriphBaseAddressLength, + Cmn600Info->RootNodeBaseAddress, + Cmn600Info->DtcCount + )); + + DEBUG_CODE ( + for (DtcIndex = 0; DtcIndex < Cmn600Info->DtcCount; DtcIndex++) { + DtcInterrupt = &Cmn600Info->DtcInterrupt[DtcIndex]; + DEBUG (( + DEBUG_ERROR, + " DTC[%d]:\n", + DtcIndex + )); + DEBUG (( + DEBUG_ERROR, + " Interrupt = 0x%lx\n", + DtcInterrupt->Interrupt + )); + DEBUG (( + DEBUG_ERROR, + " Flags = 0x%lx\n", + DtcInterrupt->Flags + )); + } // for + ); + + return EFI_INVALID_PARAMETER; +} + +/** Build a SSDT table describing the CMN-600 device. + + The table created by this function must be freed by FreeSsdtCmn600Table. + + @param [in] Cmn600Info Pointer to a Cmn600 structure. + @param [in] Name The Name to give to the Device. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + @param [in] Uid UID for the CMN600 device. + @param [out] Table If success, pointer to the created SSDT table. + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND Could not find information. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +FixupCmn600Info ( + IN CONST CM_ARM_CMN_600_INFO * Cmn600Info, + IN CONST CHAR8 * Name, + IN CONST UINT64 Uid, + OUT EFI_ACPI_DESCRIPTION_HEADER ** Table + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + UINT8 Index; + CONST CM_ARM_GENERIC_INTERRUPT * DtcInt; + + EFI_ACPI_DESCRIPTION_HEADER * SsdtCmn600Template; + AML_ROOT_NODE_HANDLE RootNodeHandle; + AML_OBJECT_NODE_HANDLE NameOpIdNode; + AML_OBJECT_NODE_HANDLE NameOpCrsNode; + AML_DATA_NODE_HANDLE CmnPeriphBaseRdNode; + AML_DATA_NODE_HANDLE CmnRootNodeBaseRdNode; + AML_OBJECT_NODE_HANDLE DeviceNode; + + // Parse the Ssdt CMN-600 Template. + SsdtCmn600Template = (EFI_ACPI_DESCRIPTION_HEADER*) + ssdtcmn600template_aml_code; + + RootNodeHandle = NULL; + Status = AmlParseDefinitionBlock ( + SsdtCmn600Template, + &RootNodeHandle + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to parse SSDT CMN-600 Template." + " Status = %r\n", + Status + )); + return Status; + } + + // Get the _UID NameOp object defined by the "Name ()" statement, + // and update its value. + Status = AmlFindNode ( + RootNodeHandle, + "\\_SB_.CMN0._UID", + &NameOpIdNode + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + Status = AmlNameOpUpdateInteger (NameOpIdNode, (UINT64)Uid); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Get the _CRS object defined by the "Name ()" statement. + Status = AmlFindNode ( + RootNodeHandle, + "\\_SB.CMN0._CRS", + &NameOpCrsNode + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Get the first Rd node in the "_CRS" object. + // This is the PERIPHBASE node. + Status = AmlNameOpCrsGetFirstRdNode (NameOpCrsNode, &CmnPeriphBaseRdNode); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + if (CmnPeriphBaseRdNode == NULL) { + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + // Update the PERIPHBASE base address and length. + Status = AmlUpdateRdQWord ( + CmnPeriphBaseRdNode, + Cmn600Info->PeriphBaseAddress, + Cmn600Info->PeriphBaseAddressLength + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Get the QWord node corresponding to the ROOTNODEBASE. + // It is the second Resource Data element in the BufferNode's + // variable list of arguments. + Status = AmlNameOpCrsGetNextRdNode ( + CmnPeriphBaseRdNode, + &CmnRootNodeBaseRdNode + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + if (CmnRootNodeBaseRdNode == NULL) { + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + // Update the ROOTNODEBASE base address and length. + Status = AmlUpdateRdQWord ( + CmnRootNodeBaseRdNode, + Cmn600Info->RootNodeBaseAddress, + ROOTNODEBASE_ADDRESS_LENGTH + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Add the Interrupt node(s). + // Generate Resource Data node(s) corresponding to the "Interrupt ()" + // ASL function and add it at the last position in the list of + // Resource Data nodes. + for (Index = 0; Index < Cmn600Info->DtcCount; Index++) { + DtcInt = &Cmn600Info->DtcInterrupt[Index]; + Status = AmlCodeGenCrsAddRdInterrupt ( + NameOpCrsNode, + ((DtcInt->Flags & + EFI_ACPI_EXTENDED_INTERRUPT_FLAG_PRODUCER_CONSUMER_MASK) != 0), + ((DtcInt->Flags & + EFI_ACPI_EXTENDED_INTERRUPT_FLAG_MODE_MASK) != 0), + ((DtcInt->Flags & + EFI_ACPI_EXTENDED_INTERRUPT_FLAG_POLARITY_MASK) != 0), + ((DtcInt->Flags & + EFI_ACPI_EXTENDED_INTERRUPT_FLAG_SHARABLE_MASK) != 0), + (UINT32*)&DtcInt->Interrupt, + 1 + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + } // for + + // Fixup the CMN600 device name. + // This MUST be done at the end, otherwise AML paths won't be valid anymore. + // Get the CMN0 variable defined by the "Device ()" statement. + Status = AmlFindNode (RootNodeHandle, "\\_SB_.CMN0", &DeviceNode); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Update the CMN600 Device's name. + Status = AmlDeviceOpUpdateName (DeviceNode, (CHAR8*)Name); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Serialise the definition block + Status = AmlSerializeDefinitionBlock ( + RootNodeHandle, + Table + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to Serialize SSDT Table Data." + " Status = %r\n", + Status + )); + } + +error_handler: + // Cleanup + if (RootNodeHandle != NULL) { + Status1 = AmlDeleteTree (RootNodeHandle); + if (EFI_ERROR (Status1)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to cleanup AML tree." + " Status = %r\n", + Status1 + )); + // If Status was success but we failed to delete the AML Tree + // return Status1 else return the original error code, i.e. Status. + if (!EFI_ERROR (Status)) { + return Status1; + } + } + } + + return Status; +} + +/** Free any resources allocated for constructing the SSDT tables for CMN-600. + + @param [in] This Pointer to the ACPI table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to an array of pointers + to ACPI Table(s). + @param [in] TableCount Number of ACPI table(s). + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +EFIAPI +FreeSsdtCmn600TableResourcesEx ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER *** CONST Table, + IN CONST UINTN TableCount + ) +{ + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + UINTN Index; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((Table == NULL) || + (*Table == NULL) || + (TableCount == 0)) { + DEBUG ((DEBUG_ERROR, "ERROR: SSDT-CMN-600: Invalid Table Pointer\n")); + return EFI_INVALID_PARAMETER; + } + + TableList = *Table; + + for (Index = 0; Index < TableCount; Index++) { + if ((TableList[Index] != NULL) && + (TableList[Index]->Signature == + EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE)) { + FreePool (TableList[Index]); + } else { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Could not free SSDT table at index %d." + " Status = %r\n", + Index, + EFI_INVALID_PARAMETER + )); + return EFI_INVALID_PARAMETER; + } + } //for + + // Free the table list. + FreePool (*Table); + *Table = NULL; + return EFI_SUCCESS; +} + +/** Construct SSDT tables for describing CMN-600 meshes. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResourcesEx function. + + @param [in] This Pointer to the ACPI table generator. + @param [in] AcpiTableInfo Pointer to the ACPI table information. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol interface. + @param [out] Table Pointer to a list of generated ACPI table(s). + @param [out] TableCount Number of generated ACPI table(s). + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for + the requested object. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND Could not find information. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. + @retval EFI_UNSUPPORTED Unsupported configuration. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildSsdtCmn600TableEx ( + IN CONST ACPI_TABLE_GENERATOR * This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER *** Table, + OUT UINTN * CONST TableCount + ) +{ + EFI_STATUS Status; + UINT64 Index; + CM_ARM_CMN_600_INFO * Cmn600Info; + UINT32 Cmn600Count; + CHAR8 NewName[5]; + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (TableCount != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + *Table = NULL; + + // Get CMN-600 information. + Status = GetEArmObjCmn600Info ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &Cmn600Info, + &Cmn600Count + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to get the CMN-600 information." + " Status = %r\n", + Status + )); + return Status; + } + + if ((Cmn600Count == 0) || (Cmn600Count > MAX_CMN600_DEVICES_SUPPORTED)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: CMN600 peripheral count = %d." + " This must be between 1 to 16.\n", + Cmn600Count + )); + return EFI_INVALID_PARAMETER; + } + + // Validate the CMN-600 Info. + Status = ValidateCmn600Info (Cmn600Info, Cmn600Count); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Invalid CMN600 information. Status = %r\n", + Status + )); + return Status; + } + + // Allocate a table to store pointers to the SSDT tables. + TableList = (EFI_ACPI_DESCRIPTION_HEADER**) + AllocateZeroPool ( + (sizeof (EFI_ACPI_DESCRIPTION_HEADER*) * Cmn600Count) + ); + if (TableList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to allocate memory for Table List." + " Status = %r\n", + Status + )); + return Status; + } + + // Setup the table list early so that that appropriate cleanup + // can be done in case of failure. + *Table = TableList; + + NewName[0] = 'C'; + NewName[1] = 'M'; + NewName[2] = 'N'; + NewName[4] = '\0'; + for (Index = 0; Index < Cmn600Count; Index++) { + NewName[3] = AsciiFromHex ((UINT8)(Index)); + + // Build a SSDT table describing the CMN600 device. + Status = FixupCmn600Info ( + &Cmn600Info[Index], + NewName, + Index, + &TableList[Index] + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to build associated SSDT table." + " Status = %r\n", + Status + )); + break; + } + + // Increment the table count here so that appropriate clean-up + // can be done in case of failure. + *TableCount += 1; + } // for + + // Note: Table list and CMN600 device count has been setup. The + // framework will invoke FreeSsdtCmn600TableResourcesEx() even + // on failure, so appropriate clean-up will be done. + return Status; +} + +/** This macro defines the Raw Generator revision. +*/ +#define SSDT_CMN_600_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the Raw Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR SsdtCmn600Generator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtCmn600), + // Generator Description + L"ACPI.STD.SSDT.CMN600.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE, + // ACPI Table Revision - Unused + 0, + // Minimum ACPI Table Revision - Unused + 0, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + SSDT_CMN_600_GENERATOR_REVISION, + // Build table function. Use the extended version instead. + NULL, + // Free table function. Use the extended version instead. + NULL, + // Build Table function + BuildSsdtCmn600TableEx, + // Free Resource function + FreeSsdtCmn600TableResourcesEx +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiSsdtCmn600LibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + + Status = RegisterAcpiTableGenerator (&SsdtCmn600Generator); + DEBUG (( + DEBUG_INFO, + "SSDT-CMN-600: Register Generator. Status = %r\n", + Status + )); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiSsdtCmn600LibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + + Status = DeregisterAcpiTableGenerator (&SsdtCmn600Generator); + DEBUG (( + DEBUG_INFO, + "SSDT-CMN-600: Deregister Generator. Status = %r\n", + Status + )); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h new file mode 100644 index 00000000..a7ecb940 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h @@ -0,0 +1,51 @@ +/** @file + + Copyright (c) 2020, Arm Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Glossary: + - Cm or CM - Configuration Manager + - Obj or OBJ - Object + - Std or STD - Standard + + @par Reference(s): + - Arm CoreLink CMN-600 Coherent Mesh Network Technical Reference Manual r3p0 + - Generic ACPI for Arm Components 1.0 Platform Design Document +**/ + +#ifndef SSDT_CMN600_GENERATOR_H_ +#define SSDT_CMN600_GENERATOR_H_ + +/** PeriphBase maximum address length is 256MB (0x10000000) + for a (X >= 4) || (Y >= 4) dimensions mesh. +*/ +#define PERIPHBASE_MAX_ADDRESS_LENGTH SIZE_256MB + +/** PeriphBase minimum address length is 64MB (0x04000000) + for a (X < 4) && (Y < 4) dimensions mesh. +*/ +#define PERIPHBASE_MIN_ADDRESS_LENGTH SIZE_64MB + +/** RootNodeBase address length is 16KB (0x00004000). +*/ +#define ROOTNODEBASE_ADDRESS_LENGTH SIZE_16KB + +/** Maximum number of CMN-600 Debug and Trace Logic Controllers (DTC). +*/ +#define MAX_DTC_COUNT 4 + +/** Starting value for the UID to represent the CMN600 devices. +*/ +#define CMN600_DEVICE_START_UID 0 + +/** Maximum CMN-600 devices supported by this generator. + This generator supports a maximum of 16 CMN-600 devices. + Note: This is not a hard limitation and can be extended if needed. + Corresponding changes would be needed to support the Name and + UID fields describing the serial port. + +*/ +#define MAX_CMN600_DEVICES_SUPPORTED 16 + +#endif // SSDT_CMN600_GENERATOR_H_ diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf new file mode 100644 index 00000000..9f10c987 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf @@ -0,0 +1,34 @@ +## @file +# Ssdt CMN-600 Table Generator +# +# Copyright (c) 2020, Arm Limited. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = SsdtCmn600LibArm + FILE_GUID = CEDB450D-8F0E-4ACC-8FB7-F72EC7D216A4 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiSsdtCmn600LibConstructor + DESTRUCTOR = AcpiSsdtCmn600LibDestructor + +[Sources] + SsdtCmn600Generator.c + SsdtCmn600Generator.h + SsdtCmn600Template.asl + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + AmlLib + BaseLib + diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl new file mode 100644 index 00000000..33c49f8f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl @@ -0,0 +1,81 @@ +/** @file + SSDT CMN-600 Template + + Copyright (c) 2020, Arm Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - Arm CoreLink CMN-600 Coherent Mesh Network Technical Reference Manual r3p0 + - Generic ACPI for Arm Components 1.0 Platform Design Document + + @par Glossary: + - {template} - Data fixed up using AML Fixup APIs. + - {codegen} - Data generated using AML Codegen APIs. +**/ + +DefinitionBlock ("SsdtCmn600.aml", "SSDT", 2, "ARMLTD", "CMN-600", 1) { + Scope (_SB) { + // CMN-600 device object for a X * Y mesh, where (X >= 4) || (Y >= 4). + Device (CMN0) { // {template} + Name (_HID, "ARMHC600") + Name (_UID, 0x0) // {template} + + Name (_CRS, ResourceTemplate () { + // Descriptor for 256 MB of the CFG region at offset PERIPHBASE. + QWordMemory ( + ResourceConsumer, // bit 0 of general flags is 0. + PosDecode, + MinFixed, // Range is fixed. + MaxFixed, // Range is Fixed. + NonCacheable, + ReadWrite, + 0x00000000, // Granularity + 0xA0000000, // MinAddress // {template} + 0xAFFFFFFF, // MaxAddress // {template} + 0x00000000, // Translation + 0x10000000, // RangeLength // {template} + , // ResourceSourceIndex + , // ResourceSource + CFGR // DescriptorName + ) // QWordMemory + + // Descriptor for the root node. This is a 16 KB region at offset + // ROOTNODEBASE. In this example, ROOTNODEBASE starts at the 16 KB + // aligned offset of PERIPHBASE. + QWordMemory ( + ResourceConsumer, // bit 0 of general flags is 0. + PosDecode, + MinFixed, // Range is fixed. + MaxFixed, // Range is Fixed. + NonCacheable, + ReadWrite, + 0x00000000, // Granularity + 0xA0000000, // MinAddress // {template} + 0xAFFFFFFF, // MaxAddress // {template} + 0x00000000, // Translation + 0x10000000, // RangeLength // {template} + , // ResourceSourceIndex + , // ResourceSource + ROOT // DescriptorName + ) // QWordMemory + + // The Interrupt information is generated using AmlCodegen. + // Interrupt on PMU0 overflow, attached to DTC [0], with GSIV = <gsiv0>. + // + // Interrupt ( // {codegen} + // ResourceConsumer, // ResourceUsage + // Level, // EdgeLevel + // ActiveHigh, // ActiveLevel + // Exclusive, // Shared + // , // ResourceSourceIndex + // , // ResourceSource + // // DescriptorName + // ) { + // 0xA5 // <gsiv0 > + // } // Interrupt + + }) // Name + } // Device + } // _SB +} // DefinitionBlock diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c new file mode 100644 index 00000000..0af74613 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortGenerator.c @@ -0,0 +1,371 @@ +/** @file + SSDT Serial Port Table Generator. + + Copyright (c) 2020, Arm Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include <IndustryStandard/DebugPort2Table.h> +#include <Library/AcpiLib.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Protocol/AcpiTable.h> + +// Module specific include files. +#include <AcpiTableGenerator.h> +#include <ConfigurationManagerObject.h> +#include <ConfigurationManagerHelper.h> +#include <Library/SsdtSerialPortFixupLib.h> +#include <Library/TableHelperLib.h> +#include <Protocol/ConfigurationManagerProtocol.h> + +/** ARM standard SSDT Serial Port Table Generator + + Constructs SSDT tables describing serial ports (other than the serial ports + used by the SPCR or DBG2 tables). + +Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjSerialPortInfo +*/ + +/** This macro expands to a function that retrieves the Serial-port + information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjSerialPortInfo, + CM_ARM_SERIAL_PORT_INFO + ); + +/** Starting value for the UID to represent the serial ports. + Note: The UID 0 and 1 are reserved for use by DBG2 port and SPCR + respectively. So, the UIDs for serial ports for general use + start at 2. +*/ +#define SERIAL_PORT_START_UID 2 + +/** Maximum serial ports supported by this generator. + This generator supports a maximum of 14 (16 - 2) serial ports. + The -2 here reflects the reservation for serial ports for the DBG2 + and SPCR ports regardless of whether the DBG2 or SPCR port is enabled. + Note: This is not a hard limitation and can be extended if needed. + Corresponding changes would be needed to support the Name and + UID fields describing the serial port. + +*/ +#define MAX_SERIAL_PORTS_SUPPORTED 14 + +/** Free any resources allocated for constructing the tables. + + @param [in] This Pointer to the ACPI table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to an array of pointers + to ACPI Table(s). + @param [in] TableCount Number of ACPI table(s). + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +EFIAPI +FreeSsdtSerialPortTableEx ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER *** CONST Table, + IN CONST UINTN TableCount + ) +{ + EFI_STATUS Status; + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + UINTN Index; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((Table == NULL) || + (*Table == NULL) || + (TableCount == 0)) { + DEBUG ((DEBUG_ERROR, "ERROR: SSDT-SERIAL-PORT: Invalid Table Pointer\n")); + return EFI_INVALID_PARAMETER; + } + + TableList = *Table; + + for (Index = 0; Index < TableCount; Index++) { + if ((TableList[Index] != NULL) && + (TableList[Index]->Signature == + EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE)) { + Status = FreeSsdtSerialPortTable (TableList[Index]); + } else { + Status = EFI_INVALID_PARAMETER; + } + + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-SERIAL-PORT: Could not free SSDT table at index %d." + " Status = %r\n", + Index, + Status + )); + return Status; + } + } //for + + // Free the table list. + FreePool (*Table); + + return EFI_SUCCESS; +} + +/** Construct SSDT tables describing serial-ports. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResourcesEx function. + + @param [in] This Pointer to the ACPI table generator. + @param [in] AcpiTableInfo Pointer to the ACPI table information. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol interface. + @param [out] Table Pointer to a list of generated ACPI table(s). + @param [out] TableCount Number of generated ACPI table(s). + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for + the requested object. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND Could not find information. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. + @retval EFI_UNSUPPORTED Unsupported configuration. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildSsdtSerialPortTableEx ( + IN CONST ACPI_TABLE_GENERATOR * This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER *** Table, + OUT UINTN * CONST TableCount + ) +{ + EFI_STATUS Status; + CM_ARM_SERIAL_PORT_INFO * SerialPortInfo; + UINT32 SerialPortCount; + UINTN Index; + CHAR8 NewName[5]; + UINT64 Uid; + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (TableCount != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + *Table = NULL; + + Status = GetEArmObjSerialPortInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &SerialPortInfo, + &SerialPortCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-SERIAL-PORT: Failed to get serial port information." + " Status = %r\n", + Status + )); + return Status; + } + + if (SerialPortCount > MAX_SERIAL_PORTS_SUPPORTED) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-SERIAL-PORT: Too many serial ports: %d." + " Maximum serial ports supported = %d.\n", + SerialPortCount, + MAX_SERIAL_PORTS_SUPPORTED + )); + return EFI_INVALID_PARAMETER; + } + + // Validate the SerialPort info. + Status = ValidateSerialPortInfo (SerialPortInfo, SerialPortCount); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-SERIAL-PORT: Invalid serial port information. Status = %r\n", + Status + )); + return Status; + } + + // Allocate a table to store pointers to the SSDT tables. + TableList = (EFI_ACPI_DESCRIPTION_HEADER**) + AllocateZeroPool ( + (sizeof (EFI_ACPI_DESCRIPTION_HEADER*) * SerialPortCount) + ); + if (TableList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-SERIAL-PORT: Failed to allocate memory for Table List." + " Status = %r\n", + Status + )); + return Status; + } + + // Setup the table list early so that that appropriate cleanup + // can be done in case of failure. + *Table = TableList; + + NewName[0] = 'C'; + NewName[1] = 'O'; + NewName[2] = 'M'; + NewName[4] = '\0'; + for (Index = 0; Index < SerialPortCount; Index++) { + Uid = SERIAL_PORT_START_UID + Index; + NewName[3] = AsciiFromHex ((UINT8)(Uid)); + + // Build a SSDT table describing the serial port. + Status = BuildSsdtSerialPortTable ( + AcpiTableInfo, + &SerialPortInfo[Index], + NewName, + Uid, + &TableList[Index] + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-SERIAL-PORT: Failed to build associated SSDT table." + " Status = %r\n", + Status + )); + goto error_handler; + } + + // Increment the table count here so that appropriate cleanup + // can be done in case of failure. + *TableCount += 1; + } // for + +error_handler: + // Note: Table list and Serial port count has been setup. The + // error handler does nothing here as the framework will invoke + // FreeSsdtSerialPortTableEx() even on failure. + return Status; +} + +/** This macro defines the SSDT Serial Port Table Generator revision. +*/ +#define SSDT_SERIAL_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the SSDT Serial Port Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR SsdtSerialPortGenerator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtSerialPort), + // Generator Description + L"ACPI.STD.SSDT.SERIAL.PORT.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE, + // ACPI Table Revision - Unused + 0, + // Minimum ACPI Table Revision - Unused + 0, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + SSDT_SERIAL_GENERATOR_REVISION, + // Build table function. Use the extended version instead. + NULL, + // Free table function. Use the extended version instead. + NULL, + // Extended Build table function. + BuildSsdtSerialPortTableEx, + // Extended free function. + FreeSsdtSerialPortTableEx +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiSsdtSerialPortLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&SsdtSerialPortGenerator); + DEBUG (( + DEBUG_INFO, + "SSDT-SERIAL-PORT: Register Generator. Status = %r\n", + Status + )); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiSsdtSerialPortLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&SsdtSerialPortGenerator); + DEBUG (( + DEBUG_INFO, + "SSDT-SERIAL-PORT: Deregister Generator. Status = %r\n", + Status + )); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf new file mode 100644 index 00000000..36907c79 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf @@ -0,0 +1,33 @@ +## @file +# Ssdt Serial Port Table Generator +# +# Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = SsdtSerialPortLibArm + FILE_GUID = D1F92325-2DFB-435C-9B4C-A6B864F19230 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiSsdtSerialPortLibConstructor + DESTRUCTOR = AcpiSsdtSerialPortLibDestructor + +[Sources] + SsdtSerialPortGenerator.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + AmlLib + BaseLib + TableHelperLib + SsdtSerialPortFixupLib |