]> git.proxmox.com Git - mirror_edk2.git/blob - ArmVirtPkg/FdtClientDxe/FdtClientDxe.c
ArmVirtPkg/FdtClientDxe: install DT as sysconfig table in protocol notify
[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 INT32 Len;
214 EFI_STATUS Status;
215
216 ASSERT (mDeviceTreeBase != NULL);
217 ASSERT (Node != NULL);
218
219 for (Prev = PrevNode;; Prev = Next) {
220 Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);
221 if (Next < 0) {
222 break;
223 }
224
225 DeviceType = fdt_getprop (mDeviceTreeBase, Next, "device_type", &Len);
226 if (DeviceType != NULL && AsciiStrCmp (DeviceType, "memory") == 0) {
227 //
228 // Get the 'reg' property of this memory node. For now, we will assume
229 // 8 byte quantities for base and size, respectively.
230 // TODO use #cells root properties instead
231 //
232 Status = GetNodeProperty (This, Next, "reg", Reg, RegSize);
233 if (EFI_ERROR (Status)) {
234 DEBUG ((EFI_D_WARN,
235 "%a: ignoring memory node with no 'reg' property\n",
236 __FUNCTION__));
237 continue;
238 }
239 if ((*RegSize % 16) != 0) {
240 DEBUG ((EFI_D_WARN,
241 "%a: ignoring memory node with invalid 'reg' property (size == 0x%x)\n",
242 __FUNCTION__, *RegSize));
243 continue;
244 }
245
246 *Node = Next;
247 *AddressCells = 2;
248 *SizeCells = 2;
249 return EFI_SUCCESS;
250 }
251 }
252 return EFI_NOT_FOUND;
253 }
254
255 STATIC
256 EFI_STATUS
257 EFIAPI
258 FindMemoryNodeReg (
259 IN FDT_CLIENT_PROTOCOL *This,
260 OUT INT32 *Node,
261 OUT CONST VOID **Reg,
262 OUT UINTN *AddressCells,
263 OUT UINTN *SizeCells,
264 OUT UINT32 *RegSize
265 )
266 {
267 return FindNextMemoryNodeReg (This, 0, Node, Reg, AddressCells, SizeCells,
268 RegSize);
269 }
270
271 STATIC
272 EFI_STATUS
273 EFIAPI
274 GetOrInsertChosenNode (
275 IN FDT_CLIENT_PROTOCOL *This,
276 OUT INT32 *Node
277 )
278 {
279 INT32 NewNode;
280
281 ASSERT (mDeviceTreeBase != NULL);
282 ASSERT (Node != NULL);
283
284 NewNode = fdt_path_offset (mDeviceTreeBase, "/chosen");
285 if (NewNode < 0) {
286 NewNode = fdt_add_subnode (mDeviceTreeBase, 0, "/chosen");
287 }
288
289 if (NewNode < 0) {
290 return EFI_OUT_OF_RESOURCES;
291 }
292
293 *Node = NewNode;
294
295 return EFI_SUCCESS;
296 }
297
298 STATIC FDT_CLIENT_PROTOCOL mFdtClientProtocol = {
299 GetNodeProperty,
300 SetNodeProperty,
301 FindCompatibleNode,
302 FindNextCompatibleNode,
303 FindCompatibleNodeProperty,
304 FindCompatibleNodeReg,
305 FindMemoryNodeReg,
306 FindNextMemoryNodeReg,
307 GetOrInsertChosenNode,
308 };
309
310 STATIC
311 VOID
312 EFIAPI
313 OnPlatformHasDeviceTree (
314 IN EFI_EVENT Event,
315 IN VOID *Context
316 )
317 {
318 EFI_STATUS Status;
319 VOID *Interface;
320 VOID *DeviceTreeBase;
321
322 Status = gBS->LocateProtocol (
323 &gEdkiiPlatformHasDeviceTreeGuid,
324 NULL, // Registration
325 &Interface
326 );
327 if (EFI_ERROR (Status)) {
328 return;
329 }
330
331 DeviceTreeBase = Context;
332 DEBUG ((
333 DEBUG_INFO,
334 "%a: exposing DTB @ 0x%p to OS\n",
335 __FUNCTION__,
336 DeviceTreeBase
337 ));
338 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);
339 ASSERT_EFI_ERROR (Status);
340
341 gBS->CloseEvent (Event);
342 }
343
344 EFI_STATUS
345 EFIAPI
346 InitializeFdtClientDxe (
347 IN EFI_HANDLE ImageHandle,
348 IN EFI_SYSTEM_TABLE *SystemTable
349 )
350 {
351 VOID *Hob;
352 VOID *DeviceTreeBase;
353 EFI_STATUS Status;
354 EFI_EVENT PlatformHasDeviceTreeEvent;
355 VOID *Registration;
356
357 Hob = GetFirstGuidHob (&gFdtHobGuid);
358 if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {
359 return EFI_NOT_FOUND;
360 }
361 DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);
362
363 if (fdt_check_header (DeviceTreeBase) != 0) {
364 DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__,
365 DeviceTreeBase));
366 return EFI_NOT_FOUND;
367 }
368
369 mDeviceTreeBase = DeviceTreeBase;
370
371 DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, mDeviceTreeBase));
372
373 //
374 // Register a protocol notify for the EDKII Platform Has Device Tree
375 // Protocol.
376 //
377 Status = gBS->CreateEvent (
378 EVT_NOTIFY_SIGNAL,
379 TPL_CALLBACK,
380 OnPlatformHasDeviceTree,
381 DeviceTreeBase, // Context
382 &PlatformHasDeviceTreeEvent
383 );
384 if (EFI_ERROR (Status)) {
385 DEBUG ((DEBUG_ERROR, "%a: CreateEvent(): %r\n", __FUNCTION__, Status));
386 return Status;
387 }
388
389 Status = gBS->RegisterProtocolNotify (
390 &gEdkiiPlatformHasDeviceTreeGuid,
391 PlatformHasDeviceTreeEvent,
392 &Registration
393 );
394 if (EFI_ERROR (Status)) {
395 DEBUG ((
396 DEBUG_ERROR,
397 "%a: RegisterProtocolNotify(): %r\n",
398 __FUNCTION__,
399 Status
400 ));
401 goto CloseEvent;
402 }
403
404 //
405 // Kick the event; the protocol could be available already.
406 //
407 Status = gBS->SignalEvent (PlatformHasDeviceTreeEvent);
408 if (EFI_ERROR (Status)) {
409 DEBUG ((DEBUG_ERROR, "%a: SignalEvent(): %r\n", __FUNCTION__, Status));
410 goto CloseEvent;
411 }
412
413 Status = gBS->InstallProtocolInterface (
414 &ImageHandle,
415 &gFdtClientProtocolGuid,
416 EFI_NATIVE_INTERFACE,
417 &mFdtClientProtocol
418 );
419 if (EFI_ERROR (Status)) {
420 DEBUG ((
421 DEBUG_ERROR,
422 "%a: InstallProtocolInterface(): %r\n",
423 __FUNCTION__,
424 Status
425 ));
426 goto CloseEvent;
427 }
428
429 return Status;
430
431 CloseEvent:
432 gBS->CloseEvent (PlatformHasDeviceTreeEvent);
433 return Status;
434 }