summaryrefslogtreecommitdiffstats
path: root/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c
diff options
context:
space:
mode:
Diffstat (limited to 'ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c')
-rw-r--r--ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c b/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c
new file mode 100644
index 0000000000..f6508e0989
--- /dev/null
+++ b/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c
@@ -0,0 +1,256 @@
+/** @file
+ Determine the base addresses of serial ports from the Device Tree.
+
+ Copyright (C) Red Hat
+ Copyright (c) 2011 - 2023, Arm Ltd. All rights reserved.<BR>
+ Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
+ Copyright (c) 2014 - 2020, Linaro Ltd. All rights reserved.<BR>
+ Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/FdtSerialPortAddressLib.h>
+#include <libfdt.h>
+
+/**
+ Read the "reg" property of Node in DeviceTree as a UINT64 base address.
+
+ @param[in] DeviceTree The flat device tree (FDT) to scan.
+
+ @param[in] Node The node to read the "reg" property of.
+
+ @param[out] BaseAddress On success, the base address read out of Node's "reg"
+ property. On error, not modified.
+
+ @retval RETURN_DEVICE_ERROR Node has a "status" property with value
+ different from "okay".
+
+ @retval RETURN_NOT_FOUND Node does not have a "reg" property.
+
+ @retval RETURN_BAD_BUFFER_SIZE The size of Node's "reg" property is not 16
+ bytes.
+
+ @retval RETURN_SUCCESS BaseAddress has been populated.
+**/
+STATIC
+RETURN_STATUS
+GetBaseAddress (
+ IN CONST VOID *DeviceTree,
+ IN INT32 Node,
+ OUT UINT64 *BaseAddress
+ )
+{
+ CONST CHAR8 *NodeStatus;
+ CONST VOID *RegProp;
+ INT32 PropSize;
+
+ NodeStatus = fdt_getprop (DeviceTree, Node, "status", NULL);
+ if ((NodeStatus != NULL) && (AsciiStrCmp (NodeStatus, "okay") != 0)) {
+ return RETURN_DEVICE_ERROR;
+ }
+
+ RegProp = fdt_getprop (DeviceTree, Node, "reg", &PropSize);
+ if (RegProp == NULL) {
+ return RETURN_NOT_FOUND;
+ }
+
+ if (PropSize != 16) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+
+ *BaseAddress = fdt64_to_cpu (ReadUnaligned64 (RegProp));
+ return RETURN_SUCCESS;
+}
+
+/**
+ Collect the first ARRAY_SIZE (Ports->BaseAddress) serial ports into Ports from
+ DeviceTree.
+
+ @param[in] DeviceTree The flat device tree (FDT) to scan.
+
+ @param[in] Compatible Look for Compatible in the "compatible" property of the
+ scanned nodes.
+
+ @param[out] Ports On successful return, Ports->NumberOfPorts contains the
+ number of serial ports found; it is (a) positive and
+ (b) at most ARRAY_SIZE (Ports->BaseAddress). If the FDT
+ had more serial ports, those are not reported. On
+ error, the contents of Ports are indeterminate.
+
+ @retval RETURN_INVALID_PARAMETER DeviceTree does not point to a valid FDT
+ header.
+
+ @retval RETURN_NOT_FOUND No compatible and enabled serial port has
+ been found.
+
+ @retval RETURN_SUCCESS At least one compatible and enabled serial
+ port has been found; Ports has been filled
+ in.
+**/
+RETURN_STATUS
+EFIAPI
+FdtSerialGetPorts (
+ IN CONST VOID *DeviceTree,
+ IN CONST CHAR8 *Compatible,
+ OUT FDT_SERIAL_PORTS *Ports
+ )
+{
+ INT32 Node;
+
+ if (fdt_check_header (DeviceTree) != 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ Ports->NumberOfPorts = 0;
+ Node = fdt_next_node (DeviceTree, 0, NULL);
+ while ((Node > 0) &&
+ (Ports->NumberOfPorts < ARRAY_SIZE (Ports->BaseAddress)))
+ {
+ CONST CHAR8 *CompatProp;
+ INT32 PropSize;
+
+ CompatProp = fdt_getprop (DeviceTree, Node, "compatible", &PropSize);
+ if (CompatProp != NULL) {
+ CONST CHAR8 *CompatItem;
+
+ CompatItem = CompatProp;
+ while ((CompatItem < CompatProp + PropSize) &&
+ (AsciiStrCmp (CompatItem, Compatible) != 0))
+ {
+ CompatItem += AsciiStrLen (CompatItem) + 1;
+ }
+
+ if (CompatItem < CompatProp + PropSize) {
+ RETURN_STATUS Status;
+ UINT64 BaseAddress;
+
+ Status = GetBaseAddress (DeviceTree, Node, &BaseAddress);
+ if (!RETURN_ERROR (Status)) {
+ Ports->BaseAddress[Ports->NumberOfPorts++] = BaseAddress;
+ }
+ }
+ }
+
+ Node = fdt_next_node (DeviceTree, Node, NULL);
+ }
+
+ return Ports->NumberOfPorts > 0 ? RETURN_SUCCESS : RETURN_NOT_FOUND;
+}
+
+/**
+ Fetch the base address of the serial port identified in the "stdout-path"
+ property of the "/chosen" node in DeviceTree.
+
+ @param[in] DeviceTree The flat device tree (FDT) to scan.
+
+ @param[out] BaseAddress On success, the base address of the preferred serial
+ port (to be used as console). On error, BaseAddress
+ is not modified.
+
+ @retval RETURN_INVALID_PARAMETER DeviceTree does not point to a valid FDT
+ header.
+
+ @retval RETURN_NOT_FOUND No enabled console port has been found.
+
+ @retval RETURN_PROTOCOL_ERROR The first (or only) node path in the
+ "stdout-path" property is an empty string.
+
+ @retval RETURN_PROTOCOL_ERROR The console port has been found in the FDT,
+ but its base address is not correctly
+ represented.
+
+ @retval RETURN_SUCCESS BaseAddress has been populated.
+**/
+RETURN_STATUS
+EFIAPI
+FdtSerialGetConsolePort (
+ IN CONST VOID *DeviceTree,
+ OUT UINT64 *BaseAddress
+ )
+{
+ INT32 ChosenNode;
+ CONST CHAR8 *StdoutPathProp;
+ INT32 PropSize;
+ CONST CHAR8 *StdoutPathEnd;
+ UINTN StdoutPathLength;
+ INT32 ConsoleNode;
+ RETURN_STATUS Status;
+
+ if (fdt_check_header (DeviceTree) != 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ ChosenNode = fdt_path_offset (DeviceTree, "/chosen");
+ if (ChosenNode < 0) {
+ return RETURN_NOT_FOUND;
+ }
+
+ StdoutPathProp = fdt_getprop (
+ DeviceTree,
+ ChosenNode,
+ "stdout-path",
+ &PropSize
+ );
+ if (StdoutPathProp == NULL) {
+ return RETURN_NOT_FOUND;
+ }
+
+ //
+ // If StdoutPathProp contains a colon (":"), then the colon terminates the
+ // path we're interested in.
+ //
+ StdoutPathEnd = AsciiStrStr (StdoutPathProp, ":");
+ if (StdoutPathEnd == NULL) {
+ StdoutPathLength = PropSize - 1;
+ } else {
+ StdoutPathLength = StdoutPathEnd - StdoutPathProp;
+ }
+
+ if (StdoutPathLength == 0) {
+ return RETURN_PROTOCOL_ERROR;
+ }
+
+ if (StdoutPathProp[0] == '/') {
+ //
+ // StdoutPathProp starts with an absolute node path.
+ //
+ ConsoleNode = fdt_path_offset_namelen (
+ DeviceTree,
+ StdoutPathProp,
+ (INT32)StdoutPathLength
+ );
+ } else {
+ //
+ // StdoutPathProp starts with an alias.
+ //
+ CONST CHAR8 *ResolvedStdoutPath;
+
+ ResolvedStdoutPath = fdt_get_alias_namelen (
+ DeviceTree,
+ StdoutPathProp,
+ (INT32)StdoutPathLength
+ );
+ if (ResolvedStdoutPath == NULL) {
+ return RETURN_NOT_FOUND;
+ }
+
+ ConsoleNode = fdt_path_offset (DeviceTree, ResolvedStdoutPath);
+ }
+
+ if (ConsoleNode < 0) {
+ return RETURN_NOT_FOUND;
+ }
+
+ Status = GetBaseAddress (DeviceTree, ConsoleNode, BaseAddress);
+ switch (Status) {
+ case RETURN_NOT_FOUND:
+ case RETURN_BAD_BUFFER_SIZE:
+ return RETURN_PROTOCOL_ERROR;
+ case RETURN_SUCCESS:
+ return RETURN_SUCCESS;
+ default:
+ return RETURN_NOT_FOUND;
+ }
+}