ArmVirtPkg/FdtClientDxe: install DT configuration table at ReadyToBoot
[mirror_edk2.git] / ArmVirtPkg / FdtClientDxe / FdtClientDxe.c
CommitLineData
30740795
AB
1/** @file\r
2* FDT client driver\r
3*\r
4* Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>\r
5*\r
6* This program and the accompanying materials are\r
7* licensed and made available under the terms and conditions of the BSD License\r
8* which accompanies this distribution. The full text of the license may be found at\r
9* http://opensource.org/licenses/bsd-license.php\r
10*\r
11* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13*\r
14**/\r
15\r
16#include <Library/BaseLib.h>\r
17#include <Library/DebugLib.h>\r
18#include <Library/UefiDriverEntryPoint.h>\r
19#include <Library/UefiBootServicesTableLib.h>\r
20#include <Library/HobLib.h>\r
21#include <libfdt.h>\r
22\r
18f6d4df 23#include <Guid/EventGroup.h>\r
1e7143d8 24#include <Guid/Fdt.h>\r
30740795
AB
25#include <Guid/FdtHob.h>\r
26\r
27#include <Protocol/FdtClient.h>\r
28\r
29STATIC VOID *mDeviceTreeBase;\r
30\r
31STATIC\r
32EFI_STATUS\r
eec1ba7d 33EFIAPI\r
30740795
AB
34GetNodeProperty (\r
35 IN FDT_CLIENT_PROTOCOL *This,\r
36 IN INT32 Node,\r
37 IN CONST CHAR8 *PropertyName,\r
38 OUT CONST VOID **Prop,\r
39 OUT UINT32 *PropSize OPTIONAL\r
40 )\r
41{\r
42 INT32 Len;\r
43\r
44 ASSERT (mDeviceTreeBase != NULL);\r
45 ASSERT (Prop != NULL);\r
46\r
47 *Prop = fdt_getprop (mDeviceTreeBase, Node, PropertyName, &Len);\r
48 if (*Prop == NULL) {\r
49 return EFI_NOT_FOUND;\r
50 }\r
51\r
52 if (PropSize != NULL) {\r
53 *PropSize = Len;\r
54 }\r
55 return EFI_SUCCESS;\r
56}\r
57\r
58STATIC\r
59EFI_STATUS\r
eec1ba7d 60EFIAPI\r
30740795
AB
61SetNodeProperty (\r
62 IN FDT_CLIENT_PROTOCOL *This,\r
63 IN INT32 Node,\r
64 IN CONST CHAR8 *PropertyName,\r
65 IN CONST VOID *Prop,\r
66 IN UINT32 PropSize\r
67 )\r
68{\r
69 INT32 Ret;\r
70\r
71 ASSERT (mDeviceTreeBase != NULL);\r
72\r
73 Ret = fdt_setprop (mDeviceTreeBase, Node, PropertyName, Prop, PropSize);\r
74 if (Ret != 0) {\r
75 return EFI_DEVICE_ERROR;\r
76 }\r
77\r
78 return EFI_SUCCESS;\r
79}\r
80\r
81STATIC\r
82EFI_STATUS\r
83EFIAPI\r
84FindNextCompatibleNode (\r
85 IN FDT_CLIENT_PROTOCOL *This,\r
86 IN CONST CHAR8 *CompatibleString,\r
87 IN INT32 PrevNode,\r
88 OUT INT32 *Node\r
89 )\r
90{\r
91 INT32 Prev, Next;\r
92 CONST CHAR8 *Type, *Compatible;\r
93 INT32 Len;\r
94\r
95 ASSERT (mDeviceTreeBase != NULL);\r
96 ASSERT (Node != NULL);\r
97\r
98 for (Prev = PrevNode;; Prev = Next) {\r
99 Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);\r
100 if (Next < 0) {\r
101 break;\r
102 }\r
103\r
104 Type = fdt_getprop (mDeviceTreeBase, Next, "compatible", &Len);\r
105 if (Type == NULL) {\r
106 continue;\r
107 }\r
108\r
109 //\r
110 // A 'compatible' node may contain a sequence of NUL terminated\r
111 // compatible strings so check each one\r
112 //\r
113 for (Compatible = Type; Compatible < Type + Len && *Compatible;\r
114 Compatible += 1 + AsciiStrLen (Compatible)) {\r
115 if (AsciiStrCmp (CompatibleString, Compatible) == 0) {\r
116 *Node = Next;\r
117 return EFI_SUCCESS;\r
118 }\r
119 }\r
120 }\r
121 return EFI_NOT_FOUND;\r
122}\r
123\r
124STATIC\r
125EFI_STATUS\r
126EFIAPI\r
127FindCompatibleNode (\r
128 IN FDT_CLIENT_PROTOCOL *This,\r
129 IN CONST CHAR8 *CompatibleString,\r
130 OUT INT32 *Node\r
131 )\r
132{\r
133 return FindNextCompatibleNode (This, CompatibleString, 0, Node);\r
134}\r
135\r
136STATIC\r
137EFI_STATUS\r
138EFIAPI\r
139FindCompatibleNodeProperty (\r
140 IN FDT_CLIENT_PROTOCOL *This,\r
141 IN CONST CHAR8 *CompatibleString,\r
142 IN CONST CHAR8 *PropertyName,\r
143 OUT CONST VOID **Prop,\r
144 OUT UINT32 *PropSize OPTIONAL\r
145 )\r
146{\r
147 EFI_STATUS Status;\r
148 INT32 Node;\r
149\r
150 Status = FindCompatibleNode (This, CompatibleString, &Node);\r
151 if (EFI_ERROR (Status)) {\r
152 return Status;\r
153 }\r
154\r
155 return GetNodeProperty (This, Node, PropertyName, Prop, PropSize);\r
156}\r
157\r
158STATIC\r
159EFI_STATUS\r
160EFIAPI\r
161FindCompatibleNodeReg (\r
162 IN FDT_CLIENT_PROTOCOL *This,\r
163 IN CONST CHAR8 *CompatibleString,\r
164 OUT CONST VOID **Reg,\r
cfc8d51c
AB
165 OUT UINTN *AddressCells,\r
166 OUT UINTN *SizeCells,\r
30740795
AB
167 OUT UINT32 *RegSize\r
168 )\r
169{\r
170 EFI_STATUS Status;\r
171\r
172 ASSERT (RegSize != NULL);\r
173\r
174 //\r
175 // Get the 'reg' property of this node. For now, we will assume\r
176 // 8 byte quantities for base and size, respectively.\r
177 // TODO use #cells root properties instead\r
178 //\r
179 Status = FindCompatibleNodeProperty (This, CompatibleString, "reg", Reg,\r
180 RegSize);\r
181 if (EFI_ERROR (Status)) {\r
182 return Status;\r
183 }\r
184\r
38ed4a9e 185 if ((*RegSize % 16) != 0) {\r
30740795
AB
186 DEBUG ((EFI_D_ERROR,\r
187 "%a: '%a' compatible node has invalid 'reg' property (size == 0x%x)\n",\r
188 __FUNCTION__, CompatibleString, *RegSize));\r
189 return EFI_NOT_FOUND;\r
190 }\r
191\r
cfc8d51c
AB
192 *AddressCells = 2;\r
193 *SizeCells = 2;\r
30740795
AB
194\r
195 return EFI_SUCCESS;\r
196}\r
197\r
969d2eb3
AB
198STATIC\r
199EFI_STATUS\r
200EFIAPI\r
201FindNextMemoryNodeReg (\r
202 IN FDT_CLIENT_PROTOCOL *This,\r
203 IN INT32 PrevNode,\r
204 OUT INT32 *Node,\r
205 OUT CONST VOID **Reg,\r
206 OUT UINTN *AddressCells,\r
207 OUT UINTN *SizeCells,\r
208 OUT UINT32 *RegSize\r
209 )\r
210{\r
211 INT32 Prev, Next;\r
212 CONST CHAR8 *DeviceType;\r
213 INT32 Len;\r
214 EFI_STATUS Status;\r
215\r
216 ASSERT (mDeviceTreeBase != NULL);\r
217 ASSERT (Node != NULL);\r
218\r
219 for (Prev = PrevNode;; Prev = Next) {\r
220 Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);\r
221 if (Next < 0) {\r
222 break;\r
223 }\r
224\r
225 DeviceType = fdt_getprop (mDeviceTreeBase, Next, "device_type", &Len);\r
226 if (DeviceType != NULL && AsciiStrCmp (DeviceType, "memory") == 0) {\r
227 //\r
228 // Get the 'reg' property of this memory node. For now, we will assume\r
229 // 8 byte quantities for base and size, respectively.\r
230 // TODO use #cells root properties instead\r
231 //\r
232 Status = GetNodeProperty (This, Next, "reg", Reg, RegSize);\r
233 if (EFI_ERROR (Status)) {\r
234 DEBUG ((EFI_D_WARN,\r
235 "%a: ignoring memory node with no 'reg' property\n",\r
236 __FUNCTION__));\r
237 continue;\r
238 }\r
239 if ((*RegSize % 16) != 0) {\r
240 DEBUG ((EFI_D_WARN,\r
241 "%a: ignoring memory node with invalid 'reg' property (size == 0x%x)\n",\r
242 __FUNCTION__, *RegSize));\r
243 continue;\r
244 }\r
245\r
246 *Node = Next;\r
247 *AddressCells = 2;\r
248 *SizeCells = 2;\r
249 return EFI_SUCCESS;\r
250 }\r
251 }\r
252 return EFI_NOT_FOUND;\r
253}\r
254\r
255STATIC\r
256EFI_STATUS\r
257EFIAPI\r
258FindMemoryNodeReg (\r
259 IN FDT_CLIENT_PROTOCOL *This,\r
260 OUT INT32 *Node,\r
261 OUT CONST VOID **Reg,\r
262 OUT UINTN *AddressCells,\r
263 OUT UINTN *SizeCells,\r
264 OUT UINT32 *RegSize\r
265 )\r
266{\r
267 return FindNextMemoryNodeReg (This, 0, Node, Reg, AddressCells, SizeCells,\r
268 RegSize);\r
269}\r
270\r
30740795
AB
271STATIC\r
272EFI_STATUS\r
eec1ba7d 273EFIAPI\r
30740795
AB
274GetOrInsertChosenNode (\r
275 IN FDT_CLIENT_PROTOCOL *This,\r
276 OUT INT32 *Node\r
277 )\r
278{\r
279 INT32 NewNode;\r
280\r
281 ASSERT (mDeviceTreeBase != NULL);\r
282 ASSERT (Node != NULL);\r
283\r
284 NewNode = fdt_path_offset (mDeviceTreeBase, "/chosen");\r
285 if (NewNode < 0) {\r
286 NewNode = fdt_add_subnode (mDeviceTreeBase, 0, "/chosen");\r
287 }\r
288\r
289 if (NewNode < 0) {\r
290 return EFI_OUT_OF_RESOURCES;\r
291 }\r
292\r
293 *Node = NewNode;\r
294\r
295 return EFI_SUCCESS;\r
296}\r
297\r
298STATIC FDT_CLIENT_PROTOCOL mFdtClientProtocol = {\r
299 GetNodeProperty,\r
300 SetNodeProperty,\r
301 FindCompatibleNode,\r
302 FindNextCompatibleNode,\r
303 FindCompatibleNodeProperty,\r
304 FindCompatibleNodeReg,\r
969d2eb3
AB
305 FindMemoryNodeReg,\r
306 FindNextMemoryNodeReg,\r
30740795
AB
307 GetOrInsertChosenNode,\r
308};\r
309\r
18f6d4df
AB
310STATIC\r
311VOID\r
312EFIAPI\r
313OnReadyToBoot (\r
314 EFI_EVENT Event,\r
315 VOID *Context\r
316 )\r
317{\r
318 EFI_STATUS Status;\r
319\r
320 if (!FeaturePcdGet (PcdPureAcpiBoot)) {\r
321 //\r
322 // Only install the FDT as a configuration table if we want to leave it up\r
323 // to the OS to decide whether it prefers ACPI over DT.\r
324 //\r
325 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, mDeviceTreeBase);\r
326 ASSERT_EFI_ERROR (Status);\r
327 }\r
328\r
329 gBS->CloseEvent (Event);\r
330}\r
331\r
332STATIC EFI_EVENT mReadyToBootEvent;\r
333\r
30740795
AB
334EFI_STATUS\r
335EFIAPI\r
336InitializeFdtClientDxe (\r
337 IN EFI_HANDLE ImageHandle,\r
338 IN EFI_SYSTEM_TABLE *SystemTable\r
339 )\r
340{\r
341 VOID *Hob;\r
342 VOID *DeviceTreeBase;\r
1e7143d8 343 EFI_STATUS Status;\r
30740795
AB
344\r
345 Hob = GetFirstGuidHob (&gFdtHobGuid);\r
346 if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {\r
347 return EFI_NOT_FOUND;\r
348 }\r
349 DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);\r
350\r
351 if (fdt_check_header (DeviceTreeBase) != 0) {\r
352 DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__,\r
353 DeviceTreeBase));\r
354 return EFI_NOT_FOUND;\r
355 }\r
356\r
357 mDeviceTreeBase = DeviceTreeBase;\r
358\r
359 DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, mDeviceTreeBase));\r
360\r
18f6d4df
AB
361 Status = gBS->InstallProtocolInterface (&ImageHandle, &gFdtClientProtocolGuid,\r
362 EFI_NATIVE_INTERFACE, &mFdtClientProtocol);\r
363 if (EFI_ERROR (Status)) {\r
364 return Status;\r
1e7143d8
AB
365 }\r
366\r
18f6d4df
AB
367 Status = gBS->CreateEventEx (\r
368 EVT_NOTIFY_SIGNAL,\r
369 TPL_CALLBACK,\r
370 OnReadyToBoot,\r
371 NULL,\r
372 &gEfiEventReadyToBootGuid,\r
373 &mReadyToBootEvent\r
374 );\r
375 ASSERT_EFI_ERROR (Status);\r
376\r
377 return EFI_SUCCESS;\r
30740795 378}\r