/** @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; | |
} | |
} |