]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/BdsLib/BdsFilePath.c
ArmPkg/BdsLib: Prevent a hang in BdsConnectDevicePath() when a sub-device path is...
[mirror_edk2.git] / ArmPkg / Library / BdsLib / BdsFilePath.c
1 /** @file
2 *
3 * Copyright (c) 2011-2014, 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 EFI_HANDLE PreviousHandle;
315
316 if ((DevicePath == NULL) || (*DevicePath == NULL) || (Handle == NULL)) {
317 return EFI_INVALID_PARAMETER;
318 }
319
320 PreviousHandle = NULL;
321 do {
322 Remaining = *DevicePath;
323
324 // The LocateDevicePath() function locates all devices on DevicePath that support Protocol and returns
325 // the handle to the device that is closest to DevicePath. On output, the device path pointer is modified
326 // to point to the remaining part of the device path
327 Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);
328
329 if (!EFI_ERROR (Status)) {
330 if (*Handle == PreviousHandle) {
331 //
332 // If no forward progress is made try invoking the Dispatcher.
333 // A new FV may have been added to the system and new drivers
334 // may now be found.
335 // Status == EFI_SUCCESS means a driver was dispatched
336 // Status == EFI_NOT_FOUND means no new drivers were dispatched
337 //
338 Status = gDS->Dispatch ();
339 }
340
341 if (!EFI_ERROR (Status)) {
342 PreviousHandle = *Handle;
343
344 // Recursive = FALSE: We do not want to start the whole device tree
345 Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);
346 }
347 }
348 } while (!EFI_ERROR (Status) && !IsDevicePathEnd (Remaining));
349
350 if (!EFI_ERROR (Status)) {
351 // Now, we have got the whole Device Path connected, call again ConnectController to ensure all the supported Driver
352 // Binding Protocol are connected (such as DiskIo and SimpleFileSystem)
353 Remaining = *DevicePath;
354 Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);
355 if (!EFI_ERROR (Status)) {
356 Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);
357 if (EFI_ERROR (Status)) {
358 // If the last node is a Memory Map Device Path just return EFI_SUCCESS.
359 if ((Remaining->Type == HARDWARE_DEVICE_PATH) && (Remaining->SubType == HW_MEMMAP_DP)) {
360 Status = EFI_SUCCESS;
361 }
362 }
363 }
364 } else if (!IsDevicePathEnd (Remaining) && !IsRemovableDevice (Remaining)) {
365
366 /*// If the remaining Device Path is a FilePath or MemoryMap then we consider the Device Path has been loaded correctly
367 if ((Remaining->Type == MEDIA_DEVICE_PATH) && (Remaining->SubType == MEDIA_FILEPATH_DP)) {
368 Status = EFI_SUCCESS;
369 } else if ((Remaining->Type == HARDWARE_DEVICE_PATH) && (Remaining->SubType == HW_MEMMAP_DP)) {
370 Status = EFI_SUCCESS;
371 }*/
372
373 //TODO: Should we just return success and leave the caller decide if it is the expected RemainingPath
374 Status = EFI_SUCCESS;
375 } else {
376 Status = TryRemovableDevice (*DevicePath, Handle, &NewDevicePath);
377 if (!EFI_ERROR (Status)) {
378 Status = BdsConnectAndUpdateDevicePath (&NewDevicePath, Handle, RemainingDevicePath);
379 *DevicePath = NewDevicePath;
380 return Status;
381 }
382 }
383
384 if (RemainingDevicePath) {
385 *RemainingDevicePath = Remaining;
386 }
387
388 return Status;
389 }
390
391 /**
392 Connect a Device Path and return the handle of the driver that support this DevicePath
393
394 @param DevicePath Device Path of the File to connect
395 @param Handle Handle of the driver that support this DevicePath
396 @param RemainingDevicePath Remaining DevicePath nodes that do not match the driver DevicePath
397
398 @retval EFI_SUCCESS A driver that matches the Device Path has been found
399 @retval EFI_NOT_FOUND No handles match the search.
400 @retval EFI_INVALID_PARAMETER DevicePath or Handle is NULL
401
402 **/
403 EFI_STATUS
404 BdsConnectDevicePath (
405 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
406 OUT EFI_HANDLE *Handle,
407 OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath
408 )
409 {
410 return BdsConnectAndUpdateDevicePath (&DevicePath, Handle, RemainingDevicePath);
411 }
412
413 BOOLEAN
414 BdsFileSystemSupport (
415 IN EFI_DEVICE_PATH *DevicePath,
416 IN EFI_HANDLE Handle,
417 IN EFI_DEVICE_PATH *RemainingDevicePath
418 )
419 {
420 EFI_STATUS Status;
421 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;
422
423 Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol);
424
425 return (!EFI_ERROR (Status) && IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP));
426 }
427
428 EFI_STATUS
429 BdsFileSystemLoadImage (
430 IN EFI_DEVICE_PATH *DevicePath,
431 IN EFI_HANDLE Handle,
432 IN EFI_DEVICE_PATH *RemainingDevicePath,
433 IN EFI_ALLOCATE_TYPE Type,
434 IN OUT EFI_PHYSICAL_ADDRESS* Image,
435 OUT UINTN *ImageSize
436 )
437 {
438 FILEPATH_DEVICE_PATH* FilePathDevicePath;
439 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;
440 EFI_FILE_PROTOCOL *Fs;
441 EFI_STATUS Status;
442 EFI_FILE_INFO *FileInfo;
443 EFI_FILE_PROTOCOL *File;
444 UINTN Size;
445
446 ASSERT (IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP));
447
448 FilePathDevicePath = (FILEPATH_DEVICE_PATH*)RemainingDevicePath;
449
450 Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol);
451 if (EFI_ERROR (Status)) {
452 return Status;
453 }
454
455 // Try to Open the volume and get root directory
456 Status = FsProtocol->OpenVolume (FsProtocol, &Fs);
457 if (EFI_ERROR (Status)) {
458 return Status;
459 }
460
461 File = NULL;
462 Status = Fs->Open (Fs, &File, FilePathDevicePath->PathName, EFI_FILE_MODE_READ, 0);
463 if (EFI_ERROR (Status)) {
464 return Status;
465 }
466
467 Size = 0;
468 File->GetInfo (File, &gEfiFileInfoGuid, &Size, NULL);
469 FileInfo = AllocatePool (Size);
470 Status = File->GetInfo (File, &gEfiFileInfoGuid, &Size, FileInfo);
471 if (EFI_ERROR (Status)) {
472 return Status;
473 }
474
475 // Get the file size
476 Size = FileInfo->FileSize;
477 if (ImageSize) {
478 *ImageSize = Size;
479 }
480 FreePool (FileInfo);
481
482 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
483 // Try to allocate in any pages if failed to allocate memory at the defined location
484 if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
485 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
486 }
487 if (!EFI_ERROR (Status)) {
488 Status = File->Read (File, &Size, (VOID*)(UINTN)(*Image));
489 }
490
491 return Status;
492 }
493
494 BOOLEAN
495 BdsMemoryMapSupport (
496 IN EFI_DEVICE_PATH *DevicePath,
497 IN EFI_HANDLE Handle,
498 IN EFI_DEVICE_PATH *RemainingDevicePath
499 )
500 {
501 return IS_DEVICE_PATH_NODE (DevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP) ||
502 IS_DEVICE_PATH_NODE (RemainingDevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP);
503 }
504
505 EFI_STATUS
506 BdsMemoryMapLoadImage (
507 IN EFI_DEVICE_PATH *DevicePath,
508 IN EFI_HANDLE Handle,
509 IN EFI_DEVICE_PATH *RemainingDevicePath,
510 IN EFI_ALLOCATE_TYPE Type,
511 IN OUT EFI_PHYSICAL_ADDRESS* Image,
512 OUT UINTN *ImageSize
513 )
514 {
515 EFI_STATUS Status;
516 MEMMAP_DEVICE_PATH* MemMapPathDevicePath;
517 UINTN Size;
518
519 if (IS_DEVICE_PATH_NODE (RemainingDevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP)) {
520 MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)RemainingDevicePath;
521 } else {
522 ASSERT (IS_DEVICE_PATH_NODE (DevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP));
523 MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)DevicePath;
524 }
525
526 Size = MemMapPathDevicePath->EndingAddress - MemMapPathDevicePath->StartingAddress;
527 if (Size == 0) {
528 return EFI_INVALID_PARAMETER;
529 }
530
531 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
532 // Try to allocate in any pages if failed to allocate memory at the defined location
533 if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
534 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
535 }
536 if (!EFI_ERROR (Status)) {
537 CopyMem ((VOID*)(UINTN)(*Image), (CONST VOID*)(UINTN)MemMapPathDevicePath->StartingAddress, Size);
538
539 if (ImageSize != NULL) {
540 *ImageSize = Size;
541 }
542 }
543
544 return Status;
545 }
546
547 BOOLEAN
548 BdsFirmwareVolumeSupport (
549 IN EFI_DEVICE_PATH *DevicePath,
550 IN EFI_HANDLE Handle,
551 IN EFI_DEVICE_PATH *RemainingDevicePath
552 )
553 {
554 return IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_FILE_DP);
555 }
556
557 EFI_STATUS
558 BdsFirmwareVolumeLoadImage (
559 IN EFI_DEVICE_PATH *DevicePath,
560 IN EFI_HANDLE Handle,
561 IN EFI_DEVICE_PATH *RemainingDevicePath,
562 IN EFI_ALLOCATE_TYPE Type,
563 IN OUT EFI_PHYSICAL_ADDRESS* Image,
564 OUT UINTN *ImageSize
565 )
566 {
567 EFI_STATUS Status;
568 EFI_FIRMWARE_VOLUME2_PROTOCOL *FwVol;
569 EFI_GUID *FvNameGuid;
570 EFI_SECTION_TYPE SectionType;
571 EFI_FV_FILETYPE FvType;
572 EFI_FV_FILE_ATTRIBUTES Attrib;
573 UINT32 AuthenticationStatus;
574 VOID* ImageBuffer;
575
576 ASSERT (IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_FILE_DP));
577
578 Status = gBS->HandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&FwVol);
579 if (EFI_ERROR (Status)) {
580 return Status;
581 }
582
583 FvNameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)RemainingDevicePath);
584 if (FvNameGuid == NULL) {
585 Status = EFI_INVALID_PARAMETER;
586 }
587
588 SectionType = EFI_SECTION_PE32;
589 AuthenticationStatus = 0;
590 //Note: ReadSection at the opposite of ReadFile does not allow to pass ImageBuffer == NULL to get the size of the file.
591 ImageBuffer = NULL;
592 Status = FwVol->ReadSection (
593 FwVol,
594 FvNameGuid,
595 SectionType,
596 0,
597 &ImageBuffer,
598 ImageSize,
599 &AuthenticationStatus
600 );
601 if (!EFI_ERROR (Status)) {
602 #if 0
603 // In case the buffer has some address requirements, we must copy the buffer to a buffer following the requirements
604 if (Type != AllocateAnyPages) {
605 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize),Image);
606 if (!EFI_ERROR (Status)) {
607 CopyMem ((VOID*)(UINTN)(*Image), ImageBuffer, *ImageSize);
608 FreePool (ImageBuffer);
609 }
610 }
611 #else
612 // We must copy the buffer into a page allocations. Otherwise, the caller could call gBS->FreePages() on the pool allocation
613 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
614 // Try to allocate in any pages if failed to allocate memory at the defined location
615 if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
616 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
617 }
618 if (!EFI_ERROR (Status)) {
619 CopyMem ((VOID*)(UINTN)(*Image), ImageBuffer, *ImageSize);
620 FreePool (ImageBuffer);
621 }
622 #endif
623 } else {
624 // Try a raw file, since a PE32 SECTION does not exist
625 Status = FwVol->ReadFile (
626 FwVol,
627 FvNameGuid,
628 NULL,
629 ImageSize,
630 &FvType,
631 &Attrib,
632 &AuthenticationStatus
633 );
634 if (!EFI_ERROR (Status)) {
635 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
636 // Try to allocate in any pages if failed to allocate memory at the defined location
637 if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
638 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
639 }
640 if (!EFI_ERROR (Status)) {
641 Status = FwVol->ReadFile (
642 FwVol,
643 FvNameGuid,
644 (VOID*)(UINTN)(*Image),
645 ImageSize,
646 &FvType,
647 &Attrib,
648 &AuthenticationStatus
649 );
650 }
651 }
652 }
653 return Status;
654 }
655
656 BOOLEAN
657 BdsPxeSupport (
658 IN EFI_DEVICE_PATH* DevicePath,
659 IN EFI_HANDLE Handle,
660 IN EFI_DEVICE_PATH* RemainingDevicePath
661 )
662 {
663 EFI_STATUS Status;
664 EFI_PXE_BASE_CODE_PROTOCOL* PxeBcProtocol;
665
666 if (!IsDevicePathEnd (RemainingDevicePath)) {
667 return FALSE;
668 }
669
670 Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol);
671 if (EFI_ERROR (Status)) {
672 return FALSE;
673 } else {
674 return TRUE;
675 }
676 }
677
678 EFI_STATUS
679 BdsPxeLoadImage (
680 IN EFI_DEVICE_PATH* DevicePath,
681 IN EFI_HANDLE Handle,
682 IN EFI_DEVICE_PATH* RemainingDevicePath,
683 IN EFI_ALLOCATE_TYPE Type,
684 IN OUT EFI_PHYSICAL_ADDRESS *Image,
685 OUT UINTN *ImageSize
686 )
687 {
688 EFI_STATUS Status;
689 EFI_LOAD_FILE_PROTOCOL *LoadFileProtocol;
690 UINTN BufferSize;
691 EFI_PXE_BASE_CODE_PROTOCOL *Pxe;
692
693 // Get Load File Protocol attached to the PXE protocol
694 Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFileProtocol);
695 if (EFI_ERROR (Status)) {
696 return Status;
697 }
698
699 Status = LoadFileProtocol->LoadFile (LoadFileProtocol, DevicePath, TRUE, &BufferSize, NULL);
700 if (Status == EFI_BUFFER_TOO_SMALL) {
701 Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(BufferSize), Image);
702 if (EFI_ERROR (Status)) {
703 return Status;
704 }
705
706 Status = LoadFileProtocol->LoadFile (LoadFileProtocol, DevicePath, TRUE, &BufferSize, (VOID*)(UINTN)(*Image));
707 if (!EFI_ERROR (Status) && (ImageSize != NULL)) {
708 *ImageSize = BufferSize;
709 }
710 }
711
712 if (Status == EFI_ALREADY_STARTED) {
713 Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe);
714 if (!EFI_ERROR(Status)) {
715 // If PXE is already started, we stop it
716 Pxe->Stop (Pxe);
717 // And we try again
718 return BdsPxeLoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, ImageSize);
719 }
720 }
721 return Status;
722 }
723
724 BOOLEAN
725 BdsTftpSupport (
726 IN EFI_DEVICE_PATH* DevicePath,
727 IN EFI_HANDLE Handle,
728 IN EFI_DEVICE_PATH* RemainingDevicePath
729 )
730 {
731 EFI_STATUS Status;
732 EFI_DEVICE_PATH *NextDevicePath;
733 EFI_PXE_BASE_CODE_PROTOCOL *PxeBcProtocol;
734
735 // Validate the Remaining Device Path
736 if (IsDevicePathEnd (RemainingDevicePath)) {
737 return FALSE;
738 }
739 if (!IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP) &&
740 !IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv6_DP)) {
741 return FALSE;
742 }
743 NextDevicePath = NextDevicePathNode (RemainingDevicePath);
744 if (IsDevicePathEnd (NextDevicePath)) {
745 return FALSE;
746 }
747 if (!IS_DEVICE_PATH_NODE (NextDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP)) {
748 return FALSE;
749 }
750
751 Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol);
752 if (EFI_ERROR (Status)) {
753 return FALSE;
754 } else {
755 return TRUE;
756 }
757 }
758
759 EFI_STATUS
760 BdsTftpLoadImage (
761 IN EFI_DEVICE_PATH* DevicePath,
762 IN EFI_HANDLE Handle,
763 IN EFI_DEVICE_PATH* RemainingDevicePath,
764 IN EFI_ALLOCATE_TYPE Type,
765 IN OUT EFI_PHYSICAL_ADDRESS *Image,
766 OUT UINTN *ImageSize
767 )
768 {
769 EFI_STATUS Status;
770 EFI_PXE_BASE_CODE_PROTOCOL *Pxe;
771 UINT64 TftpBufferSize;
772 UINT64 TftpTransferSize;
773 EFI_IP_ADDRESS ServerIp;
774 IPv4_DEVICE_PATH* IPv4DevicePathNode;
775 FILEPATH_DEVICE_PATH* FilePathDevicePath;
776 EFI_IP_ADDRESS LocalIp;
777 CHAR8* AsciiPathName;
778 EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
779
780 ASSERT(IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP));
781
782 IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath;
783 FilePathDevicePath = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1);
784
785 Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe);
786 if (EFI_ERROR (Status)) {
787 return Status;
788 }
789
790 Status = Pxe->Start (Pxe, FALSE);
791 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
792 return Status;
793 }
794
795 do {
796 if (!IPv4DevicePathNode->StaticIpAddress) {
797 Status = Pxe->Dhcp (Pxe, TRUE);
798 } else {
799 CopyMem (&LocalIp.v4, &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));
800 Status = Pxe->SetStationIp (Pxe, &LocalIp, NULL);
801 }
802
803 // If an IP Address has already been set and a different static IP address is requested then restart
804 // the Network service.
805 if (Status == EFI_ALREADY_STARTED) {
806 Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&Snp);
807 if (!EFI_ERROR (Status) && IPv4DevicePathNode->StaticIpAddress &&
808 (CompareMem (&Snp->Mode->CurrentAddress, &IPv4DevicePathNode->LocalIpAddress, sizeof(EFI_MAC_ADDRESS)) != 0))
809 {
810 Pxe->Stop (Pxe);
811 Status = Pxe->Start (Pxe, FALSE);
812 if (EFI_ERROR(Status)) {
813 break;
814 }
815 // After restarting the PXE protocol, we want to try again with our new IP Address
816 Status = EFI_ALREADY_STARTED;
817 }
818 }
819 } while (Status == EFI_ALREADY_STARTED);
820
821 if (EFI_ERROR(Status)) {
822 return Status;
823 }
824
825 CopyMem (&ServerIp.v4, &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS));
826
827 // Convert the Unicode PathName to Ascii
828 AsciiPathName = AllocatePool ((StrLen (FilePathDevicePath->PathName) + 1) * sizeof (CHAR8));
829 if (AsciiPathName == NULL) {
830 return EFI_OUT_OF_RESOURCES;
831 }
832 UnicodeStrToAsciiStr (FilePathDevicePath->PathName, AsciiPathName);
833
834 // Try to get the size (required the TFTP server to have "tsize" extension)
835 Status = Pxe->Mtftp (
836 Pxe,
837 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
838 NULL,
839 FALSE,
840 &TftpBufferSize,
841 NULL,
842 &ServerIp,
843 (UINT8*)AsciiPathName,
844 NULL,
845 FALSE
846 );
847 // Pxe.Mtftp replies EFI_PROTOCOL_ERROR if tsize is not supported by the TFTP server
848 if (EFI_ERROR (Status) && (Status != EFI_PROTOCOL_ERROR)) {
849 if (Status == EFI_TFTP_ERROR) {
850 DEBUG((EFI_D_ERROR, "TFTP Error: Fail to get the size of the file\n"));
851 }
852 goto EXIT;
853 }
854
855 //
856 // Two cases:
857 // 1) the file size is unknown (tsize extension not supported)
858 // 2) tsize returned the file size
859 //
860 if (Status == EFI_PROTOCOL_ERROR) {
861 for (TftpBufferSize = SIZE_8MB; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize); TftpBufferSize += SIZE_8MB) {
862 // Allocate a buffer to hold the whole file.
863 Status = gBS->AllocatePages (
864 Type,
865 EfiBootServicesCode,
866 EFI_SIZE_TO_PAGES (TftpBufferSize),
867 Image
868 );
869 if (EFI_ERROR (Status)) {
870 DEBUG ((EFI_D_ERROR, "Failed to allocate space for image: %r\n", Status));
871 goto EXIT;
872 }
873
874 TftpTransferSize = TftpBufferSize;
875 Status = Pxe->Mtftp (
876 Pxe,
877 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
878 (VOID *)(UINTN)*Image,
879 FALSE,
880 &TftpTransferSize,
881 NULL,
882 &ServerIp,
883 (UINT8*)AsciiPathName,
884 NULL,
885 FALSE
886 );
887 if (EFI_ERROR (Status)) {
888 gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));
889 } else {
890 *ImageSize = (UINTN)TftpBufferSize;
891 break;
892 }
893 }
894 } else {
895 // Allocate a buffer to hold the whole file.
896 Status = gBS->AllocatePages (
897 Type,
898 EfiBootServicesCode,
899 EFI_SIZE_TO_PAGES (TftpBufferSize),
900 Image
901 );
902 if (EFI_ERROR (Status)) {
903 DEBUG ((EFI_D_ERROR, "Failed to allocate space for kernel image: %r\n", Status));
904 goto EXIT;
905 }
906
907 Status = Pxe->Mtftp (
908 Pxe,
909 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
910 (VOID *)(UINTN)*Image,
911 FALSE,
912 &TftpBufferSize,
913 NULL,
914 &ServerIp,
915 (UINT8*)AsciiPathName,
916 NULL,
917 FALSE
918 );
919 if (EFI_ERROR (Status)) {
920 gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));
921 } else {
922 *ImageSize = (UINTN)TftpBufferSize;
923 }
924 }
925
926 EXIT:
927 FreePool (AsciiPathName);
928 return Status;
929 }
930
931 BDS_FILE_LOADER FileLoaders[] = {
932 { BdsFileSystemSupport, BdsFileSystemLoadImage },
933 { BdsFirmwareVolumeSupport, BdsFirmwareVolumeLoadImage },
934 //{ BdsLoadFileSupport, BdsLoadFileLoadImage },
935 { BdsMemoryMapSupport, BdsMemoryMapLoadImage },
936 { BdsPxeSupport, BdsPxeLoadImage },
937 { BdsTftpSupport, BdsTftpLoadImage },
938 { NULL, NULL }
939 };
940
941 EFI_STATUS
942 BdsLoadImageAndUpdateDevicePath (
943 IN OUT EFI_DEVICE_PATH **DevicePath,
944 IN EFI_ALLOCATE_TYPE Type,
945 IN OUT EFI_PHYSICAL_ADDRESS* Image,
946 OUT UINTN *FileSize
947 )
948 {
949 EFI_STATUS Status;
950 EFI_HANDLE Handle;
951 EFI_DEVICE_PATH *RemainingDevicePath;
952 BDS_FILE_LOADER* FileLoader;
953
954 Status = BdsConnectAndUpdateDevicePath (DevicePath, &Handle, &RemainingDevicePath);
955 if (EFI_ERROR (Status)) {
956 return Status;
957 }
958
959 FileLoader = FileLoaders;
960 while (FileLoader->Support != NULL) {
961 if (FileLoader->Support (*DevicePath, Handle, RemainingDevicePath)) {
962 return FileLoader->LoadImage (*DevicePath, Handle, RemainingDevicePath, Type, Image, FileSize);
963 }
964 FileLoader++;
965 }
966
967 return EFI_UNSUPPORTED;
968 }
969
970 EFI_STATUS
971 BdsLoadImage (
972 IN EFI_DEVICE_PATH *DevicePath,
973 IN EFI_ALLOCATE_TYPE Type,
974 IN OUT EFI_PHYSICAL_ADDRESS* Image,
975 OUT UINTN *FileSize
976 )
977 {
978 return BdsLoadImageAndUpdateDevicePath (&DevicePath, Type, Image, FileSize);
979 }
980
981 /**
982 Start an EFI Application from a Device Path
983
984 @param ParentImageHandle Handle of the calling image
985 @param DevicePath Location of the EFI Application
986
987 @retval EFI_SUCCESS All drivers have been connected
988 @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found
989 @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.
990
991 **/
992 EFI_STATUS
993 BdsStartEfiApplication (
994 IN EFI_HANDLE ParentImageHandle,
995 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
996 IN UINTN LoadOptionsSize,
997 IN VOID* LoadOptions
998 )
999 {
1000 EFI_STATUS Status;
1001 EFI_HANDLE ImageHandle;
1002 EFI_PHYSICAL_ADDRESS BinaryBuffer;
1003 UINTN BinarySize;
1004 EFI_LOADED_IMAGE_PROTOCOL* LoadedImage;
1005
1006 // Find the nearest supported file loader
1007 Status = BdsLoadImageAndUpdateDevicePath (&DevicePath, AllocateAnyPages, &BinaryBuffer, &BinarySize);
1008 if (EFI_ERROR (Status)) {
1009 return Status;
1010 }
1011
1012 // Load the image from the Buffer with Boot Services function
1013 Status = gBS->LoadImage (TRUE, ParentImageHandle, DevicePath, (VOID*)(UINTN)BinaryBuffer, BinarySize, &ImageHandle);
1014 if (EFI_ERROR (Status)) {
1015 return Status;
1016 }
1017
1018 // Passed LoadOptions to the EFI Application
1019 if (LoadOptionsSize != 0) {
1020 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage);
1021 if (EFI_ERROR (Status)) {
1022 return Status;
1023 }
1024
1025 LoadedImage->LoadOptionsSize = LoadOptionsSize;
1026 LoadedImage->LoadOptions = LoadOptions;
1027 }
1028
1029 // Before calling the image, enable the Watchdog Timer for the 5 Minute period
1030 gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
1031 // Start the image
1032 Status = gBS->StartImage (ImageHandle, NULL, NULL);
1033 // Clear the Watchdog Timer after the image returns
1034 gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
1035
1036 return Status;
1037 }