]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/Drivers/FdtPlatformDxe/FdtPlatform.c
EmbeddedPkg: do not ASSERT() on valid external input
[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 //
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
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 if (EFI_ERROR (Status)) {
174 return Status;
175 }
176
177 if (FeaturePcdGet (PcdOverridePlatformFdt) || FeaturePcdGet (PcdDumpFdtShellCommand)) {
178 //
179 // Register the strings for the user interface in the HII Database.
180 // This shows the way to the multi-language support, even if
181 // only the English language is actually supported. The strings to register
182 // are stored in the "ShellSetFdtStrings[]" array. This array is
183 // built by the building process from the "*.uni" file associated to
184 // the present driver (cf. FdtPlatfromDxe.inf). Examine your Build
185 // folder under your package's DEBUG folder and you will find the array
186 // defined in a xxxStrDefs.h file.
187 //
188 mFdtPlatformDxeHiiHandle = HiiAddPackages (
189 &mFdtPlatformDxeHiiGuid,
190 ImageHandle,
191 FdtPlatformDxeStrings,
192 NULL
193 );
194 }
195
196 //
197 // If the development features are enabled, install the dynamic shell
198 // command "setfdt" to be able to define a device path for the FDT
199 // that has precedence over the device paths defined by
200 // "PcdFdtDevicePaths".
201 //
202
203 if (FeaturePcdGet (PcdOverridePlatformFdt)) {
204 if (mFdtPlatformDxeHiiHandle != NULL) {
205 // We install dynamic EFI command on separate handles as we cannot register
206 // more than one protocol of the same protocol interface on the same handle.
207 Handle = NULL;
208 Status = gBS->InstallMultipleProtocolInterfaces (
209 &Handle,
210 &gEfiShellDynamicCommandProtocolGuid,
211 &mShellDynCmdProtocolSetFdt,
212 NULL
213 );
214 if (EFI_ERROR (Status)) {
215 HiiRemovePackages (mFdtPlatformDxeHiiHandle);
216 }
217 } else {
218 Status = EFI_LOAD_ERROR;
219 }
220 if (EFI_ERROR (Status)) {
221 DEBUG ((
222 EFI_D_WARN,
223 "Unable to install \"setfdt\" EFI Shell command - %r \n",
224 Status
225 ));
226 }
227 }
228
229 if (FeaturePcdGet (PcdDumpFdtShellCommand)) {
230 if (mFdtPlatformDxeHiiHandle != NULL) {
231 // We install dynamic EFI command on separate handles as we cannot register
232 // more than one protocol of the same protocol interface on the same handle.
233 Handle = NULL;
234 Status = gBS->InstallMultipleProtocolInterfaces (
235 &Handle,
236 &gEfiShellDynamicCommandProtocolGuid,
237 &mShellDynCmdProtocolDumpFdt,
238 NULL
239 );
240 if (EFI_ERROR (Status)) {
241 HiiRemovePackages (mFdtPlatformDxeHiiHandle);
242 }
243 } else {
244 Status = EFI_LOAD_ERROR;
245 }
246 if (EFI_ERROR (Status)) {
247 DEBUG ((
248 EFI_D_WARN,
249 "Unable to install \"dumpfdt\" EFI Shell command - %r \n",
250 Status
251 ));
252 }
253 }
254
255 return Status;
256 }
257
258 /**
259 Run the FDT installation process.
260
261 Loop in priority order over the device paths from which the FDT has
262 been asked to be retrieved for. For each device path, try to install
263 the FDT. Stop as soon as an installation succeeds.
264
265 @param[in] SuccessfullDevicePath If not NULL, address where to store the
266 pointer to the text device path from
267 which the FDT was successfully retrieved.
268 Not used if the FDT installation failed.
269 The returned address is the address of
270 an allocated buffer that has to be
271 freed by the caller.
272
273 @retval EFI_SUCCESS The FDT was installed.
274 @retval EFI_NOT_FOUND Failed to locate a protocol or a file.
275 @retval EFI_INVALID_PARAMETER Invalid device path.
276 @retval EFI_UNSUPPORTED Device path not supported.
277 @retval EFI_OUT_OF_RESOURCES An allocation failed.
278
279 **/
280 EFI_STATUS
281 RunFdtInstallation (
282 OUT CHAR16 **SuccessfullDevicePath
283 )
284 {
285 EFI_STATUS Status;
286 UINTN DataSize;
287 CHAR16 *TextDevicePath;
288 CHAR16 *TextDevicePathStart;
289 CHAR16 *TextDevicePathSeparator;
290 UINTN TextDevicePathLen;
291
292 TextDevicePath = NULL;
293 //
294 // For development purpose, if enabled through the "PcdOverridePlatformFdt"
295 // feature PCD, try first to install the FDT specified by the device path in
296 // text form stored in the "Fdt" UEFI variable.
297 //
298 if (FeaturePcdGet (PcdOverridePlatformFdt)) {
299 DataSize = 0;
300 Status = gRT->GetVariable (
301 L"Fdt",
302 &gFdtVariableGuid,
303 NULL,
304 &DataSize,
305 NULL
306 );
307
308 //
309 // Keep going only if the "Fdt" variable is defined.
310 //
311
312 if (Status == EFI_BUFFER_TOO_SMALL) {
313 TextDevicePath = AllocatePool (DataSize);
314 if (TextDevicePath == NULL) {
315 Status = EFI_OUT_OF_RESOURCES;
316 goto Error;
317 }
318
319 Status = gRT->GetVariable (
320 L"Fdt",
321 &gFdtVariableGuid,
322 NULL,
323 &DataSize,
324 TextDevicePath
325 );
326 if (EFI_ERROR (Status)) {
327 FreePool (TextDevicePath);
328 goto Error;
329 }
330
331 Status = InstallFdt (TextDevicePath);
332 if (!EFI_ERROR (Status)) {
333 DEBUG ((
334 EFI_D_WARN,
335 "Installation of the FDT using the device path <%s> completed.\n",
336 TextDevicePath
337 ));
338 goto Done;
339 }
340 DEBUG ((
341 EFI_D_ERROR,
342 "Installation of the FDT specified by the \"Fdt\" UEFI variable failed - %r\n",
343 Status
344 ));
345 FreePool (TextDevicePath);
346 }
347 }
348
349 //
350 // Loop over the device path list provided by "PcdFdtDevicePaths". The device
351 // paths are in text form and separated by a semi-colon.
352 //
353
354 Status = EFI_NOT_FOUND;
355 for (TextDevicePathStart = (CHAR16*)PcdGetPtr (PcdFdtDevicePaths);
356 *TextDevicePathStart != L'\0' ; ) {
357 TextDevicePathSeparator = StrStr (TextDevicePathStart, L";");
358
359 //
360 // Last device path of the list
361 //
362 if (TextDevicePathSeparator == NULL) {
363 TextDevicePathLen = StrLen (TextDevicePathStart);
364 } else {
365 TextDevicePathLen = (UINTN)(TextDevicePathSeparator - TextDevicePathStart);
366 }
367
368 TextDevicePath = AllocateCopyPool (
369 (TextDevicePathLen + 1) * sizeof (CHAR16),
370 TextDevicePathStart
371 );
372 if (TextDevicePath == NULL) {
373 Status = EFI_OUT_OF_RESOURCES;
374 goto Error;
375 }
376 TextDevicePath[TextDevicePathLen] = L'\0';
377
378 Status = InstallFdt (TextDevicePath);
379 if (!EFI_ERROR (Status)) {
380 DEBUG ((EFI_D_WARN, "Installation of the FDT using the device path <%s> completed.\n",
381 TextDevicePath
382 ));
383 goto Done;
384 }
385
386 DEBUG ((EFI_D_WARN, "Installation of the FDT using the device path <%s> failed - %r.\n",
387 TextDevicePath, Status
388 ));
389 FreePool (TextDevicePath);
390
391 if (TextDevicePathSeparator == NULL) {
392 goto Error;
393 }
394 TextDevicePathStart = TextDevicePathSeparator + 1;
395 }
396
397 Error:
398 Done:
399
400 if (EFI_ERROR (Status)) {
401 DEBUG ((EFI_D_ERROR, "Failed to install the FDT - %r.\n", Status));
402 return Status;
403 }
404
405 if (SuccessfullDevicePath != NULL) {
406 *SuccessfullDevicePath = TextDevicePath;
407 } else {
408 FreePool (TextDevicePath);
409 }
410
411 return EFI_SUCCESS;
412 }
413
414 /**
415 Transcode one of the EFI return code used by the model into an EFI Shell return code.
416
417 @param[in] Status EFI return code.
418
419 @return Transcoded EFI Shell return code.
420
421 **/
422 SHELL_STATUS
423 EfiCodeToShellCode (
424 IN EFI_STATUS Status
425 )
426 {
427 SHELL_STATUS ShellStatus;
428
429 switch (Status) {
430 case EFI_SUCCESS :
431 ShellStatus = SHELL_SUCCESS;
432 break;
433
434 case EFI_INVALID_PARAMETER :
435 ShellStatus = SHELL_INVALID_PARAMETER;
436 break;
437
438 case EFI_UNSUPPORTED :
439 ShellStatus = SHELL_UNSUPPORTED;
440 break;
441
442 case EFI_DEVICE_ERROR :
443 ShellStatus = SHELL_DEVICE_ERROR;
444 break;
445
446 case EFI_WRITE_PROTECTED :
447 case EFI_SECURITY_VIOLATION :
448 ShellStatus = SHELL_ACCESS_DENIED;
449 break;
450
451 case EFI_OUT_OF_RESOURCES :
452 ShellStatus = SHELL_OUT_OF_RESOURCES;
453 break;
454
455 case EFI_NOT_FOUND :
456 ShellStatus = SHELL_NOT_FOUND;
457 break;
458
459 default :
460 ShellStatus = SHELL_ABORTED;
461 }
462
463 return ShellStatus;
464 }