ArmVirtPkg/FdtClientDxe: make DT table installation !ACPI dependent
[mirror_edk2.git] / ArmVirtPkg / FdtClientDxe / FdtClientDxe.c
1 /** @file
2 * FDT client driver
3 *
4 * Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
5 *
6 * This program and the accompanying materials are
7 * licensed and made available under the terms and conditions of the BSD License
8 * which accompanies this distribution. The full text of the license may be found at
9 * http://opensource.org/licenses/bsd-license.php
10 *
11 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 *
14 **/
15
16 #include <Library/BaseLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/UefiDriverEntryPoint.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/UefiLib.h>
21 #include <Library/HobLib.h>
22 #include <libfdt.h>
23
24 #include <Guid/Acpi.h>
25 #include <Guid/EventGroup.h>
26 #include <Guid/Fdt.h>
27 #include <Guid/FdtHob.h>
28
29 #include <Protocol/FdtClient.h>
30
31 STATIC VOID *mDeviceTreeBase;
32
33 STATIC
34 EFI_STATUS
35 EFIAPI
36 GetNodeProperty (
37 IN FDT_CLIENT_PROTOCOL *This,
38 IN INT32 Node,
39 IN CONST CHAR8 *PropertyName,
40 OUT CONST VOID **Prop,
41 OUT UINT32 *PropSize OPTIONAL
42 )
43 {
44 INT32 Len;
45
46 ASSERT (mDeviceTreeBase != NULL);
47 ASSERT (Prop != NULL);
48
49 *Prop = fdt_getprop (mDeviceTreeBase, Node, PropertyName, &Len);
50 if (*Prop == NULL) {
51 return EFI_NOT_FOUND;
52 }
53
54 if (PropSize != NULL) {
55 *PropSize = Len;
56 }
57 return EFI_SUCCESS;
58 }
59
60 STATIC
61 EFI_STATUS
62 EFIAPI
63 SetNodeProperty (
64 IN FDT_CLIENT_PROTOCOL *This,
65 IN INT32 Node,
66 IN CONST CHAR8 *PropertyName,
67 IN CONST VOID *Prop,
68 IN UINT32 PropSize
69 )
70 {
71 INT32 Ret;
72
73 ASSERT (mDeviceTreeBase != NULL);
74
75 Ret = fdt_setprop (mDeviceTreeBase, Node, PropertyName, Prop, PropSize);
76 if (Ret != 0) {
77 return EFI_DEVICE_ERROR;
78 }
79
80 return EFI_SUCCESS;
81 }
82
83 STATIC
84 EFI_STATUS
85 EFIAPI
86 FindNextCompatibleNode (
87 IN FDT_CLIENT_PROTOCOL *This,
88 IN CONST CHAR8 *CompatibleString,
89 IN INT32 PrevNode,
90 OUT INT32 *Node
91 )
92 {
93 INT32 Prev, Next;
94 CONST CHAR8 *Type, *Compatible;
95 INT32 Len;
96
97 ASSERT (mDeviceTreeBase != NULL);
98 ASSERT (Node != NULL);
99
100 for (Prev = PrevNode;; Prev = Next) {
101 Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);
102 if (Next < 0) {
103 break;
104 }
105
106 Type = fdt_getprop (mDeviceTreeBase, Next, "compatible", &Len);
107 if (Type == NULL) {
108 continue;
109 }
110
111 //
112 // A 'compatible' node may contain a sequence of NUL terminated
113 // compatible strings so check each one
114 //
115 for (Compatible = Type; Compatible < Type + Len && *Compatible;
116 Compatible += 1 + AsciiStrLen (Compatible)) {
117 if (AsciiStrCmp (CompatibleString, Compatible) == 0) {
118 *Node = Next;
119 return EFI_SUCCESS;
120 }
121 }
122 }
123 return EFI_NOT_FOUND;
124 }
125
126 STATIC
127 EFI_STATUS
128 EFIAPI
129 FindCompatibleNode (
130 IN FDT_CLIENT_PROTOCOL *This,
131 IN CONST CHAR8 *CompatibleString,
132 OUT INT32 *Node
133 )
134 {
135 return FindNextCompatibleNode (This, CompatibleString, 0, Node);
136 }
137
138 STATIC
139 EFI_STATUS
140 EFIAPI
141 FindCompatibleNodeProperty (
142 IN FDT_CLIENT_PROTOCOL *This,
143 IN CONST CHAR8 *CompatibleString,
144 IN CONST CHAR8 *PropertyName,
145 OUT CONST VOID **Prop,
146 OUT UINT32 *PropSize OPTIONAL
147 )
148 {
149 EFI_STATUS Status;
150 INT32 Node;
151
152 Status = FindCompatibleNode (This, CompatibleString, &Node);
153 if (EFI_ERROR (Status)) {
154 return Status;
155 }
156
157 return GetNodeProperty (This, Node, PropertyName, Prop, PropSize);
158 }
159
160 STATIC
161 EFI_STATUS
162 EFIAPI
163 FindCompatibleNodeReg (
164 IN FDT_CLIENT_PROTOCOL *This,
165 IN CONST CHAR8 *CompatibleString,
166 OUT CONST VOID **Reg,
167 OUT UINTN *AddressCells,
168 OUT UINTN *SizeCells,
169 OUT UINT32 *RegSize
170 )
171 {
172 EFI_STATUS Status;
173
174 ASSERT (RegSize != NULL);
175
176 //
177 // Get the 'reg' property of this node. For now, we will assume
178 // 8 byte quantities for base and size, respectively.
179 // TODO use #cells root properties instead
180 //
181 Status = FindCompatibleNodeProperty (This, CompatibleString, "reg", Reg,
182 RegSize);
183 if (EFI_ERROR (Status)) {
184 return Status;
185 }
186
187 if ((*RegSize % 16) != 0) {
188 DEBUG ((EFI_D_ERROR,
189 "%a: '%a' compatible node has invalid 'reg' property (size == 0x%x)\n",
190 __FUNCTION__, CompatibleString, *RegSize));
191 return EFI_NOT_FOUND;
192 }
193
194 *AddressCells = 2;
195 *SizeCells = 2;
196
197 return EFI_SUCCESS;
198 }
199
200 STATIC
201 EFI_STATUS
202 EFIAPI
203 FindNextMemoryNodeReg (
204 IN FDT_CLIENT_PROTOCOL *This,
205 IN INT32 PrevNode,
206 OUT INT32 *Node,
207 OUT CONST VOID **Reg,
208 OUT UINTN *AddressCells,
209 OUT UINTN *SizeCells,
210 OUT UINT32 *RegSize
211 )
212 {
213 INT32 Prev, Next;
214 CONST CHAR8 *DeviceType;
215 INT32 Len;
216 EFI_STATUS Status;
217
218 ASSERT (mDeviceTreeBase != NULL);
219 ASSERT (Node != NULL);
220
221 for (Prev = PrevNode;; Prev = Next) {
222 Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);
223 if (Next < 0) {
224 break;
225 }
226
227 DeviceType = fdt_getprop (mDeviceTreeBase, Next, "device_type", &Len);
228 if (DeviceType != NULL && AsciiStrCmp (DeviceType, "memory") == 0) {
229 //
230 // Get the 'reg' property of this memory node. For now, we will assume
231 // 8 byte quantities for base and size, respectively.
232 // TODO use #cells root properties instead
233 //
234 Status = GetNodeProperty (This, Next, "reg", Reg, RegSize);
235 if (EFI_ERROR (Status)) {
236 DEBUG ((EFI_D_WARN,
237 "%a: ignoring memory node with no 'reg' property\n",
238 __FUNCTION__));
239 continue;
240 }
241 if ((*RegSize % 16) != 0) {
242 DEBUG ((EFI_D_WARN,
243 "%a: ignoring memory node with invalid 'reg' property (size == 0x%x)\n",
244 __FUNCTION__, *RegSize));
245 continue;
246 }
247
248 *Node = Next;
249 *AddressCells = 2;
250 *SizeCells = 2;
251 return EFI_SUCCESS;
252 }
253 }
254 return EFI_NOT_FOUND;
255 }
256
257 STATIC
258 EFI_STATUS
259 EFIAPI
260 FindMemoryNodeReg (
261 IN FDT_CLIENT_PROTOCOL *This,
262 OUT INT32 *Node,
263 OUT CONST VOID **Reg,
264 OUT UINTN *AddressCells,
265 OUT UINTN *SizeCells,
266 OUT UINT32 *RegSize
267 )
268 {
269 return FindNextMemoryNodeReg (This, 0, Node, Reg, AddressCells, SizeCells,
270 RegSize);
271 }
272
273 STATIC
274 EFI_STATUS
275 EFIAPI
276 GetOrInsertChosenNode (
277 IN FDT_CLIENT_PROTOCOL *This,
278 OUT INT32 *Node
279 )
280 {
281 INT32 NewNode;
282
283 ASSERT (mDeviceTreeBase != NULL);
284 ASSERT (Node != NULL);
285
286 NewNode = fdt_path_offset (mDeviceTreeBase, "/chosen");
287 if (NewNode < 0) {
288 NewNode = fdt_add_subnode (mDeviceTreeBase, 0, "/chosen");
289 }
290
291 if (NewNode < 0) {
292 return EFI_OUT_OF_RESOURCES;
293 }
294
295 *Node = NewNode;
296
297 return EFI_SUCCESS;
298 }
299
300 STATIC FDT_CLIENT_PROTOCOL mFdtClientProtocol = {
301 GetNodeProperty,
302 SetNodeProperty,
303 FindCompatibleNode,
304 FindNextCompatibleNode,
305 FindCompatibleNodeProperty,
306 FindCompatibleNodeReg,
307 FindMemoryNodeReg,
308 FindNextMemoryNodeReg,
309 GetOrInsertChosenNode,
310 };
311
312 STATIC
313 VOID
314 EFIAPI
315 OnReadyToBoot (
316 EFI_EVENT Event,
317 VOID *Context
318 )
319 {
320 EFI_STATUS Status;
321 VOID *Table;
322
323 //
324 // Only install the FDT as a configuration table if we are not exposing
325 // ACPI 2.0 (or later) tables. Note that the legacy ACPI table GUID has
326 // no meaning on ARM since we need at least ACPI 5.0 support, and the
327 // 64-bit ACPI 2.0 table GUID is mandatory in that case.
328 //
329 Status = EfiGetSystemConfigurationTable (&gEfiAcpi20TableGuid, &Table);
330 if (EFI_ERROR (Status) || Table == NULL) {
331 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, mDeviceTreeBase);
332 ASSERT_EFI_ERROR (Status);
333 }
334
335 gBS->CloseEvent (Event);
336 }
337
338 STATIC EFI_EVENT mReadyToBootEvent;
339
340 EFI_STATUS
341 EFIAPI
342 InitializeFdtClientDxe (
343 IN EFI_HANDLE ImageHandle,
344 IN EFI_SYSTEM_TABLE *SystemTable
345 )
346 {
347 VOID *Hob;
348 VOID *DeviceTreeBase;
349 EFI_STATUS Status;
350
351 Hob = GetFirstGuidHob (&gFdtHobGuid);
352 if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {
353 return EFI_NOT_FOUND;
354 }
355 DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);
356
357 if (fdt_check_header (DeviceTreeBase) != 0) {
358 DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__,
359 DeviceTreeBase));
360 return EFI_NOT_FOUND;
361 }
362
363 mDeviceTreeBase = DeviceTreeBase;
364
365 DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, mDeviceTreeBase));
366
367 Status = gBS->InstallProtocolInterface (&ImageHandle, &gFdtClientProtocolGuid,
368 EFI_NATIVE_INTERFACE, &mFdtClientProtocol);
369 if (EFI_ERROR (Status)) {
370 return Status;
371 }
372
373 Status = gBS->CreateEventEx (
374 EVT_NOTIFY_SIGNAL,
375 TPL_CALLBACK,
376 OnReadyToBoot,
377 NULL,
378 &gEfiEventReadyToBootGuid,
379 &mReadyToBootEvent
380 );
381 ASSERT_EFI_ERROR (Status);
382
383 return EFI_SUCCESS;
384 }