ArmVirtPkg/FdtClientDxe: take DT node 'status' properties into account
[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/HobLib.h>
21 #include <libfdt.h>
22
23 #include <Guid/Fdt.h>
24 #include <Guid/FdtHob.h>
25 #include <Guid/PlatformHasDeviceTree.h>
26
27 #include <Protocol/FdtClient.h>
28
29 STATIC VOID *mDeviceTreeBase;
30
31 STATIC
32 EFI_STATUS
33 EFIAPI
34 GetNodeProperty (
35 IN FDT_CLIENT_PROTOCOL *This,
36 IN INT32 Node,
37 IN CONST CHAR8 *PropertyName,
38 OUT CONST VOID **Prop,
39 OUT UINT32 *PropSize OPTIONAL
40 )
41 {
42 INT32 Len;
43
44 ASSERT (mDeviceTreeBase != NULL);
45 ASSERT (Prop != NULL);
46
47 *Prop = fdt_getprop (mDeviceTreeBase, Node, PropertyName, &Len);
48 if (*Prop == NULL) {
49 return EFI_NOT_FOUND;
50 }
51
52 if (PropSize != NULL) {
53 *PropSize = Len;
54 }
55 return EFI_SUCCESS;
56 }
57
58 STATIC
59 EFI_STATUS
60 EFIAPI
61 SetNodeProperty (
62 IN FDT_CLIENT_PROTOCOL *This,
63 IN INT32 Node,
64 IN CONST CHAR8 *PropertyName,
65 IN CONST VOID *Prop,
66 IN UINT32 PropSize
67 )
68 {
69 INT32 Ret;
70
71 ASSERT (mDeviceTreeBase != NULL);
72
73 Ret = fdt_setprop (mDeviceTreeBase, Node, PropertyName, Prop, PropSize);
74 if (Ret != 0) {
75 return EFI_DEVICE_ERROR;
76 }
77
78 return EFI_SUCCESS;
79 }
80
81 STATIC
82 BOOLEAN
83 IsNodeEnabled (
84 INT32 Node
85 )
86 {
87 CONST CHAR8 *NodeStatus;
88 INT32 Len;
89
90 //
91 // A missing status property implies 'ok' so ignore any errors that
92 // may occur here. If the status property is present, check whether
93 // it is set to 'ok' or 'okay', anything else is treated as 'disabled'.
94 //
95 NodeStatus = fdt_getprop (mDeviceTreeBase, Node, "status", &Len);
96 if (NodeStatus == NULL) {
97 return TRUE;
98 }
99 if (Len >= 5 && AsciiStrCmp (NodeStatus, "okay") == 0) {
100 return TRUE;
101 }
102 if (Len >= 3 && AsciiStrCmp (NodeStatus, "ok") == 0) {
103 return TRUE;
104 }
105 return FALSE;
106 }
107
108 STATIC
109 EFI_STATUS
110 EFIAPI
111 FindNextCompatibleNode (
112 IN FDT_CLIENT_PROTOCOL *This,
113 IN CONST CHAR8 *CompatibleString,
114 IN INT32 PrevNode,
115 OUT INT32 *Node
116 )
117 {
118 INT32 Prev, Next;
119 CONST CHAR8 *Type, *Compatible;
120 INT32 Len;
121
122 ASSERT (mDeviceTreeBase != NULL);
123 ASSERT (Node != NULL);
124
125 for (Prev = PrevNode;; Prev = Next) {
126 Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);
127 if (Next < 0) {
128 break;
129 }
130
131 if (!IsNodeEnabled (Next)) {
132 continue;
133 }
134
135 Type = fdt_getprop (mDeviceTreeBase, Next, "compatible", &Len);
136 if (Type == NULL) {
137 continue;
138 }
139
140 //
141 // A 'compatible' node may contain a sequence of NUL terminated
142 // compatible strings so check each one
143 //
144 for (Compatible = Type; Compatible < Type + Len && *Compatible;
145 Compatible += 1 + AsciiStrLen (Compatible)) {
146 if (AsciiStrCmp (CompatibleString, Compatible) == 0) {
147 *Node = Next;
148 return EFI_SUCCESS;
149 }
150 }
151 }
152 return EFI_NOT_FOUND;
153 }
154
155 STATIC
156 EFI_STATUS
157 EFIAPI
158 FindCompatibleNode (
159 IN FDT_CLIENT_PROTOCOL *This,
160 IN CONST CHAR8 *CompatibleString,
161 OUT INT32 *Node
162 )
163 {
164 return FindNextCompatibleNode (This, CompatibleString, 0, Node);
165 }
166
167 STATIC
168 EFI_STATUS
169 EFIAPI
170 FindCompatibleNodeProperty (
171 IN FDT_CLIENT_PROTOCOL *This,
172 IN CONST CHAR8 *CompatibleString,
173 IN CONST CHAR8 *PropertyName,
174 OUT CONST VOID **Prop,
175 OUT UINT32 *PropSize OPTIONAL
176 )
177 {
178 EFI_STATUS Status;
179 INT32 Node;
180
181 Status = FindCompatibleNode (This, CompatibleString, &Node);
182 if (EFI_ERROR (Status)) {
183 return Status;
184 }
185
186 return GetNodeProperty (This, Node, PropertyName, Prop, PropSize);
187 }
188
189 STATIC
190 EFI_STATUS
191 EFIAPI
192 FindCompatibleNodeReg (
193 IN FDT_CLIENT_PROTOCOL *This,
194 IN CONST CHAR8 *CompatibleString,
195 OUT CONST VOID **Reg,
196 OUT UINTN *AddressCells,
197 OUT UINTN *SizeCells,
198 OUT UINT32 *RegSize
199 )
200 {
201 EFI_STATUS Status;
202
203 ASSERT (RegSize != NULL);
204
205 //
206 // Get the 'reg' property of this node. For now, we will assume
207 // 8 byte quantities for base and size, respectively.
208 // TODO use #cells root properties instead
209 //
210 Status = FindCompatibleNodeProperty (This, CompatibleString, "reg", Reg,
211 RegSize);
212 if (EFI_ERROR (Status)) {
213 return Status;
214 }
215
216 if ((*RegSize % 16) != 0) {
217 DEBUG ((EFI_D_ERROR,
218 "%a: '%a' compatible node has invalid 'reg' property (size == 0x%x)\n",
219 __FUNCTION__, CompatibleString, *RegSize));
220 return EFI_NOT_FOUND;
221 }
222
223 *AddressCells = 2;
224 *SizeCells = 2;
225
226 return EFI_SUCCESS;
227 }
228
229 STATIC
230 EFI_STATUS
231 EFIAPI
232 FindNextMemoryNodeReg (
233 IN FDT_CLIENT_PROTOCOL *This,
234 IN INT32 PrevNode,
235 OUT INT32 *Node,
236 OUT CONST VOID **Reg,
237 OUT UINTN *AddressCells,
238 OUT UINTN *SizeCells,
239 OUT UINT32 *RegSize
240 )
241 {
242 INT32 Prev, Next;
243 CONST CHAR8 *DeviceType;
244 INT32 Len;
245 EFI_STATUS Status;
246
247 ASSERT (mDeviceTreeBase != NULL);
248 ASSERT (Node != NULL);
249
250 for (Prev = PrevNode;; Prev = Next) {
251 Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);
252 if (Next < 0) {
253 break;
254 }
255
256 if (!IsNodeEnabled (Next)) {
257 DEBUG ((DEBUG_WARN, "%a: ignoring disabled memory node\n", __FUNCTION__));
258 continue;
259 }
260
261 DeviceType = fdt_getprop (mDeviceTreeBase, Next, "device_type", &Len);
262 if (DeviceType != NULL && AsciiStrCmp (DeviceType, "memory") == 0) {
263 //
264 // Get the 'reg' property of this memory node. For now, we will assume
265 // 8 byte quantities for base and size, respectively.
266 // TODO use #cells root properties instead
267 //
268 Status = GetNodeProperty (This, Next, "reg", Reg, RegSize);
269 if (EFI_ERROR (Status)) {
270 DEBUG ((EFI_D_WARN,
271 "%a: ignoring memory node with no 'reg' property\n",
272 __FUNCTION__));
273 continue;
274 }
275 if ((*RegSize % 16) != 0) {
276 DEBUG ((EFI_D_WARN,
277 "%a: ignoring memory node with invalid 'reg' property (size == 0x%x)\n",
278 __FUNCTION__, *RegSize));
279 continue;
280 }
281
282 *Node = Next;
283 *AddressCells = 2;
284 *SizeCells = 2;
285 return EFI_SUCCESS;
286 }
287 }
288 return EFI_NOT_FOUND;
289 }
290
291 STATIC
292 EFI_STATUS
293 EFIAPI
294 FindMemoryNodeReg (
295 IN FDT_CLIENT_PROTOCOL *This,
296 OUT INT32 *Node,
297 OUT CONST VOID **Reg,
298 OUT UINTN *AddressCells,
299 OUT UINTN *SizeCells,
300 OUT UINT32 *RegSize
301 )
302 {
303 return FindNextMemoryNodeReg (This, 0, Node, Reg, AddressCells, SizeCells,
304 RegSize);
305 }
306
307 STATIC
308 EFI_STATUS
309 EFIAPI
310 GetOrInsertChosenNode (
311 IN FDT_CLIENT_PROTOCOL *This,
312 OUT INT32 *Node
313 )
314 {
315 INT32 NewNode;
316
317 ASSERT (mDeviceTreeBase != NULL);
318 ASSERT (Node != NULL);
319
320 NewNode = fdt_path_offset (mDeviceTreeBase, "/chosen");
321 if (NewNode < 0) {
322 NewNode = fdt_add_subnode (mDeviceTreeBase, 0, "/chosen");
323 }
324
325 if (NewNode < 0) {
326 return EFI_OUT_OF_RESOURCES;
327 }
328
329 *Node = NewNode;
330
331 return EFI_SUCCESS;
332 }
333
334 STATIC FDT_CLIENT_PROTOCOL mFdtClientProtocol = {
335 GetNodeProperty,
336 SetNodeProperty,
337 FindCompatibleNode,
338 FindNextCompatibleNode,
339 FindCompatibleNodeProperty,
340 FindCompatibleNodeReg,
341 FindMemoryNodeReg,
342 FindNextMemoryNodeReg,
343 GetOrInsertChosenNode,
344 };
345
346 STATIC
347 VOID
348 EFIAPI
349 OnPlatformHasDeviceTree (
350 IN EFI_EVENT Event,
351 IN VOID *Context
352 )
353 {
354 EFI_STATUS Status;
355 VOID *Interface;
356 VOID *DeviceTreeBase;
357
358 Status = gBS->LocateProtocol (
359 &gEdkiiPlatformHasDeviceTreeGuid,
360 NULL, // Registration
361 &Interface
362 );
363 if (EFI_ERROR (Status)) {
364 return;
365 }
366
367 DeviceTreeBase = Context;
368 DEBUG ((
369 DEBUG_INFO,
370 "%a: exposing DTB @ 0x%p to OS\n",
371 __FUNCTION__,
372 DeviceTreeBase
373 ));
374 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);
375 ASSERT_EFI_ERROR (Status);
376
377 gBS->CloseEvent (Event);
378 }
379
380 EFI_STATUS
381 EFIAPI
382 InitializeFdtClientDxe (
383 IN EFI_HANDLE ImageHandle,
384 IN EFI_SYSTEM_TABLE *SystemTable
385 )
386 {
387 VOID *Hob;
388 VOID *DeviceTreeBase;
389 EFI_STATUS Status;
390 EFI_EVENT PlatformHasDeviceTreeEvent;
391 VOID *Registration;
392
393 Hob = GetFirstGuidHob (&gFdtHobGuid);
394 if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {
395 return EFI_NOT_FOUND;
396 }
397 DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);
398
399 if (fdt_check_header (DeviceTreeBase) != 0) {
400 DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__,
401 DeviceTreeBase));
402 return EFI_NOT_FOUND;
403 }
404
405 mDeviceTreeBase = DeviceTreeBase;
406
407 DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, mDeviceTreeBase));
408
409 //
410 // Register a protocol notify for the EDKII Platform Has Device Tree
411 // Protocol.
412 //
413 Status = gBS->CreateEvent (
414 EVT_NOTIFY_SIGNAL,
415 TPL_CALLBACK,
416 OnPlatformHasDeviceTree,
417 DeviceTreeBase, // Context
418 &PlatformHasDeviceTreeEvent
419 );
420 if (EFI_ERROR (Status)) {
421 DEBUG ((DEBUG_ERROR, "%a: CreateEvent(): %r\n", __FUNCTION__, Status));
422 return Status;
423 }
424
425 Status = gBS->RegisterProtocolNotify (
426 &gEdkiiPlatformHasDeviceTreeGuid,
427 PlatformHasDeviceTreeEvent,
428 &Registration
429 );
430 if (EFI_ERROR (Status)) {
431 DEBUG ((
432 DEBUG_ERROR,
433 "%a: RegisterProtocolNotify(): %r\n",
434 __FUNCTION__,
435 Status
436 ));
437 goto CloseEvent;
438 }
439
440 //
441 // Kick the event; the protocol could be available already.
442 //
443 Status = gBS->SignalEvent (PlatformHasDeviceTreeEvent);
444 if (EFI_ERROR (Status)) {
445 DEBUG ((DEBUG_ERROR, "%a: SignalEvent(): %r\n", __FUNCTION__, Status));
446 goto CloseEvent;
447 }
448
449 Status = gBS->InstallProtocolInterface (
450 &ImageHandle,
451 &gFdtClientProtocolGuid,
452 EFI_NATIVE_INTERFACE,
453 &mFdtClientProtocol
454 );
455 if (EFI_ERROR (Status)) {
456 DEBUG ((
457 DEBUG_ERROR,
458 "%a: InstallProtocolInterface(): %r\n",
459 __FUNCTION__,
460 Status
461 ));
462 goto CloseEvent;
463 }
464
465 return Status;
466
467 CloseEvent:
468 gBS->CloseEvent (PlatformHasDeviceTreeEvent);
469 return Status;
470 }