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