4 * Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
6 * SPDX-License-Identifier: BSD-2-Clause-Patent
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>
18 #include <Guid/FdtHob.h>
19 #include <Guid/PlatformHasDeviceTree.h>
21 #include <Protocol/FdtClient.h>
23 STATIC VOID
*mDeviceTreeBase
;
29 IN FDT_CLIENT_PROTOCOL
*This
,
31 IN CONST CHAR8
*PropertyName
,
32 OUT CONST VOID
**Prop
,
33 OUT UINT32
*PropSize OPTIONAL
38 ASSERT (mDeviceTreeBase
!= NULL
);
39 ASSERT (Prop
!= NULL
);
41 *Prop
= fdt_getprop (mDeviceTreeBase
, Node
, PropertyName
, &Len
);
46 if (PropSize
!= NULL
) {
56 IN FDT_CLIENT_PROTOCOL
*This
,
58 IN CONST CHAR8
*PropertyName
,
65 ASSERT (mDeviceTreeBase
!= NULL
);
67 Ret
= fdt_setprop (mDeviceTreeBase
, Node
, PropertyName
, Prop
, PropSize
);
69 return EFI_DEVICE_ERROR
;
81 CONST CHAR8
*NodeStatus
;
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'.
89 NodeStatus
= fdt_getprop (mDeviceTreeBase
, Node
, "status", &Len
);
90 if (NodeStatus
== NULL
) {
93 if (Len
>= 5 && AsciiStrCmp (NodeStatus
, "okay") == 0) {
96 if (Len
>= 3 && AsciiStrCmp (NodeStatus
, "ok") == 0) {
105 FindNextCompatibleNode (
106 IN FDT_CLIENT_PROTOCOL
*This
,
107 IN CONST CHAR8
*CompatibleString
,
113 CONST CHAR8
*Type
, *Compatible
;
116 ASSERT (mDeviceTreeBase
!= NULL
);
117 ASSERT (Node
!= NULL
);
119 for (Prev
= PrevNode
;; Prev
= Next
) {
120 Next
= fdt_next_node (mDeviceTreeBase
, Prev
, NULL
);
125 if (!IsNodeEnabled (Next
)) {
129 Type
= fdt_getprop (mDeviceTreeBase
, Next
, "compatible", &Len
);
135 // A 'compatible' node may contain a sequence of NUL terminated
136 // compatible strings so check each one
138 for (Compatible
= Type
; Compatible
< Type
+ Len
&& *Compatible
;
139 Compatible
+= 1 + AsciiStrLen (Compatible
)) {
140 if (AsciiStrCmp (CompatibleString
, Compatible
) == 0) {
146 return EFI_NOT_FOUND
;
153 IN FDT_CLIENT_PROTOCOL
*This
,
154 IN CONST CHAR8
*CompatibleString
,
158 return FindNextCompatibleNode (This
, CompatibleString
, 0, Node
);
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
175 Status
= FindCompatibleNode (This
, CompatibleString
, &Node
);
176 if (EFI_ERROR (Status
)) {
180 return GetNodeProperty (This
, Node
, PropertyName
, Prop
, PropSize
);
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
,
197 ASSERT (RegSize
!= NULL
);
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
204 Status
= FindCompatibleNodeProperty (This
, CompatibleString
, "reg", Reg
,
206 if (EFI_ERROR (Status
)) {
210 if ((*RegSize
% 16) != 0) {
212 "%a: '%a' compatible node has invalid 'reg' property (size == 0x%x)\n",
213 __FUNCTION__
, CompatibleString
, *RegSize
));
214 return EFI_NOT_FOUND
;
226 FindNextMemoryNodeReg (
227 IN FDT_CLIENT_PROTOCOL
*This
,
230 OUT CONST VOID
**Reg
,
231 OUT UINTN
*AddressCells
,
232 OUT UINTN
*SizeCells
,
237 CONST CHAR8
*DeviceType
;
241 ASSERT (mDeviceTreeBase
!= NULL
);
242 ASSERT (Node
!= NULL
);
244 for (Prev
= PrevNode
;; Prev
= Next
) {
245 Next
= fdt_next_node (mDeviceTreeBase
, Prev
, NULL
);
250 if (!IsNodeEnabled (Next
)) {
251 DEBUG ((DEBUG_WARN
, "%a: ignoring disabled memory node\n", __FUNCTION__
));
255 DeviceType
= fdt_getprop (mDeviceTreeBase
, Next
, "device_type", &Len
);
256 if (DeviceType
!= NULL
&& AsciiStrCmp (DeviceType
, "memory") == 0) {
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
262 Status
= GetNodeProperty (This
, Next
, "reg", Reg
, RegSize
);
263 if (EFI_ERROR (Status
)) {
265 "%a: ignoring memory node with no 'reg' property\n",
269 if ((*RegSize
% 16) != 0) {
271 "%a: ignoring memory node with invalid 'reg' property (size == 0x%x)\n",
272 __FUNCTION__
, *RegSize
));
282 return EFI_NOT_FOUND
;
289 IN FDT_CLIENT_PROTOCOL
*This
,
291 OUT CONST VOID
**Reg
,
292 OUT UINTN
*AddressCells
,
293 OUT UINTN
*SizeCells
,
297 return FindNextMemoryNodeReg (This
, 0, Node
, Reg
, AddressCells
, SizeCells
,
304 GetOrInsertChosenNode (
305 IN FDT_CLIENT_PROTOCOL
*This
,
311 ASSERT (mDeviceTreeBase
!= NULL
);
312 ASSERT (Node
!= NULL
);
314 NewNode
= fdt_path_offset (mDeviceTreeBase
, "/chosen");
316 NewNode
= fdt_add_subnode (mDeviceTreeBase
, 0, "/chosen");
320 return EFI_OUT_OF_RESOURCES
;
328 STATIC FDT_CLIENT_PROTOCOL mFdtClientProtocol
= {
332 FindNextCompatibleNode
,
333 FindCompatibleNodeProperty
,
334 FindCompatibleNodeReg
,
336 FindNextMemoryNodeReg
,
337 GetOrInsertChosenNode
,
343 OnPlatformHasDeviceTree (
350 VOID
*DeviceTreeBase
;
352 Status
= gBS
->LocateProtocol (
353 &gEdkiiPlatformHasDeviceTreeGuid
,
354 NULL
, // Registration
357 if (EFI_ERROR (Status
)) {
361 DeviceTreeBase
= Context
;
364 "%a: exposing DTB @ 0x%p to OS\n",
368 Status
= gBS
->InstallConfigurationTable (&gFdtTableGuid
, DeviceTreeBase
);
369 ASSERT_EFI_ERROR (Status
);
371 gBS
->CloseEvent (Event
);
376 InitializeFdtClientDxe (
377 IN EFI_HANDLE ImageHandle
,
378 IN EFI_SYSTEM_TABLE
*SystemTable
382 VOID
*DeviceTreeBase
;
384 EFI_EVENT PlatformHasDeviceTreeEvent
;
387 Hob
= GetFirstGuidHob (&gFdtHobGuid
);
388 if (Hob
== NULL
|| GET_GUID_HOB_DATA_SIZE (Hob
) != sizeof (UINT64
)) {
389 return EFI_NOT_FOUND
;
391 DeviceTreeBase
= (VOID
*)(UINTN
)*(UINT64
*)GET_GUID_HOB_DATA (Hob
);
393 if (fdt_check_header (DeviceTreeBase
) != 0) {
394 DEBUG ((EFI_D_ERROR
, "%a: No DTB found @ 0x%p\n", __FUNCTION__
,
396 return EFI_NOT_FOUND
;
399 mDeviceTreeBase
= DeviceTreeBase
;
401 DEBUG ((EFI_D_INFO
, "%a: DTB @ 0x%p\n", __FUNCTION__
, mDeviceTreeBase
));
404 // Register a protocol notify for the EDKII Platform Has Device Tree
407 Status
= gBS
->CreateEvent (
410 OnPlatformHasDeviceTree
,
411 DeviceTreeBase
, // Context
412 &PlatformHasDeviceTreeEvent
414 if (EFI_ERROR (Status
)) {
415 DEBUG ((DEBUG_ERROR
, "%a: CreateEvent(): %r\n", __FUNCTION__
, Status
));
419 Status
= gBS
->RegisterProtocolNotify (
420 &gEdkiiPlatformHasDeviceTreeGuid
,
421 PlatformHasDeviceTreeEvent
,
424 if (EFI_ERROR (Status
)) {
427 "%a: RegisterProtocolNotify(): %r\n",
435 // Kick the event; the protocol could be available already.
437 Status
= gBS
->SignalEvent (PlatformHasDeviceTreeEvent
);
438 if (EFI_ERROR (Status
)) {
439 DEBUG ((DEBUG_ERROR
, "%a: SignalEvent(): %r\n", __FUNCTION__
, Status
));
443 Status
= gBS
->InstallProtocolInterface (
445 &gFdtClientProtocolGuid
,
446 EFI_NATIVE_INTERFACE
,
449 if (EFI_ERROR (Status
)) {
452 "%a: InstallProtocolInterface(): %r\n",
462 gBS
->CloseEvent (PlatformHasDeviceTreeEvent
);