]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/Drivers/FdtPlatformDxe/FdtPlatform.c
EmbeddedPkg/FdtPlatformDxe: Introduce EFI Shell command 'dumfdt'
[mirror_edk2.git] / EmbeddedPkg / Drivers / FdtPlatformDxe / FdtPlatform.c
1 /** @file
2
3 Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>
4
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "FdtPlatform.h"
16
17 #include <Library/PcdLib.h>
18 #include <Library/DevicePathLib.h>
19 #include <Library/BdsLib.h>
20
21 #include <Protocol/DevicePath.h>
22
23 //
24 // Internal variables
25 //
26
27 STATIC CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mShellDynCmdProtocolSetFdt = {
28 L"setfdt", // Name of the command
29 ShellDynCmdSetFdtHandler, // Handler
30 ShellDynCmdSetFdtGetHelp // GetHelp
31 };
32
33 STATIC CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mShellDynCmdProtocolDumpFdt = {
34 L"dumpfdt", // Name of the command
35 ShellDynCmdDumpFdtHandler, // Handler
36 ShellDynCmdDumpFdtGetHelp // GetHelp
37 };
38
39 STATIC CONST EFI_GUID mFdtPlatformDxeHiiGuid = {
40 0x8afa7610, 0x62b1, 0x46aa,
41 {0xb5, 0x34, 0xc3, 0xde, 0xff, 0x39, 0x77, 0x8c}
42 };
43
44 EFI_HANDLE mFdtPlatformDxeHiiHandle;
45
46 /**
47 Install the FDT specified by its device path in text form.
48
49 @param[in] TextDevicePath Device path of the FDT to install in text form
50
51 @retval EFI_SUCCESS The FDT was installed.
52 @retval EFI_NOT_FOUND Failed to locate a protocol or a file.
53 @retval EFI_INVALID_PARAMETER Invalid device path.
54 @retval EFI_UNSUPPORTED Device path not supported.
55 @retval EFI_OUT_OF_RESOURCES An allocation failed.
56 **/
57 STATIC
58 EFI_STATUS
59 InstallFdt (
60 IN CONST CHAR16* TextDevicePath
61 )
62 {
63 EFI_STATUS Status;
64 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
65 EFI_DEVICE_PATH *DevicePath;
66 EFI_PHYSICAL_ADDRESS FdtBlobBase;
67 UINTN FdtBlobSize;
68 UINTN NumPages;
69 EFI_PHYSICAL_ADDRESS FdtConfigurationTableBase;
70
71 Status = gBS->LocateProtocol (
72 &gEfiDevicePathFromTextProtocolGuid,
73 NULL,
74 (VOID **)&EfiDevicePathFromTextProtocol
75 );
76 if (EFI_ERROR (Status)) {
77 DEBUG ((EFI_D_ERROR, "InstallFdt() - Failed to locate EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL protocol\n"));
78 return Status;
79 }
80
81 DevicePath = (EFI_DEVICE_PATH*)EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (TextDevicePath);
82 if (DevicePath == NULL) {
83 return EFI_INVALID_PARAMETER;
84 }
85
86 //
87 // Load the FDT given its device path.
88 // This operation may fail if the device path is not supported.
89 //
90 FdtBlobBase = 0;
91 NumPages = 0;
92 Status = BdsLoadImage (DevicePath, AllocateAnyPages, &FdtBlobBase, &FdtBlobSize);
93 if (EFI_ERROR (Status)) {
94 goto Error;
95 }
96
97 // Check the FDT header is valid. We only make this check in DEBUG mode in
98 // case the FDT header change on production device and this ASSERT() becomes
99 // not valid.
100 ASSERT (fdt_check_header ((VOID*)(UINTN)FdtBlobBase) == 0);
101
102 //
103 // Ensure the Size of the Device Tree is smaller than the size of the read file
104 //
105 ASSERT ((UINTN)fdt_totalsize ((VOID*)(UINTN)FdtBlobBase) <= FdtBlobSize);
106
107 //
108 // Store the FDT as Runtime Service Data to prevent the Kernel from
109 // overwritting its data.
110 //
111 NumPages = EFI_SIZE_TO_PAGES (FdtBlobSize);
112 Status = gBS->AllocatePages (
113 AllocateAnyPages, EfiRuntimeServicesData,
114 NumPages, &FdtConfigurationTableBase
115 );
116 if (EFI_ERROR (Status)) {
117 goto Error;
118 }
119 CopyMem (
120 (VOID*)(UINTN)FdtConfigurationTableBase,
121 (VOID*)(UINTN)FdtBlobBase,
122 FdtBlobSize
123 );
124
125 //
126 // Install the FDT into the Configuration Table
127 //
128 Status = gBS->InstallConfigurationTable (
129 &gFdtTableGuid,
130 (VOID*)(UINTN)FdtConfigurationTableBase
131 );
132 if (EFI_ERROR (Status)) {
133 gBS->FreePages (FdtConfigurationTableBase, NumPages);
134 }
135
136 Error:
137 if (FdtBlobBase != 0) {
138 gBS->FreePages (FdtBlobBase, NumPages);
139 }
140 FreePool (DevicePath);
141
142 return Status;
143 }
144
145 /**
146 Main entry point of the FDT platform driver.
147
148 @param[in] ImageHandle The firmware allocated handle for the present driver
149 UEFI image.
150 @param[in] *SystemTable A pointer to the EFI System table.
151
152 @retval EFI_SUCCESS The driver was initialized.
153 @retval EFI_OUT_OF_RESOURCES The "End of DXE" event could not be allocated or
154 there was not enough memory in pool to install
155 the Shell Dynamic Command protocol.
156 @retval EFI_LOAD_ERROR Unable to add the HII package.
157
158 **/
159 EFI_STATUS
160 FdtPlatformEntryPoint (
161 IN EFI_HANDLE ImageHandle,
162 IN EFI_SYSTEM_TABLE *SystemTable
163 )
164 {
165 EFI_STATUS Status;
166 EFI_HANDLE Handle;
167
168 //
169 // Install the Device Tree from its expected location
170 //
171 Status = RunFdtInstallation (NULL);
172 if (EFI_ERROR (Status)) {
173 return Status;
174 }
175
176 if (FeaturePcdGet (PcdOverridePlatformFdt) || FeaturePcdGet (PcdDumpFdtShellCommand)) {
177 //
178 // Register the strings for the user interface in the HII Database.
179 // This shows the way to the multi-language support, even if
180 // only the English language is actually supported. The strings to register
181 // are stored in the "ShellSetFdtStrings[]" array. This array is
182 // built by the building process from the "*.uni" file associated to
183 // the present driver (cf. FdtPlatfromDxe.inf). Examine your Build
184 // folder under your package's DEBUG folder and you will find the array
185 // defined in a xxxStrDefs.h file.
186 //
187 mFdtPlatformDxeHiiHandle = HiiAddPackages (
188 &mFdtPlatformDxeHiiGuid,
189 ImageHandle,
190 FdtPlatformDxeStrings,
191 NULL
192 );
193 }
194
195 //
196 // If the development features are enabled, install the dynamic shell
197 // command "setfdt" to be able to define a device path for the FDT
198 // that has precedence over the device paths defined by
199 // "PcdFdtDevicePaths".
200 //
201
202 if (FeaturePcdGet (PcdOverridePlatformFdt)) {
203 if (mFdtPlatformDxeHiiHandle != NULL) {
204 // We install dynamic EFI command on separate handles as we cannot register
205 // more than one protocol of the same protocol interface on the same handle.
206 Handle = NULL;
207 Status = gBS->InstallMultipleProtocolInterfaces (
208 &Handle,
209 &gEfiShellDynamicCommandProtocolGuid,
210 &mShellDynCmdProtocolSetFdt,
211 NULL
212 );
213 if (EFI_ERROR (Status)) {
214 HiiRemovePackages (mFdtPlatformDxeHiiHandle);
215 }
216 } else {
217 Status = EFI_LOAD_ERROR;
218 }
219 if (EFI_ERROR (Status)) {
220 DEBUG ((
221 EFI_D_WARN,
222 "Unable to install \"setfdt\" EFI Shell command - %r \n",
223 Status
224 ));
225 }
226 }
227
228 if (FeaturePcdGet (PcdDumpFdtShellCommand)) {
229 if (mFdtPlatformDxeHiiHandle != NULL) {
230 // We install dynamic EFI command on separate handles as we cannot register
231 // more than one protocol of the same protocol interface on the same handle.
232 Handle = NULL;
233 Status = gBS->InstallMultipleProtocolInterfaces (
234 &Handle,
235 &gEfiShellDynamicCommandProtocolGuid,
236 &mShellDynCmdProtocolDumpFdt,
237 NULL
238 );
239 if (EFI_ERROR (Status)) {
240 HiiRemovePackages (mFdtPlatformDxeHiiHandle);
241 }
242 } else {
243 Status = EFI_LOAD_ERROR;
244 }
245 if (EFI_ERROR (Status)) {
246 DEBUG ((
247 EFI_D_WARN,
248 "Unable to install \"dumpfdt\" EFI Shell command - %r \n",
249 Status
250 ));
251 }
252 }
253
254 return Status;
255 }
256
257 /**
258 Run the FDT installation process.
259
260 Loop in priority order over the device paths from which the FDT has
261 been asked to be retrieved for. For each device path, try to install
262 the FDT. Stop as soon as an installation succeeds.
263
264 @param[in] SuccessfullDevicePath If not NULL, address where to store the
265 pointer to the text device path from
266 which the FDT was successfully retrieved.
267 Not used if the FDT installation failed.
268 The returned address is the address of
269 an allocated buffer that has to be
270 freed by the caller.
271
272 @retval EFI_SUCCESS The FDT was installed.
273 @retval EFI_NOT_FOUND Failed to locate a protocol or a file.
274 @retval EFI_INVALID_PARAMETER Invalid device path.
275 @retval EFI_UNSUPPORTED Device path not supported.
276 @retval EFI_OUT_OF_RESOURCES An allocation failed.
277
278 **/
279 EFI_STATUS
280 RunFdtInstallation (
281 OUT CHAR16 **SuccessfullDevicePath
282 )
283 {
284 EFI_STATUS Status;
285 UINTN DataSize;
286 CHAR16 *TextDevicePath;
287 CHAR16 *TextDevicePathStart;
288 CHAR16 *TextDevicePathSeparator;
289 UINTN TextDevicePathLen;
290
291 TextDevicePath = NULL;
292 //
293 // For development purpose, if enabled through the "PcdOverridePlatformFdt"
294 // feature PCD, try first to install the FDT specified by the device path in
295 // text form stored in the "Fdt" UEFI variable.
296 //
297 if (FeaturePcdGet (PcdOverridePlatformFdt)) {
298 DataSize = 0;
299 Status = gRT->GetVariable (
300 L"Fdt",
301 &gFdtVariableGuid,
302 NULL,
303 &DataSize,
304 NULL
305 );
306
307 //
308 // Keep going only if the "Fdt" variable is defined.
309 //
310
311 if (Status == EFI_BUFFER_TOO_SMALL) {
312 TextDevicePath = AllocatePool (DataSize);
313 if (TextDevicePath == NULL) {
314 Status = EFI_OUT_OF_RESOURCES;
315 goto Error;
316 }
317
318 Status = gRT->GetVariable (
319 L"Fdt",
320 &gFdtVariableGuid,
321 NULL,
322 &DataSize,
323 TextDevicePath
324 );
325 if (EFI_ERROR (Status)) {
326 FreePool (TextDevicePath);
327 goto Error;
328 }
329
330 Status = InstallFdt (TextDevicePath);
331 if (!EFI_ERROR (Status)) {
332 DEBUG ((
333 EFI_D_WARN,
334 "Installation of the FDT using the device path <%s> completed.\n",
335 TextDevicePath
336 ));
337 goto Done;
338 }
339 DEBUG ((
340 EFI_D_ERROR,
341 "Installation of the FDT specified by the \"Fdt\" UEFI variable failed - %r\n",
342 Status
343 ));
344 FreePool (TextDevicePath);
345 }
346 }
347
348 //
349 // Loop over the device path list provided by "PcdFdtDevicePaths". The device
350 // paths are in text form and separated by a semi-colon.
351 //
352
353 Status = EFI_NOT_FOUND;
354 for (TextDevicePathStart = (CHAR16*)PcdGetPtr (PcdFdtDevicePaths);
355 *TextDevicePathStart != L'\0' ; ) {
356 TextDevicePathSeparator = StrStr (TextDevicePathStart, L";");
357
358 //
359 // Last device path of the list
360 //
361 if (TextDevicePathSeparator == NULL) {
362 TextDevicePathLen = StrLen (TextDevicePathStart);
363 } else {
364 TextDevicePathLen = (UINTN)(TextDevicePathSeparator - TextDevicePathStart);
365 }
366
367 TextDevicePath = AllocateCopyPool (
368 (TextDevicePathLen + 1) * sizeof (CHAR16),
369 TextDevicePathStart
370 );
371 if (TextDevicePath == NULL) {
372 Status = EFI_OUT_OF_RESOURCES;
373 goto Error;
374 }
375 TextDevicePath[TextDevicePathLen] = L'\0';
376
377 Status = InstallFdt (TextDevicePath);
378 if (!EFI_ERROR (Status)) {
379 DEBUG ((EFI_D_WARN, "Installation of the FDT using the device path <%s> completed.\n",
380 TextDevicePath
381 ));
382 goto Done;
383 }
384
385 DEBUG ((EFI_D_WARN, "Installation of the FDT using the device path <%s> failed - %r.\n",
386 TextDevicePath, Status
387 ));
388 FreePool (TextDevicePath);
389
390 if (TextDevicePathSeparator == NULL) {
391 goto Error;
392 }
393 TextDevicePathStart = TextDevicePathSeparator + 1;
394 }
395
396 Error:
397 Done:
398
399 if (EFI_ERROR (Status)) {
400 DEBUG ((EFI_D_ERROR, "Failed to install the FDT - %r.\n", Status));
401 return Status;
402 }
403
404 if (SuccessfullDevicePath != NULL) {
405 *SuccessfullDevicePath = TextDevicePath;
406 } else {
407 FreePool (TextDevicePath);
408 }
409
410 return EFI_SUCCESS;
411 }
412
413 /**
414 Transcode one of the EFI return code used by the model into an EFI Shell return code.
415
416 @param[in] Status EFI return code.
417
418 @return Transcoded EFI Shell return code.
419
420 **/
421 SHELL_STATUS
422 EfiCodeToShellCode (
423 IN EFI_STATUS Status
424 )
425 {
426 SHELL_STATUS ShellStatus;
427
428 switch (Status) {
429 case EFI_SUCCESS :
430 ShellStatus = SHELL_SUCCESS;
431 break;
432
433 case EFI_INVALID_PARAMETER :
434 ShellStatus = SHELL_INVALID_PARAMETER;
435 break;
436
437 case EFI_UNSUPPORTED :
438 ShellStatus = SHELL_UNSUPPORTED;
439 break;
440
441 case EFI_DEVICE_ERROR :
442 ShellStatus = SHELL_DEVICE_ERROR;
443 break;
444
445 case EFI_WRITE_PROTECTED :
446 case EFI_SECURITY_VIOLATION :
447 ShellStatus = SHELL_ACCESS_DENIED;
448 break;
449
450 case EFI_OUT_OF_RESOURCES :
451 ShellStatus = SHELL_OUT_OF_RESOURCES;
452 break;
453
454 case EFI_NOT_FOUND :
455 ShellStatus = SHELL_NOT_FOUND;
456 break;
457
458 default :
459 ShellStatus = SHELL_ABORTED;
460 }
461
462 return ShellStatus;
463 }