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