[edk2-devel] [PATCH v4 06/15] DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser

PierreGondois pierre.gondois at arm.com
Thu Dec 9 09:31:59 UTC 2021


From: Pierre Gondois <Pierre.Gondois at arm.com>

The Microsoft Debug Port Table 2 (DBG2), the Serial Port Console
Redirector (SPCR) table are mandatory tables required for booting
a standards-based operating system. The DBG2 table is used by the
OS debugger while the SPCR table is used to configure the serial
terminal. Additionally, the serial ports available on a platform
for generic use also need to be described in DSDT/SSDT for an OS
to be able to use the serial ports.

The Arm Base System Architecture 1.0 specification a lists of
supported serial port hardware for Arm Platforms. This list
includes the following serial port UARTs:
 - SBSA/Generic UART
 - a fully 16550 compatible UART.
Along, with these the PL011 UART is the most commonly used serial
port hardware on Arm platforms.

The serial port hardware information is described in the platform
Device Tree, the bindings for which can be found at:
 - linux/Documentation/devicetree/bindings/serial/serial.yaml
 - linux/Documentation/devicetree/bindings/serial/8250.txt
 - linux/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
 - linux/Documentation/devicetree/bindings/serial/pl011.yaml

The FdtHwInfoParser implements a Serial Port Parser that parses
the platform Device Tree to create CM_ARM_SERIAL_PORT_INFO objects
with the following IDs:
 - EArmObjSerialConsolePortInfo (for use by SPCR)
 - EArmObjSerialDebugPortInfo (for use by DBG2)
 - EArmObjSerialPortInfo (for use as generic Serial Ports)

The Serial Port for use by SPCR is selected by parsing the Device
Tree for the '/chosen' node with the 'stdout-path' property. The
next Serial Port is selected for use as the Debug Serial Port and
the remaining serial ports are used as generic serial ports.

The CM_ARM_SERIAL_PORT_INFO objects are encapsulated in Configuration
Manager descriptor objects with the respective IDs and are added to
the platform information repository.

The platform Configuration Manager can then utilise this information
when generating the DBG2, SPCR and the SSDT serial port tables.

Signed-off-by: Pierre Gondois <Pierre.Gondois at arm.com>
---
 .../Serial/ArmSerialPortParser.c              | 633 ++++++++++++++++++
 .../Serial/ArmSerialPortParser.h              |  47 ++
 2 files changed, 680 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
new file mode 100644
index 000000000000..0557e416b44c
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
@@ -0,0 +1,633 @@
+/** @file
+  Arm Serial Port Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/serial/serial.yaml
+  - linux/Documentation/devicetree/bindings/serial/8250.txt
+  - linux/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
+  - linux/Documentation/devicetree/bindings/serial/pl011.yaml
+**/
+
+#include <IndustryStandard/DebugPort2Table.h>
+
+#include "CmObjectDescUtility.h"
+#include "FdtHwInfoParser.h"
+#include "Serial/ArmSerialPortParser.h"
+
+/** List of "compatible" property values for serial port nodes.
+
+  Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR  SerialCompatibleStr[] = {
+  { "ns16550a"      },
+  { "arm,sbsa-uart" },
+  { "arm,pl011"     }
+};
+
+/** COMPATIBILITY_INFO structure for the SerialCompatible.
+*/
+CONST COMPATIBILITY_INFO  SerialCompatibleInfo = {
+  ARRAY_SIZE (SerialCompatibleStr),
+  SerialCompatibleStr
+};
+
+/** 16550 UART compatible strings.
+
+  Any string of this list must be part of SerialCompatible.
+*/
+STATIC CONST COMPATIBILITY_STR  Serial16550CompatibleStr[] = {
+  { "ns16550a" }
+};
+
+/** COMPATIBILITY_INFO structure for the Serial16550Compatible.
+*/
+CONST COMPATIBILITY_INFO  Serial16550CompatibleInfo = {
+  ARRAY_SIZE (Serial16550CompatibleStr),
+  Serial16550CompatibleStr
+};
+
+/** SBSA UART compatible strings.
+
+  Include PL011 as SBSA uart is a subset of PL011.
+
+  Any string of this list must be part of SerialCompatible.
+*/
+STATIC CONST COMPATIBILITY_STR  SerialSbsaCompatibleStr[] = {
+  { "arm,sbsa-uart" },
+  { "arm,pl011"     }
+};
+
+/** COMPATIBILITY_INFO structure for the SerialSbsaCompatible.
+*/
+CONST COMPATIBILITY_INFO  SerialSbsaCompatibleInfo = {
+  ARRAY_SIZE (SerialSbsaCompatibleStr),
+  SerialSbsaCompatibleStr
+};
+
+/** Parse a serial port node.
+
+  @param [in]  Fdt               Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  SerialPortNode    Offset of a serial-port node.
+  @param [in]  SerialPortInfo    The CM_ARM_SERIAL_PORT_INFO to populate.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SerialPortNodeParser (
+  IN  CONST VOID               *Fdt,
+  IN  INT32                    SerialPortNode,
+  IN  CM_ARM_SERIAL_PORT_INFO  *SerialPortInfo
+  )
+{
+  EFI_STATUS   Status;
+  INT32        IntcNode;
+  CONST UINT8  *SizeValue;
+
+  INT32  AddressCells;
+  INT32  SizeCells;
+  INT32  IntCells;
+
+  CONST UINT8  *Data;
+  INT32        DataSize;
+  UINT8        AccessSize;
+
+  if ((Fdt == NULL) ||
+      (SerialPortInfo == NULL))
+  {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = FdtGetParentAddressInfo (
+             Fdt,
+             SerialPortNode,
+             &AddressCells,
+             &SizeCells
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Don't support more than 64 bits and less than 32 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)  ||
+      (SizeCells < 1)     ||
+      (SizeCells > 2))
+  {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  Data = fdt_getprop (Fdt, SerialPortNode, "reg", &DataSize);
+  if ((Data == NULL) ||
+      (DataSize < (INT32)(sizeof (UINT32) *
+                          GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells)) - 1))
+  {
+    // If error or not enough space.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  if (AddressCells == 2) {
+    SerialPortInfo->BaseAddress = fdt64_to_cpu (*(UINT64 *)Data);
+  } else {
+    SerialPortInfo->BaseAddress = fdt32_to_cpu (*(UINT32 *)Data);
+  }
+
+  SizeValue = Data + (sizeof (UINT32) *
+                      GET_DT_REG_SIZE_OFFSET (0, AddressCells, SizeCells));
+  if (SizeCells == 2) {
+    SerialPortInfo->BaseAddressLength = fdt64_to_cpu (*(UINT64 *)SizeValue);
+  } else {
+    SerialPortInfo->BaseAddressLength = fdt32_to_cpu (*(UINT32 *)SizeValue);
+  }
+
+  // Get the associated interrupt-controller.
+  Status = FdtGetIntcParentNode (Fdt, SerialPortNode, &IntcNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    if (Status == EFI_NOT_FOUND) {
+      // Should have found the node.
+      Status = EFI_ABORTED;
+    }
+
+    return Status;
+  }
+
+  // Get the number of cells used to encode an interrupt.
+  Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntCells);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Data = fdt_getprop (Fdt, SerialPortNode, "interrupts", &DataSize);
+  if ((Data == NULL) || (DataSize != (IntCells * sizeof (UINT32)))) {
+    // If error or not 1 interrupt.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  SerialPortInfo->Interrupt = FdtGetInterruptId ((CONST UINT32 *)Data);
+
+  // Note: clock-frequency is optional for SBSA UART.
+  Data = fdt_getprop (Fdt, SerialPortNode, "clock-frequency", &DataSize);
+  if (Data != NULL) {
+    if (DataSize < sizeof (UINT32)) {
+      // If error or not enough space.
+      ASSERT (0);
+      return EFI_ABORTED;
+    } else if (fdt_node_offset_by_phandle (Fdt, fdt32_to_cpu (*Data)) >= 0) {
+      // "clock-frequency" can be a "clocks phandle to refer to the clk used".
+      // This is not supported.
+      ASSERT (0);
+      return EFI_UNSUPPORTED;
+    }
+
+    SerialPortInfo->Clock = fdt32_to_cpu (*(UINT32 *)Data);
+  }
+
+  if (FdtNodeIsCompatible (Fdt, SerialPortNode, &Serial16550CompatibleInfo)) {
+    SerialPortInfo->PortSubtype =
+      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_16550_WITH_GAS;
+
+    /* reg-io-width:
+         description: |
+         The size (in bytes) of the IO accesses that should be performed on the
+         device. There are some systems that require 32-bit accesses to the
+         UART.
+    */
+    Data = fdt_getprop (Fdt, SerialPortNode, "reg-io-width", &DataSize);
+    if (Data != NULL) {
+      if (DataSize < sizeof (UINT32)) {
+        // If error or not enough space.
+        ASSERT (0);
+        return EFI_ABORTED;
+      }
+
+      AccessSize = fdt32_to_cpu (*(UINT32 *)Data);
+      if (AccessSize > EFI_ACPI_6_3_QWORD) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+
+      SerialPortInfo->AccessSize = AccessSize;
+    } else {
+      // 8250/16550 defaults to byte access.
+      SerialPortInfo->AccessSize = EFI_ACPI_6_3_BYTE;
+    }
+  } else if (FdtNodeIsCompatible (
+               Fdt,
+               SerialPortNode,
+               &SerialSbsaCompatibleInfo
+               ))
+  {
+    SerialPortInfo->PortSubtype =
+      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART;
+  } else {
+    ASSERT (0);
+    return EFI_UNSUPPORTED;
+  }
+
+  // Set Baudrate to 115200 by default
+  SerialPortInfo->BaudRate = 115200;
+  return EFI_SUCCESS;
+}
+
+/** Find the console serial-port node in the DT.
+
+  This function fetches the node referenced in the "stdout-path"
+  property of the "chosen" node.
+
+  @param [in]  Fdt                  Pointer to a Flattened Device Tree (Fdt).
+  @param [out] SerialConsoleNode    If success, contains the node offset
+                                    of the console serial-port node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetSerialConsoleNode (
+  IN  CONST VOID   *Fdt,
+  OUT       INT32  *SerialConsoleNode
+  )
+{
+  CONST CHAR8  *Prop;
+  INT32        PropSize;
+  CONST CHAR8  *Path;
+  INT32        PathLen;
+  INT32        ChosenNode;
+
+  if ((Fdt == NULL) ||
+      (SerialConsoleNode == NULL))
+  {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // The "chosen" node resides at the the root of the DT. Fetch it.
+  ChosenNode = fdt_path_offset (Fdt, "/chosen");
+  if (ChosenNode < 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  Prop = fdt_getprop (Fdt, ChosenNode, "stdout-path", &PropSize);
+  if ((Prop == NULL) || (PropSize < 0)) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Determine the actual path length, as a colon terminates the path.
+  Path = ScanMem8 (Prop, ':', PropSize);
+  if (Path == NULL) {
+    PathLen = (UINT32)AsciiStrLen (Prop);
+  } else {
+    PathLen = (INT32)(Path - Prop);
+  }
+
+  // Aliases cannot start with a '/', so it must be the actual path.
+  if (Prop[0] == '/') {
+    *SerialConsoleNode = fdt_path_offset_namelen (Fdt, Prop, PathLen);
+    return EFI_SUCCESS;
+  }
+
+  // Lookup the alias, as this contains the actual path.
+  Path = fdt_get_alias_namelen (Fdt, Prop, PathLen);
+  if (Path == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  *SerialConsoleNode = fdt_path_offset (Fdt, Path);
+  return EFI_SUCCESS;
+}
+
+/** CM_ARM_SERIAL_PORT_INFO dispatcher function (for a generic serial-port).
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  GenericSerialInfo  Pointer to a serial port info list.
+  @param [in]  NodeCount          Count of serial ports to dispatch.
+  @param [in]  SerialObjectId     Serial port object ID.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ArmSerialPortInfoDispatch (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE  FdtParserHandle,
+  IN  CM_ARM_SERIAL_PORT_INFO          *GenericSerialInfo,
+  IN  INT32                            NodeCount,
+  IN  EARM_OBJECT_ID                   SerialObjectId
+  )
+{
+  EFI_STATUS         Status;
+  CM_OBJ_DESCRIPTOR  *NewCmObjDesc;
+
+  if ((GenericSerialInfo == NULL) || (NodeCount == 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((SerialObjectId != EArmObjSerialPortInfo) &&
+      (SerialObjectId != EArmObjSerialDebugPortInfo) &&
+      (SerialObjectId != EArmObjSerialConsolePortInfo))
+  {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Dispatch the Generic Serial ports
+  Status = CreateCmObjDesc (
+             CREATE_CM_ARM_OBJECT_ID (SerialObjectId),
+             NodeCount,
+             GenericSerialInfo,
+             sizeof (CM_ARM_SERIAL_PORT_INFO) * NodeCount,
+             &NewCmObjDesc
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Add all the CmObjs to the Configuration Manager.
+  Status = AddMultipleCmObj (FdtParserHandle, NewCmObjDesc, 0, NULL);
+  ASSERT_EFI_ERROR (Status);
+  FreeCmObjDesc (NewCmObjDesc);
+  return Status;
+}
+
+/** CM_ARM_SERIAL_PORT_INFO parser function (for debug/console serial-port).
+
+  This parser expects FdtBranch to be the debug serial-port node.
+  At most one CmObj is created.
+  The following structure is populated:
+  typedef struct CmArmSerialPortInfo {
+    UINT64  BaseAddress;                      // {Populated}
+    UINT32  Interrupt;                        // {Populated}
+    UINT64  BaudRate;                         // {default}
+    UINT32  Clock;                            // {Populated}
+    UINT16  PortSubtype;                      // {Populated}
+    UINT64  BaseAddressLength                 // {Populated}
+  } CM_ARM_SERIAL_PORT_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+  @param [in]  SerialObjectId  ArmNamespace Object ID for the serial port.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ArmSerialPortInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE  FdtParserHandle,
+  IN        INT32                      FdtBranch,
+  IN        EARM_OBJECT_ID             SerialObjectId
+  )
+{
+  EFI_STATUS               Status;
+  CM_ARM_SERIAL_PORT_INFO  SerialInfo;
+
+  if ((SerialObjectId != EArmObjSerialDebugPortInfo) &&
+      (SerialObjectId != EArmObjSerialConsolePortInfo))
+  {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ZeroMem (&SerialInfo, sizeof (SerialInfo));
+
+  Status = SerialPortNodeParser (
+             FdtParserHandle->Fdt,
+             FdtBranch,
+             &SerialInfo
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = ArmSerialPortInfoDispatch (
+             FdtParserHandle,
+             &SerialInfo,
+             1,
+             SerialObjectId
+             );
+  ASSERT_EFI_ERROR (Status);
+  return Status;
+}
+
+/** SerialPort dispatcher.
+
+  This disptacher populates the CM_ARM_SERIAL_PORT_INFO structure for
+  the following CM_OBJ_ID:
+   - EArmObjSerialConsolePortInfo
+   - EArmObjSerialDebugPortInfo
+   - EArmObjSerialPortInfo
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+SerialPortDispatcher (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE  FdtParserHandle,
+  IN        INT32                      FdtBranch
+  )
+{
+  EFI_STATUS               Status;
+  INT32                    SerialConsoleNode;
+  INT32                    SerialDebugNode;
+  INT32                    SerialNode;
+  UINT32                   Index;
+  UINT32                   SerialNodeCount;
+  UINT32                   SerialNodesRemaining;
+  CM_ARM_SERIAL_PORT_INFO  *GenericSerialInfo;
+  UINT32                   GenericSerialIndex;
+  VOID                     *Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+
+  // Count the number of serial-ports.
+  Status = FdtCountCompatNodeInBranch (
+             Fdt,
+             FdtBranch,
+             &SerialCompatibleInfo,
+             &SerialNodeCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  if (SerialNodeCount == 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Track remaining nodes separately as SerialNodeCount
+  // is used in for loop below and reducing SerialNodeCount
+  // would result in the Generic Serial port nodes not
+  // being found if the serial console port node is among
+  // the first few serial nodes.
+  SerialNodesRemaining = SerialNodeCount;
+
+  // Identify the serial console port.
+  Status = GetSerialConsoleNode (Fdt, &SerialConsoleNode);
+  if (Status == EFI_NOT_FOUND) {
+    // No serial console.
+    SerialConsoleNode = -1;
+  } else if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  } else {
+    // Parse the console serial-port.
+    Status = ArmSerialPortInfoParser (
+               FdtParserHandle,
+               SerialConsoleNode,
+               EArmObjSerialConsolePortInfo
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    SerialNodesRemaining--;
+  }
+
+  GenericSerialInfo = NULL;
+  if (SerialNodesRemaining > 1) {
+    // We have more than one serial port remaining.
+    // This means that the first serial port will
+    // be reserved as a debug port, and the remaining
+    // will be for general purpose use.
+    SerialNodesRemaining--;
+    GenericSerialInfo = AllocateZeroPool (
+                          SerialNodesRemaining *
+                          sizeof (CM_ARM_SERIAL_PORT_INFO)
+                          );
+    if (GenericSerialInfo == NULL) {
+      ASSERT (0);
+      return EFI_OUT_OF_RESOURCES;
+    }
+  }
+
+  SerialNode         = FdtBranch;
+  SerialDebugNode    = -1;
+  GenericSerialIndex = 0;
+  for (Index = 0; Index < SerialNodeCount; Index++) {
+    // Search the next serial-port node in the branch.
+    Status = FdtGetNextCompatNodeInBranch (
+               Fdt,
+               FdtBranch,
+               &SerialCompatibleInfo,
+               &SerialNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      if (Status == EFI_NOT_FOUND) {
+        // Should have found the node.
+        Status = EFI_ABORTED;
+      }
+
+      goto exit_handler;
+    }
+
+    // Ignore the serial console node.
+    if (SerialNode == SerialConsoleNode) {
+      continue;
+    } else if (SerialDebugNode == -1) {
+      // The first serial-port node, not being the console serial-port,
+      // will be the debug serial-port.
+      SerialDebugNode = SerialNode;
+      Status          = ArmSerialPortInfoParser (
+                          FdtParserHandle,
+                          SerialDebugNode,
+                          EArmObjSerialDebugPortInfo
+                          );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        goto exit_handler;
+      }
+    } else {
+      if (GenericSerialInfo == NULL) {
+        // Should not be possible.
+        ASSERT (0);
+        Status = EFI_ABORTED;
+        goto exit_handler;
+      }
+
+      Status = SerialPortNodeParser (
+                 Fdt,
+                 SerialNode,
+                 &GenericSerialInfo[GenericSerialIndex++]
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        goto exit_handler;
+      }
+    }
+  } // for
+
+  if (GenericSerialIndex > 0) {
+    Status = ArmSerialPortInfoDispatch (
+               FdtParserHandle,
+               GenericSerialInfo,
+               GenericSerialIndex,
+               EArmObjSerialPortInfo
+               );
+  }
+
+exit_handler:
+  if (GenericSerialInfo != NULL) {
+    FreePool (GenericSerialInfo);
+  }
+
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h
new file mode 100644
index 000000000000..de08e57e6c57
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h
@@ -0,0 +1,47 @@
+/** @file
+  Arm Serial Port Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/serial/serial.yaml
+  - linux/Documentation/devicetree/bindings/serial/8250.txt
+**/
+
+#ifndef ARM_SERIAL_PORT_PARSER_H_
+#define ARM_SERIAL_PORT_PARSER_H_
+
+/** SerialPort dispatcher.
+
+  This disptacher populates the CM_ARM_SERIAL_PORT_INFO structure for
+  the following CM_OBJ_ID:
+   - EArmObjSerialConsolePortInfo
+   - EArmObjSerialDebugPortInfo
+   - EArmObjSerialPortInfo
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+SerialPortDispatcher (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE  FdtParserHandle,
+  IN        INT32                      FdtBranch
+  );
+
+#endif // ARM_SERIAL_PORT_PARSER_H_
-- 
2.25.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#84591): https://edk2.groups.io/g/devel/message/84591
Mute This Topic: https://groups.io/mt/87608909/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-






More information about the edk2-devel-archive mailing list