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