]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/BdsLib/BdsFilePath.c
ArmPkg/BdsLib: Add support to pass argument to a loaded EFI application
[mirror_edk2.git] / ArmPkg / Library / BdsLib / BdsFilePath.c
1 /** @file
2 *
3 * Copyright (c) 2011, ARM Limited. All rights reserved.
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 "BdsInternal.h"
16
17 #include <Protocol/UsbIo.h>
18 #include <Protocol/DiskIo.h>
19 #include <Protocol/LoadedImage.h>
20
21 #define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))
22
23 // Extract the FilePath from the Device Path
24 CHAR16*
25 BdsExtractFilePathFromDevicePath (
26 IN CONST CHAR16 *StrDevicePath,
27 IN UINTN NumberDevicePathNode
28 )
29 {
30 UINTN Node;
31 CHAR16 *Str;
32
33 Str = (CHAR16*)StrDevicePath;
34 Node = 0;
35 while ((Str != NULL) && (*Str != L'\0') && (Node < NumberDevicePathNode)) {
36 if ((*Str == L'/') || (*Str == L'\\')) {
37 Node++;
38 }
39 Str++;
40 }
41
42 if (*Str == L'\0') {
43 return NULL;
44 } else {
45 return Str;
46 }
47 }
48
49 BOOLEAN
50 BdsIsRemovableUsb (
51 IN EFI_DEVICE_PATH* DevicePath
52 )
53 {
54 return ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
55 ((DevicePathSubType (DevicePath) == MSG_USB_CLASS_DP) ||
56 (DevicePathSubType (DevicePath) == MSG_USB_WWID_DP)));
57 }
58
59 EFI_STATUS
60 BdsGetDeviceUsb (
61 IN EFI_DEVICE_PATH* RemovableDevicePath,
62 OUT EFI_HANDLE* DeviceHandle,
63 OUT EFI_DEVICE_PATH** NewDevicePath
64 )
65 {
66 EFI_STATUS Status;
67 UINTN Index;
68 UINTN UsbIoHandleCount;
69 EFI_HANDLE *UsbIoBuffer;
70 EFI_DEVICE_PATH* UsbIoDevicePath;
71 EFI_DEVICE_PATH* TmpDevicePath;
72 USB_WWID_DEVICE_PATH* WwidDevicePath1;
73 USB_WWID_DEVICE_PATH* WwidDevicePath2;
74 USB_CLASS_DEVICE_PATH* UsbClassDevicePath1;
75 USB_CLASS_DEVICE_PATH* UsbClassDevicePath2;
76
77 // Get all the UsbIo handles
78 UsbIoHandleCount = 0;
79 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiUsbIoProtocolGuid, NULL, &UsbIoHandleCount, &UsbIoBuffer);
80 if (EFI_ERROR(Status) || (UsbIoHandleCount == 0)) {
81 return Status;
82 }
83
84 // Check if one of the handles matches the USB description
85 for (Index = 0; Index < UsbIoHandleCount; Index++) {
86 Status = gBS->HandleProtocol (UsbIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &UsbIoDevicePath);
87 if (!EFI_ERROR(Status)) {
88 TmpDevicePath = UsbIoDevicePath;
89 while (!IsDevicePathEnd (TmpDevicePath)) {
90 // Check if the Device Path node is a USB Removable device Path node
91 if (BdsIsRemovableUsb (TmpDevicePath)) {
92 if (TmpDevicePath->SubType == MSG_USB_WWID_DP) {
93 WwidDevicePath1 = (USB_WWID_DEVICE_PATH*)RemovableDevicePath;
94 WwidDevicePath2 = (USB_WWID_DEVICE_PATH*)TmpDevicePath;
95 if ((WwidDevicePath1->VendorId == WwidDevicePath2->VendorId) &&
96 (WwidDevicePath1->ProductId == WwidDevicePath2->ProductId) &&
97 (CompareMem (WwidDevicePath1+1, WwidDevicePath2+1, DevicePathNodeLength(WwidDevicePath1)-sizeof(USB_WWID_DEVICE_PATH)) == 0))
98 {
99 *DeviceHandle = UsbIoBuffer[Index];
100 // Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path
101 *NewDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePathNode(RemovableDevicePath));
102 return EFI_SUCCESS;
103 }
104 } else {
105 UsbClassDevicePath1 = (USB_CLASS_DEVICE_PATH*)RemovableDevicePath;
106 UsbClassDevicePath2 = (USB_CLASS_DEVICE_PATH*)TmpDevicePath;
107 if ((UsbClassDevicePath1->VendorId != 0xFFFF) && (UsbClassDevicePath1->VendorId == UsbClassDevicePath2->VendorId) &&
108 (UsbClassDevicePath1->ProductId != 0xFFFF) && (UsbClassDevicePath1->ProductId == UsbClassDevicePath2->ProductId) &&
109 (UsbClassDevicePath1->DeviceClass != 0xFF) && (UsbClassDevicePath1->DeviceClass == UsbClassDevicePath2->DeviceClass) &&
110 (UsbClassDevicePath1->DeviceSubClass != 0xFF) && (UsbClassDevicePath1->DeviceSubClass == UsbClassDevicePath2->DeviceSubClass) &&
111 (UsbClassDevicePath1->DeviceProtocol != 0xFF) && (UsbClassDevicePath1->DeviceProtocol == UsbClassDevicePath2->DeviceProtocol))
112 {
113 *DeviceHandle = UsbIoBuffer[Index];
114 // Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path
115 *NewDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePathNode(RemovableDevicePath));
116 return EFI_SUCCESS;
117 }
118 }
119 }
120 TmpDevicePath = NextDevicePathNode (TmpDevicePath);
121 }
122
123 }
124 }
125
126 return EFI_NOT_FOUND;
127 }
128
129 BOOLEAN
130 BdsIsRemovableHd (
131 IN EFI_DEVICE_PATH* DevicePath
132 )
133 {
134 return IS_DEVICE_PATH_NODE(DevicePath, MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP);
135 }
136
137 EFI_STATUS
138 BdsGetDeviceHd (
139 IN EFI_DEVICE_PATH* RemovableDevicePath,
140 OUT EFI_HANDLE* DeviceHandle,
141 OUT EFI_DEVICE_PATH** NewDevicePath
142 )
143 {
144 EFI_STATUS Status;
145 UINTN Index;
146 UINTN PartitionHandleCount;
147 EFI_HANDLE *PartitionBuffer;
148 EFI_DEVICE_PATH* PartitionDevicePath;
149 EFI_DEVICE_PATH* TmpDevicePath;
150 HARDDRIVE_DEVICE_PATH* HardDriveDevicePath1;
151 HARDDRIVE_DEVICE_PATH* HardDriveDevicePath2;
152
153 // Get all the DiskIo handles
154 PartitionHandleCount = 0;
155 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiDiskIoProtocolGuid, NULL, &PartitionHandleCount, &PartitionBuffer);
156 if (EFI_ERROR(Status) || (PartitionHandleCount == 0)) {
157 return Status;
158 }
159
160 // Check if one of the handles matches the Hard Disk Description
161 for (Index = 0; Index < PartitionHandleCount; Index++) {
162 Status = gBS->HandleProtocol (PartitionBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &PartitionDevicePath);
163 if (!EFI_ERROR(Status)) {
164 TmpDevicePath = PartitionDevicePath;
165 while (!IsDevicePathEnd (TmpDevicePath)) {
166 // Check if the Device Path node is a HD Removable device Path node
167 if (BdsIsRemovableHd (TmpDevicePath)) {
168 HardDriveDevicePath1 = (HARDDRIVE_DEVICE_PATH*)RemovableDevicePath;
169 HardDriveDevicePath2 = (HARDDRIVE_DEVICE_PATH*)TmpDevicePath;
170 if ((HardDriveDevicePath1->SignatureType == HardDriveDevicePath2->SignatureType) &&
171 (CompareGuid ((EFI_GUID *)HardDriveDevicePath1->Signature,(EFI_GUID *)HardDriveDevicePath2->Signature) == TRUE) &&
172 (HardDriveDevicePath1->PartitionNumber == HardDriveDevicePath2->PartitionNumber))
173 {
174 *DeviceHandle = PartitionBuffer[Index];
175 // Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path
176 *NewDevicePath = AppendDevicePath (PartitionDevicePath, NextDevicePathNode(RemovableDevicePath));
177 return EFI_SUCCESS;
178 }
179 }
180 TmpDevicePath = NextDevicePathNode (TmpDevicePath);
181 }
182
183 }
184 }
185
186 return EFI_NOT_FOUND;
187 }
188
189 /*BOOLEAN
190 BdsIsRemovableCdrom (
191 IN EFI_DEVICE_PATH* DevicePath
192 )
193 {
194 return IS_DEVICE_PATH_NODE(DevicePath, MEDIA_DEVICE_PATH, MEDIA_CDROM_DP);
195 }
196
197 EFI_STATUS
198 BdsGetDeviceCdrom (
199 IN EFI_DEVICE_PATH* RemovableDevicePath,
200 OUT EFI_HANDLE* DeviceHandle,
201 OUT EFI_DEVICE_PATH** DevicePath
202 )
203 {
204 ASSERT(0);
205 return EFI_UNSUPPORTED;
206 }*/
207
208 typedef BOOLEAN
209 (*BDS_IS_REMOVABLE) (
210 IN EFI_DEVICE_PATH* DevicePath
211 );
212
213 typedef EFI_STATUS
214 (*BDS_GET_DEVICE) (
215 IN EFI_DEVICE_PATH* RemovableDevicePath,
216 OUT EFI_HANDLE* DeviceHandle,
217 OUT EFI_DEVICE_PATH** DevicePath
218 );
219
220 typedef struct {
221 BDS_IS_REMOVABLE IsRemovable;
222 BDS_GET_DEVICE GetDevice;
223 } BDS_REMOVABLE_DEVICE_SUPPORT;
224
225 BDS_REMOVABLE_DEVICE_SUPPORT RemovableDeviceSupport[] = {
226 { BdsIsRemovableUsb, BdsGetDeviceUsb },
227 { BdsIsRemovableHd, BdsGetDeviceHd },
228 //{ BdsIsRemovableCdrom, BdsGetDeviceCdrom }
229 };
230
231 STATIC
232 BOOLEAN
233 IsRemovableDevice (
234 IN EFI_DEVICE_PATH* DevicePath
235 )
236 {
237 UINTN Index;
238 EFI_DEVICE_PATH* TmpDevicePath;
239
240 TmpDevicePath = DevicePath;
241 while (!IsDevicePathEnd (TmpDevicePath)) {
242 for (Index = 0; Index < sizeof(RemovableDeviceSupport) / sizeof(BDS_REMOVABLE_DEVICE_SUPPORT); Index++) {
243 if (RemovableDeviceSupport[Index].IsRemovable(TmpDevicePath)) {
244 return TRUE;
245 }
246 }
247 TmpDevicePath = NextDevicePathNode (TmpDevicePath);
248 }
249
250 return FALSE;
251 }
252
253 STATIC
254 EFI_STATUS
255 TryRemovableDevice (
256 IN EFI_DEVICE_PATH* DevicePath,
257 OUT EFI_HANDLE* DeviceHandle,
258 OUT EFI_DEVICE_PATH** NewDevicePath
259 )
260 {
261 EFI_STATUS Status;
262 UINTN Index;
263 EFI_DEVICE_PATH* TmpDevicePath;
264 BDS_REMOVABLE_DEVICE_SUPPORT* RemovableDevice;
265 EFI_DEVICE_PATH* RemovableDevicePath;
266 BOOLEAN RemovableFound;
267
268 RemovableDevice = NULL;
269 RemovableDevicePath = NULL;
270 RemovableFound = FALSE;
271 TmpDevicePath = DevicePath;
272
273 while (!IsDevicePathEnd (TmpDevicePath) && !RemovableFound) {
274 for (Index = 0; Index < sizeof(RemovableDeviceSupport) / sizeof(BDS_REMOVABLE_DEVICE_SUPPORT); Index++) {
275 RemovableDevice = &RemovableDeviceSupport[Index];
276 if (RemovableDevice->IsRemovable(TmpDevicePath)) {
277 RemovableDevicePath = TmpDevicePath;
278 RemovableFound = TRUE;
279 break;
280 }
281 }
282 TmpDevicePath = NextDevicePathNode (TmpDevicePath);
283 }
284
285 if (!RemovableFound) {
286 return EFI_NOT_FOUND;
287 }
288
289 // Search into the current started drivers
290 Status = RemovableDevice->GetDevice (RemovableDevicePath, DeviceHandle, NewDevicePath);
291 if (Status == EFI_NOT_FOUND) {
292 // Connect all the drivers
293 BdsConnectAllDrivers ();
294
295 // Search again into all the drivers
296 Status = RemovableDevice->GetDevice (RemovableDevicePath, DeviceHandle, NewDevicePath);
297 }
298
299 return Status;
300 }
301
302 /**
303 Connect a Device Path and return the handle of the driver that support this DevicePath
304
305 @param DevicePath Device Path of the File to connect
306 @param Handle Handle of the driver that support this DevicePath
307 @param RemainingDevicePath Remaining DevicePath nodes that do not match the driver DevicePath
308
309 @retval EFI_SUCCESS A driver that matches the Device Path has been found
310 @retval EFI_NOT_FOUND No handles match the search.
311 @retval EFI_INVALID_PARAMETER DevicePath or Handle is NULL
312
313 **/
314 EFI_STATUS
315 BdsConnectDevicePath (
316 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
317 OUT EFI_HANDLE *Handle,
318 OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath
319 )
320 {
321 EFI_DEVICE_PATH* Remaining;
322 EFI_DEVICE_PATH* NewDevicePath;
323 EFI_STATUS Status;
324
325 if ((DevicePath == NULL) || (Handle == NULL)) {
326 return EFI_INVALID_PARAMETER;
327 }
328
329 do {
330 Remaining = DevicePath;
331 // The LocateDevicePath() function locates all devices on DevicePath that support Protocol and returns
332 // the handle to the device that is closest to DevicePath. On output, the device path pointer is modified
333 // to point to the remaining part of the device path
334 Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);
335 if (!EFI_ERROR (Status)) {
336 // Recursive = FALSE: We do not want to start all the device tree
337 Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);
338 }
339
340 /*// We need to check if RemainingDevicePath does not point on the last node. Otherwise, calling
341 // NextDevicePathNode() will return an undetermined Device Path Node
342 if (!IsDevicePathEnd (RemainingDevicePath)) {
343 RemainingDevicePath = NextDevicePathNode (RemainingDevicePath);
344 }*/
345 } while (!EFI_ERROR (Status) && !IsDevicePathEnd (Remaining));
346
347 if (!EFI_ERROR (Status)) {
348 // Now, we have got the whole Device Path connected, call again ConnectController to ensure all the supported Driver
349 // Binding Protocol are connected (such as DiskIo and SimpleFileSystem)
350 Remaining = DevicePath;
351 Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid,&Remaining,Handle);
352 if (!EFI_ERROR (Status)) {
353 Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);
354 if (EFI_ERROR (Status)) {
355 // If the last node is a Memory Map Device Path just return EFI_SUCCESS.
356 if ((Remaining->Type == HARDWARE_DEVICE_PATH) && (Remaining->SubType == HW_MEMMAP_DP)) {
357 Status = EFI_SUCCESS;
358 }
359 }
360 }
361 } else if (!IsDevicePathEnd (Remaining) && !IsRemovableDevice (Remaining)) {
362
363 /*// If the remaining Device Path is a FilePath or MemoryMap then we consider the Device Path has been loaded correctly
364 if ((Remaining->Type == MEDIA_DEVICE_PATH) && (Remaining->SubType == MEDIA_FILEPATH_DP)) {
365 Status = EFI_SUCCESS;
366 } else if ((Remaining->Type == HARDWARE_DEVICE_PATH) && (Remaining->SubType == HW_MEMMAP_DP)) {
367 Status = EFI_SUCCESS;
368 }*/
369
370 //TODO: Should we just return success and leave the caller decide if it is the expected RemainingPath
371 Status = EFI_SUCCESS;
372 } else {
373 Status = TryRemovableDevice (DevicePath, Handle, &NewDevicePath);
374 if (!EFI_ERROR (Status)) {
375 return BdsConnectDevicePath (NewDevicePath, Handle, RemainingDevicePath);
376 }
377 }
378
379 if (RemainingDevicePath) {
380 *RemainingDevicePath = Remaining;
381 }
382
383 return Status;
384 }
385
386 BOOLEAN
387 BdsFileSystemSupport (
388 IN EFI_DEVICE_PATH *DevicePath,
389 IN EFI_HANDLE Handle,
390 IN EFI_DEVICE_PATH *RemainingDevicePath
391 )
392 {
393 EFI_STATUS Status;
394 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;
395
396 Status = gBS->HandleProtocol (Handle,&gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol);
397
398 return (!EFI_ERROR(Status) && IS_DEVICE_PATH_NODE(RemainingDevicePath,MEDIA_DEVICE_PATH,MEDIA_FILEPATH_DP));
399 }
400
401 EFI_STATUS
402 BdsFileSystemLoadImage (
403 IN EFI_DEVICE_PATH *DevicePath,
404 IN EFI_HANDLE Handle,
405 IN EFI_DEVICE_PATH *RemainingDevicePath,
406 IN EFI_ALLOCATE_TYPE Type,
407 IN OUT EFI_PHYSICAL_ADDRESS* Image,
408 OUT UINTN *ImageSize
409 )
410 {
411 FILEPATH_DEVICE_PATH* FilePathDevicePath;
412 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;
413 EFI_FILE_PROTOCOL *Fs;
414 EFI_STATUS Status;
415 EFI_FILE_INFO *FileInfo;
416 EFI_FILE_PROTOCOL *File;
417 UINTN Size;
418
419 ASSERT (IS_DEVICE_PATH_NODE(RemainingDevicePath,MEDIA_DEVICE_PATH,MEDIA_FILEPATH_DP));
420
421 FilePathDevicePath = (FILEPATH_DEVICE_PATH*)RemainingDevicePath;
422
423 Status = gBS->HandleProtocol(Handle,&gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol);
424 if (EFI_ERROR(Status)) {
425 return Status;
426 }
427
428 // Try to Open the volume and get root directory
429 Status = FsProtocol->OpenVolume (FsProtocol, &Fs);
430 if (EFI_ERROR(Status)) {
431 return Status;
432 }
433
434 File = NULL;
435 Status = Fs->Open(Fs, &File, FilePathDevicePath->PathName, EFI_FILE_MODE_READ, 0);
436 if (EFI_ERROR(Status)) {
437 return Status;
438 }
439
440 Size = 0;
441 File->GetInfo(File, &gEfiFileInfoGuid, &Size, NULL);
442 FileInfo = AllocatePool (Size);
443 Status = File->GetInfo(File, &gEfiFileInfoGuid, &Size, FileInfo);
444 if (EFI_ERROR(Status)) {
445 return Status;
446 }
447
448 // Get the file size
449 Size = FileInfo->FileSize;
450 if (ImageSize) {
451 *ImageSize = Size;
452 }
453 FreePool(FileInfo);
454
455 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
456 // Try to allocate in any pages if failed to allocate memory at the defined location
457 if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
458 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
459 }
460 if (!EFI_ERROR(Status)) {
461 Status = File->Read (File, &Size, (VOID*)(UINTN)(*Image));
462 }
463
464 return Status;
465 }
466
467 BOOLEAN
468 BdsMemoryMapSupport (
469 IN EFI_DEVICE_PATH *DevicePath,
470 IN EFI_HANDLE Handle,
471 IN EFI_DEVICE_PATH *RemainingDevicePath
472 )
473 {
474 return IS_DEVICE_PATH_NODE(RemainingDevicePath,HARDWARE_DEVICE_PATH,HW_MEMMAP_DP);
475 }
476
477 EFI_STATUS
478 BdsMemoryMapLoadImage (
479 IN EFI_DEVICE_PATH *DevicePath,
480 IN EFI_HANDLE Handle,
481 IN EFI_DEVICE_PATH *RemainingDevicePath,
482 IN EFI_ALLOCATE_TYPE Type,
483 IN OUT EFI_PHYSICAL_ADDRESS* Image,
484 OUT UINTN *ImageSize
485 )
486 {
487 EFI_STATUS Status;
488 MEMMAP_DEVICE_PATH* MemMapPathDevicePath;
489 UINTN Size;
490
491 ASSERT (IS_DEVICE_PATH_NODE(RemainingDevicePath,HARDWARE_DEVICE_PATH,HW_MEMMAP_DP));
492
493 MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)RemainingDevicePath;
494
495 Size = MemMapPathDevicePath->EndingAddress - MemMapPathDevicePath->StartingAddress;
496 if (Size == 0) {
497 return EFI_INVALID_PARAMETER;
498 }
499
500 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
501 // Try to allocate in any pages if failed to allocate memory at the defined location
502 if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
503 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
504 }
505 if (!EFI_ERROR(Status)) {
506 CopyMem ((VOID*)(UINTN)(*Image), (CONST VOID*)(UINTN)MemMapPathDevicePath->StartingAddress, Size);
507
508 if (ImageSize != NULL) {
509 *ImageSize = Size;
510 }
511 }
512
513 return Status;
514 }
515
516 BOOLEAN
517 BdsFirmwareVolumeSupport (
518 IN EFI_DEVICE_PATH *DevicePath,
519 IN EFI_HANDLE Handle,
520 IN EFI_DEVICE_PATH *RemainingDevicePath
521 )
522 {
523 return IS_DEVICE_PATH_NODE(RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_FILE_DP);
524 }
525
526 EFI_STATUS
527 BdsFirmwareVolumeLoadImage (
528 IN EFI_DEVICE_PATH *DevicePath,
529 IN EFI_HANDLE Handle,
530 IN EFI_DEVICE_PATH *RemainingDevicePath,
531 IN EFI_ALLOCATE_TYPE Type,
532 IN OUT EFI_PHYSICAL_ADDRESS* Image,
533 OUT UINTN *ImageSize
534 )
535 {
536 EFI_STATUS Status;
537 EFI_FIRMWARE_VOLUME2_PROTOCOL *FwVol;
538 EFI_GUID *FvNameGuid;
539 EFI_SECTION_TYPE SectionType;
540 EFI_FV_FILETYPE FvType;
541 EFI_FV_FILE_ATTRIBUTES Attrib;
542 UINT32 AuthenticationStatus;
543 VOID* ImageBuffer;
544
545 ASSERT (IS_DEVICE_PATH_NODE(RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_FILE_DP));
546
547 Status = gBS->HandleProtocol(Handle,&gEfiFirmwareVolume2ProtocolGuid, (VOID **)&FwVol);
548 if (EFI_ERROR(Status)) {
549 return Status;
550 }
551
552 FvNameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)RemainingDevicePath);
553 if (FvNameGuid == NULL) {
554 Status = EFI_INVALID_PARAMETER;
555 }
556
557 SectionType = EFI_SECTION_PE32;
558 AuthenticationStatus = 0;
559 //Note: ReadSection at the opposite of ReadFile does not allow to pass ImageBuffer == NULL to get the size of the file.
560 ImageBuffer = NULL;
561 Status = FwVol->ReadSection (
562 FwVol,
563 FvNameGuid,
564 SectionType,
565 0,
566 &ImageBuffer,
567 ImageSize,
568 &AuthenticationStatus
569 );
570 if (!EFI_ERROR (Status)) {
571 #if 0
572 // In case the buffer has some address requirements, we must copy the buffer to a buffer following the requirements
573 if (Type != AllocateAnyPages) {
574 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize),Image);
575 if (!EFI_ERROR(Status)) {
576 CopyMem ((VOID*)(UINTN)(*Image), ImageBuffer, *ImageSize);
577 FreePool (ImageBuffer);
578 }
579 }
580 #else
581 // We must copy the buffer into a page allocations. Otherwise, the caller could call gBS->FreePages() on the pool allocation
582 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
583 // Try to allocate in any pages if failed to allocate memory at the defined location
584 if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
585 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
586 }
587 if (!EFI_ERROR(Status)) {
588 CopyMem ((VOID*)(UINTN)(*Image), ImageBuffer, *ImageSize);
589 FreePool (ImageBuffer);
590 }
591 #endif
592 } else {
593 // Try a raw file, since a PE32 SECTION does not exist
594 Status = FwVol->ReadFile (
595 FwVol,
596 FvNameGuid,
597 NULL,
598 ImageSize,
599 &FvType,
600 &Attrib,
601 &AuthenticationStatus
602 );
603 if (!EFI_ERROR(Status)) {
604 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
605 // Try to allocate in any pages if failed to allocate memory at the defined location
606 if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
607 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
608 }
609 if (!EFI_ERROR(Status)) {
610 Status = FwVol->ReadFile (
611 FwVol,
612 FvNameGuid,
613 (VOID*)(UINTN)(*Image),
614 ImageSize,
615 &FvType,
616 &Attrib,
617 &AuthenticationStatus
618 );
619 }
620 }
621 }
622 return Status;
623 }
624
625 BOOLEAN
626 BdsPxeSupport (
627 IN EFI_DEVICE_PATH* DevicePath,
628 IN EFI_HANDLE Handle,
629 IN EFI_DEVICE_PATH* RemainingDevicePath
630 )
631 {
632 EFI_STATUS Status;
633 EFI_PXE_BASE_CODE_PROTOCOL* PxeBcProtocol;
634
635 if (!IsDevicePathEnd(RemainingDevicePath)) {
636 return FALSE;
637 }
638
639 Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol);
640 if (EFI_ERROR (Status)) {
641 return FALSE;
642 } else {
643 return TRUE;
644 }
645 }
646
647 EFI_STATUS
648 BdsPxeLoadImage (
649 IN EFI_DEVICE_PATH* DevicePath,
650 IN EFI_HANDLE Handle,
651 IN EFI_DEVICE_PATH* RemainingDevicePath,
652 IN EFI_ALLOCATE_TYPE Type,
653 IN OUT EFI_PHYSICAL_ADDRESS *Image,
654 OUT UINTN *ImageSize
655 )
656 {
657 EFI_STATUS Status;
658 EFI_LOAD_FILE_PROTOCOL *LoadFileProtocol;
659 UINTN BufferSize;
660
661 // Get Load File Protocol attached to the PXE protocol
662 Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFileProtocol);
663 if (EFI_ERROR (Status)) {
664 return Status;
665 }
666
667 Status = LoadFileProtocol->LoadFile (LoadFileProtocol, DevicePath, TRUE, &BufferSize, NULL);
668 if (Status == EFI_BUFFER_TOO_SMALL) {
669 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(BufferSize), Image);
670 if (EFI_ERROR(Status)) {
671 return Status;
672 }
673
674 Status = LoadFileProtocol->LoadFile (LoadFileProtocol, DevicePath, TRUE, &BufferSize, (VOID*)(UINTN)(*Image));
675 if (!EFI_ERROR(Status) && (ImageSize != NULL)) {
676 *ImageSize = BufferSize;
677 }
678 }
679
680 return Status;
681 }
682
683 BOOLEAN
684 BdsTftpSupport (
685 IN EFI_DEVICE_PATH* DevicePath,
686 IN EFI_HANDLE Handle,
687 IN EFI_DEVICE_PATH* RemainingDevicePath
688 )
689 {
690 EFI_STATUS Status;
691 EFI_DEVICE_PATH *NextDevicePath;
692 EFI_PXE_BASE_CODE_PROTOCOL *PxeBcProtocol;
693
694 // Validate the Remaining Device Path
695 if (IsDevicePathEnd(RemainingDevicePath)) {
696 return FALSE;
697 }
698 if (!IS_DEVICE_PATH_NODE(RemainingDevicePath,MESSAGING_DEVICE_PATH,MSG_IPv4_DP) &&
699 !IS_DEVICE_PATH_NODE(RemainingDevicePath,MESSAGING_DEVICE_PATH,MSG_IPv6_DP)) {
700 return FALSE;
701 }
702 NextDevicePath = NextDevicePathNode (RemainingDevicePath);
703 if (IsDevicePathEnd(NextDevicePath)) {
704 return FALSE;
705 }
706 if (!IS_DEVICE_PATH_NODE(NextDevicePath,MEDIA_DEVICE_PATH,MEDIA_FILEPATH_DP)) {
707 return FALSE;
708 }
709
710 Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol);
711 if (EFI_ERROR (Status)) {
712 return FALSE;
713 } else {
714 return TRUE;
715 }
716 }
717
718 EFI_STATUS
719 BdsTftpLoadImage (
720 IN EFI_DEVICE_PATH* DevicePath,
721 IN EFI_HANDLE Handle,
722 IN EFI_DEVICE_PATH* RemainingDevicePath,
723 IN EFI_ALLOCATE_TYPE Type,
724 IN OUT EFI_PHYSICAL_ADDRESS *Image,
725 OUT UINTN *ImageSize
726 )
727 {
728 EFI_STATUS Status;
729 EFI_PXE_BASE_CODE_PROTOCOL *Pxe;
730 UINT64 TftpBufferSize;
731 VOID* TftpBuffer;
732 EFI_IP_ADDRESS ServerIp;
733 IPv4_DEVICE_PATH* IPv4DevicePathNode;
734 FILEPATH_DEVICE_PATH* FilePathDevicePath;
735 EFI_IP_ADDRESS LocalIp;
736
737 ASSERT(IS_DEVICE_PATH_NODE(RemainingDevicePath,MESSAGING_DEVICE_PATH,MSG_IPv4_DP));
738
739 IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath;
740 FilePathDevicePath = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1);
741
742 Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe);
743 if (EFI_ERROR(Status)) {
744 return Status;
745 }
746
747 Status = Pxe->Start (Pxe, FALSE);
748 if (EFI_ERROR(Status) && (Status != EFI_ALREADY_STARTED)) {
749 return Status;
750 }
751
752 if (!IPv4DevicePathNode->StaticIpAddress) {
753 Status = Pxe->Dhcp(Pxe, TRUE);
754 } else {
755 CopyMem (&LocalIp.v4, &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));
756 Status = Pxe->SetStationIp (Pxe, &LocalIp, NULL);
757 }
758 if (EFI_ERROR(Status)) {
759 return Status;
760 }
761
762 CopyMem (&ServerIp.v4, &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS));
763
764 Status = Pxe->Mtftp (
765 Pxe,
766 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
767 NULL,
768 FALSE,
769 &TftpBufferSize,
770 NULL,
771 &ServerIp,
772 (UINT8 *)FilePathDevicePath->PathName,
773 NULL,
774 TRUE
775 );
776 if (EFI_ERROR(Status)) {
777 return Status;
778 }
779
780 // Allocate a buffer to hold the whole file.
781 TftpBuffer = AllocatePool(TftpBufferSize);
782 if (TftpBuffer == NULL) {
783 return EFI_OUT_OF_RESOURCES;
784 }
785
786 Status = Pxe->Mtftp (
787 Pxe,
788 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
789 TftpBuffer,
790 FALSE,
791 &TftpBufferSize,
792 NULL,
793 &ServerIp,
794 (UINT8 *)FilePathDevicePath->PathName,
795 NULL,
796 FALSE
797 );
798 if (EFI_ERROR(Status)) {
799 FreePool(TftpBuffer);
800 } else if (ImageSize != NULL) {
801 *ImageSize = (UINTN)TftpBufferSize;
802 }
803
804 return Status;
805 }
806
807 BDS_FILE_LOADER FileLoaders[] = {
808 { BdsFileSystemSupport, BdsFileSystemLoadImage },
809 { BdsFirmwareVolumeSupport, BdsFirmwareVolumeLoadImage },
810 //{ BdsLoadFileSupport, BdsLoadFileLoadImage },
811 { BdsMemoryMapSupport, BdsMemoryMapLoadImage },
812 { BdsPxeSupport, BdsPxeLoadImage },
813 { BdsTftpSupport, BdsTftpLoadImage },
814 { NULL, NULL }
815 };
816
817 EFI_STATUS
818 BdsLoadImage (
819 IN EFI_DEVICE_PATH *DevicePath,
820 IN EFI_ALLOCATE_TYPE Type,
821 IN OUT EFI_PHYSICAL_ADDRESS* Image,
822 OUT UINTN *FileSize
823 )
824 {
825 EFI_STATUS Status;
826 EFI_HANDLE Handle;
827 EFI_DEVICE_PATH *RemainingDevicePath;
828 BDS_FILE_LOADER* FileLoader;
829
830 Status = BdsConnectDevicePath (DevicePath, &Handle, &RemainingDevicePath);
831 if (EFI_ERROR (Status)) {
832 return Status;
833 }
834
835 FileLoader = FileLoaders;
836 while (FileLoader->Support != NULL) {
837 if (FileLoader->Support (DevicePath, Handle, RemainingDevicePath)) {
838 return FileLoader->LoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, FileSize);
839 }
840 FileLoader++;
841 }
842
843 return EFI_UNSUPPORTED;
844 }
845
846 /**
847 Start an EFI Application from a Device Path
848
849 @param ParentImageHandle Handle of the calling image
850 @param DevicePath Location of the EFI Application
851
852 @retval EFI_SUCCESS All drivers have been connected
853 @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found
854 @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.
855
856 **/
857 EFI_STATUS
858 BdsStartEfiApplication (
859 IN EFI_HANDLE ParentImageHandle,
860 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
861 IN UINTN LoadOptionsSize,
862 IN VOID* LoadOptions
863 )
864 {
865 EFI_STATUS Status;
866 EFI_HANDLE ImageHandle;
867 EFI_PHYSICAL_ADDRESS BinaryBuffer;
868 UINTN BinarySize;
869 EFI_LOADED_IMAGE_PROTOCOL* LoadedImage;
870
871 // Find the nearest supported file loader
872 Status = BdsLoadImage (DevicePath, AllocateAnyPages, &BinaryBuffer, &BinarySize);
873 if (EFI_ERROR(Status)) {
874 return Status;
875 }
876
877 // Load the image from the Buffer with Boot Services function
878 Status = gBS->LoadImage (TRUE, ParentImageHandle, DevicePath, (VOID*)(UINTN)BinaryBuffer, BinarySize, &ImageHandle);
879 if (EFI_ERROR(Status)) {
880 return Status;
881 }
882
883 // Passed LoadOptions to the EFI Application
884 if (LoadOptionsSize != 0) {
885 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage);
886 if (EFI_ERROR(Status)) {
887 return Status;
888 }
889
890 LoadedImage->LoadOptionsSize = LoadOptionsSize;
891 LoadedImage->LoadOptions = LoadOptions;
892 }
893
894 // Before calling the image, enable the Watchdog Timer for the 5 Minute period
895 gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
896 // Start the image
897 Status = gBS->StartImage (ImageHandle, NULL, NULL);
898 // Clear the Watchdog Timer after the image returns
899 gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
900
901 return Status;
902 }