]> git.proxmox.com Git - mirror_edk2.git/blame - ArmVirtPkg/FdtClientDxe/FdtClientDxe.c
ArmVirtPkg/FdtClientDxe: add methods to iterate over memory nodes
[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
1e7143d8 23#include <Guid/Fdt.h>\r
30740795
AB
24#include <Guid/FdtHob.h>\r
25\r
26#include <Protocol/FdtClient.h>\r
27\r
28STATIC VOID *mDeviceTreeBase;\r
29\r
30STATIC\r
31EFI_STATUS\r
32GetNodeProperty (\r
33 IN FDT_CLIENT_PROTOCOL *This,\r
34 IN INT32 Node,\r
35 IN CONST CHAR8 *PropertyName,\r
36 OUT CONST VOID **Prop,\r
37 OUT UINT32 *PropSize OPTIONAL\r
38 )\r
39{\r
40 INT32 Len;\r
41\r
42 ASSERT (mDeviceTreeBase != NULL);\r
43 ASSERT (Prop != NULL);\r
44\r
45 *Prop = fdt_getprop (mDeviceTreeBase, Node, PropertyName, &Len);\r
46 if (*Prop == NULL) {\r
47 return EFI_NOT_FOUND;\r
48 }\r
49\r
50 if (PropSize != NULL) {\r
51 *PropSize = Len;\r
52 }\r
53 return EFI_SUCCESS;\r
54}\r
55\r
56STATIC\r
57EFI_STATUS\r
58SetNodeProperty (\r
59 IN FDT_CLIENT_PROTOCOL *This,\r
60 IN INT32 Node,\r
61 IN CONST CHAR8 *PropertyName,\r
62 IN CONST VOID *Prop,\r
63 IN UINT32 PropSize\r
64 )\r
65{\r
66 INT32 Ret;\r
67\r
68 ASSERT (mDeviceTreeBase != NULL);\r
69\r
70 Ret = fdt_setprop (mDeviceTreeBase, Node, PropertyName, Prop, PropSize);\r
71 if (Ret != 0) {\r
72 return EFI_DEVICE_ERROR;\r
73 }\r
74\r
75 return EFI_SUCCESS;\r
76}\r
77\r
78STATIC\r
79EFI_STATUS\r
80EFIAPI\r
81FindNextCompatibleNode (\r
82 IN FDT_CLIENT_PROTOCOL *This,\r
83 IN CONST CHAR8 *CompatibleString,\r
84 IN INT32 PrevNode,\r
85 OUT INT32 *Node\r
86 )\r
87{\r
88 INT32 Prev, Next;\r
89 CONST CHAR8 *Type, *Compatible;\r
90 INT32 Len;\r
91\r
92 ASSERT (mDeviceTreeBase != NULL);\r
93 ASSERT (Node != NULL);\r
94\r
95 for (Prev = PrevNode;; Prev = Next) {\r
96 Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);\r
97 if (Next < 0) {\r
98 break;\r
99 }\r
100\r
101 Type = fdt_getprop (mDeviceTreeBase, Next, "compatible", &Len);\r
102 if (Type == NULL) {\r
103 continue;\r
104 }\r
105\r
106 //\r
107 // A 'compatible' node may contain a sequence of NUL terminated\r
108 // compatible strings so check each one\r
109 //\r
110 for (Compatible = Type; Compatible < Type + Len && *Compatible;\r
111 Compatible += 1 + AsciiStrLen (Compatible)) {\r
112 if (AsciiStrCmp (CompatibleString, Compatible) == 0) {\r
113 *Node = Next;\r
114 return EFI_SUCCESS;\r
115 }\r
116 }\r
117 }\r
118 return EFI_NOT_FOUND;\r
119}\r
120\r
121STATIC\r
122EFI_STATUS\r
123EFIAPI\r
124FindCompatibleNode (\r
125 IN FDT_CLIENT_PROTOCOL *This,\r
126 IN CONST CHAR8 *CompatibleString,\r
127 OUT INT32 *Node\r
128 )\r
129{\r
130 return FindNextCompatibleNode (This, CompatibleString, 0, Node);\r
131}\r
132\r
133STATIC\r
134EFI_STATUS\r
135EFIAPI\r
136FindCompatibleNodeProperty (\r
137 IN FDT_CLIENT_PROTOCOL *This,\r
138 IN CONST CHAR8 *CompatibleString,\r
139 IN CONST CHAR8 *PropertyName,\r
140 OUT CONST VOID **Prop,\r
141 OUT UINT32 *PropSize OPTIONAL\r
142 )\r
143{\r
144 EFI_STATUS Status;\r
145 INT32 Node;\r
146\r
147 Status = FindCompatibleNode (This, CompatibleString, &Node);\r
148 if (EFI_ERROR (Status)) {\r
149 return Status;\r
150 }\r
151\r
152 return GetNodeProperty (This, Node, PropertyName, Prop, PropSize);\r
153}\r
154\r
155STATIC\r
156EFI_STATUS\r
157EFIAPI\r
158FindCompatibleNodeReg (\r
159 IN FDT_CLIENT_PROTOCOL *This,\r
160 IN CONST CHAR8 *CompatibleString,\r
161 OUT CONST VOID **Reg,\r
cfc8d51c
AB
162 OUT UINTN *AddressCells,\r
163 OUT UINTN *SizeCells,\r
30740795
AB
164 OUT UINT32 *RegSize\r
165 )\r
166{\r
167 EFI_STATUS Status;\r
168\r
169 ASSERT (RegSize != NULL);\r
170\r
171 //\r
172 // Get the 'reg' property of this node. For now, we will assume\r
173 // 8 byte quantities for base and size, respectively.\r
174 // TODO use #cells root properties instead\r
175 //\r
176 Status = FindCompatibleNodeProperty (This, CompatibleString, "reg", Reg,\r
177 RegSize);\r
178 if (EFI_ERROR (Status)) {\r
179 return Status;\r
180 }\r
181\r
38ed4a9e 182 if ((*RegSize % 16) != 0) {\r
30740795
AB
183 DEBUG ((EFI_D_ERROR,\r
184 "%a: '%a' compatible node has invalid 'reg' property (size == 0x%x)\n",\r
185 __FUNCTION__, CompatibleString, *RegSize));\r
186 return EFI_NOT_FOUND;\r
187 }\r
188\r
cfc8d51c
AB
189 *AddressCells = 2;\r
190 *SizeCells = 2;\r
30740795
AB
191\r
192 return EFI_SUCCESS;\r
193}\r
194\r
969d2eb3
AB
195STATIC\r
196EFI_STATUS\r
197EFIAPI\r
198FindNextMemoryNodeReg (\r
199 IN FDT_CLIENT_PROTOCOL *This,\r
200 IN INT32 PrevNode,\r
201 OUT INT32 *Node,\r
202 OUT CONST VOID **Reg,\r
203 OUT UINTN *AddressCells,\r
204 OUT UINTN *SizeCells,\r
205 OUT UINT32 *RegSize\r
206 )\r
207{\r
208 INT32 Prev, Next;\r
209 CONST CHAR8 *DeviceType;\r
210 INT32 Len;\r
211 EFI_STATUS Status;\r
212\r
213 ASSERT (mDeviceTreeBase != NULL);\r
214 ASSERT (Node != NULL);\r
215\r
216 for (Prev = PrevNode;; Prev = Next) {\r
217 Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);\r
218 if (Next < 0) {\r
219 break;\r
220 }\r
221\r
222 DeviceType = fdt_getprop (mDeviceTreeBase, Next, "device_type", &Len);\r
223 if (DeviceType != NULL && AsciiStrCmp (DeviceType, "memory") == 0) {\r
224 //\r
225 // Get the 'reg' property of this memory node. For now, we will assume\r
226 // 8 byte quantities for base and size, respectively.\r
227 // TODO use #cells root properties instead\r
228 //\r
229 Status = GetNodeProperty (This, Next, "reg", Reg, RegSize);\r
230 if (EFI_ERROR (Status)) {\r
231 DEBUG ((EFI_D_WARN,\r
232 "%a: ignoring memory node with no 'reg' property\n",\r
233 __FUNCTION__));\r
234 continue;\r
235 }\r
236 if ((*RegSize % 16) != 0) {\r
237 DEBUG ((EFI_D_WARN,\r
238 "%a: ignoring memory node with invalid 'reg' property (size == 0x%x)\n",\r
239 __FUNCTION__, *RegSize));\r
240 continue;\r
241 }\r
242\r
243 *Node = Next;\r
244 *AddressCells = 2;\r
245 *SizeCells = 2;\r
246 return EFI_SUCCESS;\r
247 }\r
248 }\r
249 return EFI_NOT_FOUND;\r
250}\r
251\r
252STATIC\r
253EFI_STATUS\r
254EFIAPI\r
255FindMemoryNodeReg (\r
256 IN FDT_CLIENT_PROTOCOL *This,\r
257 OUT INT32 *Node,\r
258 OUT CONST VOID **Reg,\r
259 OUT UINTN *AddressCells,\r
260 OUT UINTN *SizeCells,\r
261 OUT UINT32 *RegSize\r
262 )\r
263{\r
264 return FindNextMemoryNodeReg (This, 0, Node, Reg, AddressCells, SizeCells,\r
265 RegSize);\r
266}\r
267\r
30740795
AB
268STATIC\r
269EFI_STATUS\r
270GetOrInsertChosenNode (\r
271 IN FDT_CLIENT_PROTOCOL *This,\r
272 OUT INT32 *Node\r
273 )\r
274{\r
275 INT32 NewNode;\r
276\r
277 ASSERT (mDeviceTreeBase != NULL);\r
278 ASSERT (Node != NULL);\r
279\r
280 NewNode = fdt_path_offset (mDeviceTreeBase, "/chosen");\r
281 if (NewNode < 0) {\r
282 NewNode = fdt_add_subnode (mDeviceTreeBase, 0, "/chosen");\r
283 }\r
284\r
285 if (NewNode < 0) {\r
286 return EFI_OUT_OF_RESOURCES;\r
287 }\r
288\r
289 *Node = NewNode;\r
290\r
291 return EFI_SUCCESS;\r
292}\r
293\r
294STATIC FDT_CLIENT_PROTOCOL mFdtClientProtocol = {\r
295 GetNodeProperty,\r
296 SetNodeProperty,\r
297 FindCompatibleNode,\r
298 FindNextCompatibleNode,\r
299 FindCompatibleNodeProperty,\r
300 FindCompatibleNodeReg,\r
969d2eb3
AB
301 FindMemoryNodeReg,\r
302 FindNextMemoryNodeReg,\r
30740795
AB
303 GetOrInsertChosenNode,\r
304};\r
305\r
306EFI_STATUS\r
307EFIAPI\r
308InitializeFdtClientDxe (\r
309 IN EFI_HANDLE ImageHandle,\r
310 IN EFI_SYSTEM_TABLE *SystemTable\r
311 )\r
312{\r
313 VOID *Hob;\r
314 VOID *DeviceTreeBase;\r
1e7143d8 315 EFI_STATUS Status;\r
30740795
AB
316\r
317 Hob = GetFirstGuidHob (&gFdtHobGuid);\r
318 if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {\r
319 return EFI_NOT_FOUND;\r
320 }\r
321 DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);\r
322\r
323 if (fdt_check_header (DeviceTreeBase) != 0) {\r
324 DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__,\r
325 DeviceTreeBase));\r
326 return EFI_NOT_FOUND;\r
327 }\r
328\r
329 mDeviceTreeBase = DeviceTreeBase;\r
330\r
331 DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, mDeviceTreeBase));\r
332\r
1e7143d8
AB
333 if (!FeaturePcdGet (PcdPureAcpiBoot)) {\r
334 //\r
335 // Only install the FDT as a configuration table if we want to leave it up\r
336 // to the OS to decide whether it prefers ACPI over DT.\r
337 //\r
338 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);\r
339 ASSERT_EFI_ERROR (Status);\r
340 }\r
341\r
30740795
AB
342 return gBS->InstallProtocolInterface (&ImageHandle, &gFdtClientProtocolGuid,\r
343 EFI_NATIVE_INTERFACE, &mFdtClientProtocol);\r
344}\r