/** @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; | |
} |