| /** @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/FdtLib.h> | |
| #include <Library/FdtSerialPortAddressLib.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 = FdtGetProp (DeviceTree, Node, "status", NULL); | |
| if ((NodeStatus != NULL) && (AsciiStrCmp (NodeStatus, "okay") != 0)) { | |
| return RETURN_DEVICE_ERROR; | |
| } | |
| RegProp = FdtGetProp (DeviceTree, Node, "reg", &PropSize); | |
| if (RegProp == NULL) { | |
| return RETURN_NOT_FOUND; | |
| } | |
| if (PropSize != 16) { | |
| return RETURN_BAD_BUFFER_SIZE; | |
| } | |
| *BaseAddress = Fdt64ToCpu (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 (FdtCheckHeader (DeviceTree) != 0) { | |
| return RETURN_INVALID_PARAMETER; | |
| } | |
| Ports->NumberOfPorts = 0; | |
| Node = FdtNextNode (DeviceTree, 0, NULL); | |
| while ((Node > 0) && | |
| (Ports->NumberOfPorts < ARRAY_SIZE (Ports->BaseAddress))) | |
| { | |
| CONST CHAR8 *CompatProp; | |
| INT32 PropSize; | |
| CompatProp = FdtGetProp (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 = FdtNextNode (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 (FdtCheckHeader (DeviceTree) != 0) { | |
| return RETURN_INVALID_PARAMETER; | |
| } | |
| ChosenNode = FdtPathOffset (DeviceTree, "/chosen"); | |
| if (ChosenNode < 0) { | |
| return RETURN_NOT_FOUND; | |
| } | |
| StdoutPathProp = FdtGetProp ( | |
| 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 = FdtPathOffsetNameLen ( | |
| DeviceTree, | |
| StdoutPathProp, | |
| (INT32)StdoutPathLength | |
| ); | |
| } else { | |
| // | |
| // StdoutPathProp starts with an alias. | |
| // | |
| CONST CHAR8 *ResolvedStdoutPath; | |
| ResolvedStdoutPath = FdtGetAliasNameLen ( | |
| DeviceTree, | |
| StdoutPathProp, | |
| (INT32)StdoutPathLength | |
| ); | |
| if (ResolvedStdoutPath == NULL) { | |
| return RETURN_NOT_FOUND; | |
| } | |
| ConsoleNode = FdtPathOffset (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; | |
| } | |
| } |