audk/OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c

257 lines
7.7 KiB
C

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