mirror of
https://github.com/tianocore/edk2.git
synced 2025-08-29 01:47:04 +00:00

This commit adds support for building non-serial DBG2 devices 1. Add new common namespace objects: - EArchCommonObjMemoryRangeDescriptor - EArchCommonObjDbg2DeviceInfo 2. Enhance DBG2 table generator: - Add support for non-serial DBG2 devices - Support multiple debug devices - Improve memory handling with proper allocation checks - Add better cleanup in error paths 3. Update configuration manager: - Add parsers for new common namespace objects Signed-off-by: Jeff Brasen <jbrasen@nvidia.com>
842 lines
30 KiB
C
842 lines
30 KiB
C
/** @file
|
|
DBG2 Table Generator
|
|
|
|
Copyright (c) 2017 - 2022, Arm Limited. All rights reserved.<BR>
|
|
Copyright (c) 2024 - 2025, NVIDIA CORPORATION & AFFILIATES. 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/AcpiAml.h>
|
|
#include <IndustryStandard/DebugPort2Table.h>
|
|
#include <Library/AcpiLib.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PrintLib.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>
|
|
|
|
#include "Dbg2Generator.h"
|
|
|
|
/** ARM standard DBG2 Table Generator
|
|
|
|
Constructs the DBG2 table for corresponding DBG2 peripheral.
|
|
|
|
Requirements:
|
|
The following Configuration Manager Object(s) are required by
|
|
this Generator:
|
|
- EArchCommonObjSerialDebugPortInfo
|
|
- EArchCommonObjGenericDbg2DeviceInfo
|
|
*/
|
|
|
|
/** A string representing the name of the serial debug port 0.
|
|
*/
|
|
#define NAME_STR_DBG_PORT0 "COM0"
|
|
|
|
// _SB scope of the AML namespace.
|
|
#define SB_SCOPE "\\_SB_."
|
|
|
|
/** An UID representing the serial debug port 0.
|
|
*/
|
|
#define UID_DBG_PORT0 0
|
|
|
|
/** The length of the namespace string.
|
|
*/
|
|
#define DBG2_NAMESPACESTRING_FIELD_SIZE (sizeof (SB_SCOPE) + AML_NAME_SEG_SIZE)
|
|
|
|
/** This macro expands to a function that retrieves the Serial
|
|
debug port information from the Configuration Manager
|
|
*/
|
|
GET_OBJECT_LIST (
|
|
EObjNameSpaceArchCommon,
|
|
EArchCommonObjSerialDebugPortInfo,
|
|
CM_ARCH_COMMON_SERIAL_PORT_INFO
|
|
);
|
|
|
|
/** This macro expands to a function that retrieves the DBG2
|
|
device information from the Configuration Manager
|
|
*/
|
|
GET_OBJECT_LIST (
|
|
EObjNameSpaceArchCommon,
|
|
EArchCommonObjGenericDbg2DeviceInfo,
|
|
CM_ARCH_COMMON_DBG2_DEVICE_INFO
|
|
);
|
|
|
|
/** This macro expands to a function that retrieves the
|
|
Memory Range Descriptor Array information from the Configuration Manager.
|
|
*/
|
|
GET_OBJECT_LIST (
|
|
EObjNameSpaceArchCommon,
|
|
EArchCommonObjMemoryRangeDescriptor,
|
|
CM_ARCH_COMMON_MEMORY_RANGE_DESCRIPTOR
|
|
);
|
|
|
|
/** Initialize the DBG2 UART with the parameters obtained from
|
|
the Configuration Manager.
|
|
|
|
@param [in] SerialPortInfo Pointer to the Serial Port Information.
|
|
|
|
@retval EFI_SUCCESS Success.
|
|
@retval EFI_INVALID_PARAMETER The parameters for serial port initialization
|
|
are invalid.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
SetupDebugUart (
|
|
IN CONST CM_ARCH_COMMON_SERIAL_PORT_INFO *CONST SerialPortInfo
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 BaudRate;
|
|
UINT32 ReceiveFifoDepth;
|
|
EFI_PARITY_TYPE Parity;
|
|
UINT8 DataBits;
|
|
EFI_STOP_BITS_TYPE StopBits;
|
|
|
|
ASSERT (SerialPortInfo != NULL);
|
|
|
|
// Initialize the Serial Debug UART
|
|
DEBUG ((DEBUG_INFO, "Initializing Serial Debug UART...\n"));
|
|
ReceiveFifoDepth = 0; // Use the default value for FIFO depth
|
|
Parity = (EFI_PARITY_TYPE)FixedPcdGet8 (PcdUartDefaultParity);
|
|
DataBits = FixedPcdGet8 (PcdUartDefaultDataBits);
|
|
StopBits = (EFI_STOP_BITS_TYPE)FixedPcdGet8 (PcdUartDefaultStopBits);
|
|
|
|
BaudRate = SerialPortInfo->BaudRate;
|
|
Status = Dbg2InitializePort (
|
|
SerialPortInfo,
|
|
&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;
|
|
UINTN Index;
|
|
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))
|
|
{
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Invalid Table Pointer\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
TableList = *Table;
|
|
|
|
// Free the DBG2 table
|
|
if (TableCount != 0) {
|
|
FreePool (TableList[0]);
|
|
}
|
|
|
|
// Free the SSDT tables if any exist
|
|
for (Index = 1; Index < TableCount; Index++) {
|
|
if ((TableList[Index] == NULL) ||
|
|
(TableList[Index]->Signature !=
|
|
EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE))
|
|
{
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Invalid SSDT table pointer.\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = FreeSsdtSerialPortTable (TableList[Index]);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
// Free the table list.
|
|
FreePool (*Table);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/** Populates the DBG2 device info structure.
|
|
|
|
@param [in, out] AcpiDbg2Device Pointer to the DBG2 ACPI table to add device to.
|
|
Pointer will be updated to point to after the new DBG2 device.
|
|
@param [in] DeviceInfo Pointer to the Device Info structure.
|
|
@param [in] MemoryRanges The memory ranges of the device.
|
|
@param [in] MemoryRangesCount The number of memory ranges in the device.
|
|
|
|
@retval EFI_SUCCESS The structure was populated correctly.
|
|
@retval EFI_INVALID_PARAMETER The parameters are invalid.
|
|
@retval EFI_BUFFER_TOO_SMALL The namespace string is too long.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PopulateDbg2Device (
|
|
IN OUT EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT **AcpiDbg2Device,
|
|
IN CM_ARCH_COMMON_DBG2_DEVICE_INFO *DeviceInfo,
|
|
IN CM_ARCH_COMMON_MEMORY_RANGE_DESCRIPTOR *MemoryRanges,
|
|
IN UINT32 MemoryRangesCount
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT16 Dbg2DeviceSize;
|
|
EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT *CurrentDbg2Device;
|
|
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE *BaseAddressRegister;
|
|
UINT32 *AddressSize;
|
|
UINT64 TotalSize;
|
|
CHAR8 *NamespaceString;
|
|
|
|
// Parameter validation
|
|
if ((AcpiDbg2Device == NULL) || (DeviceInfo == NULL) || (MemoryRanges == NULL)) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Invalid parameters to PopulateDbg2Device\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (MemoryRangesCount == 0) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Memory ranges count cannot be zero\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Check string length before concatenation
|
|
if ((DeviceInfo->ObjectName[0] != '\0') &&
|
|
(AsciiStrLen (SB_SCOPE) + AsciiStrLen (DeviceInfo->ObjectName) >= DBG2_NAMESPACESTRING_FIELD_SIZE))
|
|
{
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Namespace string too long\n"));
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (MemoryRangesCount > MAX_UINT8) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Too many memory ranges. Count = %u\n", MemoryRangesCount));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Validate all memory ranges
|
|
for (Index = 0; Index < MemoryRangesCount; Index++) {
|
|
if (MemoryRanges[Index].BaseAddress == 0) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Memory range base address is 0. Index = %u\n", Index));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (MemoryRanges[Index].Length == 0) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Memory range length is 0. Index = %u\n", Index));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (MemoryRanges[Index].Length > MAX_UINT32) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Memory range length too large. Length = %u\n", MemoryRanges[Index].Length));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
// Calculate total size with overflow check
|
|
TotalSize = (UINT64)sizeof (EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT) +
|
|
((UINT64)sizeof (EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE) + sizeof (UINT32)) * MemoryRangesCount +
|
|
(UINT64)sizeof (CHAR8) * DBG2_NAMESPACESTRING_FIELD_SIZE;
|
|
|
|
if (TotalSize > MAX_UINT16) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Device size too large\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Dbg2DeviceSize = (UINT16)TotalSize;
|
|
CurrentDbg2Device = *AcpiDbg2Device;
|
|
|
|
// Initialize device structure
|
|
CurrentDbg2Device->Revision = EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT_REVISION;
|
|
CurrentDbg2Device->Length = Dbg2DeviceSize;
|
|
CurrentDbg2Device->NumberofGenericAddressRegisters = (UINT8)MemoryRangesCount;
|
|
CurrentDbg2Device->OemDataLength = 0;
|
|
CurrentDbg2Device->OemDataOffset = 0;
|
|
CurrentDbg2Device->PortType = DeviceInfo->PortType;
|
|
CurrentDbg2Device->PortSubtype = DeviceInfo->PortSubtype;
|
|
CurrentDbg2Device->BaseAddressRegisterOffset = sizeof (EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT);
|
|
CurrentDbg2Device->AddressSizeOffset = CurrentDbg2Device->BaseAddressRegisterOffset +
|
|
((UINT16)sizeof (EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE) * ((UINT8)MemoryRangesCount));
|
|
CurrentDbg2Device->NameSpaceStringLength = DBG2_NAMESPACESTRING_FIELD_SIZE;
|
|
CurrentDbg2Device->NameSpaceStringOffset = CurrentDbg2Device->AddressSizeOffset +
|
|
((UINT16)sizeof (UINT32) * ((UINT8)MemoryRangesCount));
|
|
BaseAddressRegister = (EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE *)((UINT8 *)CurrentDbg2Device + CurrentDbg2Device->BaseAddressRegisterOffset);
|
|
AddressSize = (UINT32 *)((UINT8 *)CurrentDbg2Device + CurrentDbg2Device->AddressSizeOffset);
|
|
for (Index = 0; Index < MemoryRangesCount; Index++) {
|
|
BaseAddressRegister->AddressSpaceId = EFI_ACPI_6_3_SYSTEM_MEMORY;
|
|
BaseAddressRegister->RegisterBitWidth = 32;
|
|
BaseAddressRegister->RegisterBitOffset = 0;
|
|
BaseAddressRegister->AccessSize = DeviceInfo->AccessSize;
|
|
BaseAddressRegister->Address = MemoryRanges[Index].BaseAddress;
|
|
*AddressSize = (UINT32)(MemoryRanges[Index].Length);
|
|
BaseAddressRegister++;
|
|
AddressSize++;
|
|
}
|
|
|
|
NamespaceString = (CHAR8 *)CurrentDbg2Device + CurrentDbg2Device->NameSpaceStringOffset;
|
|
if (DeviceInfo->ObjectName[0] == '\0') {
|
|
// If device string is empty then use "." as the name per the DBG2 specification.
|
|
AsciiSPrint (
|
|
NamespaceString,
|
|
DBG2_NAMESPACESTRING_FIELD_SIZE,
|
|
"."
|
|
);
|
|
} else {
|
|
// Construct the namespace string for the device (e.g. \_SB_.COM1)
|
|
AsciiSPrint (
|
|
NamespaceString,
|
|
DBG2_NAMESPACESTRING_FIELD_SIZE,
|
|
"%a%a",
|
|
SB_SCOPE,
|
|
DeviceInfo->ObjectName
|
|
);
|
|
}
|
|
|
|
// Update the pointer to point to the next device
|
|
*AcpiDbg2Device = (EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT *)((UINT8 *)CurrentDbg2Device + Dbg2DeviceSize);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/** Builds the serial port device entry.
|
|
|
|
@param [in] AcpiTableInfo Pointer to the ACPI table information.
|
|
@param [in, out] AcpiDbg2Device Pointer to the DBG2 ACPI table to add device to.
|
|
Pointer will be updated to point to after the new DBG2 device.
|
|
@param [in] SerialPortInfo Pointer to the serial port information.
|
|
@param [in] ObjectName Pointer to the object name.
|
|
@param [in] Uid The UID of the device.
|
|
@param [out] Table Pointer to the table to add the device to.
|
|
|
|
@retval EFI_SUCCESS The serial port device was built successfully.
|
|
@retval EFI_INVALID_PARAMETER The parameters are invalid.
|
|
@retval EFI_OUT_OF_RESOURCES Could not allocate memory.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BuildDbg2SerialPortEntry (
|
|
IN CONST CM_STD_OBJ_ACPI_TABLE_INFO *CONST AcpiTableInfo,
|
|
IN OUT EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT **AcpiDbg2Device,
|
|
IN CONST CM_ARCH_COMMON_SERIAL_PORT_INFO *SerialPortInfo,
|
|
IN CONST CHAR8 *ObjectName,
|
|
IN UINT32 Uid,
|
|
OUT EFI_ACPI_DESCRIPTION_HEADER **Table
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CM_ARCH_COMMON_MEMORY_RANGE_DESCRIPTOR SerialMemoryRange;
|
|
CM_ARCH_COMMON_DBG2_DEVICE_INFO SerialPortDeviceInfo;
|
|
|
|
SerialMemoryRange.BaseAddress = SerialPortInfo->BaseAddress;
|
|
SerialMemoryRange.Length = SerialPortInfo->BaseAddressLength;
|
|
SerialPortDeviceInfo.AddressResourceToken = CM_NULL_TOKEN;
|
|
SerialPortDeviceInfo.PortType = EFI_ACPI_DBG2_PORT_TYPE_SERIAL;
|
|
SerialPortDeviceInfo.PortSubtype = SerialPortInfo->PortSubtype;
|
|
// 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
|
|
SerialPortDeviceInfo.AccessSize = EFI_ACPI_6_3_DWORD;
|
|
} else {
|
|
SerialPortDeviceInfo.AccessSize = SerialPortInfo->AccessSize;
|
|
}
|
|
|
|
AsciiSPrint (
|
|
SerialPortDeviceInfo.ObjectName,
|
|
sizeof (SerialPortDeviceInfo.ObjectName),
|
|
ObjectName
|
|
);
|
|
|
|
// Populate serial port device
|
|
Status = PopulateDbg2Device (
|
|
AcpiDbg2Device,
|
|
&SerialPortDeviceInfo,
|
|
&SerialMemoryRange,
|
|
1
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Failed to populate serial port device. Status = %r\n", Status));
|
|
goto error_handler;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Build a SSDT table describing the serial port.
|
|
Status = BuildSsdtSerialPortTable (
|
|
AcpiTableInfo,
|
|
SerialPortInfo,
|
|
SerialPortDeviceInfo.ObjectName,
|
|
Uid,
|
|
Table
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"ERROR: DBG2: Failed to build associated SSDT table. Status = %r\n",
|
|
Status
|
|
));
|
|
goto error_handler;
|
|
}
|
|
|
|
error_handler:
|
|
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;
|
|
EFI_ACPI_DEBUG_PORT_2_DESCRIPTION_TABLE *AcpiDbg2;
|
|
UINT32 AcpiDbg2Len;
|
|
UINT32 Index;
|
|
CM_ARCH_COMMON_DBG2_DEVICE_INFO *Dbg2DeviceInfo;
|
|
UINT32 Dbg2DeviceCount;
|
|
UINT32 Dbg2DeviceSize;
|
|
CM_ARCH_COMMON_MEMORY_RANGE_DESCRIPTOR **Dbg2DevicesMemoryRange;
|
|
UINT32 *Dbg2DevicesMemoryRangeCount;
|
|
CM_ARCH_COMMON_SERIAL_PORT_INFO *SerialPortInfo;
|
|
UINT32 SerialPortCount;
|
|
EFI_ACPI_DESCRIPTION_HEADER **TableList;
|
|
UINT32 TotalDevices;
|
|
EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT *CurrentDbg2Device;
|
|
UINT64 TotalSize;
|
|
|
|
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;
|
|
*TableCount = 0;
|
|
AcpiDbg2 = NULL;
|
|
TableList = NULL;
|
|
Dbg2DevicesMemoryRange = NULL;
|
|
Dbg2DevicesMemoryRangeCount = NULL;
|
|
Dbg2DeviceSize = 0;
|
|
Dbg2DeviceCount = 0;
|
|
SerialPortCount = 0;
|
|
|
|
// Get serial port information
|
|
Status = GetEArchCommonObjSerialDebugPortInfo (
|
|
CfgMgrProtocol,
|
|
CM_NULL_TOKEN,
|
|
&SerialPortInfo,
|
|
&SerialPortCount
|
|
);
|
|
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Failed to get serial port information. Status = %r\n", Status));
|
|
goto error_handler;
|
|
}
|
|
|
|
// Only one serial port is supported
|
|
if (SerialPortCount > 1) {
|
|
DEBUG ((DEBUG_WARN, "WARNING: DBG2: Too many serial ports to populate. Count = %u\n", SerialPortCount));
|
|
SerialPortCount = 1;
|
|
}
|
|
|
|
if (SerialPortCount != 0) {
|
|
Status = ValidateSerialPortInfo (SerialPortInfo, SerialPortCount);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Invalid serial port information. Status = %r\n", Status));
|
|
goto error_handler;
|
|
}
|
|
}
|
|
|
|
// Get DBG2 device information
|
|
Status = GetEArchCommonObjGenericDbg2DeviceInfo (
|
|
CfgMgrProtocol,
|
|
CM_NULL_TOKEN,
|
|
&Dbg2DeviceInfo,
|
|
&Dbg2DeviceCount
|
|
);
|
|
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Failed to get DBG2 device information. Status = %r\n", Status));
|
|
goto error_handler;
|
|
}
|
|
|
|
if (Dbg2DeviceCount != 0) {
|
|
// Get all the memory ranges for the DBG2 devices
|
|
Dbg2DevicesMemoryRange = (CM_ARCH_COMMON_MEMORY_RANGE_DESCRIPTOR **)AllocateZeroPool (sizeof (CM_ARCH_COMMON_MEMORY_RANGE_DESCRIPTOR *) * Dbg2DeviceCount);
|
|
if (Dbg2DevicesMemoryRange == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Failed to allocate memory for device ranges\n"));
|
|
goto error_handler;
|
|
}
|
|
|
|
Dbg2DevicesMemoryRangeCount = (UINT32 *)AllocateZeroPool (sizeof (UINT32) * Dbg2DeviceCount);
|
|
if (Dbg2DevicesMemoryRangeCount == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Failed to allocate memory for range counts\n"));
|
|
goto error_handler;
|
|
}
|
|
|
|
// Get memory ranges for each device
|
|
for (Index = 0; Index < Dbg2DeviceCount; Index++) {
|
|
if (Dbg2DeviceInfo[Index].AddressResourceToken != CM_NULL_TOKEN) {
|
|
Status = GetEArchCommonObjMemoryRangeDescriptor (
|
|
CfgMgrProtocol,
|
|
Dbg2DeviceInfo[Index].AddressResourceToken,
|
|
&Dbg2DevicesMemoryRange[Index],
|
|
&Dbg2DevicesMemoryRangeCount[Index]
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Failed to get memory range for device %u. Status = %r\n", Index, Status));
|
|
goto error_handler;
|
|
}
|
|
|
|
// Calculate total size with overflow check
|
|
TotalSize = (UINT64)sizeof (EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT) +
|
|
((UINT64)sizeof (EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE) + sizeof (UINT32)) * Dbg2DevicesMemoryRangeCount[Index] +
|
|
(UINT64)sizeof (CHAR8) * DBG2_NAMESPACESTRING_FIELD_SIZE;
|
|
|
|
if (TotalSize > MAX_UINT32) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Device size too large for device %u\n", Index));
|
|
goto error_handler;
|
|
}
|
|
|
|
Dbg2DeviceSize += (UINT32)TotalSize;
|
|
} else {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Missing address resource token for device %u\n", Index));
|
|
goto error_handler;
|
|
}
|
|
}
|
|
}
|
|
|
|
TotalDevices = SerialPortCount + Dbg2DeviceCount;
|
|
if (TotalDevices == 0) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: No devices found\n"));
|
|
Status = EFI_NOT_FOUND;
|
|
goto error_handler;
|
|
}
|
|
|
|
// Allocate table list
|
|
TableList = (EFI_ACPI_DESCRIPTION_HEADER **)AllocateZeroPool (
|
|
sizeof (EFI_ACPI_DESCRIPTION_HEADER *) * (1 + SerialPortCount)
|
|
);
|
|
if (TableList == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Failed to allocate table list\n"));
|
|
goto error_handler;
|
|
}
|
|
|
|
// Calculate total DBG2 table size
|
|
AcpiDbg2Len = sizeof (EFI_ACPI_DEBUG_PORT_2_DESCRIPTION_TABLE) + Dbg2DeviceSize;
|
|
|
|
if (SerialPortCount > 0) {
|
|
TotalSize = (UINT64)AcpiDbg2Len +
|
|
(UINT64)sizeof (EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT) +
|
|
((UINT64)sizeof (EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE) + sizeof (UINT32)) * SerialPortCount +
|
|
(UINT64)sizeof (CHAR8) * DBG2_NAMESPACESTRING_FIELD_SIZE;
|
|
|
|
if (TotalSize > MAX_UINT32) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Total table size too large\n"));
|
|
goto error_handler;
|
|
}
|
|
|
|
AcpiDbg2Len = (UINT32)TotalSize;
|
|
}
|
|
|
|
// Allocate DBG2 table
|
|
AcpiDbg2 = (EFI_ACPI_DEBUG_PORT_2_DESCRIPTION_TABLE *)AllocateZeroPool (AcpiDbg2Len);
|
|
if (AcpiDbg2 == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Failed to allocate DBG2 table\n"));
|
|
goto error_handler;
|
|
}
|
|
|
|
Status = AddAcpiHeader (
|
|
CfgMgrProtocol,
|
|
This,
|
|
&AcpiDbg2->Header,
|
|
AcpiTableInfo,
|
|
AcpiDbg2Len
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Failed to add ACPI header. Status = %r\n", Status));
|
|
goto error_handler;
|
|
}
|
|
|
|
AcpiDbg2->OffsetDbgDeviceInfo = sizeof (EFI_ACPI_DEBUG_PORT_2_DESCRIPTION_TABLE);
|
|
AcpiDbg2->NumberDbgDeviceInfo = TotalDevices;
|
|
TableList[0] = &AcpiDbg2->Header;
|
|
|
|
CurrentDbg2Device = (EFI_ACPI_DBG2_DEBUG_DEVICE_INFORMATION_STRUCT *)((UINT8 *)AcpiDbg2 + AcpiDbg2->OffsetDbgDeviceInfo);
|
|
|
|
// Populate DBG2 devices
|
|
for (Index = 0; Index < Dbg2DeviceCount; Index++) {
|
|
if ((CurrentDbg2Device == NULL) ||
|
|
((UINTN)CurrentDbg2Device - (UINTN)AcpiDbg2 > AcpiDbg2Len))
|
|
{
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Device pointer out of range\n"));
|
|
goto error_handler;
|
|
}
|
|
|
|
if ((Dbg2DeviceInfo == NULL) || (Dbg2DevicesMemoryRange == NULL) || (Dbg2DevicesMemoryRangeCount == NULL)) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Dbg2DeviceInfo, Dbg2DevicesMemoryRange, or Dbg2DevicesMemoryRangeCount is NULL. Status = %r\n", Status));
|
|
goto error_handler;
|
|
}
|
|
|
|
Status = PopulateDbg2Device (
|
|
&CurrentDbg2Device,
|
|
&Dbg2DeviceInfo[Index],
|
|
Dbg2DevicesMemoryRange[Index],
|
|
Dbg2DevicesMemoryRangeCount[Index]
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Failed to populate device %u. Status = %r\n", Index, Status));
|
|
goto error_handler;
|
|
}
|
|
}
|
|
|
|
// Currently only one serial port is supported
|
|
if (SerialPortCount > 0) {
|
|
// Validate device pointer
|
|
if ((CurrentDbg2Device == NULL) ||
|
|
((UINTN)CurrentDbg2Device - (UINTN)AcpiDbg2 > AcpiDbg2Len))
|
|
{
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((DEBUG_ERROR, "ERROR: DBG2: Serial port device pointer out of range\n"));
|
|
goto error_handler;
|
|
}
|
|
|
|
Status = BuildDbg2SerialPortEntry (
|
|
AcpiTableInfo,
|
|
&CurrentDbg2Device,
|
|
&SerialPortInfo[0],
|
|
NAME_STR_DBG_PORT0,
|
|
UID_DBG_PORT0,
|
|
&TableList[1]
|
|
);
|
|
}
|
|
|
|
*TableCount = 1 + SerialPortCount;
|
|
*Table = TableList;
|
|
|
|
error_handler:
|
|
// Clear the memory ranges in all cases
|
|
if (Dbg2DevicesMemoryRange != NULL) {
|
|
FreePool (Dbg2DevicesMemoryRange);
|
|
Dbg2DevicesMemoryRange = NULL;
|
|
}
|
|
|
|
if (Dbg2DevicesMemoryRangeCount != NULL) {
|
|
FreePool (Dbg2DevicesMemoryRangeCount);
|
|
Dbg2DevicesMemoryRangeCount = NULL;
|
|
}
|
|
|
|
// Free the tables in case of error
|
|
if (EFI_ERROR (Status)) {
|
|
if (AcpiDbg2 != NULL) {
|
|
FreePool (AcpiDbg2);
|
|
}
|
|
|
|
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,
|
|
// 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;
|
|
}
|