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