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