edk2/DynamicTablesPkg/Library/Acpi/Common/AcpiDbg2Lib/Dbg2Generator.c
Jeff Brasen d8d47a0ae6 DynamicTablesPkg: Add support for non-serial DBG2 devices
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>
2025-04-24 09:01:22 +02:00

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;
}