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