diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiDbg2LibArm/Dbg2Generator.c | 577 |
1 files changed, 577 insertions, 0 deletions
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; +} |