--- /dev/null
+/** @file\r
+ SSDT Serial Port Fixup Library.\r
+\r
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+ @par Reference(s):\r
+ - Arm Server Base Boot Requirements (SBBR), s4.2.1.8 "SPCR".\r
+ - Microsoft Debug Port Table 2 (DBG2) Specification - December 10, 2015.\r
+**/\r
+\r
+#include <IndustryStandard/DebugPort2Table.h>\r
+#include <Library/AcpiLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Protocol/AcpiTable.h>\r
+\r
+// Module specific include files.\r
+#include <AcpiTableGenerator.h>\r
+#include <ConfigurationManagerObject.h>\r
+#include <ConfigurationManagerHelper.h>\r
+#include <Library/AmlLib/AmlLib.h>\r
+#include <Library/TableHelperLib.h>\r
+#include <Protocol/ConfigurationManagerProtocol.h>\r
+\r
+/** C array containing the compiled AML template.\r
+ This symbol is defined in the auto generated C file\r
+ containing the AML bytecode array.\r
+*/\r
+extern CHAR8 ssdtserialporttemplate_aml_code[];\r
+\r
+/** UART address range length.\r
+*/\r
+#define MIN_UART_ADDRESS_LENGTH 0x1000U\r
+\r
+/** Validate the Serial Port Information.\r
+\r
+ @param [in] SerialPortInfoTable Table of CM_ARM_SERIAL_PORT_INFO.\r
+ @param [in] SerialPortCount Count of SerialPort in the table.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ValidateSerialPortInfo (\r
+ IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfoTable,\r
+ IN UINT32 SerialPortCount\r
+ )\r
+{\r
+ UINT32 Index;\r
+ CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo;\r
+\r
+ if ((SerialPortInfoTable == NULL) ||\r
+ (SerialPortCount == 0)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ for (Index = 0; Index < SerialPortCount; Index++) {\r
+ SerialPortInfo = &SerialPortInfoTable[Index];\r
+ ASSERT (SerialPortInfo != NULL);\r
+\r
+ if ((SerialPortInfo == NULL ) ||\r
+ (SerialPortInfo->BaseAddress == 0)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: UART port base address is invalid. BaseAddress = 0x%llx\n",\r
+ SerialPortInfo->BaseAddress\r
+ ));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((SerialPortInfo->PortSubtype !=\r
+ EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART) &&\r
+ (SerialPortInfo->PortSubtype !=\r
+ EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X) &&\r
+ (SerialPortInfo->PortSubtype !=\r
+ EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART) &&\r
+ (SerialPortInfo->PortSubtype !=\r
+ EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_DCC) &&\r
+ (SerialPortInfo->PortSubtype !=\r
+ EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: UART port subtype is invalid."\r
+ " UART Base = 0x%llx, PortSubtype = 0x%x\n",\r
+ SerialPortInfo->BaseAddress,\r
+ SerialPortInfo->PortSubtype\r
+ ));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "UART Configuration:\n"));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " UART Base = 0x%llx\n", SerialPortInfo->BaseAddress\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " Length = 0x%llx\n",\r
+ SerialPortInfo->BaseAddressLength\r
+ ));\r
+ DEBUG ((DEBUG_INFO, " Clock = %lu\n", SerialPortInfo->Clock));\r
+ DEBUG ((DEBUG_INFO, " BaudRate = %llu\n", SerialPortInfo->BaudRate));\r
+ DEBUG ((DEBUG_INFO, " Interrupt = %lu\n", SerialPortInfo->Interrupt));\r
+ } // for\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Fixup the Serial Port Ids (_UID, _HID, _CID).\r
+\r
+ @param [in] RootNodeHandle Pointer to the root of an AML tree.\r
+ @param [in] Uid UID for the Serial Port.\r
+ @param [in] SerialPortInfo Pointer to a Serial Port Information\r
+ structure.\r
+ Get the Serial Port Information from there.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_NOT_FOUND Could not find information.\r
+ @retval EFI_OUT_OF_RESOURCES Out of resources.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+FixupIds (\r
+ IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle,\r
+ IN CONST UINT64 Uid,\r
+ IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ AML_OBJECT_NODE_HANDLE NameOpIdNode;\r
+ CONST CHAR8 * HidString;\r
+ CONST CHAR8 * CidString;\r
+\r
+ // Get the _CID and _HID value to write.\r
+ switch (SerialPortInfo->PortSubtype) {\r
+ case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550:\r
+ {\r
+ HidString = "PNP0501";\r
+ CidString = "PNP0500";\r
+ break;\r
+ }\r
+ case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART:\r
+ {\r
+ HidString = "ARMH0011";\r
+ CidString = "ARMHB000";\r
+ break;\r
+ }\r
+ case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART:\r
+ case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X:\r
+ {\r
+ HidString = "ARMH0011";\r
+ CidString = "";\r
+ break;\r
+ }\r
+ default:\r
+ {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ } // switch\r
+\r
+ // Get the _UID NameOp object defined by the "Name ()" statement,\r
+ // and update its value.\r
+ Status = AmlFindNode (\r
+ RootNodeHandle,\r
+ "\\_SB_.COM0._UID",\r
+ &NameOpIdNode\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = AmlNameOpUpdateInteger (NameOpIdNode, (UINT64)Uid);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Get the _HID NameOp object defined by the "Name ()" statement,\r
+ // and update its value.\r
+ Status = AmlFindNode (\r
+ RootNodeHandle,\r
+ "\\_SB_.COM0._HID",\r
+ &NameOpIdNode\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = AmlNameOpUpdateString (NameOpIdNode, HidString);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Get the _CID NameOp object defined by the "Name ()" statement,\r
+ // and update its value.\r
+ Status = AmlFindNode (\r
+ RootNodeHandle,\r
+ "\\_SB_.COM0._CID",\r
+ &NameOpIdNode\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // If we have a CID then update a _CID node else delete the node.\r
+ if (AsciiStrLen (CidString) != 0) {\r
+ Status = AmlNameOpUpdateString (NameOpIdNode, CidString);\r
+ } else {\r
+ // First detach the node from the tree.\r
+ Status = AmlDetachNode (NameOpIdNode);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Delete the detached node.\r
+ Status = AmlDeleteTree (NameOpIdNode);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/** Fixup the Serial Port _CRS values (BaseAddress, ...).\r
+\r
+ @param [in] RootNodeHandle Pointer to the root of an AML tree.\r
+ @param [in] SerialPortInfo Pointer to a Serial Port Information\r
+ structure.\r
+ Get the Serial Port Information from there.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_NOT_FOUND Could not find information.\r
+ @retval EFI_OUT_OF_RESOURCES Out of resources.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+FixupCrs (\r
+ IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle,\r
+ IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ AML_OBJECT_NODE_HANDLE NameOpCrsNode;\r
+ AML_DATA_NODE_HANDLE QWordRdNode;\r
+ AML_DATA_NODE_HANDLE InterruptRdNode;\r
+\r
+ // Get the "_CRS" object defined by the "Name ()" statement.\r
+ Status = AmlFindNode (\r
+ RootNodeHandle,\r
+ "\\_SB_.COM0._CRS",\r
+ &NameOpCrsNode\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Get the first Rd node in the "_CRS" object.\r
+ Status = AmlNameOpCrsGetFirstRdNode (NameOpCrsNode, &QWordRdNode);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (QWordRdNode == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Update the Serial Port base address and length.\r
+ Status = AmlUpdateRdQWord (\r
+ QWordRdNode,\r
+ SerialPortInfo->BaseAddress,\r
+ ((SerialPortInfo->BaseAddressLength < MIN_UART_ADDRESS_LENGTH) ?\r
+ MIN_UART_ADDRESS_LENGTH: SerialPortInfo->BaseAddressLength)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Get the Interrupt node.\r
+ // It is the second Resource Data element in the NameOpCrsNode's\r
+ // variable list of arguments.\r
+ Status = AmlNameOpCrsGetNextRdNode (QWordRdNode, &InterruptRdNode);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (InterruptRdNode == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Update the interrupt number.\r
+ return AmlUpdateRdInterrupt (InterruptRdNode, SerialPortInfo->Interrupt);\r
+}\r
+\r
+/** Fixup the Serial Port device name.\r
+\r
+ @param [in] RootNodeHandle Pointer to the root of an AML tree.\r
+ @param [in] SerialPortInfo Pointer to a Serial Port Information\r
+ structure.\r
+ Get the Serial Port Information from there.\r
+ @param [in] Name The Name to give to the Device.\r
+ Must be a NULL-terminated ASL NameString\r
+ e.g.: "DEV0", "DV15.DEV0", etc.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_NOT_FOUND Could not find information.\r
+ @retval EFI_OUT_OF_RESOURCES Out of resources.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+FixupName (\r
+ IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle,\r
+ IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo,\r
+ IN CONST CHAR8 * Name\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ AML_OBJECT_NODE_HANDLE DeviceNode;\r
+\r
+ // Get the COM0 variable defined by the "Device ()" statement.\r
+ Status = AmlFindNode (RootNodeHandle, "\\_SB_.COM0", &DeviceNode);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Update the Device's name.\r
+ return AmlDeviceOpUpdateName (DeviceNode, (CHAR8*)Name);\r
+}\r
+\r
+/** Fixup the Serial Port Information in the AML tree.\r
+\r
+ For each template value:\r
+ - find the node to update;\r
+ - update the value.\r
+\r
+ @param [in] RootNodeHandle Pointer to the root of the AML tree.\r
+ @param [in] SerialPortInfo Pointer to a Serial Port Information\r
+ structure.\r
+ Get the Serial Port Information from there.\r
+ @param [in] Name The Name to give to the Device.\r
+ Must be a NULL-terminated ASL NameString\r
+ e.g.: "DEV0", "DV15.DEV0", etc.\r
+ @param [in] Uid UID for the Serial Port.\r
+ @param [out] Table If success, contains the serialized\r
+ SSDT table.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_NOT_FOUND Could not find information.\r
+ @retval EFI_OUT_OF_RESOURCES Out of resources.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+FixupSerialPortInfo (\r
+ IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle,\r
+ IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo,\r
+ IN CONST CHAR8 * Name,\r
+ IN CONST UINT64 Uid,\r
+ OUT EFI_ACPI_DESCRIPTION_HEADER ** Table\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (RootNodeHandle != NULL);\r
+ ASSERT (SerialPortInfo != NULL);\r
+ ASSERT (Name != NULL);\r
+ ASSERT (Table != NULL);\r
+\r
+ // Fixup the _UID, _HID and _CID values.\r
+ Status = FixupIds (RootNodeHandle, Uid, SerialPortInfo);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Fixup the _CRS values.\r
+ Status = FixupCrs (RootNodeHandle, SerialPortInfo);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Fixup the serial-port name.\r
+ // This MUST be done at the end, otherwise AML paths won't be valid anymore.\r
+ return FixupName (RootNodeHandle, SerialPortInfo, Name);\r
+}\r
+\r
+/** Free an SSDT table previously created by\r
+ the BuildSsdtSerialTable function.\r
+\r
+ @param [in] Table Pointer to a SSDT table allocated by\r
+ the BuildSsdtSerialTable function.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FreeSsdtSerialPortTable (\r
+ IN EFI_ACPI_DESCRIPTION_HEADER * Table\r
+ )\r
+{\r
+ ASSERT (Table != NULL);\r
+ FreePool (Table);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Build a SSDT table describing the input serial port.\r
+\r
+ The table created by this function must be freed by FreeSsdtSerialTable.\r
+\r
+ @param [in] AcpiTableInfo Pointer to the ACPI table information.\r
+ @param [in] SerialPortInfo Serial port to describe in the SSDT table.\r
+ @param [in] Name The Name to give to the Device.\r
+ Must be a NULL-terminated ASL NameString\r
+ e.g.: "DEV0", "DV15.DEV0", etc.\r
+ @param [in] Uid UID for the Serial Port.\r
+ @param [out] Table If success, pointer to the created SSDT table.\r
+\r
+ @retval EFI_SUCCESS Table generated successfully.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_NOT_FOUND Could not find information.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BuildSsdtSerialPortTable (\r
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * AcpiTableInfo,\r
+ IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo,\r
+ IN CONST CHAR8 * Name,\r
+ IN CONST UINT64 Uid,\r
+ OUT EFI_ACPI_DESCRIPTION_HEADER ** Table\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS Status1;\r
+ AML_ROOT_NODE_HANDLE RootNodeHandle;\r
+\r
+ ASSERT (AcpiTableInfo != NULL);\r
+ ASSERT (SerialPortInfo != NULL);\r
+ ASSERT (Name != NULL);\r
+ ASSERT (Table != NULL);\r
+\r
+ // Validate the Serial Port Info.\r
+ Status = ValidateSerialPortInfo (SerialPortInfo, 1);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Parse the SSDT Serial Port Template.\r
+ Status = AmlParseDefinitionBlock (\r
+ (EFI_ACPI_DESCRIPTION_HEADER*)ssdtserialporttemplate_aml_code,\r
+ &RootNodeHandle\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: SSDT-SERIAL-PORT-FIXUP:"\r
+ " Failed to parse SSDT Serial Port Template. Status = %r\n",\r
+ Status\r
+ ));\r
+ return Status;\r
+ }\r
+\r
+ // Fixup the template values.\r
+ Status = FixupSerialPortInfo (\r
+ RootNodeHandle,\r
+ SerialPortInfo,\r
+ Name,\r
+ Uid,\r
+ Table\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to fixup SSDT Serial Port Table."\r
+ " Status = %r\n",\r
+ Status\r
+ ));\r
+ goto exit_handler;\r
+ }\r
+\r
+ // Serialize the tree.\r
+ Status = AmlSerializeDefinitionBlock (\r
+ RootNodeHandle,\r
+ Table\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to Serialize SSDT Table Data."\r
+ " Status = %r\n",\r
+ Status\r
+ ));\r
+ }\r
+\r
+exit_handler:\r
+ // Cleanup\r
+ if (RootNodeHandle != NULL) {\r
+ Status1 = AmlDeleteTree (RootNodeHandle);\r
+ if (EFI_ERROR (Status1)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to cleanup AML tree."\r
+ " Status = %r\n",\r
+ Status1\r
+ ));\r
+ // If Status was success but we failed to delete the AML Tree\r
+ // return Status1 else return the original error code, i.e. Status.\r
+ if (!EFI_ERROR (Status)) {\r
+ return Status1;\r
+ }\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r