mirror of https://github.com/acidanthera/audk.git
257 lines
7.7 KiB
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;
|
|
}
|
|
}
|