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 <Library/NetLib.h>
18 
19 #include <Protocol/Bds.h>
20 #include <Protocol/UsbIo.h>
21 #include <Protocol/DiskIo.h>
22 #include <Protocol/LoadedImage.h>
23 #include <Protocol/SimpleNetwork.h>
24 #include <Protocol/Dhcp4.h>
25 #include <Protocol/Mtftp4.h>
26 
27 
28 #define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))
29 
30 /* Type and defines to set up the DHCP4 options */
31 
32 typedef struct {
33   EFI_DHCP4_PACKET_OPTION Head;
34   UINT8                   Route;
35 } DHCP4_OPTION;
36 
37 #define DHCP_TAG_PARA_LIST  55
38 #define DHCP_TAG_NETMASK     1
39 #define DHCP_TAG_ROUTER      3
40 
41 /*
42    Constant strings and define related to the message indicating the amount of
43    progress in the dowloading of a TFTP file.
44 */
45 
46 // Frame for the progression slider
47 STATIC CONST CHAR16 mTftpProgressFrame[] = L"[                                        ]";
48 
49 // Number of steps in the progression slider
50 #define TFTP_PROGRESS_SLIDER_STEPS  ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 3)
51 
52 // Size in number of characters plus one (final zero) of the message to
53 // indicate the progress of a tftp download. The format is "[(progress slider:
54 // 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". There
55 // are thus the number of characters in mTftpProgressFrame[] plus 11 characters
56 // (2 // spaces, "Kb" and seven characters for the number of KBytes).
57 #define TFTP_PROGRESS_MESSAGE_SIZE  ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) + 12)
58 
59 // String to delete the tftp progress message to be able to update it :
60 // (TFTP_PROGRESS_MESSAGE_SIZE-1) '\b'
61 STATIC CONST CHAR16 mTftpProgressDelete[] = L"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
62 
63 
64 // Extract the FilePath from the Device Path
65 CHAR16*
BdsExtractFilePathFromDevicePath(IN CONST CHAR16 * StrDevicePath,IN UINTN NumberDevicePathNode)66 BdsExtractFilePathFromDevicePath (
67   IN  CONST CHAR16    *StrDevicePath,
68   IN  UINTN           NumberDevicePathNode
69   )
70 {
71   UINTN       Node;
72   CHAR16      *Str;
73 
74   Str = (CHAR16*)StrDevicePath;
75   Node = 0;
76   while ((Str != NULL) && (*Str != L'\0') && (Node < NumberDevicePathNode)) {
77     if ((*Str == L'/') || (*Str == L'\\')) {
78         Node++;
79     }
80     Str++;
81   }
82 
83   if (*Str == L'\0') {
84     return NULL;
85   } else {
86     return Str;
87   }
88 }
89 
90 BOOLEAN
BdsIsRemovableUsb(IN EFI_DEVICE_PATH * DevicePath)91 BdsIsRemovableUsb (
92   IN  EFI_DEVICE_PATH*  DevicePath
93   )
94 {
95   return ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
96           ((DevicePathSubType (DevicePath) == MSG_USB_CLASS_DP) ||
97            (DevicePathSubType (DevicePath) == MSG_USB_WWID_DP)));
98 }
99 
100 EFI_STATUS
BdsGetDeviceUsb(IN EFI_DEVICE_PATH * RemovableDevicePath,OUT EFI_HANDLE * DeviceHandle,OUT EFI_DEVICE_PATH ** NewDevicePath)101 BdsGetDeviceUsb (
102   IN  EFI_DEVICE_PATH*  RemovableDevicePath,
103   OUT EFI_HANDLE*       DeviceHandle,
104   OUT EFI_DEVICE_PATH** NewDevicePath
105   )
106 {
107   EFI_STATUS                    Status;
108   UINTN                         Index;
109   UINTN                         UsbIoHandleCount;
110   EFI_HANDLE                    *UsbIoBuffer;
111   EFI_DEVICE_PATH*              UsbIoDevicePath;
112   EFI_DEVICE_PATH*              TmpDevicePath;
113   USB_WWID_DEVICE_PATH*         WwidDevicePath1;
114   USB_WWID_DEVICE_PATH*         WwidDevicePath2;
115   USB_CLASS_DEVICE_PATH*        UsbClassDevicePath1;
116   USB_CLASS_DEVICE_PATH*        UsbClassDevicePath2;
117 
118   // Get all the UsbIo handles
119   UsbIoHandleCount = 0;
120   Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiUsbIoProtocolGuid, NULL, &UsbIoHandleCount, &UsbIoBuffer);
121   if (EFI_ERROR (Status) || (UsbIoHandleCount == 0)) {
122     return Status;
123   }
124 
125   // Check if one of the handles matches the USB description
126   for (Index = 0; Index < UsbIoHandleCount; Index++) {
127     Status = gBS->HandleProtocol (UsbIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **) &UsbIoDevicePath);
128     if (!EFI_ERROR (Status)) {
129       TmpDevicePath = UsbIoDevicePath;
130       while (!IsDevicePathEnd (TmpDevicePath)) {
131         // Check if the Device Path node is a USB Removable device Path node
132         if (BdsIsRemovableUsb (TmpDevicePath)) {
133           if (TmpDevicePath->SubType == MSG_USB_WWID_DP) {
134             WwidDevicePath1 = (USB_WWID_DEVICE_PATH*)RemovableDevicePath;
135             WwidDevicePath2 = (USB_WWID_DEVICE_PATH*)TmpDevicePath;
136             if ((WwidDevicePath1->VendorId == WwidDevicePath2->VendorId) &&
137                 (WwidDevicePath1->ProductId == WwidDevicePath2->ProductId) &&
138                 (CompareMem (WwidDevicePath1+1, WwidDevicePath2+1, DevicePathNodeLength(WwidDevicePath1)-sizeof (USB_WWID_DEVICE_PATH)) == 0))
139             {
140               *DeviceHandle = UsbIoBuffer[Index];
141               // Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path
142               *NewDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePathNode (RemovableDevicePath));
143               return EFI_SUCCESS;
144             }
145           } else {
146             UsbClassDevicePath1 = (USB_CLASS_DEVICE_PATH*)RemovableDevicePath;
147             UsbClassDevicePath2 = (USB_CLASS_DEVICE_PATH*)TmpDevicePath;
148             if ((UsbClassDevicePath1->VendorId != 0xFFFF) && (UsbClassDevicePath1->VendorId == UsbClassDevicePath2->VendorId) &&
149                 (UsbClassDevicePath1->ProductId != 0xFFFF) && (UsbClassDevicePath1->ProductId == UsbClassDevicePath2->ProductId) &&
150                 (UsbClassDevicePath1->DeviceClass != 0xFF) && (UsbClassDevicePath1->DeviceClass == UsbClassDevicePath2->DeviceClass) &&
151                 (UsbClassDevicePath1->DeviceSubClass != 0xFF) && (UsbClassDevicePath1->DeviceSubClass == UsbClassDevicePath2->DeviceSubClass) &&
152                 (UsbClassDevicePath1->DeviceProtocol != 0xFF) && (UsbClassDevicePath1->DeviceProtocol == UsbClassDevicePath2->DeviceProtocol))
153             {
154               *DeviceHandle = UsbIoBuffer[Index];
155               // Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path
156               *NewDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePathNode (RemovableDevicePath));
157               return EFI_SUCCESS;
158             }
159           }
160         }
161         TmpDevicePath = NextDevicePathNode (TmpDevicePath);
162       }
163 
164     }
165   }
166 
167   return EFI_NOT_FOUND;
168 }
169 
170 BOOLEAN
BdsIsRemovableHd(IN EFI_DEVICE_PATH * DevicePath)171 BdsIsRemovableHd (
172   IN  EFI_DEVICE_PATH*  DevicePath
173   )
174 {
175   return IS_DEVICE_PATH_NODE (DevicePath, MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP);
176 }
177 
178 EFI_STATUS
BdsGetDeviceHd(IN EFI_DEVICE_PATH * RemovableDevicePath,OUT EFI_HANDLE * DeviceHandle,OUT EFI_DEVICE_PATH ** NewDevicePath)179 BdsGetDeviceHd (
180   IN  EFI_DEVICE_PATH*  RemovableDevicePath,
181   OUT EFI_HANDLE*       DeviceHandle,
182   OUT EFI_DEVICE_PATH** NewDevicePath
183   )
184 {
185   EFI_STATUS                    Status;
186   UINTN                         Index;
187   UINTN                         PartitionHandleCount;
188   EFI_HANDLE                    *PartitionBuffer;
189   EFI_DEVICE_PATH*              PartitionDevicePath;
190   EFI_DEVICE_PATH*              TmpDevicePath;
191   HARDDRIVE_DEVICE_PATH*        HardDriveDevicePath1;
192   HARDDRIVE_DEVICE_PATH*        HardDriveDevicePath2;
193 
194   // Get all the DiskIo handles
195   PartitionHandleCount = 0;
196   Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiDiskIoProtocolGuid, NULL, &PartitionHandleCount, &PartitionBuffer);
197   if (EFI_ERROR (Status) || (PartitionHandleCount == 0)) {
198     return Status;
199   }
200 
201   // Check if one of the handles matches the Hard Disk Description
202   for (Index = 0; Index < PartitionHandleCount; Index++) {
203     Status = gBS->HandleProtocol (PartitionBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **) &PartitionDevicePath);
204     if (!EFI_ERROR (Status)) {
205       TmpDevicePath = PartitionDevicePath;
206       while (!IsDevicePathEnd (TmpDevicePath)) {
207         // Check if the Device Path node is a HD Removable device Path node
208         if (BdsIsRemovableHd (TmpDevicePath)) {
209           HardDriveDevicePath1 = (HARDDRIVE_DEVICE_PATH*)RemovableDevicePath;
210           HardDriveDevicePath2 = (HARDDRIVE_DEVICE_PATH*)TmpDevicePath;
211           if ((HardDriveDevicePath1->SignatureType == HardDriveDevicePath2->SignatureType) &&
212               (CompareGuid ((EFI_GUID *)HardDriveDevicePath1->Signature, (EFI_GUID *)HardDriveDevicePath2->Signature) == TRUE) &&
213               (HardDriveDevicePath1->PartitionNumber == HardDriveDevicePath2->PartitionNumber))
214           {
215             *DeviceHandle = PartitionBuffer[Index];
216             // Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path
217             *NewDevicePath = AppendDevicePath (PartitionDevicePath, NextDevicePathNode (RemovableDevicePath));
218             return EFI_SUCCESS;
219           }
220         }
221         TmpDevicePath = NextDevicePathNode (TmpDevicePath);
222       }
223 
224     }
225   }
226 
227   return EFI_NOT_FOUND;
228 }
229 
230 /*BOOLEAN
231 BdsIsRemovableCdrom (
232   IN  EFI_DEVICE_PATH*  DevicePath
233   )
234 {
235   return IS_DEVICE_PATH_NODE (DevicePath, MEDIA_DEVICE_PATH, MEDIA_CDROM_DP);
236 }
237 
238 EFI_STATUS
239 BdsGetDeviceCdrom (
240   IN  EFI_DEVICE_PATH*  RemovableDevicePath,
241   OUT EFI_HANDLE*       DeviceHandle,
242   OUT EFI_DEVICE_PATH** DevicePath
243   )
244 {
245   ASSERT(0);
246   return EFI_UNSUPPORTED;
247 }*/
248 
249 typedef BOOLEAN
250 (*BDS_IS_REMOVABLE) (
251   IN  EFI_DEVICE_PATH*  DevicePath
252   );
253 
254 typedef EFI_STATUS
255 (*BDS_GET_DEVICE) (
256   IN  EFI_DEVICE_PATH*  RemovableDevicePath,
257   OUT EFI_HANDLE*       DeviceHandle,
258   OUT EFI_DEVICE_PATH** DevicePath
259   );
260 
261 typedef struct {
262   BDS_IS_REMOVABLE    IsRemovable;
263   BDS_GET_DEVICE      GetDevice;
264 } BDS_REMOVABLE_DEVICE_SUPPORT;
265 
266 BDS_REMOVABLE_DEVICE_SUPPORT  RemovableDeviceSupport[] = {
267   { BdsIsRemovableUsb, BdsGetDeviceUsb },
268   { BdsIsRemovableHd, BdsGetDeviceHd },
269   //{ BdsIsRemovableCdrom, BdsGetDeviceCdrom }
270 };
271 
272 STATIC
273 BOOLEAN
IsRemovableDevice(IN EFI_DEVICE_PATH * DevicePath)274 IsRemovableDevice (
275   IN  EFI_DEVICE_PATH*  DevicePath
276   )
277 {
278   UINTN             Index;
279   EFI_DEVICE_PATH*  TmpDevicePath;
280 
281   TmpDevicePath = DevicePath;
282   while (!IsDevicePathEnd (TmpDevicePath)) {
283     for (Index = 0; Index < sizeof (RemovableDeviceSupport) / sizeof (BDS_REMOVABLE_DEVICE_SUPPORT); Index++) {
284       if (RemovableDeviceSupport[Index].IsRemovable (TmpDevicePath)) {
285         return TRUE;
286       }
287     }
288     TmpDevicePath = NextDevicePathNode (TmpDevicePath);
289   }
290 
291   return FALSE;
292 }
293 
294 STATIC
295 EFI_STATUS
TryRemovableDevice(IN EFI_DEVICE_PATH * DevicePath,OUT EFI_HANDLE * DeviceHandle,OUT EFI_DEVICE_PATH ** NewDevicePath)296 TryRemovableDevice (
297   IN  EFI_DEVICE_PATH*  DevicePath,
298   OUT EFI_HANDLE*       DeviceHandle,
299   OUT EFI_DEVICE_PATH** NewDevicePath
300   )
301 {
302   EFI_STATUS        Status;
303   UINTN             Index;
304   EFI_DEVICE_PATH*  TmpDevicePath;
305   BDS_REMOVABLE_DEVICE_SUPPORT* RemovableDevice;
306   EFI_DEVICE_PATH* RemovableDevicePath;
307   BOOLEAN         RemovableFound;
308 
309   RemovableDevice     = NULL;
310   RemovableDevicePath = NULL;
311   RemovableFound      = FALSE;
312   TmpDevicePath       = DevicePath;
313 
314   while (!IsDevicePathEnd (TmpDevicePath) && !RemovableFound) {
315     for (Index = 0; Index < sizeof (RemovableDeviceSupport) / sizeof (BDS_REMOVABLE_DEVICE_SUPPORT); Index++) {
316       RemovableDevice = &RemovableDeviceSupport[Index];
317       if (RemovableDevice->IsRemovable (TmpDevicePath)) {
318         RemovableDevicePath = TmpDevicePath;
319         RemovableFound = TRUE;
320         break;
321       }
322     }
323     TmpDevicePath = NextDevicePathNode (TmpDevicePath);
324   }
325 
326   if (!RemovableFound) {
327     return EFI_NOT_FOUND;
328   }
329 
330   // Search into the current started drivers
331   Status = RemovableDevice->GetDevice (RemovableDevicePath, DeviceHandle, NewDevicePath);
332   if (Status == EFI_NOT_FOUND) {
333     // Connect all the drivers
334     BdsConnectAllDrivers ();
335 
336     // Search again into all the drivers
337     Status = RemovableDevice->GetDevice (RemovableDevicePath, DeviceHandle, NewDevicePath);
338   }
339 
340   return Status;
341 }
342 
343 STATIC
344 EFI_STATUS
BdsConnectAndUpdateDevicePath(IN OUT EFI_DEVICE_PATH_PROTOCOL ** DevicePath,OUT EFI_HANDLE * Handle,OUT EFI_DEVICE_PATH_PROTOCOL ** RemainingDevicePath)345 BdsConnectAndUpdateDevicePath (
346   IN OUT EFI_DEVICE_PATH_PROTOCOL  **DevicePath,
347   OUT    EFI_HANDLE                *Handle,
348   OUT    EFI_DEVICE_PATH_PROTOCOL  **RemainingDevicePath
349   )
350 {
351   EFI_DEVICE_PATH*            Remaining;
352   EFI_DEVICE_PATH*            NewDevicePath;
353   EFI_STATUS                  Status;
354   EFI_HANDLE                  PreviousHandle;
355 
356   if ((DevicePath == NULL) || (*DevicePath == NULL) || (Handle == NULL)) {
357     return EFI_INVALID_PARAMETER;
358   }
359 
360   PreviousHandle = NULL;
361   do {
362     Remaining = *DevicePath;
363 
364     // The LocateDevicePath() function locates all devices on DevicePath that support Protocol and returns
365     // the handle to the device that is closest to DevicePath. On output, the device path pointer is modified
366     // to point to the remaining part of the device path
367     Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);
368 
369     if (!EFI_ERROR (Status)) {
370       if (*Handle == PreviousHandle) {
371         //
372         // If no forward progress is made try invoking the Dispatcher.
373         // A new FV may have been added to the system and new drivers
374         // may now be found.
375         // Status == EFI_SUCCESS means a driver was dispatched
376         // Status == EFI_NOT_FOUND means no new drivers were dispatched
377         //
378         Status = gDS->Dispatch ();
379       }
380 
381       if (!EFI_ERROR (Status)) {
382         PreviousHandle = *Handle;
383 
384         // Recursive = FALSE: We do not want to start the whole device tree
385         Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);
386       }
387     }
388   } while (!EFI_ERROR (Status) && !IsDevicePathEnd (Remaining));
389 
390   if (!EFI_ERROR (Status)) {
391     // Now, we have got the whole Device Path connected, call again ConnectController to ensure all the supported Driver
392     // Binding Protocol are connected (such as DiskIo and SimpleFileSystem)
393     Remaining = *DevicePath;
394     Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);
395     if (!EFI_ERROR (Status)) {
396       Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);
397       if (EFI_ERROR (Status)) {
398         // If the last node is a Memory Map Device Path just return EFI_SUCCESS.
399         if ((Remaining->Type == HARDWARE_DEVICE_PATH) && (Remaining->SubType == HW_MEMMAP_DP)) {
400             Status = EFI_SUCCESS;
401         }
402       }
403     }
404   } else if (!IsDevicePathEnd (Remaining) && !IsRemovableDevice (Remaining)) {
405 
406     /*// If the remaining Device Path is a FilePath or MemoryMap then we consider the Device Path has been loaded correctly
407     if ((Remaining->Type == MEDIA_DEVICE_PATH) && (Remaining->SubType == MEDIA_FILEPATH_DP)) {
408       Status = EFI_SUCCESS;
409     } else if ((Remaining->Type == HARDWARE_DEVICE_PATH) && (Remaining->SubType == HW_MEMMAP_DP)) {
410       Status = EFI_SUCCESS;
411     }*/
412 
413     //TODO: Should we just return success and leave the caller decide if it is the expected RemainingPath
414     Status = EFI_SUCCESS;
415   } else {
416     Status = TryRemovableDevice (*DevicePath, Handle, &NewDevicePath);
417     if (!EFI_ERROR (Status)) {
418       Status = BdsConnectAndUpdateDevicePath (&NewDevicePath, Handle, RemainingDevicePath);
419       *DevicePath = NewDevicePath;
420       return Status;
421     }
422   }
423 
424   if (RemainingDevicePath) {
425     *RemainingDevicePath = Remaining;
426   }
427 
428   return Status;
429 }
430 
431 /**
432   Connect a Device Path and return the handle of the driver that support this DevicePath
433 
434   @param  DevicePath            Device Path of the File to connect
435   @param  Handle                Handle of the driver that support this DevicePath
436   @param  RemainingDevicePath   Remaining DevicePath nodes that do not match the driver DevicePath
437 
438   @retval EFI_SUCCESS           A driver that matches the Device Path has been found
439   @retval EFI_NOT_FOUND         No handles match the search.
440   @retval EFI_INVALID_PARAMETER DevicePath or Handle is NULL
441 
442 **/
443 EFI_STATUS
BdsConnectDevicePath(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath,OUT EFI_HANDLE * Handle,OUT EFI_DEVICE_PATH_PROTOCOL ** RemainingDevicePath)444 BdsConnectDevicePath (
445   IN  EFI_DEVICE_PATH_PROTOCOL* DevicePath,
446   OUT EFI_HANDLE                *Handle,
447   OUT EFI_DEVICE_PATH_PROTOCOL  **RemainingDevicePath
448   )
449 {
450   return BdsConnectAndUpdateDevicePath (&DevicePath, Handle, RemainingDevicePath);
451 }
452 
453 BOOLEAN
BdsFileSystemSupport(IN EFI_DEVICE_PATH * DevicePath,IN EFI_HANDLE Handle,IN EFI_DEVICE_PATH * RemainingDevicePath)454 BdsFileSystemSupport (
455   IN EFI_DEVICE_PATH *DevicePath,
456   IN EFI_HANDLE Handle,
457   IN EFI_DEVICE_PATH *RemainingDevicePath
458   )
459 {
460   EFI_STATUS  Status;
461   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL     *FsProtocol;
462 
463   Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol);
464 
465   return (!EFI_ERROR (Status) && IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP));
466 }
467 
468 EFI_STATUS
BdsFileSystemLoadImage(IN OUT EFI_DEVICE_PATH ** DevicePath,IN EFI_HANDLE Handle,IN EFI_DEVICE_PATH * RemainingDevicePath,IN EFI_ALLOCATE_TYPE Type,IN OUT EFI_PHYSICAL_ADDRESS * Image,OUT UINTN * ImageSize)469 BdsFileSystemLoadImage (
470   IN OUT EFI_DEVICE_PATH       **DevicePath,
471   IN     EFI_HANDLE            Handle,
472   IN     EFI_DEVICE_PATH       *RemainingDevicePath,
473   IN     EFI_ALLOCATE_TYPE     Type,
474   IN OUT EFI_PHYSICAL_ADDRESS  *Image,
475   OUT    UINTN                 *ImageSize
476   )
477 {
478   EFI_STATUS                       Status;
479   FILEPATH_DEVICE_PATH             *FilePathDevicePath;
480   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *FsProtocol;
481   EFI_FILE_PROTOCOL                *Fs;
482   EFI_FILE_INFO                    *FileInfo;
483   EFI_FILE_PROTOCOL                *File;
484   UINTN                            Size;
485 
486   ASSERT (IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP));
487 
488   FilePathDevicePath = (FILEPATH_DEVICE_PATH*)RemainingDevicePath;
489 
490   Status = gBS->OpenProtocol (
491                   Handle,
492                   &gEfiSimpleFileSystemProtocolGuid,
493                   (VOID**)&FsProtocol,
494                   gImageHandle,
495                   Handle,
496                   EFI_OPEN_PROTOCOL_BY_DRIVER
497                   );
498   if (EFI_ERROR (Status)) {
499     return Status;
500   }
501 
502   // Try to Open the volume and get root directory
503   Status = FsProtocol->OpenVolume (FsProtocol, &Fs);
504   if (EFI_ERROR (Status)) {
505     goto CLOSE_PROTOCOL;
506   }
507 
508   Status = Fs->Open (Fs, &File, FilePathDevicePath->PathName, EFI_FILE_MODE_READ, 0);
509   if (EFI_ERROR (Status)) {
510     goto CLOSE_PROTOCOL;
511   }
512 
513   Size = 0;
514   File->GetInfo (File, &gEfiFileInfoGuid, &Size, NULL);
515   FileInfo = AllocatePool (Size);
516   Status = File->GetInfo (File, &gEfiFileInfoGuid, &Size, FileInfo);
517   if (EFI_ERROR (Status)) {
518     goto CLOSE_FILE;
519   }
520 
521   // Get the file size
522   Size = FileInfo->FileSize;
523   if (ImageSize) {
524     *ImageSize = Size;
525   }
526   FreePool (FileInfo);
527 
528   Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
529   // Try to allocate in any pages if failed to allocate memory at the defined location
530   if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
531     Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
532   }
533   if (!EFI_ERROR (Status)) {
534     Status = File->Read (File, &Size, (VOID*)(UINTN)(*Image));
535   }
536 
537 CLOSE_FILE:
538   File->Close (File);
539 
540 CLOSE_PROTOCOL:
541   gBS->CloseProtocol (
542          Handle,
543          &gEfiSimpleFileSystemProtocolGuid,
544          gImageHandle,
545          Handle);
546 
547   return Status;
548 }
549 
550 BOOLEAN
BdsMemoryMapSupport(IN EFI_DEVICE_PATH * DevicePath,IN EFI_HANDLE Handle,IN EFI_DEVICE_PATH * RemainingDevicePath)551 BdsMemoryMapSupport (
552   IN EFI_DEVICE_PATH *DevicePath,
553   IN EFI_HANDLE Handle,
554   IN EFI_DEVICE_PATH *RemainingDevicePath
555   )
556 {
557   return IS_DEVICE_PATH_NODE (DevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP) ||
558          IS_DEVICE_PATH_NODE (RemainingDevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP);
559 }
560 
561 EFI_STATUS
BdsMemoryMapLoadImage(IN OUT EFI_DEVICE_PATH ** DevicePath,IN EFI_HANDLE Handle,IN EFI_DEVICE_PATH * RemainingDevicePath,IN EFI_ALLOCATE_TYPE Type,IN OUT EFI_PHYSICAL_ADDRESS * Image,OUT UINTN * ImageSize)562 BdsMemoryMapLoadImage (
563   IN OUT EFI_DEVICE_PATH       **DevicePath,
564   IN     EFI_HANDLE            Handle,
565   IN     EFI_DEVICE_PATH       *RemainingDevicePath,
566   IN     EFI_ALLOCATE_TYPE     Type,
567   IN OUT EFI_PHYSICAL_ADDRESS* Image,
568   OUT    UINTN                 *ImageSize
569   )
570 {
571   EFI_STATUS            Status;
572   MEMMAP_DEVICE_PATH*   MemMapPathDevicePath;
573   UINTN                 Size;
574 
575   if (IS_DEVICE_PATH_NODE (RemainingDevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP)) {
576     MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)RemainingDevicePath;
577   } else {
578     ASSERT (IS_DEVICE_PATH_NODE (*DevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP));
579     MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)*DevicePath;
580   }
581 
582   Size = MemMapPathDevicePath->EndingAddress - MemMapPathDevicePath->StartingAddress;
583   if (Size == 0) {
584       return EFI_INVALID_PARAMETER;
585   }
586 
587   Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
588   // Try to allocate in any pages if failed to allocate memory at the defined location
589   if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
590     Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
591   }
592   if (!EFI_ERROR (Status)) {
593     CopyMem ((VOID*)(UINTN)(*Image), (CONST VOID*)(UINTN)MemMapPathDevicePath->StartingAddress, Size);
594 
595     if (ImageSize != NULL) {
596         *ImageSize = Size;
597     }
598   }
599 
600   return Status;
601 }
602 
603 BOOLEAN
BdsFirmwareVolumeSupport(IN EFI_DEVICE_PATH * DevicePath,IN EFI_HANDLE Handle,IN EFI_DEVICE_PATH * RemainingDevicePath)604 BdsFirmwareVolumeSupport (
605   IN EFI_DEVICE_PATH *DevicePath,
606   IN EFI_HANDLE Handle,
607   IN EFI_DEVICE_PATH *RemainingDevicePath
608   )
609 {
610   return IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_FILE_DP);
611 }
612 
613 EFI_STATUS
BdsFirmwareVolumeLoadImage(IN OUT EFI_DEVICE_PATH ** DevicePath,IN EFI_HANDLE Handle,IN EFI_DEVICE_PATH * RemainingDevicePath,IN EFI_ALLOCATE_TYPE Type,IN OUT EFI_PHYSICAL_ADDRESS * Image,OUT UINTN * ImageSize)614 BdsFirmwareVolumeLoadImage (
615   IN OUT EFI_DEVICE_PATH       **DevicePath,
616   IN     EFI_HANDLE            Handle,
617   IN     EFI_DEVICE_PATH       *RemainingDevicePath,
618   IN     EFI_ALLOCATE_TYPE     Type,
619   IN OUT EFI_PHYSICAL_ADDRESS* Image,
620   OUT    UINTN                 *ImageSize
621   )
622 {
623   EFI_STATUS            Status;
624   EFI_FIRMWARE_VOLUME2_PROTOCOL     *FwVol;
625   EFI_GUID                          *FvNameGuid;
626   EFI_SECTION_TYPE                  SectionType;
627   EFI_FV_FILETYPE                   FvType;
628   EFI_FV_FILE_ATTRIBUTES            Attrib;
629   UINT32                            AuthenticationStatus;
630   VOID* ImageBuffer;
631 
632   ASSERT (IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_FILE_DP));
633 
634   Status = gBS->HandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&FwVol);
635   if (EFI_ERROR (Status)) {
636     return Status;
637   }
638 
639   FvNameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)RemainingDevicePath);
640   if (FvNameGuid == NULL) {
641     Status = EFI_INVALID_PARAMETER;
642   }
643 
644   SectionType = EFI_SECTION_PE32;
645   AuthenticationStatus = 0;
646   //Note: ReadSection at the opposite of ReadFile does not allow to pass ImageBuffer == NULL to get the size of the file.
647   ImageBuffer = NULL;
648   Status = FwVol->ReadSection (
649                     FwVol,
650                     FvNameGuid,
651                     SectionType,
652                     0,
653                     &ImageBuffer,
654                     ImageSize,
655                     &AuthenticationStatus
656                     );
657   if (!EFI_ERROR (Status)) {
658 #if 0
659     // In case the buffer has some address requirements, we must copy the buffer to a buffer following the requirements
660     if (Type != AllocateAnyPages) {
661       Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize),Image);
662       if (!EFI_ERROR (Status)) {
663         CopyMem ((VOID*)(UINTN)(*Image), ImageBuffer, *ImageSize);
664         FreePool (ImageBuffer);
665       }
666     }
667 #else
668     // We must copy the buffer into a page allocations. Otherwise, the caller could call gBS->FreePages() on the pool allocation
669     Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
670     // Try to allocate in any pages if failed to allocate memory at the defined location
671     if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
672       Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
673     }
674     if (!EFI_ERROR (Status)) {
675       CopyMem ((VOID*)(UINTN)(*Image), ImageBuffer, *ImageSize);
676       FreePool (ImageBuffer);
677     }
678 #endif
679   } else {
680     // Try a raw file, since a PE32 SECTION does not exist
681     Status = FwVol->ReadFile (
682                         FwVol,
683                         FvNameGuid,
684                         NULL,
685                         ImageSize,
686                         &FvType,
687                         &Attrib,
688                         &AuthenticationStatus
689                         );
690     if (!EFI_ERROR (Status)) {
691       Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
692       // Try to allocate in any pages if failed to allocate memory at the defined location
693       if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
694         Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
695       }
696       if (!EFI_ERROR (Status)) {
697         Status = FwVol->ReadFile (
698                                 FwVol,
699                                 FvNameGuid,
700                                 (VOID**)Image,
701                                 ImageSize,
702                                 &FvType,
703                                 &Attrib,
704                                 &AuthenticationStatus
705                                 );
706       }
707     }
708   }
709   return Status;
710 }
711 
712 BOOLEAN
BdsPxeSupport(IN EFI_DEVICE_PATH * DevicePath,IN EFI_HANDLE Handle,IN EFI_DEVICE_PATH * RemainingDevicePath)713 BdsPxeSupport (
714   IN EFI_DEVICE_PATH*           DevicePath,
715   IN EFI_HANDLE                 Handle,
716   IN EFI_DEVICE_PATH*           RemainingDevicePath
717   )
718 {
719   EFI_STATUS                  Status;
720   EFI_PXE_BASE_CODE_PROTOCOL* PxeBcProtocol;
721 
722   if (!IsDevicePathEnd (RemainingDevicePath)) {
723     return FALSE;
724   }
725 
726   Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol);
727   if (EFI_ERROR (Status)) {
728     return FALSE;
729   } else {
730     return TRUE;
731   }
732 }
733 
734 EFI_STATUS
BdsPxeLoadImage(IN OUT EFI_DEVICE_PATH ** DevicePath,IN EFI_HANDLE Handle,IN EFI_DEVICE_PATH * RemainingDevicePath,IN EFI_ALLOCATE_TYPE Type,IN OUT EFI_PHYSICAL_ADDRESS * Image,OUT UINTN * ImageSize)735 BdsPxeLoadImage (
736   IN OUT EFI_DEVICE_PATH       **DevicePath,
737   IN     EFI_HANDLE            Handle,
738   IN     EFI_DEVICE_PATH       *RemainingDevicePath,
739   IN     EFI_ALLOCATE_TYPE     Type,
740   IN OUT EFI_PHYSICAL_ADDRESS* Image,
741   OUT    UINTN                 *ImageSize
742   )
743 {
744   EFI_STATUS              Status;
745   EFI_LOAD_FILE_PROTOCOL  *LoadFileProtocol;
746   UINTN                   BufferSize;
747   EFI_PXE_BASE_CODE_PROTOCOL *Pxe;
748 
749   // Get Load File Protocol attached to the PXE protocol
750   Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFileProtocol);
751   if (EFI_ERROR (Status)) {
752     return Status;
753   }
754 
755   Status = LoadFileProtocol->LoadFile (LoadFileProtocol, RemainingDevicePath, TRUE, &BufferSize, NULL);
756   if (Status == EFI_BUFFER_TOO_SMALL) {
757     Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(BufferSize), Image);
758     if (EFI_ERROR (Status)) {
759       return Status;
760     }
761 
762     Status = LoadFileProtocol->LoadFile (LoadFileProtocol, RemainingDevicePath, TRUE, &BufferSize, (VOID*)(UINTN)(*Image));
763     if (!EFI_ERROR (Status) && (ImageSize != NULL)) {
764       *ImageSize = BufferSize;
765     }
766   }
767 
768   if (Status == EFI_ALREADY_STARTED) {
769     Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe);
770     if (!EFI_ERROR(Status)) {
771       // If PXE is already started, we stop it
772       Pxe->Stop (Pxe);
773       // And we try again
774       return BdsPxeLoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, ImageSize);
775     }
776   }
777   return Status;
778 }
779 
780 BOOLEAN
BdsTftpSupport(IN EFI_DEVICE_PATH * DevicePath,IN EFI_HANDLE Handle,IN EFI_DEVICE_PATH * RemainingDevicePath)781 BdsTftpSupport (
782   IN EFI_DEVICE_PATH  *DevicePath,
783   IN EFI_HANDLE       Handle,
784   IN EFI_DEVICE_PATH  *RemainingDevicePath
785   )
786 {
787   EFI_STATUS       Status;
788   EFI_DEVICE_PATH  *NextDevicePath;
789   VOID             *Interface;
790 
791   // Validate the Remaining Device Path
792   if (IsDevicePathEnd (RemainingDevicePath)) {
793     return FALSE;
794   }
795   if (!IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP) &&
796       !IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv6_DP)) {
797     return FALSE;
798   }
799   NextDevicePath = NextDevicePathNode (RemainingDevicePath);
800   if (IsDevicePathEnd (NextDevicePath)) {
801     return FALSE;
802   }
803   if (!IS_DEVICE_PATH_NODE (NextDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP)) {
804     return FALSE;
805   }
806 
807   Status = gBS->HandleProtocol (
808                   Handle, &gEfiDevicePathProtocolGuid,
809                   &Interface
810                   );
811   if (EFI_ERROR (Status)) {
812     return FALSE;
813   }
814 
815   //
816   // Check that the controller (identified by its handle "Handle") supports the
817   // MTFTPv4 Service Binding Protocol. If it does, it means that it supports the
818   // EFI MTFTPv4 Protocol needed to download the image through TFTP.
819   //
820   Status = gBS->HandleProtocol (
821                   Handle, &gEfiMtftp4ServiceBindingProtocolGuid,
822                   &Interface
823                   );
824   if (EFI_ERROR (Status)) {
825     return FALSE;
826   }
827 
828   return TRUE;
829 }
830 
831 /**
832   Worker function that get the size in numbers of bytes of a file from a TFTP
833   server before to download the file.
834 
835   @param[in]   Mtftp4    MTFTP4 protocol interface
836   @param[in]   FilePath  Path of the file, Ascii encoded
837   @param[out]  FileSize  Address where to store the file size in number of
838                          bytes.
839 
840   @retval  EFI_SUCCESS   The size of the file was returned.
841   @retval  !EFI_SUCCESS  The size of the file was not returned.
842 
843 **/
844 STATIC
845 EFI_STATUS
Mtftp4GetFileSize(IN EFI_MTFTP4_PROTOCOL * Mtftp4,IN CHAR8 * FilePath,OUT UINT64 * FileSize)846 Mtftp4GetFileSize (
847   IN  EFI_MTFTP4_PROTOCOL  *Mtftp4,
848   IN  CHAR8                *FilePath,
849   OUT UINT64               *FileSize
850   )
851 {
852   EFI_STATUS         Status;
853   EFI_MTFTP4_OPTION  ReqOpt[1];
854   EFI_MTFTP4_PACKET  *Packet;
855   UINT32             PktLen;
856   EFI_MTFTP4_OPTION  *TableOfOptions;
857   EFI_MTFTP4_OPTION  *Option;
858   UINT32             OptCnt;
859   UINT8              OptBuf[128];
860 
861   ReqOpt[0].OptionStr = (UINT8*)"tsize";
862   OptBuf[0] = '0';
863   OptBuf[1] = 0;
864   ReqOpt[0].ValueStr = OptBuf;
865 
866   Status = Mtftp4->GetInfo (
867              Mtftp4,
868              NULL,
869              (UINT8*)FilePath,
870              NULL,
871              1,
872              ReqOpt,
873              &PktLen,
874              &Packet
875              );
876 
877   if (EFI_ERROR (Status)) {
878     goto Error;
879   }
880 
881   Status = Mtftp4->ParseOptions (
882                      Mtftp4,
883                      PktLen,
884                      Packet,
885                      (UINT32 *) &OptCnt,
886                      &TableOfOptions
887                      );
888   if (EFI_ERROR (Status)) {
889     goto Error;
890   }
891 
892   Option = TableOfOptions;
893   while (OptCnt != 0) {
894     if (AsciiStrnCmp ((CHAR8 *)Option->OptionStr, "tsize", 5) == 0) {
895       *FileSize = AsciiStrDecimalToUint64 ((CHAR8 *)Option->ValueStr);
896       break;
897     }
898     OptCnt--;
899     Option++;
900   }
901   FreePool (TableOfOptions);
902 
903   if (OptCnt == 0) {
904     Status = EFI_UNSUPPORTED;
905   }
906 
907 Error :
908 
909   return Status;
910 }
911 
912 /**
913   Update the progress of a file download
914   This procedure is called each time a new TFTP packet is received.
915 
916   @param[in]  This       MTFTP4 protocol interface
917   @param[in]  Token      Parameters for the download of the file
918   @param[in]  PacketLen  Length of the packet
919   @param[in]  Packet     Address of the packet
920 
921   @retval  EFI_SUCCESS  All packets are accepted.
922 
923 **/
924 STATIC
925 EFI_STATUS
Mtftp4CheckPacket(IN EFI_MTFTP4_PROTOCOL * This,IN EFI_MTFTP4_TOKEN * Token,IN UINT16 PacketLen,IN EFI_MTFTP4_PACKET * Packet)926 Mtftp4CheckPacket (
927   IN EFI_MTFTP4_PROTOCOL  *This,
928   IN EFI_MTFTP4_TOKEN     *Token,
929   IN UINT16               PacketLen,
930   IN EFI_MTFTP4_PACKET    *Packet
931   )
932 {
933   BDS_TFTP_CONTEXT  *Context;
934   CHAR16            Progress[TFTP_PROGRESS_MESSAGE_SIZE];
935   UINT64            NbOfKb;
936   UINTN             Index;
937   UINTN             LastStep;
938   UINTN             Step;
939   UINT64            LastNbOf50Kb;
940   UINT64            NbOf50Kb;
941 
942   if ((NTOHS (Packet->OpCode)) == EFI_MTFTP4_OPCODE_DATA) {
943     Context = (BDS_TFTP_CONTEXT*)Token->Context;
944 
945     if (Context->DownloadedNbOfBytes == 0) {
946       if (Context->FileSize > 0) {
947         Print (L"%s       0 Kb", mTftpProgressFrame);
948       } else {
949         Print (L"    0 Kb");
950       }
951     }
952 
953     //
954     // The data is the packet are prepended with two UINT16 :
955     // . OpCode = EFI_MTFTP4_OPCODE_DATA
956     // . Block  = the number of this block of data
957     //
958     Context->DownloadedNbOfBytes += PacketLen - sizeof (Packet->OpCode) - sizeof (Packet->Data.Block);
959     NbOfKb = Context->DownloadedNbOfBytes / 1024;
960 
961     Progress[0] = L'\0';
962     if (Context->FileSize > 0) {
963       LastStep  = (Context->LastReportedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize;
964       Step      = (Context->DownloadedNbOfBytes   * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize;
965       if (Step > LastStep) {
966         Print (mTftpProgressDelete);
967         CopyMem (Progress, mTftpProgressFrame, sizeof mTftpProgressFrame);
968         for (Index = 1; Index < Step; Index++) {
969           Progress[Index] = L'=';
970         }
971         Progress[Step] = L'>';
972 
973         UnicodeSPrint (
974           Progress + (sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 1,
975           sizeof (Progress) - sizeof (mTftpProgressFrame),
976           L" %7d Kb",
977           NbOfKb
978           );
979         Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes;
980       }
981     } else {
982       //
983       // Case when we do not know the size of the final file.
984       // We print the updated size every 50KB of downloaded data
985       //
986       LastNbOf50Kb = Context->LastReportedNbOfBytes / (50*1024);
987       NbOf50Kb     = Context->DownloadedNbOfBytes   / (50*1024);
988       if (NbOf50Kb > LastNbOf50Kb) {
989         Print (L"\b\b\b\b\b\b\b\b\b\b");
990         UnicodeSPrint (Progress, sizeof (Progress), L"%7d Kb", NbOfKb);
991         Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes;
992       }
993     }
994     if (Progress[0] != L'\0') {
995       Print (L"%s", Progress);
996     }
997   }
998 
999   return EFI_SUCCESS;
1000 }
1001 
1002 /**
1003   Download an image from a TFTP server
1004 
1005   @param[in]   DevicePath           Device path of the TFTP boot option
1006   @param[in]   ControllerHandle     Handle of the network controller
1007   @param[in]   RemainingDevicePath  Device path of the TFTP boot option but
1008                                     the first node that identifies the network controller
1009   @param[in]   Type                 Type to allocate memory pages
1010   @param[out]  Image                Address of the bufer where the image is stored in
1011                                     case of success
1012   @param[out]  ImageSize            Size in number of bytes of the i;age in case of
1013                                     success
1014 
1015   @retval  EFI_SUCCESS   The image was returned.
1016   @retval  !EFI_SUCCESS  Something went wrong.
1017 
1018 **/
1019 EFI_STATUS
BdsTftpLoadImage(IN OUT EFI_DEVICE_PATH ** DevicePath,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH * RemainingDevicePath,IN EFI_ALLOCATE_TYPE Type,IN OUT EFI_PHYSICAL_ADDRESS * Image,OUT UINTN * ImageSize)1020 BdsTftpLoadImage (
1021   IN OUT EFI_DEVICE_PATH       **DevicePath,
1022   IN     EFI_HANDLE            ControllerHandle,
1023   IN     EFI_DEVICE_PATH       *RemainingDevicePath,
1024   IN     EFI_ALLOCATE_TYPE     Type,
1025   IN OUT EFI_PHYSICAL_ADDRESS  *Image,
1026   OUT    UINTN                 *ImageSize
1027   )
1028 {
1029   EFI_STATUS               Status;
1030   EFI_HANDLE               Dhcp4ChildHandle;
1031   EFI_DHCP4_PROTOCOL       *Dhcp4;
1032   BOOLEAN                  Dhcp4ToStop;
1033   EFI_HANDLE               Mtftp4ChildHandle;
1034   EFI_MTFTP4_PROTOCOL      *Mtftp4;
1035   DHCP4_OPTION             ParaList;
1036   EFI_DHCP4_PACKET_OPTION  *OptionList[2];
1037   EFI_DHCP4_CONFIG_DATA    Dhcp4CfgData;
1038   EFI_DHCP4_MODE_DATA      Dhcp4Mode;
1039   EFI_MTFTP4_CONFIG_DATA   Mtftp4CfgData;
1040   IPv4_DEVICE_PATH         *IPv4DevicePathNode;
1041   CHAR16                   *PathName;
1042   CHAR8                    *AsciiFilePath;
1043   EFI_MTFTP4_TOKEN         Mtftp4Token;
1044   UINT64                   FileSize;
1045   UINT64                   TftpBufferSize;
1046   BDS_TFTP_CONTEXT         *TftpContext;
1047   UINTN                    PathNameLen;
1048 
1049   ASSERT(IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP));
1050   IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath;
1051 
1052   Dhcp4ChildHandle  = NULL;
1053   Dhcp4             = NULL;
1054   Dhcp4ToStop       = FALSE;
1055   Mtftp4ChildHandle = NULL;
1056   Mtftp4            = NULL;
1057   AsciiFilePath     = NULL;
1058   TftpContext       = NULL;
1059 
1060   if (!IPv4DevicePathNode->StaticIpAddress) {
1061     //
1062     // Using the DHCP4 Service Binding Protocol, create a child handle of the DHCP4 service and
1063     // install the DHCP4 protocol on it. Then, open the DHCP protocol.
1064     //
1065     Status = NetLibCreateServiceChild (
1066                ControllerHandle,
1067                gImageHandle,
1068                &gEfiDhcp4ServiceBindingProtocolGuid,
1069                &Dhcp4ChildHandle
1070                );
1071     if (!EFI_ERROR (Status)) {
1072       Status = gBS->OpenProtocol (
1073                       Dhcp4ChildHandle,
1074                       &gEfiDhcp4ProtocolGuid,
1075                       (VOID **) &Dhcp4,
1076                       gImageHandle,
1077                       ControllerHandle,
1078                       EFI_OPEN_PROTOCOL_BY_DRIVER
1079                       );
1080     }
1081     if (EFI_ERROR (Status)) {
1082       Print (L"Unable to open DHCP4 protocol\n");
1083       goto Error;
1084     }
1085   }
1086 
1087   //
1088   // Using the MTFTP4 Service Binding Protocol, create a child handle of the MTFTP4 service and
1089   // install the MTFTP4 protocol on it. Then, open the MTFTP4 protocol.
1090   //
1091   Status = NetLibCreateServiceChild (
1092              ControllerHandle,
1093              gImageHandle,
1094              &gEfiMtftp4ServiceBindingProtocolGuid,
1095              &Mtftp4ChildHandle
1096              );
1097   if (!EFI_ERROR (Status)) {
1098     Status = gBS->OpenProtocol (
1099                     Mtftp4ChildHandle,
1100                     &gEfiMtftp4ProtocolGuid,
1101                     (VOID **) &Mtftp4,
1102                     gImageHandle,
1103                     ControllerHandle,
1104                     EFI_OPEN_PROTOCOL_BY_DRIVER
1105                     );
1106   }
1107   if (EFI_ERROR (Status)) {
1108     Print (L"Unable to open MTFTP4 protocol\n");
1109     goto Error;
1110   }
1111 
1112   if (!IPv4DevicePathNode->StaticIpAddress) {
1113     //
1114     // Configure the DHCP4, all default settings. It is acceptable for the configuration to
1115     // fail if the return code is equal to EFI_ACCESS_DENIED which means that the configuration
1116     // has been done by another instance of the DHCP4 protocol or that the DHCP configuration
1117     // process has been started but is not completed yet.
1118     //
1119     ZeroMem (&Dhcp4CfgData, sizeof (EFI_DHCP4_CONFIG_DATA));
1120     ParaList.Head.OpCode     = DHCP_TAG_PARA_LIST;
1121     ParaList.Head.Length     = 2;
1122     ParaList.Head.Data[0]    = DHCP_TAG_NETMASK;
1123     ParaList.Route           = DHCP_TAG_ROUTER;
1124     OptionList[0]            = &ParaList.Head;
1125     Dhcp4CfgData.OptionCount = 1;
1126     Dhcp4CfgData.OptionList  = OptionList;
1127 
1128     Status = Dhcp4->Configure (Dhcp4, &Dhcp4CfgData);
1129     if (EFI_ERROR (Status)) {
1130       if (Status != EFI_ACCESS_DENIED) {
1131         Print (L"Error while configuring the DHCP4 protocol\n");
1132         goto Error;
1133       }
1134     }
1135 
1136     //
1137     // Start the DHCP configuration. This may have already been done thus do not leave in error
1138     // if the return code is EFI_ALREADY_STARTED.
1139     //
1140     Status = Dhcp4->Start (Dhcp4, NULL);
1141     if (EFI_ERROR (Status)) {
1142       if (Status != EFI_ALREADY_STARTED) {
1143         Print (L"DHCP configuration failed\n");
1144         goto Error;
1145       }
1146     } else {
1147       Dhcp4ToStop = TRUE;
1148     }
1149 
1150     Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4Mode);
1151     if (EFI_ERROR (Status)) {
1152       goto Error;
1153     }
1154 
1155     if (Dhcp4Mode.State != Dhcp4Bound) {
1156       Status = EFI_TIMEOUT;
1157       Print (L"DHCP configuration failed\n");
1158       goto Error;
1159     }
1160   }
1161 
1162   //
1163   // Configure the TFTP4 protocol
1164   //
1165 
1166   ZeroMem (&Mtftp4CfgData, sizeof (EFI_MTFTP4_CONFIG_DATA));
1167   Mtftp4CfgData.UseDefaultSetting = FALSE;
1168   Mtftp4CfgData.TimeoutValue      = 4;
1169   Mtftp4CfgData.TryCount          = 6;
1170 
1171   if (IPv4DevicePathNode->StaticIpAddress) {
1172     CopyMem (&Mtftp4CfgData.StationIp , &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));
1173     CopyMem (&Mtftp4CfgData.SubnetMask, &IPv4DevicePathNode->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
1174     CopyMem (&Mtftp4CfgData.GatewayIp , &IPv4DevicePathNode->GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));
1175   } else {
1176     CopyMem (&Mtftp4CfgData.StationIp , &Dhcp4Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
1177     CopyMem (&Mtftp4CfgData.SubnetMask, &Dhcp4Mode.SubnetMask   , sizeof (EFI_IPv4_ADDRESS));
1178     CopyMem (&Mtftp4CfgData.GatewayIp , &Dhcp4Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
1179   }
1180 
1181   CopyMem (&Mtftp4CfgData.ServerIp  , &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS));
1182 
1183   Status = Mtftp4->Configure (Mtftp4, &Mtftp4CfgData);
1184   if (EFI_ERROR (Status)) {
1185     Print (L"Error while configuring the MTFTP4 protocol\n");
1186     goto Error;
1187   }
1188 
1189   // The Device Path might contain multiple FilePath nodes
1190   PathName      = ConvertDevicePathToText ((EFI_DEVICE_PATH_PROTOCOL*)(IPv4DevicePathNode + 1), FALSE, FALSE);
1191   PathNameLen   = StrLen (PathName) + 1;
1192   AsciiFilePath = AllocatePool (PathNameLen);
1193   UnicodeStrToAsciiStrS (PathName, AsciiFilePath, PathNameLen);
1194 
1195   //
1196   // Try to get the size of the file in bytes from the server. If it fails,
1197   // start with a 8MB buffer to download the file.
1198   //
1199   FileSize = 0;
1200   if (Mtftp4GetFileSize (Mtftp4, AsciiFilePath, &FileSize) == EFI_SUCCESS) {
1201     TftpBufferSize = FileSize;
1202   } else {
1203     TftpBufferSize = SIZE_16MB;
1204   }
1205 
1206   TftpContext = AllocatePool (sizeof (BDS_TFTP_CONTEXT));
1207   if (TftpContext == NULL) {
1208     Status = EFI_OUT_OF_RESOURCES;
1209     goto Error;
1210   }
1211   TftpContext->FileSize = FileSize;
1212 
1213   for (; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize);
1214          TftpBufferSize = (TftpBufferSize + SIZE_16MB) & (~(SIZE_16MB-1))) {
1215     //
1216     // Allocate a buffer to hold the whole file.
1217     //
1218     Status = gBS->AllocatePages (
1219                     Type,
1220                     EfiBootServicesCode,
1221                     EFI_SIZE_TO_PAGES (TftpBufferSize),
1222                     Image
1223                     );
1224     if (EFI_ERROR (Status)) {
1225       Print (L"Failed to allocate space for image\n");
1226       goto Error;
1227     }
1228 
1229     TftpContext->DownloadedNbOfBytes   = 0;
1230     TftpContext->LastReportedNbOfBytes = 0;
1231 
1232     ZeroMem (&Mtftp4Token, sizeof (EFI_MTFTP4_TOKEN));
1233     Mtftp4Token.Filename    = (UINT8*)AsciiFilePath;
1234     Mtftp4Token.BufferSize  = TftpBufferSize;
1235     Mtftp4Token.Buffer      = (VOID *)(UINTN)*Image;
1236     Mtftp4Token.CheckPacket = Mtftp4CheckPacket;
1237     Mtftp4Token.Context     = (VOID*)TftpContext;
1238 
1239     Print (L"Downloading the file <%a> from the TFTP server\n", AsciiFilePath);
1240     Status = Mtftp4->ReadFile (Mtftp4, &Mtftp4Token);
1241     Print (L"\n");
1242     if (EFI_ERROR (Status)) {
1243       gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));
1244       if (Status == EFI_BUFFER_TOO_SMALL) {
1245         Print (L"Downloading failed, file larger than expected.\n");
1246         continue;
1247       } else {
1248         goto Error;
1249       }
1250     }
1251 
1252     *ImageSize = Mtftp4Token.BufferSize;
1253     break;
1254   }
1255 
1256 Error:
1257   if (Dhcp4ChildHandle != NULL) {
1258     if (Dhcp4 != NULL) {
1259       if (Dhcp4ToStop) {
1260         Dhcp4->Stop (Dhcp4);
1261       }
1262       gBS->CloseProtocol (
1263              Dhcp4ChildHandle,
1264              &gEfiDhcp4ProtocolGuid,
1265              gImageHandle,
1266              ControllerHandle
1267             );
1268     }
1269     NetLibDestroyServiceChild (
1270       ControllerHandle,
1271       gImageHandle,
1272       &gEfiDhcp4ServiceBindingProtocolGuid,
1273       Dhcp4ChildHandle
1274       );
1275   }
1276 
1277   if (Mtftp4ChildHandle != NULL) {
1278     if (Mtftp4 != NULL) {
1279       if (AsciiFilePath != NULL) {
1280         FreePool (AsciiFilePath);
1281       }
1282       if (TftpContext != NULL) {
1283         FreePool (TftpContext);
1284       }
1285       gBS->CloseProtocol (
1286              Mtftp4ChildHandle,
1287              &gEfiMtftp4ProtocolGuid,
1288              gImageHandle,
1289              ControllerHandle
1290             );
1291     }
1292     NetLibDestroyServiceChild (
1293       ControllerHandle,
1294       gImageHandle,
1295       &gEfiMtftp4ServiceBindingProtocolGuid,
1296       Mtftp4ChildHandle
1297       );
1298   }
1299 
1300   if (EFI_ERROR (Status)) {
1301     *Image = 0;
1302     Print (L"Failed to download the file - Error=%r\n", Status);
1303   }
1304 
1305   return Status;
1306 }
1307 
1308 BDS_FILE_LOADER FileLoaders[] = {
1309     { BdsFileSystemSupport, BdsFileSystemLoadImage },
1310     { BdsFirmwareVolumeSupport, BdsFirmwareVolumeLoadImage },
1311     //{ BdsLoadFileSupport, BdsLoadFileLoadImage },
1312     { BdsMemoryMapSupport, BdsMemoryMapLoadImage },
1313     { BdsPxeSupport, BdsPxeLoadImage },
1314     { BdsTftpSupport, BdsTftpLoadImage },
1315     { NULL, NULL }
1316 };
1317 
1318 EFI_STATUS
BdsLoadImageAndUpdateDevicePath(IN OUT EFI_DEVICE_PATH ** DevicePath,IN EFI_ALLOCATE_TYPE Type,IN OUT EFI_PHYSICAL_ADDRESS * Image,OUT UINTN * FileSize)1319 BdsLoadImageAndUpdateDevicePath (
1320   IN OUT EFI_DEVICE_PATH       **DevicePath,
1321   IN     EFI_ALLOCATE_TYPE     Type,
1322   IN OUT EFI_PHYSICAL_ADDRESS* Image,
1323   OUT    UINTN                 *FileSize
1324   )
1325 {
1326   EFI_STATUS      Status;
1327   EFI_HANDLE      Handle;
1328   EFI_DEVICE_PATH *RemainingDevicePath;
1329   BDS_FILE_LOADER*  FileLoader;
1330 
1331   Status = BdsConnectAndUpdateDevicePath (DevicePath, &Handle, &RemainingDevicePath);
1332   if (EFI_ERROR (Status)) {
1333     return Status;
1334   }
1335 
1336   FileLoader = FileLoaders;
1337   while (FileLoader->Support != NULL) {
1338     if (FileLoader->Support (*DevicePath, Handle, RemainingDevicePath)) {
1339       return FileLoader->LoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, FileSize);
1340     }
1341     FileLoader++;
1342   }
1343 
1344   return EFI_UNSUPPORTED;
1345 }
1346 
1347 EFI_STATUS
BdsLoadImage(IN EFI_DEVICE_PATH * DevicePath,IN EFI_ALLOCATE_TYPE Type,IN OUT EFI_PHYSICAL_ADDRESS * Image,OUT UINTN * FileSize)1348 BdsLoadImage (
1349   IN     EFI_DEVICE_PATH       *DevicePath,
1350   IN     EFI_ALLOCATE_TYPE     Type,
1351   IN OUT EFI_PHYSICAL_ADDRESS* Image,
1352   OUT    UINTN                 *FileSize
1353   )
1354 {
1355   return BdsLoadImageAndUpdateDevicePath (&DevicePath, Type, Image, FileSize);
1356 }
1357 
1358 /**
1359   Start an EFI Application from a Device Path
1360 
1361   @param  ParentImageHandle     Handle of the calling image
1362   @param  DevicePath            Location of the EFI Application
1363 
1364   @retval EFI_SUCCESS           All drivers have been connected
1365   @retval EFI_NOT_FOUND         The Linux kernel Device Path has not been found
1366   @retval EFI_OUT_OF_RESOURCES  There is not enough resource memory to store the matching results.
1367 
1368 **/
1369 EFI_STATUS
BdsStartEfiApplication(IN EFI_HANDLE ParentImageHandle,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN UINTN LoadOptionsSize,IN VOID * LoadOptions)1370 BdsStartEfiApplication (
1371   IN EFI_HANDLE                  ParentImageHandle,
1372   IN EFI_DEVICE_PATH_PROTOCOL    *DevicePath,
1373   IN UINTN                       LoadOptionsSize,
1374   IN VOID*                       LoadOptions
1375   )
1376 {
1377   EFI_STATUS                   Status;
1378   EFI_HANDLE                   ImageHandle;
1379   EFI_PHYSICAL_ADDRESS         BinaryBuffer;
1380   UINTN                        BinarySize;
1381   EFI_LOADED_IMAGE_PROTOCOL*   LoadedImage;
1382 
1383   // Find the nearest supported file loader
1384   Status = BdsLoadImageAndUpdateDevicePath (&DevicePath, AllocateAnyPages, &BinaryBuffer, &BinarySize);
1385   if (EFI_ERROR (Status)) {
1386     return Status;
1387   }
1388 
1389   // Load the image from the Buffer with Boot Services function
1390   Status = gBS->LoadImage (TRUE, ParentImageHandle, DevicePath, (VOID*)(UINTN)BinaryBuffer, BinarySize, &ImageHandle);
1391   if (EFI_ERROR (Status)) {
1392     return Status;
1393   }
1394 
1395   // Passed LoadOptions to the EFI Application
1396   if (LoadOptionsSize != 0) {
1397     Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage);
1398     if (EFI_ERROR (Status)) {
1399       return Status;
1400     }
1401 
1402     LoadedImage->LoadOptionsSize  = LoadOptionsSize;
1403     LoadedImage->LoadOptions      = LoadOptions;
1404   }
1405 
1406   // Before calling the image, enable the Watchdog Timer for  the 5 Minute period
1407   gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
1408   // Start the image
1409   Status = gBS->StartImage (ImageHandle, NULL, NULL);
1410   // Clear the Watchdog Timer after the image returns
1411   gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
1412 
1413   return Status;
1414 }
1415