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