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