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
InstallFdt(IN CONST CHAR16 * TextDevicePath)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 //
98 // Ensure that the FDT header is valid and that the Size of the Device Tree
99 // is smaller than the size of the read file
100 //
101 if (fdt_check_header ((VOID*)(UINTN)FdtBlobBase) != 0 ||
102 (UINTN)fdt_totalsize ((VOID*)(UINTN)FdtBlobBase) > FdtBlobSize) {
103 DEBUG ((EFI_D_ERROR, "InstallFdt() - loaded FDT binary image seems corrupt\n"));
104 Status = EFI_LOAD_ERROR;
105 goto Error;
106 }
107
108 //
109 // Store the FDT as Runtime Service Data to prevent the Kernel from
110 // overwritting its data.
111 //
112 NumPages = EFI_SIZE_TO_PAGES (FdtBlobSize);
113 Status = gBS->AllocatePages (
114 AllocateAnyPages, EfiRuntimeServicesData,
115 NumPages, &FdtConfigurationTableBase
116 );
117 if (EFI_ERROR (Status)) {
118 goto Error;
119 }
120 CopyMem (
121 (VOID*)(UINTN)FdtConfigurationTableBase,
122 (VOID*)(UINTN)FdtBlobBase,
123 FdtBlobSize
124 );
125
126 //
127 // Install the FDT into the Configuration Table
128 //
129 Status = gBS->InstallConfigurationTable (
130 &gFdtTableGuid,
131 (VOID*)(UINTN)FdtConfigurationTableBase
132 );
133 if (EFI_ERROR (Status)) {
134 gBS->FreePages (FdtConfigurationTableBase, NumPages);
135 }
136
137 Error:
138 if (FdtBlobBase != 0) {
139 gBS->FreePages (FdtBlobBase, NumPages);
140 }
141 FreePool (DevicePath);
142
143 return Status;
144 }
145
146 /**
147 Main entry point of the FDT platform driver.
148
149 @param[in] ImageHandle The firmware allocated handle for the present driver
150 UEFI image.
151 @param[in] *SystemTable A pointer to the EFI System table.
152
153 @retval EFI_SUCCESS The driver was initialized.
154 @retval EFI_OUT_OF_RESOURCES The "End of DXE" event could not be allocated or
155 there was not enough memory in pool to install
156 the Shell Dynamic Command protocol.
157 @retval EFI_LOAD_ERROR Unable to add the HII package.
158
159 **/
160 EFI_STATUS
FdtPlatformEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)161 FdtPlatformEntryPoint (
162 IN EFI_HANDLE ImageHandle,
163 IN EFI_SYSTEM_TABLE *SystemTable
164 )
165 {
166 EFI_STATUS Status;
167 EFI_HANDLE Handle;
168
169 //
170 // Install the Device Tree from its expected location
171 //
172 Status = RunFdtInstallation (NULL);
173
174 if (FeaturePcdGet (PcdOverridePlatformFdt) || FeaturePcdGet (PcdDumpFdtShellCommand)) {
175 //
176 // Register the strings for the user interface in the HII Database.
177 // This shows the way to the multi-language support, even if
178 // only the English language is actually supported. The strings to register
179 // are stored in the "ShellSetFdtStrings[]" array. This array is
180 // built by the building process from the "*.uni" file associated to
181 // the present driver (cf. FdtPlatfromDxe.inf). Examine your Build
182 // folder under your package's DEBUG folder and you will find the array
183 // defined in a xxxStrDefs.h file.
184 //
185 mFdtPlatformDxeHiiHandle = HiiAddPackages (
186 &mFdtPlatformDxeHiiGuid,
187 ImageHandle,
188 FdtPlatformDxeStrings,
189 NULL
190 );
191 }
192
193 //
194 // If the development features are enabled, install the dynamic shell
195 // command "setfdt" to be able to define a device path for the FDT
196 // that has precedence over the device paths defined by
197 // "PcdFdtDevicePaths".
198 //
199
200 if (FeaturePcdGet (PcdOverridePlatformFdt)) {
201 if (mFdtPlatformDxeHiiHandle != NULL) {
202 // We install dynamic EFI command on separate handles as we cannot register
203 // more than one protocol of the same protocol interface on the same handle.
204 Handle = NULL;
205 Status = gBS->InstallMultipleProtocolInterfaces (
206 &Handle,
207 &gEfiShellDynamicCommandProtocolGuid,
208 &mShellDynCmdProtocolSetFdt,
209 NULL
210 );
211 if (EFI_ERROR (Status)) {
212 HiiRemovePackages (mFdtPlatformDxeHiiHandle);
213 }
214 } else {
215 Status = EFI_LOAD_ERROR;
216 }
217 if (EFI_ERROR (Status)) {
218 DEBUG ((
219 EFI_D_WARN,
220 "Unable to install \"setfdt\" EFI Shell command - %r \n",
221 Status
222 ));
223 }
224 }
225
226 if (FeaturePcdGet (PcdDumpFdtShellCommand)) {
227 if (mFdtPlatformDxeHiiHandle != NULL) {
228 // We install dynamic EFI command on separate handles as we cannot register
229 // more than one protocol of the same protocol interface on the same handle.
230 Handle = NULL;
231 Status = gBS->InstallMultipleProtocolInterfaces (
232 &Handle,
233 &gEfiShellDynamicCommandProtocolGuid,
234 &mShellDynCmdProtocolDumpFdt,
235 NULL
236 );
237 if (EFI_ERROR (Status)) {
238 HiiRemovePackages (mFdtPlatformDxeHiiHandle);
239 }
240 } else {
241 Status = EFI_LOAD_ERROR;
242 }
243 if (EFI_ERROR (Status)) {
244 DEBUG ((
245 EFI_D_WARN,
246 "Unable to install \"dumpfdt\" EFI Shell command - %r \n",
247 Status
248 ));
249 }
250 }
251
252 return Status;
253 }
254
255 /**
256 Run the FDT installation process.
257
258 Loop in priority order over the device paths from which the FDT has
259 been asked to be retrieved for. For each device path, try to install
260 the FDT. Stop as soon as an installation succeeds.
261
262 @param[in] SuccessfullDevicePath If not NULL, address where to store the
263 pointer to the text device path from
264 which the FDT was successfully retrieved.
265 Not used if the FDT installation failed.
266 The returned address is the address of
267 an allocated buffer that has to be
268 freed by the caller.
269
270 @retval EFI_SUCCESS The FDT was installed.
271 @retval EFI_NOT_FOUND Failed to locate a protocol or a file.
272 @retval EFI_INVALID_PARAMETER Invalid device path.
273 @retval EFI_UNSUPPORTED Device path not supported.
274 @retval EFI_OUT_OF_RESOURCES An allocation failed.
275
276 **/
277 EFI_STATUS
RunFdtInstallation(OUT CHAR16 ** SuccessfullDevicePath)278 RunFdtInstallation (
279 OUT CHAR16 **SuccessfullDevicePath
280 )
281 {
282 EFI_STATUS Status;
283 UINTN DataSize;
284 CHAR16 *TextDevicePath;
285 CHAR16 *TextDevicePathStart;
286 CHAR16 *TextDevicePathSeparator;
287 UINTN TextDevicePathLen;
288
289 TextDevicePath = NULL;
290 //
291 // For development purpose, if enabled through the "PcdOverridePlatformFdt"
292 // feature PCD, try first to install the FDT specified by the device path in
293 // text form stored in the "Fdt" UEFI variable.
294 //
295 if (FeaturePcdGet (PcdOverridePlatformFdt)) {
296 DataSize = 0;
297 Status = gRT->GetVariable (
298 L"Fdt",
299 &gFdtVariableGuid,
300 NULL,
301 &DataSize,
302 NULL
303 );
304
305 //
306 // Keep going only if the "Fdt" variable is defined.
307 //
308
309 if (Status == EFI_BUFFER_TOO_SMALL) {
310 TextDevicePath = AllocatePool (DataSize);
311 if (TextDevicePath == NULL) {
312 Status = EFI_OUT_OF_RESOURCES;
313 goto Error;
314 }
315
316 Status = gRT->GetVariable (
317 L"Fdt",
318 &gFdtVariableGuid,
319 NULL,
320 &DataSize,
321 TextDevicePath
322 );
323 if (EFI_ERROR (Status)) {
324 FreePool (TextDevicePath);
325 goto Error;
326 }
327
328 Status = InstallFdt (TextDevicePath);
329 if (!EFI_ERROR (Status)) {
330 DEBUG ((
331 EFI_D_WARN,
332 "Installation of the FDT using the device path <%s> completed.\n",
333 TextDevicePath
334 ));
335 goto Done;
336 }
337 DEBUG ((
338 EFI_D_ERROR,
339 "Installation of the FDT specified by the \"Fdt\" UEFI variable failed - %r\n",
340 Status
341 ));
342 FreePool (TextDevicePath);
343 }
344 }
345
346 //
347 // Loop over the device path list provided by "PcdFdtDevicePaths". The device
348 // paths are in text form and separated by a semi-colon.
349 //
350
351 Status = EFI_NOT_FOUND;
352 for (TextDevicePathStart = (CHAR16*)PcdGetPtr (PcdFdtDevicePaths);
353 *TextDevicePathStart != L'\0' ; ) {
354 TextDevicePathSeparator = StrStr (TextDevicePathStart, L";");
355
356 //
357 // Last device path of the list
358 //
359 if (TextDevicePathSeparator == NULL) {
360 TextDevicePathLen = StrLen (TextDevicePathStart);
361 } else {
362 TextDevicePathLen = (UINTN)(TextDevicePathSeparator - TextDevicePathStart);
363 }
364
365 TextDevicePath = AllocateCopyPool (
366 (TextDevicePathLen + 1) * sizeof (CHAR16),
367 TextDevicePathStart
368 );
369 if (TextDevicePath == NULL) {
370 Status = EFI_OUT_OF_RESOURCES;
371 goto Error;
372 }
373 TextDevicePath[TextDevicePathLen] = L'\0';
374
375 Status = InstallFdt (TextDevicePath);
376 if (!EFI_ERROR (Status)) {
377 DEBUG ((EFI_D_WARN, "Installation of the FDT using the device path <%s> completed.\n",
378 TextDevicePath
379 ));
380 goto Done;
381 }
382
383 DEBUG ((EFI_D_WARN, "Installation of the FDT using the device path <%s> failed - %r.\n",
384 TextDevicePath, Status
385 ));
386 FreePool (TextDevicePath);
387
388 if (TextDevicePathSeparator == NULL) {
389 goto Error;
390 }
391 TextDevicePathStart = TextDevicePathSeparator + 1;
392 }
393
394 Error:
395 Done:
396
397 if (EFI_ERROR (Status)) {
398 DEBUG ((EFI_D_ERROR, "Failed to install the FDT - %r.\n", Status));
399 return Status;
400 }
401
402 if (SuccessfullDevicePath != NULL) {
403 *SuccessfullDevicePath = TextDevicePath;
404 } else {
405 FreePool (TextDevicePath);
406 }
407
408 return EFI_SUCCESS;
409 }
410
411 /**
412 Transcode one of the EFI return code used by the model into an EFI Shell return code.
413
414 @param[in] Status EFI return code.
415
416 @return Transcoded EFI Shell return code.
417
418 **/
419 SHELL_STATUS
EfiCodeToShellCode(IN EFI_STATUS Status)420 EfiCodeToShellCode (
421 IN EFI_STATUS Status
422 )
423 {
424 SHELL_STATUS ShellStatus;
425
426 switch (Status) {
427 case EFI_SUCCESS :
428 ShellStatus = SHELL_SUCCESS;
429 break;
430
431 case EFI_INVALID_PARAMETER :
432 ShellStatus = SHELL_INVALID_PARAMETER;
433 break;
434
435 case EFI_UNSUPPORTED :
436 ShellStatus = SHELL_UNSUPPORTED;
437 break;
438
439 case EFI_DEVICE_ERROR :
440 ShellStatus = SHELL_DEVICE_ERROR;
441 break;
442
443 case EFI_WRITE_PROTECTED :
444 case EFI_SECURITY_VIOLATION :
445 ShellStatus = SHELL_ACCESS_DENIED;
446 break;
447
448 case EFI_OUT_OF_RESOURCES :
449 ShellStatus = SHELL_OUT_OF_RESOURCES;
450 break;
451
452 case EFI_NOT_FOUND :
453 ShellStatus = SHELL_NOT_FOUND;
454 break;
455
456 default :
457 ShellStatus = SHELL_ABORTED;
458 }
459
460 return ShellStatus;
461 }
462