summaryrefslogtreecommitdiffstats
path: root/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c
blob: f6508e09898a485174746c45b1f51d0242e4b2f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/** @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;
  }
}