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