/** @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.
Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.
Copyright (c) 2014 - 2020, Linaro Ltd. All rights reserved.
Copyright (c) 2015, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include /** 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; } }