4 * Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
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
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.
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>
24 #include <Guid/FdtHob.h>
25 #include <Guid/PlatformHasDeviceTree.h>
27 #include <Protocol/FdtClient.h>
29 STATIC VOID
*mDeviceTreeBase
;
35 IN FDT_CLIENT_PROTOCOL
*This
,
37 IN CONST CHAR8
*PropertyName
,
38 OUT CONST VOID
**Prop
,
39 OUT UINT32
*PropSize OPTIONAL
44 ASSERT (mDeviceTreeBase
!= NULL
);
45 ASSERT (Prop
!= NULL
);
47 *Prop
= fdt_getprop (mDeviceTreeBase
, Node
, PropertyName
, &Len
);
52 if (PropSize
!= NULL
) {
62 IN FDT_CLIENT_PROTOCOL
*This
,
64 IN CONST CHAR8
*PropertyName
,
71 ASSERT (mDeviceTreeBase
!= NULL
);
73 Ret
= fdt_setprop (mDeviceTreeBase
, Node
, PropertyName
, Prop
, PropSize
);
75 return EFI_DEVICE_ERROR
;
87 CONST CHAR8
*NodeStatus
;
91 // A missing status property implies 'ok' so ignore any errors that
92 // may occur here. If the status property is present, check whether
93 // it is set to 'ok' or 'okay', anything else is treated as 'disabled'.
95 NodeStatus
= fdt_getprop (mDeviceTreeBase
, Node
, "status", &Len
);
96 if (NodeStatus
== NULL
) {
99 if (Len
>= 5 && AsciiStrCmp (NodeStatus
, "okay") == 0) {
102 if (Len
>= 3 && AsciiStrCmp (NodeStatus
, "ok") == 0) {
111 FindNextCompatibleNode (
112 IN FDT_CLIENT_PROTOCOL
*This
,
113 IN CONST CHAR8
*CompatibleString
,
119 CONST CHAR8
*Type
, *Compatible
;
122 ASSERT (mDeviceTreeBase
!= NULL
);
123 ASSERT (Node
!= NULL
);
125 for (Prev
= PrevNode
;; Prev
= Next
) {
126 Next
= fdt_next_node (mDeviceTreeBase
, Prev
, NULL
);
131 if (!IsNodeEnabled (Next
)) {
135 Type
= fdt_getprop (mDeviceTreeBase
, Next
, "compatible", &Len
);
141 // A 'compatible' node may contain a sequence of NUL terminated
142 // compatible strings so check each one
144 for (Compatible
= Type
; Compatible
< Type
+ Len
&& *Compatible
;
145 Compatible
+= 1 + AsciiStrLen (Compatible
)) {
146 if (AsciiStrCmp (CompatibleString
, Compatible
) == 0) {
152 return EFI_NOT_FOUND
;
159 IN FDT_CLIENT_PROTOCOL
*This
,
160 IN CONST CHAR8
*CompatibleString
,
164 return FindNextCompatibleNode (This
, CompatibleString
, 0, Node
);
170 FindCompatibleNodeProperty (
171 IN FDT_CLIENT_PROTOCOL
*This
,
172 IN CONST CHAR8
*CompatibleString
,
173 IN CONST CHAR8
*PropertyName
,
174 OUT CONST VOID
**Prop
,
175 OUT UINT32
*PropSize OPTIONAL
181 Status
= FindCompatibleNode (This
, CompatibleString
, &Node
);
182 if (EFI_ERROR (Status
)) {
186 return GetNodeProperty (This
, Node
, PropertyName
, Prop
, PropSize
);
192 FindCompatibleNodeReg (
193 IN FDT_CLIENT_PROTOCOL
*This
,
194 IN CONST CHAR8
*CompatibleString
,
195 OUT CONST VOID
**Reg
,
196 OUT UINTN
*AddressCells
,
197 OUT UINTN
*SizeCells
,
203 ASSERT (RegSize
!= NULL
);
206 // Get the 'reg' property of this node. For now, we will assume
207 // 8 byte quantities for base and size, respectively.
208 // TODO use #cells root properties instead
210 Status
= FindCompatibleNodeProperty (This
, CompatibleString
, "reg", Reg
,
212 if (EFI_ERROR (Status
)) {
216 if ((*RegSize
% 16) != 0) {
218 "%a: '%a' compatible node has invalid 'reg' property (size == 0x%x)\n",
219 __FUNCTION__
, CompatibleString
, *RegSize
));
220 return EFI_NOT_FOUND
;
232 FindNextMemoryNodeReg (
233 IN FDT_CLIENT_PROTOCOL
*This
,
236 OUT CONST VOID
**Reg
,
237 OUT UINTN
*AddressCells
,
238 OUT UINTN
*SizeCells
,
243 CONST CHAR8
*DeviceType
;
247 ASSERT (mDeviceTreeBase
!= NULL
);
248 ASSERT (Node
!= NULL
);
250 for (Prev
= PrevNode
;; Prev
= Next
) {
251 Next
= fdt_next_node (mDeviceTreeBase
, Prev
, NULL
);
256 if (!IsNodeEnabled (Next
)) {
257 DEBUG ((DEBUG_WARN
, "%a: ignoring disabled memory node\n", __FUNCTION__
));
261 DeviceType
= fdt_getprop (mDeviceTreeBase
, Next
, "device_type", &Len
);
262 if (DeviceType
!= NULL
&& AsciiStrCmp (DeviceType
, "memory") == 0) {
264 // Get the 'reg' property of this memory node. For now, we will assume
265 // 8 byte quantities for base and size, respectively.
266 // TODO use #cells root properties instead
268 Status
= GetNodeProperty (This
, Next
, "reg", Reg
, RegSize
);
269 if (EFI_ERROR (Status
)) {
271 "%a: ignoring memory node with no 'reg' property\n",
275 if ((*RegSize
% 16) != 0) {
277 "%a: ignoring memory node with invalid 'reg' property (size == 0x%x)\n",
278 __FUNCTION__
, *RegSize
));
288 return EFI_NOT_FOUND
;
295 IN FDT_CLIENT_PROTOCOL
*This
,
297 OUT CONST VOID
**Reg
,
298 OUT UINTN
*AddressCells
,
299 OUT UINTN
*SizeCells
,
303 return FindNextMemoryNodeReg (This
, 0, Node
, Reg
, AddressCells
, SizeCells
,
310 GetOrInsertChosenNode (
311 IN FDT_CLIENT_PROTOCOL
*This
,
317 ASSERT (mDeviceTreeBase
!= NULL
);
318 ASSERT (Node
!= NULL
);
320 NewNode
= fdt_path_offset (mDeviceTreeBase
, "/chosen");
322 NewNode
= fdt_add_subnode (mDeviceTreeBase
, 0, "/chosen");
326 return EFI_OUT_OF_RESOURCES
;
334 STATIC FDT_CLIENT_PROTOCOL mFdtClientProtocol
= {
338 FindNextCompatibleNode
,
339 FindCompatibleNodeProperty
,
340 FindCompatibleNodeReg
,
342 FindNextMemoryNodeReg
,
343 GetOrInsertChosenNode
,
349 OnPlatformHasDeviceTree (
356 VOID
*DeviceTreeBase
;
358 Status
= gBS
->LocateProtocol (
359 &gEdkiiPlatformHasDeviceTreeGuid
,
360 NULL
, // Registration
363 if (EFI_ERROR (Status
)) {
367 DeviceTreeBase
= Context
;
370 "%a: exposing DTB @ 0x%p to OS\n",
374 Status
= gBS
->InstallConfigurationTable (&gFdtTableGuid
, DeviceTreeBase
);
375 ASSERT_EFI_ERROR (Status
);
377 gBS
->CloseEvent (Event
);
382 InitializeFdtClientDxe (
383 IN EFI_HANDLE ImageHandle
,
384 IN EFI_SYSTEM_TABLE
*SystemTable
388 VOID
*DeviceTreeBase
;
390 EFI_EVENT PlatformHasDeviceTreeEvent
;
393 Hob
= GetFirstGuidHob (&gFdtHobGuid
);
394 if (Hob
== NULL
|| GET_GUID_HOB_DATA_SIZE (Hob
) != sizeof (UINT64
)) {
395 return EFI_NOT_FOUND
;
397 DeviceTreeBase
= (VOID
*)(UINTN
)*(UINT64
*)GET_GUID_HOB_DATA (Hob
);
399 if (fdt_check_header (DeviceTreeBase
) != 0) {
400 DEBUG ((EFI_D_ERROR
, "%a: No DTB found @ 0x%p\n", __FUNCTION__
,
402 return EFI_NOT_FOUND
;
405 mDeviceTreeBase
= DeviceTreeBase
;
407 DEBUG ((EFI_D_INFO
, "%a: DTB @ 0x%p\n", __FUNCTION__
, mDeviceTreeBase
));
410 // Register a protocol notify for the EDKII Platform Has Device Tree
413 Status
= gBS
->CreateEvent (
416 OnPlatformHasDeviceTree
,
417 DeviceTreeBase
, // Context
418 &PlatformHasDeviceTreeEvent
420 if (EFI_ERROR (Status
)) {
421 DEBUG ((DEBUG_ERROR
, "%a: CreateEvent(): %r\n", __FUNCTION__
, Status
));
425 Status
= gBS
->RegisterProtocolNotify (
426 &gEdkiiPlatformHasDeviceTreeGuid
,
427 PlatformHasDeviceTreeEvent
,
430 if (EFI_ERROR (Status
)) {
433 "%a: RegisterProtocolNotify(): %r\n",
441 // Kick the event; the protocol could be available already.
443 Status
= gBS
->SignalEvent (PlatformHasDeviceTreeEvent
);
444 if (EFI_ERROR (Status
)) {
445 DEBUG ((DEBUG_ERROR
, "%a: SignalEvent(): %r\n", __FUNCTION__
, Status
));
449 Status
= gBS
->InstallProtocolInterface (
451 &gFdtClientProtocolGuid
,
452 EFI_NATIVE_INTERFACE
,
455 if (EFI_ERROR (Status
)) {
458 "%a: InstallProtocolInterface(): %r\n",
468 gBS
->CloseEvent (PlatformHasDeviceTreeEvent
);